diff --git a/Changelog b/Changelog index 196f37644e1..be7588bbbf6 100644 --- a/Changelog +++ b/Changelog @@ -1,73 +1,123 @@ Entries are sorted chronologically from oldest to youngest within each release, releases are sorted from youngest to oldest. -version 4.2.1: -- avformat/vividas: check for tiny blocks using alignment -- avcodec/vc1_pred: Fix refdist in scaleforopp() -- avcodec/vorbisdec: fix FASTDIV usage for vr_type == 2 -- avcodec/iff: Check for overlap in cmap_read_palette() -- avcodec/apedec: Fix 32bit int overflow in do_apply_filter() -- lavf/rawenc: Only accept the appropriate stream type for raw muxers. -- avformat/matroskadec: use av_fast_realloc to reallocate ebml list arrays -- avformat/matroskadec: use proper types for some EbmlSyntax fields -- avcodec/ralf: fix undefined shift in extend_code() -- avcodec/ralf: fix undefined shift -- avcodec/bgmc: Check input space in ff_bgmc_decode_init() -- avcodec/vp3: Check for end of input in 2 places of vp4_unpack_macroblocks() -- avcodec/truemotion2: Fix multiple integer overflows in tm2_null_res_block() -- avcodec/vc1_block: Check the return code from vc1_decode_p_block() -- avcodec/vc1dec: Require res_sprite for wmv3images -- avcodec/vc1_block: Check for double escapes -- avcodec/vorbisdec: Check get_vlc2() failure -- avcodec/tta: Fix integer overflow in prediction -- avcodec/vb: Check input packet size to be large enough to contain flags -- avcodec/cavsdec: Limit the number of access units per packet to 2 -- avcodec/atrac9dec: Check block_align -- avcodec/alac: Check for bps of 0 -- avcodec/alac: Fix multiple integer overflows in lpc_prediction() -- avcodec/rl2: set dimensions -- avcodec/aacdec: Add FF_CODEC_CAP_INIT_CLEANUP -- avcodec/idcinvideo: Add 320x240 default maximum resolution -- avformat/realtextdec: free queue on error -- avcodec/vp5/6/8: use vpX_rac_is_end() -- avformat/vividas: Check av_xiphlacing() return value before use -- avcodec/alsdec: Fix integer overflow in decode_var_block_data() -- avcodec/alsdec: Limit maximum channels to 512 -- avcodec/anm: Check input size for a frame with just a stop code -- avcodec/flicvideo: Optimize and Simplify FLI_COPY in flic_decode_frame_24BPP() by using bytestream2_get_buffer() -- avcodec/loco: Check left column value -- avcodec/ffwavesynth: Fixes invalid shift with pink noise seeking -- avcodec/ffwavesynth: Fix integer overflow for some corner case values -- avcodec/indeo2: Check remaining input more often -- avcodec/diracdec: Check that slices are fewer than pixels -- avcodec/vp56: Consider the alpha start as end of the prior header -- avcodec/4xm: Check for end of input in decode_p_block() -- avcodec/hevcdec: Check delta_luma_weight_l0/1 -- avcodec/hnm4video: Optimize postprocess_current_frame() -- avcodec/hevc_refs: Optimize 16bit generate_missing_ref() -- avcodec/scpr: Use av_memcpy_backptr() in type 17 and 33 -- avcodec/tiff: Enforce increasing offsets -- avcodec/dds: Use ff_set_dimensions() -- avformat/vividas: Fix another infinite loop -- avformat/vividas: Fix infinite loop in header parser -- avcodec/mpc8: Fix 32bit mask/enum -- avcodec/alsdec: Fix integer overflows of raw_samples in decode_var_block_data() -- avcodec/alsdec: Fix integer overflow of raw_samples in decode_blocks() -- avcodec/alsdec: fix mantisse shift -- avcodec/pngdec: consider chunk size in minimal size check -- avcodec/vc1_block: Fix invalid shifts in vc1_decode_i_blocks() -- avcodec/vc1_block: fix invalid shift in vc1_decode_p_mb() -- avcodec/aacdec_template: fix integer overflow in imdct_and_windowing() -- avformat/mpegts: Check if ready on SCTE reception -- avcodec/omx: fix xFramerate calculation -- avformat/avidec: add support for recognizing HEVC fourcc when demuxing -- avformat/mpegts: fix teletext PTS when selecting teletext streams only -- avcodec/h2645_parse: zero initialize the rbsp buffer -- avcodec/omx: Fix handling of fragmented buffers -- avcodec/omx: ensure zerocopy mode can be disabled on rpi builds -- avformat/mxfdec: do not ignore bad size errors -- avformat/matroskadec: Fix seeking -- ffplay: properly detect all window size changes +version 4.3.1: + avcodec/tiff: Check input space in dng_decode_jpeg() + avcodec/mjpeg_parser: Adjust size rejection threshold + avcodec/cbs_jpeg: Fix uninitialized end index in cbs_jpeg_split_fragment() + avformat/sdp: Fix potential write beyond end of buffer + avformat/mm: Check for existence of audio stream + avformat/mov: Fix unaligned read of uint32_t and endian-dependance in mov_read_default + avcodec/apedec: Fix undefined integer overflow with 24bit + avcodec/loco: Fix integer overflow with large values from loco_get_rice() + avformat/smjpegdec: Check the existence of referred streams + avcodec/tiff: Check frame parameters before blit for DNG + avcodec/mjpegdec: Limit bayer to single plane outputting format + avcodec/pnmdec: Fix misaligned reads + avcodec/mv30: Fix integer overflows in idct2_1d() + avcodec/hcadec: Check total_band_count against imdct_in size + avcodec/scpr3: Fix out of array access with dectab + avcodec/tiff: Do not overrun the array ends in dng_blit() + avcodec/dstdec: Replace AC overread check by sample rate check + dnn_backend_native: Add overflow check for length calculation. + avcodec/h264_metadata_bsf: Fix invalid av_freep + avcodec/cbs_h265: set default VUI parameters when vui_parameters_present_flag is false + avcodec/av1_parser: initialize avctx->pix_fmt + avcodec/av1_parser: add missing parsing for RGB pixel format signaling + avcodec/av1_parser: set context values outside the OBU parsing loop + avutil/avsscanf: Add () to avoid integer overflow in scanexp() + avformat/utils: reorder duration computation to avoid overflow + avcodec/pngdec: Check for fctl after idat + avformat/hls: Pass a copy of the URL for probing + avutil/common: Fix integer overflow in av_ceil_log2_c() + avcodec/wmalosslessdec: fix overflow with pred in revert_cdlms + avformat/mvdec: Fix integer overflow with billions of channels + avformat/microdvddec: skip malformed lines without frame number. + dnn_backend_native: check operand index + dnn_backend_native.c: refine code for fail case + avformat/mov: fix memleaks + libavformat/mov: Fix memleaks when demuxing DV audio + avcodec/cbs_av1: Fix writing uvlc numbers >= INT_MAX + avformat/avc, mxfenc: Avoid allocation of H264 SPS structure, fix memleak + avcodec/bitstream: Don't check for undefined behaviour after it happened + avformat/aviobuf: Also return truncated buffer in avio_get_dyn_buf() + avformat/aviobuf: Don't check for overflow after it happened + +version 4.3: +- v360 filter +- Intel QSV-accelerated MJPEG decoding +- Intel QSV-accelerated VP9 decoding +- Support for TrueHD in mp4 +- Support AMD AMF encoder on Linux (via Vulkan) +- IMM5 video decoder +- ZeroMQ protocol +- support Sipro ACELP.KELVIN decoding +- streamhash muxer +- sierpinski video source +- scroll video filter +- photosensitivity filter +- anlms filter +- arnndn filter +- bilateral filter +- maskedmin and maskedmax filters +- VDPAU VP9 hwaccel +- median filter +- QSV-accelerated VP9 encoding +- AV1 encoding support via librav1e +- AV1 frame merge bitstream filter +- AV1 Annex B demuxer +- axcorrelate filter +- mvdv decoder +- mvha decoder +- MPEG-H 3D Audio support in mp4 +- thistogram filter +- freezeframes filter +- Argonaut Games ADPCM decoder +- Argonaut Games ASF demuxer +- xfade video filter +- xfade_opencl filter +- afirsrc audio filter source +- pad_opencl filter +- Simon & Schuster Interactive ADPCM decoder +- Real War KVAG demuxer +- CDToons video decoder +- siren audio decoder +- Rayman 2 ADPCM decoder +- Rayman 2 APM demuxer +- cas video filter +- High Voltage Software ADPCM decoder +- LEGO Racers ALP (.tun & .pcm) demuxer +- AMQP 0-9-1 protocol (RabbitMQ) +- Vulkan support +- avgblur_vulkan, overlay_vulkan, scale_vulkan and chromaber_vulkan filters +- ADPCM IMA MTF decoder +- FWSE demuxer +- DERF DPCM decoder +- DERF demuxer +- CRI HCA decoder +- CRI HCA demuxer +- overlay_cuda filter +- switch from AvxSynth to AviSynth+ on Linux +- mv30 decoder +- Expanded styling support for 3GPP Timed Text Subtitles (movtext) +- WebP parser +- tmedian filter +- maskedthreshold filter +- Support for muxing pcm and pgs in m2ts +- Cunning Developments ADPCM decoder +- asubboost filter +- Pro Pinball Series Soundbank demuxer +- pcm_rechunk bitstream filter +- scdet filter +- NotchLC decoder +- gradients source video filter +- MediaFoundation encoder wrapper +- untile filter +- Simon & Schuster Interactive ADPCM encoder +- PFM decoder +- dblur video filter +- Real War KVAG muxer + version 4.2: - tpad filter diff --git a/LICENSE.md b/LICENSE.md index c7d6bf7121d..613070e1b63 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -21,10 +21,11 @@ Specifically, the GPL parts of FFmpeg are: - `compat/solaris/make_sunver.pl` - `doc/t2h.pm` - `doc/texi2pod.pl` - - `libswresample/swresample-test.c` + - `libswresample/tests/swresample.c` - `tests/checkasm/*` - `tests/tiny_ssim.c` - the following filters in libavfilter: + - `signature_lookup.c` - `vf_blackframe.c` - `vf_boxblur.c` - `vf_colormatrix.c` @@ -34,13 +35,13 @@ Specifically, the GPL parts of FFmpeg are: - `vf_eq.c` - `vf_find_rect.c` - `vf_fspp.c` - - `vf_geq.c` - `vf_histeq.c` - `vf_hqdn3d.c` - - `vf_interlace.c` - `vf_kerndeint.c` + - `vf_lensfun.c` (GPL version 3 or later) - `vf_mcdeint.c` - `vf_mpdecimate.c` + - `vf_nnedi.c` - `vf_owdenoise.c` - `vf_perspective.c` - `vf_phase.c` @@ -49,12 +50,14 @@ Specifically, the GPL parts of FFmpeg are: - `vf_pullup.c` - `vf_repeatfields.c` - `vf_sab.c` + - `vf_signature.c` - `vf_smartblur.c` - `vf_spp.c` - `vf_stereo3d.c` - `vf_super2xsai.c` - `vf_tinterlace.c` - `vf_uspp.c` + - `vf_vaguedenoiser.c` - `vsrc_mptestsrc.c` Should you, for whatever reason, prefer to use version 3 of the (L)GPL, then @@ -80,24 +83,39 @@ affect the licensing of binaries resulting from the combination. ### Compatible libraries -The following libraries are under GPL: +The following libraries are under GPL version 2: +- avisynth - frei0r - libcdio +- libdavs2 - librubberband - libvidstab - libx264 - libx265 - libxavs +- libxavs2 - libxvid When combining them with FFmpeg, FFmpeg needs to be licensed as GPL as well by passing `--enable-gpl` to configure. -The OpenCORE and VisualOn libraries are under the Apache License 2.0. That -license is incompatible with the LGPL v2.1 and the GPL v2, but not with +The following libraries are under LGPL version 3: +- gmp +- libaribb24 +- liblensfun + +When combining them with FFmpeg, use the configure option `--enable-version3` to +upgrade FFmpeg to the LGPL v3. + +The VMAF, mbedTLS, RK MPI, OpenCORE and VisualOn libraries are under the Apache License +2.0. That license is incompatible with the LGPL v2.1 and the GPL v2, but not with version 3 of those licenses. So to combine these libraries with FFmpeg, the license version needs to be upgraded by passing `--enable-version3` to configure. +The smbclient library is under the GPL v3, to combine it with FFmpeg, +the options `--enable-gpl` and `--enable-version3` have to be passed to +configure to upgrade FFmpeg to the GPL v3. + ### Incompatible libraries There are certain libraries you can combine with FFmpeg whose licenses are not diff --git a/MAINTAINERS b/MAINTAINERS index 88b0109f228..af02cf00a99 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -53,8 +53,8 @@ Communication website Deby Barbara Lepage fate.ffmpeg.org Timothy Gu Trac bug tracker Alexander Strasser, Michael Niedermayer, Carl Eugen Hoyos +Patchwork Andriy Gelman mailing lists Baptiste Coudurier -Google+ Paul B Mahol, Michael Niedermayer, Alexander Strasser Twitter Lou Logan, Reynaldo H. Verdejo Pinochet Launchpad Timothy Gu ffmpeg-security Andreas Cadhalpun, Carl Eugen Hoyos, Clément Bœsch, Michael Niedermayer, Reimar Doeffinger, Rodger Combs, wm4 @@ -78,6 +78,7 @@ Other: float_dsp Loren Merritt hash Reimar Doeffinger hwcontext_cuda* Timo Rothenpieler + hwcontext_vulkan* Lynne intfloat* Michael Niedermayer integer.c, integer.h Michael Niedermayer lzo Reimar Doeffinger @@ -88,6 +89,7 @@ Other: rational.c, rational.h Michael Niedermayer rc4 Reimar Doeffinger ripemd.c, ripemd.h James Almer + tx* Lynne libavcodec @@ -192,12 +194,14 @@ Codecs: libdavs2.c Huiwen Ren libgsm.c Michel Bardiaux libkvazaar.c Arttu Ylä-Outinen + libopenh264enc.c Martin Storsjo, Linjie Fu libopenjpeg.c Jaikrishnan Menon libopenjpegenc.c Michael Bradshaw libtheoraenc.c David Conrad libvorbis.c David Conrad libvpx* James Zern libxavs.c Stefan Gehrer + libxavs2.c Huiwen Ren libzvbi-teletextdec.c Marton Balint lzo.h, lzo.c Reimar Doeffinger mdec.c Michael Niedermayer @@ -213,6 +217,7 @@ Codecs: 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 @@ -368,6 +373,8 @@ Filters: Sources: vsrc_mandelbrot.c Michael Niedermayer +dnn Yejun Guo + libavformat =========== @@ -429,9 +436,9 @@ Muxers/Demuxers: lmlm4.c Ivo van Poorten lvfdec.c Paul B Mahol lxfdec.c Tomas Härdin - matroska.c Aurelien Jacobs - matroskadec.c Aurelien Jacobs - matroskaenc.c David Conrad + 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 @@ -446,7 +453,7 @@ Muxers/Demuxers: mpegtsenc.c Baptiste Coudurier msnwc_tcp.c Ramiro Polla mtv.c Reynaldo H. Verdejo Pinochet - mxf* Baptiste Coudurier + mxf* Baptiste Coudurier, Tomas Härdin nistspheredec.c Paul B Mahol nsvdec.c Francois Revol nut* Michael Niedermayer @@ -454,7 +461,6 @@ Muxers/Demuxers: oggdec.c, oggdec.h David Conrad oggenc.c Baptiste Coudurier oggparse*.c David Conrad - oggparsedaala* Rostislav Pehlivanov oma.c Maxim Poliakovski paf.c Paul B Mahol psxstr.c Mike Melanson @@ -502,6 +508,7 @@ Protocols: ftp.c Lukasz Marek http.c Ronald S. Bultje libssh.c Lukasz Marek + libzmq.c Andriy Gelman mms*.c Ronald S. Bultje udp.c Luca Abeni icecast.c Marvin Scholz @@ -557,6 +564,7 @@ Joakim Plate Jun Zhao Kieran Kunhya Kirill Gavrilov +Limin Wang Martin Storsjö Panagiotis Issaris Pedro Arthur @@ -600,12 +608,14 @@ James Almer 7751 2E8C FD94 A169 57E6 9A7A 1463 01AD 7376 59E0 Jean Delvare 7CA6 9F44 60F1 BDC4 1FD2 C858 A552 6B9B B3CD 4E6A Loren Merritt ABD9 08F4 C920 3F65 D8BE 35D7 1540 DAA7 060F 56DE Lou Logan (llogan) 7D68 DC73 CBEF EABB 671A B6CF 621C 2E28 82F8 DC3A +Lynne FE50 139C 6805 72CA FD52 1F8D A2FE A5F0 3F03 4464 Michael Niedermayer 9FF2 128B 147E F673 0BAD F133 611E C787 040B 0FAB Nicolas George 24CE 01CE 9ACC 5CEB 74D8 8D9D B063 D997 36E5 4C93 Nikolay Aleksandrov 8978 1D8C FB71 588E 4B27 EAA8 C4F0 B5FC E011 13B1 Panagiotis Issaris 6571 13A3 33D9 3726 F728 AA98 F643 B12E ECF3 E029 Peter Ross A907 E02F A6E5 0CD2 34CD 20D2 6760 79C5 AC40 DD6B Philip Langdale 5DC5 8D66 5FBA 3A43 18EC 045E F8D6 B194 6A75 682E +Ramiro Polla 7859 C65B 751B 1179 792E DAE8 8E95 8B2F 9B6C 5700 Reimar Doeffinger C61D 16E5 9E2C D10C 8958 38A4 0899 A2B9 06D4 D9C7 Reinhard Tartler 9300 5DC2 7E87 6C37 ED7B CA9A 9808 3544 9453 48A4 Reynaldo H. Verdejo Pinochet 6E27 CD34 170C C78E 4D4F 5F40 C18E 077F 3114 452A @@ -614,6 +624,7 @@ Sascha Sommer 38A0 F88B 868E 9D3A 97D4 D6A0 E823 706F 1E07 0D3C Stefano Sabatini 0D0B AD6B 5330 BBAD D3D6 6A0C 719C 2839 FC43 2D5F Steinar H. Gunderson C2E9 004F F028 C18E 4EAD DB83 7F61 7561 7797 8F76 Stephan Hilb 4F38 0B3A 5F39 B99B F505 E562 8D5C 5554 4E17 8863 +Thilo Borgmann (thilo) CE1D B7F4 4D20 FC3A DD9F FE5A 257C 5B8F 1D20 B92F Tiancheng "Timothy" Gu 9456 AFC0 814A 8139 E994 8351 7FE6 B095 B582 B0D4 Tim Nicholson 38CF DB09 3ED0 F607 8B67 6CED 0C0B FC44 8B0B FC83 Tomas Härdin (thardin) A79D 4E3D F38F 763F 91F5 8B33 A01E 8AE0 41BB 2551 diff --git a/Makefile b/Makefile index 532372c9c46..45a22b0cb3c 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,9 @@ $(TOOLS): %$(EXESUF): %.o target_dec_%_fuzzer$(EXESUF): target_dec_%_fuzzer.o $(FF_DEP_LIBS) $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH) +tools/target_bsf_%_fuzzer$(EXESUF): tools/target_bsf_%_fuzzer.o $(FF_DEP_LIBS) + $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH) + tools/target_dem_fuzzer$(EXESUF): tools/target_dem_fuzzer.o $(FF_DEP_LIBS) $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH) @@ -148,6 +151,7 @@ distclean:: clean version.h libavutil/ffversion.h libavcodec/codec_names.h \ libavcodec/bsf_list.c libavformat/protocol_list.c \ libavcodec/codec_list.c libavcodec/parser_list.c \ + libavfilter/filter_list.c libavdevice/indev_list.c libavdevice/outdev_list.c \ libavformat/muxer_list.c libavformat/demuxer_list.c ifeq ($(SRC_LINK),src) $(RM) src diff --git a/RELEASE b/RELEASE index fae6e3d04b2..f77856a6f1a 100644 --- a/RELEASE +++ b/RELEASE @@ -1 +1 @@ -4.2.1 +4.3.1 diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 324dcc37957..2511706d5d0 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,10 +1,10 @@ ┌────────────────────────────────────┐ - │ RELEASE NOTES for FFmpeg 4.2 "Ada" │ + │ RELEASE NOTES for FFmpeg 4.3 "4:3" │ └────────────────────────────────────┘ - The FFmpeg Project proudly presents FFmpeg 4.2 "Ada", about 8 - months after the release of FFmpeg 4.1. + The FFmpeg Project proudly presents FFmpeg 4.3 "4:3", about 10 + months after the release of FFmpeg 4.2. 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 fae6e3d04b2..f77856a6f1a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.2.1 +4.3.1 diff --git a/build.yaml b/build.yaml index 4f324461d08..d39ec25f3fa 100644 --- a/build.yaml +++ b/build.yaml @@ -1,7 +1,7 @@ --- # We just wrap `build` so this is really it name: "jellyfin-ffmpeg" -version: "4.2.1-7" +version: "4.3.1-1" packages: - stretch-amd64 - stretch-armhf diff --git a/compat/avisynth/avisynth_c.h b/compat/avisynth/avisynth_c.h deleted file mode 100644 index 8d17125adca..00000000000 --- a/compat/avisynth/avisynth_c.h +++ /dev/null @@ -1,1264 +0,0 @@ -// Avisynth C Interface Version 0.20 -// Copyright 2003 Kevin Atkinson - -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -// MA 02110-1301 USA, or visit -// http://www.gnu.org/copyleft/gpl.html . -// -// As a special exception, I give you permission to link to the -// Avisynth C interface with independent modules that communicate with -// the Avisynth C interface solely through the interfaces defined in -// avisynth_c.h, regardless of the license terms of these independent -// modules, and to copy and distribute the resulting combined work -// under terms of your choice, provided that every copy of the -// combined work is accompanied by a complete copy of the source code -// of the Avisynth C interface and Avisynth itself (with the version -// used to produce the combined work), being distributed under the -// terms of the GNU General Public License plus this exception. An -// independent module is a module which is not derived from or based -// on Avisynth C Interface, such as 3rd-party filters, import and -// export plugins, or graphical user interfaces. - -// NOTE: this is a partial update of the Avisynth C interface to recognize -// new color spaces added in Avisynth 2.60. By no means is this document -// completely Avisynth 2.60 compliant. -// 170103: added new CPU constants (FMA4, AVX512xx) -// 171102: define SIZETMOD. do not use yet, experimental. Offsets are size_t instead of int. Affects x64. -// 171106: avs_get_row_size calls into avs_get_row_size_p, instead of direct field access -// 171106: avs_get_height calls into avs_get_row_size_p, instead of direct field access -// 180524: AVSC_EXPORT to dllexport in capi.h for avisynth_c_plugin_init -// 180524: avs_is_same_colorspace VideoInfo parameters to const -// 181230: Readability: functions regrouped to mix less AVSC_API and AVSC_INLINE, put together Avisynth+ specific stuff -// 181230: use #ifndef AVSC_NO_DECLSPEC for AVSC_INLINE functions which are calling API functions -// 181230: comments on avs_load_library (helper for loading API entries dynamically into a struct using AVSC_NO_DECLSPEC define) -// 181230: define alias AVS_FRAME_ALIGN as FRAME_ALIGN -// 181230: remove unused form of avs_get_rowsize and avs_get_height (kept earlier for reference) -// 190104: avs_load_library: smart fallback mechanism for Avisynth+ specific functions: -// if they are not loadable, they will work in a classic Avisynth compatible mode -// Example#1: e.g. avs_is_444 will call the existing avs_is_yv24 instead -// Example#2: avs_bits_per_component will return 8 for all colorspaces (Classic Avisynth supports only 8 bits/pixel) -// Thus the Avisynth+ specific API functions are safely callable even when connected to classic Avisynth DLL - -#ifndef __AVISYNTH_C__ -#define __AVISYNTH_C__ - -#include "avs/config.h" -#include "avs/capi.h" -#include "avs/types.h" - -#define AVS_FRAME_ALIGN FRAME_ALIGN -///////////////////////////////////////////////////////////////////// -// -// Constants -// - -#ifndef __AVISYNTH_6_H__ -enum { AVISYNTH_INTERFACE_VERSION = 6 }; -#endif - -enum {AVS_SAMPLE_INT8 = 1<<0, - AVS_SAMPLE_INT16 = 1<<1, - AVS_SAMPLE_INT24 = 1<<2, - AVS_SAMPLE_INT32 = 1<<3, - AVS_SAMPLE_FLOAT = 1<<4}; - -enum {AVS_PLANAR_Y=1<<0, - AVS_PLANAR_U=1<<1, - AVS_PLANAR_V=1<<2, - AVS_PLANAR_ALIGNED=1<<3, - AVS_PLANAR_Y_ALIGNED=AVS_PLANAR_Y|AVS_PLANAR_ALIGNED, - AVS_PLANAR_U_ALIGNED=AVS_PLANAR_U|AVS_PLANAR_ALIGNED, - AVS_PLANAR_V_ALIGNED=AVS_PLANAR_V|AVS_PLANAR_ALIGNED, - AVS_PLANAR_A=1<<4, - AVS_PLANAR_R=1<<5, - AVS_PLANAR_G=1<<6, - AVS_PLANAR_B=1<<7, - AVS_PLANAR_A_ALIGNED=AVS_PLANAR_A|AVS_PLANAR_ALIGNED, - AVS_PLANAR_R_ALIGNED=AVS_PLANAR_R|AVS_PLANAR_ALIGNED, - AVS_PLANAR_G_ALIGNED=AVS_PLANAR_G|AVS_PLANAR_ALIGNED, - AVS_PLANAR_B_ALIGNED=AVS_PLANAR_B|AVS_PLANAR_ALIGNED}; - - // Colorspace properties. -enum { - AVS_CS_YUVA = 1 << 27, - AVS_CS_BGR = 1 << 28, - AVS_CS_YUV = 1 << 29, - AVS_CS_INTERLEAVED = 1 << 30, - AVS_CS_PLANAR = 1 << 31, - - AVS_CS_SHIFT_SUB_WIDTH = 0, - AVS_CS_SHIFT_SUB_HEIGHT = 8, - AVS_CS_SHIFT_SAMPLE_BITS = 16, - - AVS_CS_SUB_WIDTH_MASK = 7 << AVS_CS_SHIFT_SUB_WIDTH, - AVS_CS_SUB_WIDTH_1 = 3 << AVS_CS_SHIFT_SUB_WIDTH, // YV24 - AVS_CS_SUB_WIDTH_2 = 0 << AVS_CS_SHIFT_SUB_WIDTH, // YV12, I420, YV16 - AVS_CS_SUB_WIDTH_4 = 1 << AVS_CS_SHIFT_SUB_WIDTH, // YUV9, YV411 - - AVS_CS_VPLANEFIRST = 1 << 3, // YV12, YV16, YV24, YV411, YUV9 - AVS_CS_UPLANEFIRST = 1 << 4, // I420 - - AVS_CS_SUB_HEIGHT_MASK = 7 << AVS_CS_SHIFT_SUB_HEIGHT, - AVS_CS_SUB_HEIGHT_1 = 3 << AVS_CS_SHIFT_SUB_HEIGHT, // YV16, YV24, YV411 - AVS_CS_SUB_HEIGHT_2 = 0 << AVS_CS_SHIFT_SUB_HEIGHT, // YV12, I420 - AVS_CS_SUB_HEIGHT_4 = 1 << AVS_CS_SHIFT_SUB_HEIGHT, // YUV9 - - AVS_CS_SAMPLE_BITS_MASK = 7 << AVS_CS_SHIFT_SAMPLE_BITS, - AVS_CS_SAMPLE_BITS_8 = 0 << AVS_CS_SHIFT_SAMPLE_BITS, - AVS_CS_SAMPLE_BITS_10 = 5 << AVS_CS_SHIFT_SAMPLE_BITS, - AVS_CS_SAMPLE_BITS_12 = 6 << AVS_CS_SHIFT_SAMPLE_BITS, - AVS_CS_SAMPLE_BITS_14 = 7 << AVS_CS_SHIFT_SAMPLE_BITS, - AVS_CS_SAMPLE_BITS_16 = 1 << AVS_CS_SHIFT_SAMPLE_BITS, - AVS_CS_SAMPLE_BITS_32 = 2 << AVS_CS_SHIFT_SAMPLE_BITS, - - AVS_CS_PLANAR_MASK = AVS_CS_PLANAR | AVS_CS_INTERLEAVED | AVS_CS_YUV | AVS_CS_BGR | AVS_CS_YUVA | AVS_CS_SAMPLE_BITS_MASK | AVS_CS_SUB_HEIGHT_MASK | AVS_CS_SUB_WIDTH_MASK, - AVS_CS_PLANAR_FILTER = ~(AVS_CS_VPLANEFIRST | AVS_CS_UPLANEFIRST), - - AVS_CS_RGB_TYPE = 1 << 0, - AVS_CS_RGBA_TYPE = 1 << 1, - - AVS_CS_GENERIC_YUV420 = AVS_CS_PLANAR | AVS_CS_YUV | AVS_CS_VPLANEFIRST | AVS_CS_SUB_HEIGHT_2 | AVS_CS_SUB_WIDTH_2, // 4:2:0 planar - AVS_CS_GENERIC_YUV422 = AVS_CS_PLANAR | AVS_CS_YUV | AVS_CS_VPLANEFIRST | AVS_CS_SUB_HEIGHT_1 | AVS_CS_SUB_WIDTH_2, // 4:2:2 planar - AVS_CS_GENERIC_YUV444 = AVS_CS_PLANAR | AVS_CS_YUV | AVS_CS_VPLANEFIRST | AVS_CS_SUB_HEIGHT_1 | AVS_CS_SUB_WIDTH_1, // 4:4:4 planar - AVS_CS_GENERIC_Y = AVS_CS_PLANAR | AVS_CS_INTERLEAVED | AVS_CS_YUV, // Y only (4:0:0) - AVS_CS_GENERIC_RGBP = AVS_CS_PLANAR | AVS_CS_BGR | AVS_CS_RGB_TYPE, // planar RGB - AVS_CS_GENERIC_RGBAP = AVS_CS_PLANAR | AVS_CS_BGR | AVS_CS_RGBA_TYPE, // planar RGBA - AVS_CS_GENERIC_YUVA420 = AVS_CS_PLANAR | AVS_CS_YUVA | AVS_CS_VPLANEFIRST | AVS_CS_SUB_HEIGHT_2 | AVS_CS_SUB_WIDTH_2, // 4:2:0:A planar - AVS_CS_GENERIC_YUVA422 = AVS_CS_PLANAR | AVS_CS_YUVA | AVS_CS_VPLANEFIRST | AVS_CS_SUB_HEIGHT_1 | AVS_CS_SUB_WIDTH_2, // 4:2:2:A planar - AVS_CS_GENERIC_YUVA444 = AVS_CS_PLANAR | AVS_CS_YUVA | AVS_CS_VPLANEFIRST | AVS_CS_SUB_HEIGHT_1 | AVS_CS_SUB_WIDTH_1 }; // 4:4:4:A planar - - - // Specific color formats -enum { - AVS_CS_UNKNOWN = 0, - AVS_CS_BGR24 = AVS_CS_RGB_TYPE | AVS_CS_BGR | AVS_CS_INTERLEAVED, - AVS_CS_BGR32 = AVS_CS_RGBA_TYPE | AVS_CS_BGR | AVS_CS_INTERLEAVED, - AVS_CS_YUY2 = 1<<2 | AVS_CS_YUV | AVS_CS_INTERLEAVED, - // AVS_CS_YV12 = 1<<3 Reserved - // AVS_CS_I420 = 1<<4 Reserved - AVS_CS_RAW32 = 1<<5 | AVS_CS_INTERLEAVED, - - AVS_CS_YV24 = AVS_CS_GENERIC_YUV444 | AVS_CS_SAMPLE_BITS_8, // YUV 4:4:4 planar - AVS_CS_YV16 = AVS_CS_GENERIC_YUV422 | AVS_CS_SAMPLE_BITS_8, // YUV 4:2:2 planar - AVS_CS_YV12 = AVS_CS_GENERIC_YUV420 | AVS_CS_SAMPLE_BITS_8, // YUV 4:2:0 planar - AVS_CS_I420 = AVS_CS_PLANAR | AVS_CS_YUV | AVS_CS_SAMPLE_BITS_8 | AVS_CS_UPLANEFIRST | AVS_CS_SUB_HEIGHT_2 | AVS_CS_SUB_WIDTH_2, // YUV 4:2:0 planar - AVS_CS_IYUV = AVS_CS_I420, - AVS_CS_YV411 = AVS_CS_PLANAR | AVS_CS_YUV | AVS_CS_SAMPLE_BITS_8 | AVS_CS_VPLANEFIRST | AVS_CS_SUB_HEIGHT_1 | AVS_CS_SUB_WIDTH_4, // YUV 4:1:1 planar - AVS_CS_YUV9 = AVS_CS_PLANAR | AVS_CS_YUV | AVS_CS_SAMPLE_BITS_8 | AVS_CS_VPLANEFIRST | AVS_CS_SUB_HEIGHT_4 | AVS_CS_SUB_WIDTH_4, // YUV 4:1:0 planar - AVS_CS_Y8 = AVS_CS_GENERIC_Y | AVS_CS_SAMPLE_BITS_8, // Y 4:0:0 planar - - //------------------------- - // AVS16: new planar constants go live! Experimental PF 160613 - // 10-12-14-16 bit + planar RGB + BGR48/64 160725 - AVS_CS_YUV444P10 = AVS_CS_GENERIC_YUV444 | AVS_CS_SAMPLE_BITS_10, // YUV 4:4:4 10bit samples - AVS_CS_YUV422P10 = AVS_CS_GENERIC_YUV422 | AVS_CS_SAMPLE_BITS_10, // YUV 4:2:2 10bit samples - AVS_CS_YUV420P10 = AVS_CS_GENERIC_YUV420 | AVS_CS_SAMPLE_BITS_10, // YUV 4:2:0 10bit samples - AVS_CS_Y10 = AVS_CS_GENERIC_Y | AVS_CS_SAMPLE_BITS_10, // Y 4:0:0 10bit samples - - AVS_CS_YUV444P12 = AVS_CS_GENERIC_YUV444 | AVS_CS_SAMPLE_BITS_12, // YUV 4:4:4 12bit samples - AVS_CS_YUV422P12 = AVS_CS_GENERIC_YUV422 | AVS_CS_SAMPLE_BITS_12, // YUV 4:2:2 12bit samples - AVS_CS_YUV420P12 = AVS_CS_GENERIC_YUV420 | AVS_CS_SAMPLE_BITS_12, // YUV 4:2:0 12bit samples - AVS_CS_Y12 = AVS_CS_GENERIC_Y | AVS_CS_SAMPLE_BITS_12, // Y 4:0:0 12bit samples - - AVS_CS_YUV444P14 = AVS_CS_GENERIC_YUV444 | AVS_CS_SAMPLE_BITS_14, // YUV 4:4:4 14bit samples - AVS_CS_YUV422P14 = AVS_CS_GENERIC_YUV422 | AVS_CS_SAMPLE_BITS_14, // YUV 4:2:2 14bit samples - AVS_CS_YUV420P14 = AVS_CS_GENERIC_YUV420 | AVS_CS_SAMPLE_BITS_14, // YUV 4:2:0 14bit samples - AVS_CS_Y14 = AVS_CS_GENERIC_Y | AVS_CS_SAMPLE_BITS_14, // Y 4:0:0 14bit samples - - AVS_CS_YUV444P16 = AVS_CS_GENERIC_YUV444 | AVS_CS_SAMPLE_BITS_16, // YUV 4:4:4 16bit samples - AVS_CS_YUV422P16 = AVS_CS_GENERIC_YUV422 | AVS_CS_SAMPLE_BITS_16, // YUV 4:2:2 16bit samples - AVS_CS_YUV420P16 = AVS_CS_GENERIC_YUV420 | AVS_CS_SAMPLE_BITS_16, // YUV 4:2:0 16bit samples - AVS_CS_Y16 = AVS_CS_GENERIC_Y | AVS_CS_SAMPLE_BITS_16, // Y 4:0:0 16bit samples - - // 32 bit samples (float) - AVS_CS_YUV444PS = AVS_CS_GENERIC_YUV444 | AVS_CS_SAMPLE_BITS_32, // YUV 4:4:4 32bit samples - AVS_CS_YUV422PS = AVS_CS_GENERIC_YUV422 | AVS_CS_SAMPLE_BITS_32, // YUV 4:2:2 32bit samples - AVS_CS_YUV420PS = AVS_CS_GENERIC_YUV420 | AVS_CS_SAMPLE_BITS_32, // YUV 4:2:0 32bit samples - AVS_CS_Y32 = AVS_CS_GENERIC_Y | AVS_CS_SAMPLE_BITS_32, // Y 4:0:0 32bit samples - - // RGB packed - AVS_CS_BGR48 = AVS_CS_RGB_TYPE | AVS_CS_BGR | AVS_CS_INTERLEAVED | AVS_CS_SAMPLE_BITS_16, // BGR 3x16 bit - AVS_CS_BGR64 = AVS_CS_RGBA_TYPE | AVS_CS_BGR | AVS_CS_INTERLEAVED | AVS_CS_SAMPLE_BITS_16, // BGR 4x16 bit - // no packed 32 bit (float) support for these legacy types - - // RGB planar - AVS_CS_RGBP = AVS_CS_GENERIC_RGBP | AVS_CS_SAMPLE_BITS_8, // Planar RGB 8 bit samples - AVS_CS_RGBP10 = AVS_CS_GENERIC_RGBP | AVS_CS_SAMPLE_BITS_10, // Planar RGB 10bit samples - AVS_CS_RGBP12 = AVS_CS_GENERIC_RGBP | AVS_CS_SAMPLE_BITS_12, // Planar RGB 12bit samples - AVS_CS_RGBP14 = AVS_CS_GENERIC_RGBP | AVS_CS_SAMPLE_BITS_14, // Planar RGB 14bit samples - AVS_CS_RGBP16 = AVS_CS_GENERIC_RGBP | AVS_CS_SAMPLE_BITS_16, // Planar RGB 16bit samples - AVS_CS_RGBPS = AVS_CS_GENERIC_RGBP | AVS_CS_SAMPLE_BITS_32, // Planar RGB 32bit samples - - // RGBA planar - AVS_CS_RGBAP = AVS_CS_GENERIC_RGBAP | AVS_CS_SAMPLE_BITS_8, // Planar RGBA 8 bit samples - AVS_CS_RGBAP10 = AVS_CS_GENERIC_RGBAP | AVS_CS_SAMPLE_BITS_10, // Planar RGBA 10bit samples - AVS_CS_RGBAP12 = AVS_CS_GENERIC_RGBAP | AVS_CS_SAMPLE_BITS_12, // Planar RGBA 12bit samples - AVS_CS_RGBAP14 = AVS_CS_GENERIC_RGBAP | AVS_CS_SAMPLE_BITS_14, // Planar RGBA 14bit samples - AVS_CS_RGBAP16 = AVS_CS_GENERIC_RGBAP | AVS_CS_SAMPLE_BITS_16, // Planar RGBA 16bit samples - AVS_CS_RGBAPS = AVS_CS_GENERIC_RGBAP | AVS_CS_SAMPLE_BITS_32, // Planar RGBA 32bit samples - - // Planar YUVA - AVS_CS_YUVA444 = AVS_CS_GENERIC_YUVA444 | AVS_CS_SAMPLE_BITS_8, // YUVA 4:4:4 8bit samples - AVS_CS_YUVA422 = AVS_CS_GENERIC_YUVA422 | AVS_CS_SAMPLE_BITS_8, // YUVA 4:2:2 8bit samples - AVS_CS_YUVA420 = AVS_CS_GENERIC_YUVA420 | AVS_CS_SAMPLE_BITS_8, // YUVA 4:2:0 8bit samples - - AVS_CS_YUVA444P10 = AVS_CS_GENERIC_YUVA444 | AVS_CS_SAMPLE_BITS_10, // YUVA 4:4:4 10bit samples - AVS_CS_YUVA422P10 = AVS_CS_GENERIC_YUVA422 | AVS_CS_SAMPLE_BITS_10, // YUVA 4:2:2 10bit samples - AVS_CS_YUVA420P10 = AVS_CS_GENERIC_YUVA420 | AVS_CS_SAMPLE_BITS_10, // YUVA 4:2:0 10bit samples - - AVS_CS_YUVA444P12 = AVS_CS_GENERIC_YUVA444 | AVS_CS_SAMPLE_BITS_12, // YUVA 4:4:4 12bit samples - AVS_CS_YUVA422P12 = AVS_CS_GENERIC_YUVA422 | AVS_CS_SAMPLE_BITS_12, // YUVA 4:2:2 12bit samples - AVS_CS_YUVA420P12 = AVS_CS_GENERIC_YUVA420 | AVS_CS_SAMPLE_BITS_12, // YUVA 4:2:0 12bit samples - - AVS_CS_YUVA444P14 = AVS_CS_GENERIC_YUVA444 | AVS_CS_SAMPLE_BITS_14, // YUVA 4:4:4 14bit samples - AVS_CS_YUVA422P14 = AVS_CS_GENERIC_YUVA422 | AVS_CS_SAMPLE_BITS_14, // YUVA 4:2:2 14bit samples - AVS_CS_YUVA420P14 = AVS_CS_GENERIC_YUVA420 | AVS_CS_SAMPLE_BITS_14, // YUVA 4:2:0 14bit samples - - AVS_CS_YUVA444P16 = AVS_CS_GENERIC_YUVA444 | AVS_CS_SAMPLE_BITS_16, // YUVA 4:4:4 16bit samples - AVS_CS_YUVA422P16 = AVS_CS_GENERIC_YUVA422 | AVS_CS_SAMPLE_BITS_16, // YUVA 4:2:2 16bit samples - AVS_CS_YUVA420P16 = AVS_CS_GENERIC_YUVA420 | AVS_CS_SAMPLE_BITS_16, // YUVA 4:2:0 16bit samples - - AVS_CS_YUVA444PS = AVS_CS_GENERIC_YUVA444 | AVS_CS_SAMPLE_BITS_32, // YUVA 4:4:4 32bit samples - AVS_CS_YUVA422PS = AVS_CS_GENERIC_YUVA422 | AVS_CS_SAMPLE_BITS_32, // YUVA 4:2:2 32bit samples - AVS_CS_YUVA420PS = AVS_CS_GENERIC_YUVA420 | AVS_CS_SAMPLE_BITS_32, // YUVA 4:2:0 32bit samples - -}; - -enum { - AVS_IT_BFF = 1<<0, - AVS_IT_TFF = 1<<1, - AVS_IT_FIELDBASED = 1<<2}; - -enum { - AVS_FILTER_TYPE=1, - AVS_FILTER_INPUT_COLORSPACE=2, - AVS_FILTER_OUTPUT_TYPE=9, - AVS_FILTER_NAME=4, - AVS_FILTER_AUTHOR=5, - AVS_FILTER_VERSION=6, - AVS_FILTER_ARGS=7, - AVS_FILTER_ARGS_INFO=8, - AVS_FILTER_ARGS_DESCRIPTION=10, - AVS_FILTER_DESCRIPTION=11}; - -enum { //SUBTYPES - AVS_FILTER_TYPE_AUDIO=1, - AVS_FILTER_TYPE_VIDEO=2, - AVS_FILTER_OUTPUT_TYPE_SAME=3, - AVS_FILTER_OUTPUT_TYPE_DIFFERENT=4}; - -enum { - // New 2.6 explicitly defined cache hints. - AVS_CACHE_NOTHING=10, // Do not cache video. - AVS_CACHE_WINDOW=11, // Hard protect up to X frames within a range of X from the current frame N. - AVS_CACHE_GENERIC=12, // LRU cache up to X frames. - AVS_CACHE_FORCE_GENERIC=13, // LRU cache up to X frames, override any previous CACHE_WINDOW. - - AVS_CACHE_GET_POLICY=30, // Get the current policy. - AVS_CACHE_GET_WINDOW=31, // Get the current window h_span. - AVS_CACHE_GET_RANGE=32, // Get the current generic frame range. - - AVS_CACHE_AUDIO=50, // Explicitly do cache audio, X byte cache. - AVS_CACHE_AUDIO_NOTHING=51, // Explicitly do not cache audio. - AVS_CACHE_AUDIO_NONE=52, // Audio cache off (auto mode), X byte initial cache. - AVS_CACHE_AUDIO_AUTO=53, // Audio cache on (auto mode), X byte initial cache. - - AVS_CACHE_GET_AUDIO_POLICY=70, // Get the current audio policy. - AVS_CACHE_GET_AUDIO_SIZE=71, // Get the current audio cache size. - - AVS_CACHE_PREFETCH_FRAME=100, // Queue request to prefetch frame N. - AVS_CACHE_PREFETCH_GO=101, // Action video prefetches. - - AVS_CACHE_PREFETCH_AUDIO_BEGIN=120, // Begin queue request transaction to prefetch audio (take critical section). - AVS_CACHE_PREFETCH_AUDIO_STARTLO=121, // Set low 32 bits of start. - AVS_CACHE_PREFETCH_AUDIO_STARTHI=122, // Set high 32 bits of start. - AVS_CACHE_PREFETCH_AUDIO_COUNT=123, // Set low 32 bits of length. - AVS_CACHE_PREFETCH_AUDIO_COMMIT=124, // Enqueue request transaction to prefetch audio (release critical section). - AVS_CACHE_PREFETCH_AUDIO_GO=125, // Action audio prefetches. - - AVS_CACHE_GETCHILD_CACHE_MODE=200, // Cache ask Child for desired video cache mode. - AVS_CACHE_GETCHILD_CACHE_SIZE=201, // Cache ask Child for desired video cache size. - AVS_CACHE_GETCHILD_AUDIO_MODE=202, // Cache ask Child for desired audio cache mode. - AVS_CACHE_GETCHILD_AUDIO_SIZE=203, // Cache ask Child for desired audio cache size. - - AVS_CACHE_GETCHILD_COST=220, // Cache ask Child for estimated processing cost. - AVS_CACHE_COST_ZERO=221, // Child response of zero cost (ptr arithmetic only). - AVS_CACHE_COST_UNIT=222, // Child response of unit cost (less than or equal 1 full frame blit). - AVS_CACHE_COST_LOW=223, // Child response of light cost. (Fast) - AVS_CACHE_COST_MED=224, // Child response of medium cost. (Real time) - AVS_CACHE_COST_HI=225, // Child response of heavy cost. (Slow) - - AVS_CACHE_GETCHILD_THREAD_MODE=240, // Cache ask Child for thread safety. - AVS_CACHE_THREAD_UNSAFE=241, // Only 1 thread allowed for all instances. 2.5 filters default! - AVS_CACHE_THREAD_CLASS=242, // Only 1 thread allowed for each instance. 2.6 filters default! - AVS_CACHE_THREAD_SAFE=243, // Allow all threads in any instance. - AVS_CACHE_THREAD_OWN=244, // Safe but limit to 1 thread, internally threaded. - - AVS_CACHE_GETCHILD_ACCESS_COST=260, // Cache ask Child for preferred access pattern. - AVS_CACHE_ACCESS_RAND=261, // Filter is access order agnostic. - AVS_CACHE_ACCESS_SEQ0=262, // Filter prefers sequential access (low cost) - AVS_CACHE_ACCESS_SEQ1=263, // Filter needs sequential access (high cost) - }; - -#ifdef BUILDING_AVSCORE -AVSValue create_c_video_filter(AVSValue args, void * user_data, IScriptEnvironment * e0); - -struct AVS_ScriptEnvironment { - IScriptEnvironment * env; - const char * error; - AVS_ScriptEnvironment(IScriptEnvironment * e = 0) - : env(e), error(0) {} -}; -#endif - -typedef struct AVS_Clip AVS_Clip; -typedef struct AVS_ScriptEnvironment AVS_ScriptEnvironment; - -///////////////////////////////////////////////////////////////////// -// -// AVS_VideoInfo -// - -// AVS_VideoInfo is laid out identically to VideoInfo -typedef struct AVS_VideoInfo { - int width, height; // width=0 means no video - unsigned fps_numerator, fps_denominator; - int num_frames; - - int pixel_type; - - int audio_samples_per_second; // 0 means no audio - int sample_type; - INT64 num_audio_samples; - int nchannels; - - // Image type properties - - int image_type; -} AVS_VideoInfo; - -// useful functions of the above -AVSC_INLINE int avs_has_video(const AVS_VideoInfo * p) - { return (p->width!=0); } - -AVSC_INLINE int avs_has_audio(const AVS_VideoInfo * p) - { return (p->audio_samples_per_second!=0); } - -AVSC_INLINE int avs_is_rgb(const AVS_VideoInfo * p) - { return !!(p->pixel_type&AVS_CS_BGR); } - -AVSC_INLINE int avs_is_rgb24(const AVS_VideoInfo * p) - { return ((p->pixel_type&AVS_CS_BGR24)==AVS_CS_BGR24) && ((p->pixel_type & AVS_CS_SAMPLE_BITS_MASK) == AVS_CS_SAMPLE_BITS_8); } - -AVSC_INLINE int avs_is_rgb32(const AVS_VideoInfo * p) - { return ((p->pixel_type&AVS_CS_BGR32)==AVS_CS_BGR32) && ((p->pixel_type & AVS_CS_SAMPLE_BITS_MASK) == AVS_CS_SAMPLE_BITS_8); } - -AVSC_INLINE int avs_is_yuv(const AVS_VideoInfo * p) - { return !!(p->pixel_type&AVS_CS_YUV ); } - -AVSC_INLINE int avs_is_yuy2(const AVS_VideoInfo * p) - { return (p->pixel_type & AVS_CS_YUY2) == AVS_CS_YUY2; } - -AVSC_API(int, avs_is_yv24)(const AVS_VideoInfo * p); // avs+: for generic 444 check, use avs_is_yuv444 - -AVSC_API(int, avs_is_yv16)(const AVS_VideoInfo * p); // avs+: for generic 422 check, use avs_is_yuv422 - -AVSC_API(int, avs_is_yv12)(const AVS_VideoInfo * p) ; // avs+: for generic 420 check, use avs_is_yuv420 - -AVSC_API(int, avs_is_yv411)(const AVS_VideoInfo * p); - -AVSC_API(int, avs_is_y8)(const AVS_VideoInfo * p); // avs+: for generic grayscale, use avs_is_y - -AVSC_API(int, avs_get_plane_width_subsampling)(const AVS_VideoInfo * p, int plane); - -AVSC_API(int, avs_get_plane_height_subsampling)(const AVS_VideoInfo * p, int plane); - -AVSC_API(int, avs_bits_per_pixel)(const AVS_VideoInfo * p); - -AVSC_API(int, avs_bytes_from_pixels)(const AVS_VideoInfo * p, int pixels); - -AVSC_API(int, avs_row_size)(const AVS_VideoInfo * p, int plane); - -AVSC_API(int, avs_bmp_size)(const AVS_VideoInfo * vi); - -AVSC_API(int, avs_is_color_space)(const AVS_VideoInfo * p, int c_space); - -// no API for these, inline helper functions -AVSC_INLINE int avs_is_property(const AVS_VideoInfo * p, int property) -{ - return ((p->image_type & property) == property); -} - -AVSC_INLINE int avs_is_planar(const AVS_VideoInfo * p) -{ - return !!(p->pixel_type & AVS_CS_PLANAR); -} - -AVSC_INLINE int avs_is_field_based(const AVS_VideoInfo * p) -{ - return !!(p->image_type & AVS_IT_FIELDBASED); -} - -AVSC_INLINE int avs_is_parity_known(const AVS_VideoInfo * p) -{ - return ((p->image_type & AVS_IT_FIELDBASED) && (p->image_type & (AVS_IT_BFF | AVS_IT_TFF))); -} - -AVSC_INLINE int avs_is_bff(const AVS_VideoInfo * p) -{ - return !!(p->image_type & AVS_IT_BFF); -} - -AVSC_INLINE int avs_is_tff(const AVS_VideoInfo * p) -{ - return !!(p->image_type & AVS_IT_TFF); -} - -AVSC_INLINE int avs_samples_per_second(const AVS_VideoInfo * p) - { return p->audio_samples_per_second; } - -AVSC_INLINE int avs_bytes_per_channel_sample(const AVS_VideoInfo * p) -{ - switch (p->sample_type) { - case AVS_SAMPLE_INT8: return sizeof(signed char); - case AVS_SAMPLE_INT16: return sizeof(signed short); - case AVS_SAMPLE_INT24: return 3; - case AVS_SAMPLE_INT32: return sizeof(signed int); - case AVS_SAMPLE_FLOAT: return sizeof(float); - default: return 0; - } -} - -AVSC_INLINE int avs_bytes_per_audio_sample(const AVS_VideoInfo * p) - { return p->nchannels*avs_bytes_per_channel_sample(p);} - -AVSC_INLINE INT64 avs_audio_samples_from_frames(const AVS_VideoInfo * p, INT64 frames) - { return ((INT64)(frames) * p->audio_samples_per_second * p->fps_denominator / p->fps_numerator); } - -AVSC_INLINE int avs_frames_from_audio_samples(const AVS_VideoInfo * p, INT64 samples) - { return (int)(samples * (INT64)p->fps_numerator / (INT64)p->fps_denominator / (INT64)p->audio_samples_per_second); } - -AVSC_INLINE INT64 avs_audio_samples_from_bytes(const AVS_VideoInfo * p, INT64 bytes) - { return bytes / avs_bytes_per_audio_sample(p); } - -AVSC_INLINE INT64 avs_bytes_from_audio_samples(const AVS_VideoInfo * p, INT64 samples) - { return samples * avs_bytes_per_audio_sample(p); } - -AVSC_INLINE int avs_audio_channels(const AVS_VideoInfo * p) - { return p->nchannels; } - -AVSC_INLINE int avs_sample_type(const AVS_VideoInfo * p) - { return p->sample_type;} - -// useful mutator -AVSC_INLINE void avs_set_property(AVS_VideoInfo * p, int property) - { p->image_type|=property; } - -AVSC_INLINE void avs_clear_property(AVS_VideoInfo * p, int property) - { p->image_type&=~property; } - -AVSC_INLINE void avs_set_field_based(AVS_VideoInfo * p, int isfieldbased) - { if (isfieldbased) p->image_type|=AVS_IT_FIELDBASED; else p->image_type&=~AVS_IT_FIELDBASED; } - -AVSC_INLINE void avs_set_fps(AVS_VideoInfo * p, unsigned numerator, unsigned denominator) -{ - unsigned x=numerator, y=denominator; - while (y) { // find gcd - unsigned t = x%y; x = y; y = t; - } - p->fps_numerator = numerator/x; - p->fps_denominator = denominator/x; -} - -#ifndef AVSC_NO_DECLSPEC -// this inline function is calling an API function -AVSC_INLINE int avs_is_same_colorspace(const AVS_VideoInfo * x, const AVS_VideoInfo * y) -{ - return (x->pixel_type == y->pixel_type) - || (avs_is_yv12(x) && avs_is_yv12(y)); -} -#endif - -// Avisynth+ extensions -AVSC_API(int, avs_is_rgb48)(const AVS_VideoInfo * p); - -AVSC_API(int, avs_is_rgb64)(const AVS_VideoInfo * p); - -AVSC_API(int, avs_is_yuv444p16)(const AVS_VideoInfo * p); // obsolete, use avs_is_yuv444 - -AVSC_API(int, avs_is_yuv422p16)(const AVS_VideoInfo * p); // obsolete, use avs_is_yuv422 - -AVSC_API(int, avs_is_yuv420p16)(const AVS_VideoInfo * p); // obsolete, use avs_is_yuv420 - -AVSC_API(int, avs_is_y16)(const AVS_VideoInfo * p); // obsolete, use avs_is_y - -AVSC_API(int, avs_is_yuv444ps)(const AVS_VideoInfo * p); // obsolete, use avs_is_yuv444 - -AVSC_API(int, avs_is_yuv422ps)(const AVS_VideoInfo * p); // obsolete, use avs_is_yuv422 - -AVSC_API(int, avs_is_yuv420ps)(const AVS_VideoInfo * p); // obsolete, use avs_is_yuv420 - -AVSC_API(int, avs_is_y32)(const AVS_VideoInfo * p); // obsolete, use avs_is_y - -AVSC_API(int, avs_is_444)(const AVS_VideoInfo * p); - -AVSC_API(int, avs_is_422)(const AVS_VideoInfo * p); - -AVSC_API(int, avs_is_420)(const AVS_VideoInfo * p); - -AVSC_API(int, avs_is_y)(const AVS_VideoInfo * p); - -AVSC_API(int, avs_is_yuva)(const AVS_VideoInfo * p); - -AVSC_API(int, avs_is_planar_rgb)(const AVS_VideoInfo * p); - -AVSC_API(int, avs_is_planar_rgba)(const AVS_VideoInfo * p); - -AVSC_API(int, avs_num_components)(const AVS_VideoInfo * p); - -AVSC_API(int, avs_component_size)(const AVS_VideoInfo * p); - -AVSC_API(int, avs_bits_per_component)(const AVS_VideoInfo * p); -// end of Avisynth+ specific - -///////////////////////////////////////////////////////////////////// -// -// AVS_VideoFrame -// - -// VideoFrameBuffer holds information about a memory block which is used -// for video data. For efficiency, instances of this class are not deleted -// when the refcount reaches zero; instead they're stored in a linked list -// to be reused. The instances are deleted when the corresponding AVS -// file is closed. - -// AVS_VideoFrameBuffer is laid out identically to VideoFrameBuffer -// DO NOT USE THIS STRUCTURE DIRECTLY -typedef struct AVS_VideoFrameBuffer { - BYTE * data; -#ifdef SIZETMOD - size_t data_size; -#else - int data_size; -#endif - // sequence_number is incremented every time the buffer is changed, so - // that stale views can tell they're no longer valid. - volatile long sequence_number; - - volatile long refcount; -} AVS_VideoFrameBuffer; - -// VideoFrame holds a "window" into a VideoFrameBuffer. - -// AVS_VideoFrame is laid out identically to IVideoFrame -// DO NOT USE THIS STRUCTURE DIRECTLY -typedef struct AVS_VideoFrame { - volatile long refcount; - AVS_VideoFrameBuffer * vfb; -#ifdef SIZETMOD - size_t offset; -#else - int offset; -#endif - int pitch, row_size, height; -#ifdef SIZETMOD - size_t offsetU, offsetV; -#else - int offsetU, offsetV; -#endif - int pitchUV; // U&V offsets are from top of picture. - int row_sizeUV, heightUV; // for Planar RGB offsetU, offsetV is for the 2nd and 3rd Plane. - // for Planar RGB pitchUV and row_sizeUV = 0, because when no VideoInfo (MakeWriteable) - // the decision on existence of UV is checked by zero pitch - // AVS+ extension, avisynth.h: class does not break plugins if appended here -#ifdef SIZETMOD - size_t offsetA; -#else - int offsetA; -#endif - int pitchA, row_sizeA; // 4th alpha plane support, pitch and row_size is 0 is none -} AVS_VideoFrame; - -// Access functions for AVS_VideoFrame -AVSC_API(int, avs_get_pitch_p)(const AVS_VideoFrame * p, int plane); - -AVSC_API(int, avs_get_row_size_p)(const AVS_VideoFrame * p, int plane); - -AVSC_API(int, avs_get_height_p)(const AVS_VideoFrame * p, int plane); - -AVSC_API(const BYTE *, avs_get_read_ptr_p)(const AVS_VideoFrame * p, int plane); - -AVSC_API(int, avs_is_writable)(const AVS_VideoFrame * p); - -AVSC_API(BYTE *, avs_get_write_ptr_p)(const AVS_VideoFrame * p, int plane); - -AVSC_API(void, avs_release_video_frame)(AVS_VideoFrame *); -// makes a shallow copy of a video frame -AVSC_API(AVS_VideoFrame *, avs_copy_video_frame)(AVS_VideoFrame *); - -// no API for these, inline helper functions -#ifndef AVSC_NO_DECLSPEC -// this inline function is calling an API function -AVSC_INLINE int avs_get_pitch(const AVS_VideoFrame * p) { - return avs_get_pitch_p(p, 0); -} -#endif - -#ifndef AVSC_NO_DECLSPEC -// this inline function is calling an API function -AVSC_INLINE int avs_get_row_size(const AVS_VideoFrame * p) { - return avs_get_row_size_p(p, 0); } -#endif - - -#ifndef AVSC_NO_DECLSPEC -// this inline function is calling an API function -AVSC_INLINE int avs_get_height(const AVS_VideoFrame * p) { - return avs_get_height_p(p, 0); -} -#endif - -#ifndef AVSC_NO_DECLSPEC -// this inline function is calling an API function -AVSC_INLINE const BYTE* avs_get_read_ptr(const AVS_VideoFrame * p) { - return avs_get_read_ptr_p(p, 0);} -#endif - -#ifndef AVSC_NO_DECLSPEC -// this inline function is calling an API function -AVSC_INLINE BYTE* avs_get_write_ptr(const AVS_VideoFrame * p) { - return avs_get_write_ptr_p(p, 0);} -#endif - -#ifndef AVSC_NO_DECLSPEC -// this inline function is calling an API function -AVSC_INLINE void avs_release_frame(AVS_VideoFrame * f) - {avs_release_video_frame(f);} -#endif - -#ifndef AVSC_NO_DECLSPEC -// this inline function is calling an API function -AVSC_INLINE AVS_VideoFrame * avs_copy_frame(AVS_VideoFrame * f) - {return avs_copy_video_frame(f);} -#endif - -///////////////////////////////////////////////////////////////////// -// -// AVS_Value -// - -// Treat AVS_Value as a fat pointer. That is use avs_copy_value -// and avs_release_value appropriately as you would if AVS_Value was -// a pointer. - -// To maintain source code compatibility with future versions of the -// avisynth_c API don't use the AVS_Value directly. Use the helper -// functions below. - -// AVS_Value is laid out identically to AVSValue -typedef struct AVS_Value AVS_Value; -struct AVS_Value { - short type; // 'a'rray, 'c'lip, 'b'ool, 'i'nt, 'f'loat, 's'tring, 'v'oid, or 'l'ong - // for some function e'rror - short array_size; - union { - void * clip; // do not use directly, use avs_take_clip - char boolean; - int integer; - float floating_pt; - const char * string; - const AVS_Value * array; - } d; -}; - -// AVS_Value should be initialized with avs_void. -// Should also set to avs_void after the value is released -// with avs_copy_value. Consider it the equivalent of setting -// a pointer to NULL -static const AVS_Value avs_void = {'v'}; - -AVSC_API(void, avs_copy_value)(AVS_Value * dest, AVS_Value src); -AVSC_API(void, avs_release_value)(AVS_Value); -AVSC_API(AVS_Clip *, avs_take_clip)(AVS_Value, AVS_ScriptEnvironment *); -AVSC_API(void, avs_set_to_clip)(AVS_Value *, AVS_Clip *); - - -// no API for these, inline helper functions -AVSC_INLINE int avs_defined(AVS_Value v) { return v.type != 'v'; } -AVSC_INLINE int avs_is_clip(AVS_Value v) { return v.type == 'c'; } -AVSC_INLINE int avs_is_bool(AVS_Value v) { return v.type == 'b'; } -AVSC_INLINE int avs_is_int(AVS_Value v) { return v.type == 'i'; } -AVSC_INLINE int avs_is_float(AVS_Value v) { return v.type == 'f' || v.type == 'i'; } -AVSC_INLINE int avs_is_string(AVS_Value v) { return v.type == 's'; } -AVSC_INLINE int avs_is_array(AVS_Value v) { return v.type == 'a'; } -AVSC_INLINE int avs_is_error(AVS_Value v) { return v.type == 'e'; } - -AVSC_INLINE int avs_as_bool(AVS_Value v) - { return v.d.boolean; } -AVSC_INLINE int avs_as_int(AVS_Value v) - { return v.d.integer; } -AVSC_INLINE const char * avs_as_string(AVS_Value v) - { return avs_is_error(v) || avs_is_string(v) ? v.d.string : 0; } -AVSC_INLINE double avs_as_float(AVS_Value v) - { return avs_is_int(v) ? v.d.integer : v.d.floating_pt; } -AVSC_INLINE const char * avs_as_error(AVS_Value v) - { return avs_is_error(v) ? v.d.string : 0; } -AVSC_INLINE const AVS_Value * avs_as_array(AVS_Value v) - { return v.d.array; } -AVSC_INLINE int avs_array_size(AVS_Value v) - { return avs_is_array(v) ? v.array_size : 1; } -AVSC_INLINE AVS_Value avs_array_elt(AVS_Value v, int index) - { return avs_is_array(v) ? v.d.array[index] : v; } - -// only use these functions on an AVS_Value that does not already have -// an active value. Remember, treat AVS_Value as a fat pointer. -AVSC_INLINE AVS_Value avs_new_value_bool(int v0) - { AVS_Value v; v.type = 'b'; v.d.boolean = v0 == 0 ? 0 : 1; return v; } -AVSC_INLINE AVS_Value avs_new_value_int(int v0) - { AVS_Value v; v.type = 'i'; v.d.integer = v0; return v; } -AVSC_INLINE AVS_Value avs_new_value_string(const char * v0) - { AVS_Value v; v.type = 's'; v.d.string = v0; return v; } -AVSC_INLINE AVS_Value avs_new_value_float(float v0) - { AVS_Value v; v.type = 'f'; v.d.floating_pt = v0; return v;} -AVSC_INLINE AVS_Value avs_new_value_error(const char * v0) - { AVS_Value v; v.type = 'e'; v.d.string = v0; return v; } -#ifndef AVSC_NO_DECLSPEC -// this inline function is calling an API function -AVSC_INLINE AVS_Value avs_new_value_clip(AVS_Clip * v0) - { AVS_Value v; avs_set_to_clip(&v, v0); return v; } -#endif -AVSC_INLINE AVS_Value avs_new_value_array(AVS_Value * v0, int size) - { AVS_Value v; v.type = 'a'; v.d.array = v0; v.array_size = (short)size; return v; } -// end of inline helper functions - -///////////////////////////////////////////////////////////////////// -// -// AVS_Clip -// - -AVSC_API(void, avs_release_clip)(AVS_Clip *); -AVSC_API(AVS_Clip *, avs_copy_clip)(AVS_Clip *); - -AVSC_API(const char *, avs_clip_get_error)(AVS_Clip *); // return 0 if no error - -AVSC_API(const AVS_VideoInfo *, avs_get_video_info)(AVS_Clip *); - -AVSC_API(int, avs_get_version)(AVS_Clip *); - -AVSC_API(AVS_VideoFrame *, avs_get_frame)(AVS_Clip *, int n); -// The returned video frame must be released with avs_release_video_frame - -AVSC_API(int, avs_get_parity)(AVS_Clip *, int n); -// return field parity if field_based, else parity of first field in frame - -AVSC_API(int, avs_get_audio)(AVS_Clip *, void * buf, - INT64 start, INT64 count); -// start and count are in samples - -AVSC_API(int, avs_set_cache_hints)(AVS_Clip *, - int cachehints, int frame_range); - -// This is the callback type used by avs_add_function -typedef AVS_Value (AVSC_CC * AVS_ApplyFunc) - (AVS_ScriptEnvironment *, AVS_Value args, void * user_data); - -typedef struct AVS_FilterInfo AVS_FilterInfo; -struct AVS_FilterInfo -{ - // these members should not be modified outside of the AVS_ApplyFunc callback - AVS_Clip * child; - AVS_VideoInfo vi; - AVS_ScriptEnvironment * env; - AVS_VideoFrame * (AVSC_CC * get_frame)(AVS_FilterInfo *, int n); - int (AVSC_CC * get_parity)(AVS_FilterInfo *, int n); - int (AVSC_CC * get_audio)(AVS_FilterInfo *, void * buf, - INT64 start, INT64 count); - int (AVSC_CC * set_cache_hints)(AVS_FilterInfo *, int cachehints, - int frame_range); - void (AVSC_CC * free_filter)(AVS_FilterInfo *); - - // Should be set when ever there is an error to report. - // It is cleared before any of the above methods are called - const char * error; - // this is to store whatever and may be modified at will - void * user_data; -}; - -// Create a new filter -// fi is set to point to the AVS_FilterInfo so that you can -// modify it once it is initialized. -// store_child should generally be set to true. If it is not -// set than ALL methods (the function pointers) must be defined -// If it is set than you do not need to worry about freeing the child -// clip. -AVSC_API(AVS_Clip *, avs_new_c_filter)(AVS_ScriptEnvironment * e, - AVS_FilterInfo * * fi, - AVS_Value child, int store_child); - -///////////////////////////////////////////////////////////////////// -// -// AVS_ScriptEnvironment -// - -// For GetCPUFlags. These are backwards-compatible with those in VirtualDub. -enum { - /* slowest CPU to support extension */ - AVS_CPU_FORCE = 0x01, // N/A - AVS_CPU_FPU = 0x02, // 386/486DX - AVS_CPU_MMX = 0x04, // P55C, K6, PII - AVS_CPU_INTEGER_SSE = 0x08, // PIII, Athlon - AVS_CPU_SSE = 0x10, // PIII, Athlon XP/MP - AVS_CPU_SSE2 = 0x20, // PIV, Hammer - AVS_CPU_3DNOW = 0x40, // K6-2 - AVS_CPU_3DNOW_EXT = 0x80, // Athlon - AVS_CPU_X86_64 = 0xA0, // Hammer (note: equiv. to 3DNow + SSE2, - // which only Hammer will have anyway) - AVS_CPUF_SSE3 = 0x100, // PIV+, K8 Venice - AVS_CPUF_SSSE3 = 0x200, // Core 2 - AVS_CPUF_SSE4 = 0x400, // Penryn, Wolfdale, Yorkfield - AVS_CPUF_SSE4_1 = 0x400, - AVS_CPUF_AVX = 0x800, // Sandy Bridge, Bulldozer - AVS_CPUF_SSE4_2 = 0x1000, // Nehalem - // AVS+ - AVS_CPUF_AVX2 = 0x2000, // Haswell - AVS_CPUF_FMA3 = 0x4000, - AVS_CPUF_F16C = 0x8000, - AVS_CPUF_MOVBE = 0x10000, // Big Endian Move - AVS_CPUF_POPCNT = 0x20000, - AVS_CPUF_AES = 0x40000, - AVS_CPUF_FMA4 = 0x80000, - - AVS_CPUF_AVX512F = 0x100000, // AVX-512 Foundation. - AVS_CPUF_AVX512DQ = 0x200000, // AVX-512 DQ (Double/Quad granular) Instructions - AVS_CPUF_AVX512PF = 0x400000, // AVX-512 Prefetch - AVS_CPUF_AVX512ER = 0x800000, // AVX-512 Exponential and Reciprocal - AVS_CPUF_AVX512CD = 0x1000000, // AVX-512 Conflict Detection - AVS_CPUF_AVX512BW = 0x2000000, // AVX-512 BW (Byte/Word granular) Instructions - AVS_CPUF_AVX512VL = 0x4000000, // AVX-512 VL (128/256 Vector Length) Extensions - AVS_CPUF_AVX512IFMA = 0x8000000, // AVX-512 IFMA integer 52 bit - AVS_CPUF_AVX512VBMI = 0x10000000 // AVX-512 VBMI -}; - - -AVSC_API(const char *, avs_get_error)(AVS_ScriptEnvironment *); // return 0 if no error - -AVSC_API(int, avs_get_cpu_flags)(AVS_ScriptEnvironment *); -AVSC_API(int, avs_check_version)(AVS_ScriptEnvironment *, int version); - -AVSC_API(char *, avs_save_string)(AVS_ScriptEnvironment *, const char* s, int length); -AVSC_API(char *, avs_sprintf)(AVS_ScriptEnvironment *, const char * fmt, ...); - -AVSC_API(char *, avs_vsprintf)(AVS_ScriptEnvironment *, const char * fmt, void* val); - // note: val is really a va_list; I hope everyone typedefs va_list to a pointer - -AVSC_API(int, avs_add_function)(AVS_ScriptEnvironment *, - const char * name, const char * params, - AVS_ApplyFunc apply, void * user_data); - -AVSC_API(int, avs_function_exists)(AVS_ScriptEnvironment *, const char * name); - -AVSC_API(AVS_Value, avs_invoke)(AVS_ScriptEnvironment *, const char * name, - AVS_Value args, const char** arg_names); -// The returned value must be be released with avs_release_value - -AVSC_API(AVS_Value, avs_get_var)(AVS_ScriptEnvironment *, const char* name); -// The returned value must be be released with avs_release_value - -AVSC_API(int, avs_set_var)(AVS_ScriptEnvironment *, const char* name, AVS_Value val); - -AVSC_API(int, avs_set_global_var)(AVS_ScriptEnvironment *, const char* name, const AVS_Value val); - -//void avs_push_context(AVS_ScriptEnvironment *, int level=0); -//void avs_pop_context(AVS_ScriptEnvironment *); - -AVSC_API(AVS_VideoFrame *, avs_new_video_frame_a)(AVS_ScriptEnvironment *, - const AVS_VideoInfo * vi, int align); -// align should be at least 16 for classic Avisynth -// Avisynth+: any value, Avs+ ensures a minimum alignment if too small align is provided - -// no API for these, inline helper functions -#ifndef AVSC_NO_DECLSPEC -// this inline function is calling an API function -AVSC_INLINE AVS_VideoFrame * avs_new_video_frame(AVS_ScriptEnvironment * env, - const AVS_VideoInfo * vi) - {return avs_new_video_frame_a(env,vi,AVS_FRAME_ALIGN);} - -// an older compatibility alias -// this inline function is calling an API function -AVSC_INLINE AVS_VideoFrame * avs_new_frame(AVS_ScriptEnvironment * env, - const AVS_VideoInfo * vi) - {return avs_new_video_frame_a(env,vi,AVS_FRAME_ALIGN);} -#endif -// end of inline helper functions - -AVSC_API(int, avs_make_writable)(AVS_ScriptEnvironment *, AVS_VideoFrame * * pvf); - -AVSC_API(void, avs_bit_blt)(AVS_ScriptEnvironment *, BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height); - -typedef void (AVSC_CC *AVS_ShutdownFunc)(void* user_data, AVS_ScriptEnvironment * env); -AVSC_API(void, avs_at_exit)(AVS_ScriptEnvironment *, AVS_ShutdownFunc function, void * user_data); - -AVSC_API(AVS_VideoFrame *, avs_subframe)(AVS_ScriptEnvironment *, AVS_VideoFrame * src, int rel_offset, int new_pitch, int new_row_size, int new_height); -// The returned video frame must be be released - -AVSC_API(int, avs_set_memory_max)(AVS_ScriptEnvironment *, int mem); - -AVSC_API(int, avs_set_working_dir)(AVS_ScriptEnvironment *, const char * newdir); - -// avisynth.dll exports this; it's a way to use it as a library, without -// writing an AVS script or without going through AVIFile. -AVSC_API(AVS_ScriptEnvironment *, avs_create_script_environment)(int version); - -// this symbol is the entry point for the plugin and must -// be defined -AVSC_EXPORT -const char * AVSC_CC avisynth_c_plugin_init(AVS_ScriptEnvironment* env); - - -AVSC_API(void, avs_delete_script_environment)(AVS_ScriptEnvironment *); - - -AVSC_API(AVS_VideoFrame *, avs_subframe_planar)(AVS_ScriptEnvironment *, AVS_VideoFrame * src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV); -// The returned video frame must be be released - -#ifdef AVSC_NO_DECLSPEC -// This part uses LoadLibrary and related functions to dynamically load Avisynth instead of declspec(dllimport) -// When AVSC_NO_DECLSPEC is defined, you can use avs_load_library to populate API functions into a struct -// AVSC_INLINE functions which call onto an API functions should be treated specially (todo) - -/* - The following functions needs to have been declared, probably from windows.h - - void* malloc(size_t) - void free(void*); - - HMODULE LoadLibrary(const char*); - void* GetProcAddress(HMODULE, const char*); - FreeLibrary(HMODULE); -*/ - - -typedef struct AVS_Library AVS_Library; - -#define AVSC_DECLARE_FUNC(name) name##_func name - -// AVSC_DECLARE_FUNC helps keeping naming convention: type is xxxxx_func, function name is xxxxx -// e.g. "AVSC_DECLARE_FUNC(avs_add_function);" -// is a shortcut for "avs_add_function_func avs_add_function;" - -// Note: AVSC_INLINE functions which call into API, -// are guarded by #ifndef AVSC_NO_DECLSPEC -// They should call the appropriate library-> API entry - -struct AVS_Library { - HMODULE handle; - - AVSC_DECLARE_FUNC(avs_add_function); - AVSC_DECLARE_FUNC(avs_at_exit); - AVSC_DECLARE_FUNC(avs_bit_blt); - AVSC_DECLARE_FUNC(avs_check_version); - AVSC_DECLARE_FUNC(avs_clip_get_error); - AVSC_DECLARE_FUNC(avs_copy_clip); - AVSC_DECLARE_FUNC(avs_copy_value); - AVSC_DECLARE_FUNC(avs_copy_video_frame); - AVSC_DECLARE_FUNC(avs_create_script_environment); - AVSC_DECLARE_FUNC(avs_delete_script_environment); - AVSC_DECLARE_FUNC(avs_function_exists); - AVSC_DECLARE_FUNC(avs_get_audio); - AVSC_DECLARE_FUNC(avs_get_cpu_flags); - AVSC_DECLARE_FUNC(avs_get_frame); - AVSC_DECLARE_FUNC(avs_get_parity); - AVSC_DECLARE_FUNC(avs_get_var); - AVSC_DECLARE_FUNC(avs_get_version); - AVSC_DECLARE_FUNC(avs_get_video_info); - AVSC_DECLARE_FUNC(avs_invoke); - AVSC_DECLARE_FUNC(avs_make_writable); - AVSC_DECLARE_FUNC(avs_new_c_filter); - AVSC_DECLARE_FUNC(avs_new_video_frame_a); - AVSC_DECLARE_FUNC(avs_release_clip); - AVSC_DECLARE_FUNC(avs_release_value); - AVSC_DECLARE_FUNC(avs_release_video_frame); - AVSC_DECLARE_FUNC(avs_save_string); - AVSC_DECLARE_FUNC(avs_set_cache_hints); - AVSC_DECLARE_FUNC(avs_set_global_var); - AVSC_DECLARE_FUNC(avs_set_memory_max); - AVSC_DECLARE_FUNC(avs_set_to_clip); - AVSC_DECLARE_FUNC(avs_set_var); - AVSC_DECLARE_FUNC(avs_set_working_dir); - AVSC_DECLARE_FUNC(avs_sprintf); - AVSC_DECLARE_FUNC(avs_subframe); - AVSC_DECLARE_FUNC(avs_subframe_planar); - AVSC_DECLARE_FUNC(avs_take_clip); - AVSC_DECLARE_FUNC(avs_vsprintf); - - AVSC_DECLARE_FUNC(avs_get_error); - AVSC_DECLARE_FUNC(avs_is_yv24); - AVSC_DECLARE_FUNC(avs_is_yv16); - AVSC_DECLARE_FUNC(avs_is_yv12); - AVSC_DECLARE_FUNC(avs_is_yv411); - AVSC_DECLARE_FUNC(avs_is_y8); - AVSC_DECLARE_FUNC(avs_is_color_space); - - AVSC_DECLARE_FUNC(avs_get_plane_width_subsampling); - AVSC_DECLARE_FUNC(avs_get_plane_height_subsampling); - AVSC_DECLARE_FUNC(avs_bits_per_pixel); - AVSC_DECLARE_FUNC(avs_bytes_from_pixels); - AVSC_DECLARE_FUNC(avs_row_size); - AVSC_DECLARE_FUNC(avs_bmp_size); - AVSC_DECLARE_FUNC(avs_get_pitch_p); - AVSC_DECLARE_FUNC(avs_get_row_size_p); - AVSC_DECLARE_FUNC(avs_get_height_p); - AVSC_DECLARE_FUNC(avs_get_read_ptr_p); - AVSC_DECLARE_FUNC(avs_is_writable); - AVSC_DECLARE_FUNC(avs_get_write_ptr_p); - - // Avisynth+ specific - // Note: these functions are simulated/use fallback to existing functions - AVSC_DECLARE_FUNC(avs_is_rgb48); - AVSC_DECLARE_FUNC(avs_is_rgb64); - AVSC_DECLARE_FUNC(avs_is_yuv444p16); - AVSC_DECLARE_FUNC(avs_is_yuv422p16); - AVSC_DECLARE_FUNC(avs_is_yuv420p16); - AVSC_DECLARE_FUNC(avs_is_y16); - AVSC_DECLARE_FUNC(avs_is_yuv444ps); - AVSC_DECLARE_FUNC(avs_is_yuv422ps); - AVSC_DECLARE_FUNC(avs_is_yuv420ps); - AVSC_DECLARE_FUNC(avs_is_y32); - AVSC_DECLARE_FUNC(avs_is_444); - AVSC_DECLARE_FUNC(avs_is_422); - AVSC_DECLARE_FUNC(avs_is_420); - AVSC_DECLARE_FUNC(avs_is_y); - AVSC_DECLARE_FUNC(avs_is_yuva); - AVSC_DECLARE_FUNC(avs_is_planar_rgb); - AVSC_DECLARE_FUNC(avs_is_planar_rgba); - AVSC_DECLARE_FUNC(avs_num_components); - AVSC_DECLARE_FUNC(avs_component_size); - AVSC_DECLARE_FUNC(avs_bits_per_component); - // end of Avisynth+ specific - -}; - -#undef AVSC_DECLARE_FUNC - -// Helper functions for fallback simulation -// Avisynth+ extensions do not exist in classic Avisynth so they are simulated -AVSC_INLINE int avs_is_xx_fallback_return_false(const AVS_VideoInfo * p) -{ - return 0; -} - -// Avisynth+ extensions do not exist in classic Avisynth so they are simulated -AVSC_INLINE int avs_num_components_fallback(const AVS_VideoInfo * p) -{ - switch (p->pixel_type) { - case AVS_CS_UNKNOWN: - return 0; - case AVS_CS_RAW32: - case AVS_CS_Y8: - return 1; - case AVS_CS_BGR32: - return 4; // not planar but return the count - default: - return 3; - } -} - -// Avisynth+ extensions do not exist in classic Avisynth so they are simulated -AVSC_INLINE int avs_component_size_fallback(const AVS_VideoInfo * p) -{ - return 1; -} - -// Avisynth+ extensions do not exist in classic Avisynth so they are simulated -AVSC_INLINE int avs_bits_per_component_fallback(const AVS_VideoInfo * p) -{ - return 8; -} -// End of helper functions for fallback simulation - -// avs_load_library() allocates an array for API procedure entries -// reads and fills the entries with live procedure addresses. -// AVSC_INLINE helpers which are calling into API procedures are not treated here (todo) - -AVSC_INLINE AVS_Library * avs_load_library() { - AVS_Library *library = (AVS_Library *)malloc(sizeof(AVS_Library)); - if (library == NULL) - return NULL; - library->handle = LoadLibrary("avisynth"); - if (library->handle == NULL) - goto fail; - -#define __AVSC_STRINGIFY(x) #x -#define AVSC_STRINGIFY(x) __AVSC_STRINGIFY(x) -#define AVSC_LOAD_FUNC(name) {\ - library->name = (name##_func) GetProcAddress(library->handle, AVSC_STRINGIFY(name));\ - if (library->name == NULL)\ - goto fail;\ -} - -#if 0 -// FFmpeg-specific: we don't use the FALLBACK stuff, and it causes build errors, -// so ifdef it out on our side. - -// When an API function is not loadable, let's try a replacement -// Missing Avisynth+ functions will be substituted with classic Avisynth compatible methods -/* -Avisynth+ When method is missing (classic Avisynth) -avs_is_rgb48 constant false -avs_is_rgb64 constant false -avs_is_yuv444p16 constant false -avs_is_yuv422p16 constant false -avs_is_yuv420p16 constant false -avs_is_y16 constant false -avs_is_yuv444ps constant false -avs_is_yuv422ps constant false -avs_is_yuv420ps constant false -avs_is_y32 constant false -avs_is_444 avs_is_yv24 -avs_is_422 avs_is_yv16 -avs_is_420 avs_is_yv12 -avs_is_y avs_is_y8 -avs_is_yuva constant false -avs_is_planar_rgb constant false -avs_is_planar_rgba constant false -avs_num_components special: avs_num_components_fake Y8:1 RGB32:4 else 3 -avs_component_size constant 1 (1 bytes/component) -avs_bits_per_component constant 8 (8 bits/component) -*/ - - // try to load an alternative function -#define AVSC_LOAD_FUNC_FALLBACK(name,name2) {\ - library->name = (name##_func) GetProcAddress(library->handle, AVSC_STRINGIFY(name));\ - if (library->name == NULL)\ - library->name = (name##_func) GetProcAddress(library->handle, AVSC_STRINGIFY(name2));\ - if (library->name == NULL)\ - goto fail;\ -} - - // try to assign a replacement function -#define AVSC_LOAD_FUNC_FALLBACK_SIMULATED(name,name2) {\ - library->name = (name##_func) GetProcAddress(library->handle, AVSC_STRINGIFY(name));\ - if (library->name == NULL)\ - library->name = name2;\ - if (library->name == NULL)\ - goto fail;\ -} -#endif - - AVSC_LOAD_FUNC(avs_add_function); - AVSC_LOAD_FUNC(avs_at_exit); - AVSC_LOAD_FUNC(avs_bit_blt); - AVSC_LOAD_FUNC(avs_check_version); - AVSC_LOAD_FUNC(avs_clip_get_error); - AVSC_LOAD_FUNC(avs_copy_clip); - AVSC_LOAD_FUNC(avs_copy_value); - AVSC_LOAD_FUNC(avs_copy_video_frame); - AVSC_LOAD_FUNC(avs_create_script_environment); - AVSC_LOAD_FUNC(avs_delete_script_environment); - AVSC_LOAD_FUNC(avs_function_exists); - AVSC_LOAD_FUNC(avs_get_audio); - AVSC_LOAD_FUNC(avs_get_cpu_flags); - AVSC_LOAD_FUNC(avs_get_frame); - AVSC_LOAD_FUNC(avs_get_parity); - AVSC_LOAD_FUNC(avs_get_var); - AVSC_LOAD_FUNC(avs_get_version); - AVSC_LOAD_FUNC(avs_get_video_info); - AVSC_LOAD_FUNC(avs_invoke); - AVSC_LOAD_FUNC(avs_make_writable); - AVSC_LOAD_FUNC(avs_new_c_filter); - AVSC_LOAD_FUNC(avs_new_video_frame_a); - AVSC_LOAD_FUNC(avs_release_clip); - AVSC_LOAD_FUNC(avs_release_value); - AVSC_LOAD_FUNC(avs_release_video_frame); - AVSC_LOAD_FUNC(avs_save_string); - AVSC_LOAD_FUNC(avs_set_cache_hints); - AVSC_LOAD_FUNC(avs_set_global_var); - AVSC_LOAD_FUNC(avs_set_memory_max); - AVSC_LOAD_FUNC(avs_set_to_clip); - AVSC_LOAD_FUNC(avs_set_var); - AVSC_LOAD_FUNC(avs_set_working_dir); - AVSC_LOAD_FUNC(avs_sprintf); - AVSC_LOAD_FUNC(avs_subframe); - AVSC_LOAD_FUNC(avs_subframe_planar); - AVSC_LOAD_FUNC(avs_take_clip); - AVSC_LOAD_FUNC(avs_vsprintf); - - AVSC_LOAD_FUNC(avs_get_error); - AVSC_LOAD_FUNC(avs_is_yv24); - AVSC_LOAD_FUNC(avs_is_yv16); - AVSC_LOAD_FUNC(avs_is_yv12); - AVSC_LOAD_FUNC(avs_is_yv411); - AVSC_LOAD_FUNC(avs_is_y8); - AVSC_LOAD_FUNC(avs_is_color_space); - - AVSC_LOAD_FUNC(avs_get_plane_width_subsampling); - AVSC_LOAD_FUNC(avs_get_plane_height_subsampling); - AVSC_LOAD_FUNC(avs_bits_per_pixel); - AVSC_LOAD_FUNC(avs_bytes_from_pixels); - AVSC_LOAD_FUNC(avs_row_size); - AVSC_LOAD_FUNC(avs_bmp_size); - AVSC_LOAD_FUNC(avs_get_pitch_p); - AVSC_LOAD_FUNC(avs_get_row_size_p); - AVSC_LOAD_FUNC(avs_get_height_p); - AVSC_LOAD_FUNC(avs_get_read_ptr_p); - AVSC_LOAD_FUNC(avs_is_writable); - AVSC_LOAD_FUNC(avs_get_write_ptr_p); - -#if 0 - // Avisynth+ specific but made them callable for classic Avisynth hosts - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_is_rgb48, avs_is_xx_fallback_return_false); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_is_rgb64, avs_is_xx_fallback_return_false); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_is_yuv444p16, avs_is_xx_fallback_return_false); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_is_yuv422p16, avs_is_xx_fallback_return_false); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_is_yuv420p16, avs_is_xx_fallback_return_false); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_is_y16, avs_is_xx_fallback_return_false); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_is_yuv444ps, avs_is_xx_fallback_return_false); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_is_yuv422ps, avs_is_xx_fallback_return_false); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_is_yuv420ps, avs_is_xx_fallback_return_false); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_is_y32, avs_is_xx_fallback_return_false); - AVSC_LOAD_FUNC_FALLBACK(avs_is_444, avs_is_yv24); - AVSC_LOAD_FUNC_FALLBACK(avs_is_422, avs_is_yv16); - AVSC_LOAD_FUNC_FALLBACK(avs_is_420, avs_is_yv12); - AVSC_LOAD_FUNC_FALLBACK(avs_is_y, avs_is_y8); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_is_yuva, avs_is_xx_fallback_return_false); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_is_planar_rgb, avs_is_xx_fallback_return_false); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_is_planar_rgba, avs_is_xx_fallback_return_false); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_num_components, avs_num_components_fallback); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_component_size, avs_component_size_fallback); - AVSC_LOAD_FUNC_FALLBACK_SIMULATED(avs_bits_per_component, avs_bits_per_component_fallback); -#endif - -#undef __AVSC_STRINGIFY -#undef AVSC_STRINGIFY -#undef AVSC_LOAD_FUNC -#undef AVSC_LOAD_FUNC_FALLBACK -#undef AVSC_LOAD_FUNC_FALLBACK_SIMULATED - - return library; - -fail: - free(library); - return NULL; -} - -AVSC_INLINE void avs_free_library(AVS_Library *library) { - if (library == NULL) - return; - FreeLibrary(library->handle); - free(library); -} -#endif - -#endif diff --git a/compat/avisynth/avs/capi.h b/compat/avisynth/avs/capi.h deleted file mode 100644 index 8799bf1fbbc..00000000000 --- a/compat/avisynth/avs/capi.h +++ /dev/null @@ -1,94 +0,0 @@ -// Avisynth C Interface Version 0.20 -// Copyright 2003 Kevin Atkinson - -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit -// http://www.gnu.org/copyleft/gpl.html . -// -// As a special exception, I give you permission to link to the -// Avisynth C interface with independent modules that communicate with -// the Avisynth C interface solely through the interfaces defined in -// avisynth_c.h, regardless of the license terms of these independent -// modules, and to copy and distribute the resulting combined work -// under terms of your choice, provided that every copy of the -// combined work is accompanied by a complete copy of the source code -// of the Avisynth C interface and Avisynth itself (with the version -// used to produce the combined work), being distributed under the -// terms of the GNU General Public License plus this exception. An -// independent module is a module which is not derived from or based -// on Avisynth C Interface, such as 3rd-party filters, import and -// export plugins, or graphical user interfaces. - -#ifndef AVS_CAPI_H -#define AVS_CAPI_H - -#ifdef __cplusplus -# define EXTERN_C extern "C" -#else -# define EXTERN_C -#endif - -#ifdef BUILDING_AVSCORE -# if defined(GCC) && defined(X86_32) -# define AVSC_CC -# else // MSVC builds and 64-bit GCC -# ifndef AVSC_USE_STDCALL -# define AVSC_CC __cdecl -# else -# define AVSC_CC __stdcall -# endif -# endif -#else // needed for programs that talk to AviSynth+ -# ifndef AVSC_WIN32_GCC32 // see comment below -# ifndef AVSC_USE_STDCALL -# define AVSC_CC __cdecl -# else -# define AVSC_CC __stdcall -# endif -# else -# define AVSC_CC -# endif -#endif - -// On 64-bit Windows, there's only one calling convention, -// so there is no difference between MSVC and GCC. On 32-bit, -// this isn't true. The convention that GCC needs to use to -// even build AviSynth+ as 32-bit makes anything that uses -// it incompatible with 32-bit MSVC builds of AviSynth+. -// The AVSC_WIN32_GCC32 define is meant to provide a user -// switchable way to make builds of FFmpeg to test 32-bit -// GCC builds of AviSynth+ without having to screw around -// with alternate headers, while still default to the usual -// situation of using 32-bit MSVC builds of AviSynth+. - -// Hopefully, this situation will eventually be resolved -// and a broadly compatible solution will arise so the -// same 32-bit FFmpeg build can handle either MSVC or GCC -// builds of AviSynth+. - -#define AVSC_INLINE static __inline - -#ifdef BUILDING_AVSCORE -# define AVSC_EXPORT __declspec(dllexport) -# define AVSC_API(ret, name) EXTERN_C AVSC_EXPORT ret AVSC_CC name -#else -# define AVSC_EXPORT EXTERN_C __declspec(dllexport) -# ifndef AVSC_NO_DECLSPEC -# define AVSC_API(ret, name) EXTERN_C __declspec(dllimport) ret AVSC_CC name -# else -# define AVSC_API(ret, name) typedef ret (AVSC_CC *name##_func) -# endif -#endif - -#endif //AVS_CAPI_H diff --git a/compat/avisynth/avs/config.h b/compat/avisynth/avs/config.h deleted file mode 100644 index a7d3e692ea4..00000000000 --- a/compat/avisynth/avs/config.h +++ /dev/null @@ -1,70 +0,0 @@ -// Avisynth C Interface Version 0.20 -// Copyright 2003 Kevin Atkinson - -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit -// http://www.gnu.org/copyleft/gpl.html . -// -// As a special exception, I give you permission to link to the -// Avisynth C interface with independent modules that communicate with -// the Avisynth C interface solely through the interfaces defined in -// avisynth_c.h, regardless of the license terms of these independent -// modules, and to copy and distribute the resulting combined work -// under terms of your choice, provided that every copy of the -// combined work is accompanied by a complete copy of the source code -// of the Avisynth C interface and Avisynth itself (with the version -// used to produce the combined work), being distributed under the -// terms of the GNU General Public License plus this exception. An -// independent module is a module which is not derived from or based -// on Avisynth C Interface, such as 3rd-party filters, import and -// export plugins, or graphical user interfaces. - -#ifndef AVS_CONFIG_H -#define AVS_CONFIG_H - -// Undefine this to get cdecl calling convention -#define AVSC_USE_STDCALL 1 - -// NOTE TO PLUGIN AUTHORS: -// Because FRAME_ALIGN can be substantially higher than the alignment -// a plugin actually needs, plugins should not use FRAME_ALIGN to check for -// alignment. They should always request the exact alignment value they need. -// This is to make sure that plugins work over the widest range of AviSynth -// builds possible. -#define FRAME_ALIGN 64 - -#if defined(_M_AMD64) || defined(__x86_64) -# define X86_64 -#elif defined(_M_IX86) || defined(__i386__) -# define X86_32 -#else -# error Unsupported CPU architecture. -#endif - -#if defined(_MSC_VER) -# define MSVC -#elif defined(__GNUC__) -# define GCC -#elif defined(__clang__) -# define CLANG -#else -# error Unsupported compiler. -#endif - -#if defined(GCC) -# undef __forceinline -# define __forceinline inline -#endif - -#endif //AVS_CONFIG_H diff --git a/compat/avisynth/avs/types.h b/compat/avisynth/avs/types.h deleted file mode 100644 index df15f1d8e5c..00000000000 --- a/compat/avisynth/avs/types.h +++ /dev/null @@ -1,57 +0,0 @@ -// Avisynth C Interface Version 0.20 -// Copyright 2003 Kevin Atkinson - -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit -// http://www.gnu.org/copyleft/gpl.html . -// -// As a special exception, I give you permission to link to the -// Avisynth C interface with independent modules that communicate with -// the Avisynth C interface solely through the interfaces defined in -// avisynth_c.h, regardless of the license terms of these independent -// modules, and to copy and distribute the resulting combined work -// under terms of your choice, provided that every copy of the -// combined work is accompanied by a complete copy of the source code -// of the Avisynth C interface and Avisynth itself (with the version -// used to produce the combined work), being distributed under the -// terms of the GNU General Public License plus this exception. An -// independent module is a module which is not derived from or based -// on Avisynth C Interface, such as 3rd-party filters, import and -// export plugins, or graphical user interfaces. - -#ifndef AVS_TYPES_H -#define AVS_TYPES_H - -// Define all types necessary for interfacing with avisynth.dll - -#ifdef __cplusplus - #include -#else - #include -#endif - -// Raster types used by VirtualDub & Avisynth -typedef unsigned int Pixel32; -typedef unsigned char BYTE; - -// Audio Sample information -typedef float SFLOAT; - -#ifdef __GNUC__ -typedef long long int INT64; -#else -typedef __int64 INT64; -#endif - -#endif //AVS_TYPES_H diff --git a/compat/avisynth/avxsynth_c.h b/compat/avisynth/avxsynth_c.h deleted file mode 100644 index 991f4be1ab2..00000000000 --- a/compat/avisynth/avxsynth_c.h +++ /dev/null @@ -1,728 +0,0 @@ -// Avisynth C Interface Version 0.20 -// Copyright 2003 Kevin Atkinson - -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -// MA 02110-1301 USA, or visit -// http://www.gnu.org/copyleft/gpl.html . -// -// As a special exception, I give you permission to link to the -// Avisynth C interface with independent modules that communicate with -// the Avisynth C interface solely through the interfaces defined in -// avisynth_c.h, regardless of the license terms of these independent -// modules, and to copy and distribute the resulting combined work -// under terms of your choice, provided that every copy of the -// combined work is accompanied by a complete copy of the source code -// of the Avisynth C interface and Avisynth itself (with the version -// used to produce the combined work), being distributed under the -// terms of the GNU General Public License plus this exception. An -// independent module is a module which is not derived from or based -// on Avisynth C Interface, such as 3rd-party filters, import and -// export plugins, or graphical user interfaces. - -#ifndef __AVXSYNTH_C__ -#define __AVXSYNTH_C__ - -#include "windowsPorts/windows2linux.h" -#include - -#ifdef __cplusplus -# define EXTERN_C extern "C" -#else -# define EXTERN_C -#endif - -#define AVSC_USE_STDCALL 1 - -#ifndef AVSC_USE_STDCALL -# define AVSC_CC __cdecl -#else -# define AVSC_CC __stdcall -#endif - -#define AVSC_INLINE static __inline - -#ifdef AVISYNTH_C_EXPORTS -# define AVSC_EXPORT EXTERN_C -# define AVSC_API(ret, name) EXTERN_C __declspec(dllexport) ret AVSC_CC name -#else -# define AVSC_EXPORT EXTERN_C __declspec(dllexport) -# ifndef AVSC_NO_DECLSPEC -# define AVSC_API(ret, name) EXTERN_C __declspec(dllimport) ret AVSC_CC name -# else -# define AVSC_API(ret, name) typedef ret (AVSC_CC *name##_func) -# endif -#endif - -#ifdef __GNUC__ -typedef long long int INT64; -#else -typedef __int64 INT64; -#endif - - -///////////////////////////////////////////////////////////////////// -// -// Constants -// - -#ifndef __AVXSYNTH_H__ -enum { AVISYNTH_INTERFACE_VERSION = 3 }; -#endif - -enum {AVS_SAMPLE_INT8 = 1<<0, - AVS_SAMPLE_INT16 = 1<<1, - AVS_SAMPLE_INT24 = 1<<2, - AVS_SAMPLE_INT32 = 1<<3, - AVS_SAMPLE_FLOAT = 1<<4}; - -enum {AVS_PLANAR_Y=1<<0, - AVS_PLANAR_U=1<<1, - AVS_PLANAR_V=1<<2, - AVS_PLANAR_ALIGNED=1<<3, - AVS_PLANAR_Y_ALIGNED=AVS_PLANAR_Y|AVS_PLANAR_ALIGNED, - AVS_PLANAR_U_ALIGNED=AVS_PLANAR_U|AVS_PLANAR_ALIGNED, - AVS_PLANAR_V_ALIGNED=AVS_PLANAR_V|AVS_PLANAR_ALIGNED}; - - // Colorspace properties. -enum {AVS_CS_BGR = 1<<28, - AVS_CS_YUV = 1<<29, - AVS_CS_INTERLEAVED = 1<<30, - AVS_CS_PLANAR = 1<<31}; - - // Specific colorformats -enum { - AVS_CS_UNKNOWN = 0, - AVS_CS_BGR24 = 1<<0 | AVS_CS_BGR | AVS_CS_INTERLEAVED, - AVS_CS_BGR32 = 1<<1 | AVS_CS_BGR | AVS_CS_INTERLEAVED, - AVS_CS_YUY2 = 1<<2 | AVS_CS_YUV | AVS_CS_INTERLEAVED, - AVS_CS_YV12 = 1<<3 | AVS_CS_YUV | AVS_CS_PLANAR, // y-v-u, planar - AVS_CS_I420 = 1<<4 | AVS_CS_YUV | AVS_CS_PLANAR, // y-u-v, planar - AVS_CS_IYUV = 1<<4 | AVS_CS_YUV | AVS_CS_PLANAR // same as above -}; - -enum { - AVS_IT_BFF = 1<<0, - AVS_IT_TFF = 1<<1, - AVS_IT_FIELDBASED = 1<<2}; - -enum { - AVS_FILTER_TYPE=1, - AVS_FILTER_INPUT_COLORSPACE=2, - AVS_FILTER_OUTPUT_TYPE=9, - AVS_FILTER_NAME=4, - AVS_FILTER_AUTHOR=5, - AVS_FILTER_VERSION=6, - AVS_FILTER_ARGS=7, - AVS_FILTER_ARGS_INFO=8, - AVS_FILTER_ARGS_DESCRIPTION=10, - AVS_FILTER_DESCRIPTION=11}; - -enum { //SUBTYPES - AVS_FILTER_TYPE_AUDIO=1, - AVS_FILTER_TYPE_VIDEO=2, - AVS_FILTER_OUTPUT_TYPE_SAME=3, - AVS_FILTER_OUTPUT_TYPE_DIFFERENT=4}; - -enum { - AVS_CACHE_NOTHING=0, - AVS_CACHE_RANGE=1, - AVS_CACHE_ALL=2, - AVS_CACHE_AUDIO=3, - AVS_CACHE_AUDIO_NONE=4, - AVS_CACHE_AUDIO_AUTO=5 -}; - -#define AVS_FRAME_ALIGN 16 - -typedef struct AVS_Clip AVS_Clip; -typedef struct AVS_ScriptEnvironment AVS_ScriptEnvironment; - -///////////////////////////////////////////////////////////////////// -// -// AVS_VideoInfo -// - -// AVS_VideoInfo is layed out identicly to VideoInfo -typedef struct AVS_VideoInfo { - int width, height; // width=0 means no video - unsigned fps_numerator, fps_denominator; - int num_frames; - - int pixel_type; - - int audio_samples_per_second; // 0 means no audio - int sample_type; - INT64 num_audio_samples; - int nchannels; - - // Imagetype properties - - int image_type; -} AVS_VideoInfo; - -// useful functions of the above -AVSC_INLINE int avs_has_video(const AVS_VideoInfo * p) - { return (p->width!=0); } - -AVSC_INLINE int avs_has_audio(const AVS_VideoInfo * p) - { return (p->audio_samples_per_second!=0); } - -AVSC_INLINE int avs_is_rgb(const AVS_VideoInfo * p) - { return !!(p->pixel_type&AVS_CS_BGR); } - -AVSC_INLINE int avs_is_rgb24(const AVS_VideoInfo * p) - { return (p->pixel_type&AVS_CS_BGR24)==AVS_CS_BGR24; } // Clear out additional properties - -AVSC_INLINE int avs_is_rgb32(const AVS_VideoInfo * p) - { return (p->pixel_type & AVS_CS_BGR32) == AVS_CS_BGR32 ; } - -AVSC_INLINE int avs_is_yuv(const AVS_VideoInfo * p) - { return !!(p->pixel_type&AVS_CS_YUV ); } - -AVSC_INLINE int avs_is_yuy2(const AVS_VideoInfo * p) - { return (p->pixel_type & AVS_CS_YUY2) == AVS_CS_YUY2; } - -AVSC_INLINE int avs_is_yv12(const AVS_VideoInfo * p) - { return ((p->pixel_type & AVS_CS_YV12) == AVS_CS_YV12)||((p->pixel_type & AVS_CS_I420) == AVS_CS_I420); } - -AVSC_INLINE int avs_is_color_space(const AVS_VideoInfo * p, int c_space) - { return ((p->pixel_type & c_space) == c_space); } - -AVSC_INLINE int avs_is_property(const AVS_VideoInfo * p, int property) - { return ((p->pixel_type & property)==property ); } - -AVSC_INLINE int avs_is_planar(const AVS_VideoInfo * p) - { return !!(p->pixel_type & AVS_CS_PLANAR); } - -AVSC_INLINE int avs_is_field_based(const AVS_VideoInfo * p) - { return !!(p->image_type & AVS_IT_FIELDBASED); } - -AVSC_INLINE int avs_is_parity_known(const AVS_VideoInfo * p) - { return ((p->image_type & AVS_IT_FIELDBASED)&&(p->image_type & (AVS_IT_BFF | AVS_IT_TFF))); } - -AVSC_INLINE int avs_is_bff(const AVS_VideoInfo * p) - { return !!(p->image_type & AVS_IT_BFF); } - -AVSC_INLINE int avs_is_tff(const AVS_VideoInfo * p) - { return !!(p->image_type & AVS_IT_TFF); } - -AVSC_INLINE int avs_bits_per_pixel(const AVS_VideoInfo * p) -{ - switch (p->pixel_type) { - case AVS_CS_BGR24: return 24; - case AVS_CS_BGR32: return 32; - case AVS_CS_YUY2: return 16; - case AVS_CS_YV12: - case AVS_CS_I420: return 12; - default: return 0; - } -} -AVSC_INLINE int avs_bytes_from_pixels(const AVS_VideoInfo * p, int pixels) - { return pixels * (avs_bits_per_pixel(p)>>3); } // Will work on planar images, but will return only luma planes - -AVSC_INLINE int avs_row_size(const AVS_VideoInfo * p) - { return avs_bytes_from_pixels(p,p->width); } // Also only returns first plane on planar images - -AVSC_INLINE int avs_bmp_size(const AVS_VideoInfo * vi) - { if (avs_is_planar(vi)) {int p = vi->height * ((avs_row_size(vi)+3) & ~3); p+=p>>1; return p; } return vi->height * ((avs_row_size(vi)+3) & ~3); } - -AVSC_INLINE int avs_samples_per_second(const AVS_VideoInfo * p) - { return p->audio_samples_per_second; } - - -AVSC_INLINE int avs_bytes_per_channel_sample(const AVS_VideoInfo * p) -{ - switch (p->sample_type) { - case AVS_SAMPLE_INT8: return sizeof(signed char); - case AVS_SAMPLE_INT16: return sizeof(signed short); - case AVS_SAMPLE_INT24: return 3; - case AVS_SAMPLE_INT32: return sizeof(signed int); - case AVS_SAMPLE_FLOAT: return sizeof(float); - default: return 0; - } -} -AVSC_INLINE int avs_bytes_per_audio_sample(const AVS_VideoInfo * p) - { return p->nchannels*avs_bytes_per_channel_sample(p);} - -AVSC_INLINE INT64 avs_audio_samples_from_frames(const AVS_VideoInfo * p, INT64 frames) - { return ((INT64)(frames) * p->audio_samples_per_second * p->fps_denominator / p->fps_numerator); } - -AVSC_INLINE int avs_frames_from_audio_samples(const AVS_VideoInfo * p, INT64 samples) - { return (int)(samples * (INT64)p->fps_numerator / (INT64)p->fps_denominator / (INT64)p->audio_samples_per_second); } - -AVSC_INLINE INT64 avs_audio_samples_from_bytes(const AVS_VideoInfo * p, INT64 bytes) - { return bytes / avs_bytes_per_audio_sample(p); } - -AVSC_INLINE INT64 avs_bytes_from_audio_samples(const AVS_VideoInfo * p, INT64 samples) - { return samples * avs_bytes_per_audio_sample(p); } - -AVSC_INLINE int avs_audio_channels(const AVS_VideoInfo * p) - { return p->nchannels; } - -AVSC_INLINE int avs_sample_type(const AVS_VideoInfo * p) - { return p->sample_type;} - -// useful mutator -AVSC_INLINE void avs_set_property(AVS_VideoInfo * p, int property) - { p->image_type|=property; } - -AVSC_INLINE void avs_clear_property(AVS_VideoInfo * p, int property) - { p->image_type&=~property; } - -AVSC_INLINE void avs_set_field_based(AVS_VideoInfo * p, int isfieldbased) - { if (isfieldbased) p->image_type|=AVS_IT_FIELDBASED; else p->image_type&=~AVS_IT_FIELDBASED; } - -AVSC_INLINE void avs_set_fps(AVS_VideoInfo * p, unsigned numerator, unsigned denominator) -{ - unsigned x=numerator, y=denominator; - while (y) { // find gcd - unsigned t = x%y; x = y; y = t; - } - p->fps_numerator = numerator/x; - p->fps_denominator = denominator/x; -} - -AVSC_INLINE int avs_is_same_colorspace(AVS_VideoInfo * x, AVS_VideoInfo * y) -{ - return (x->pixel_type == y->pixel_type) - || (avs_is_yv12(x) && avs_is_yv12(y)); -} - -///////////////////////////////////////////////////////////////////// -// -// AVS_VideoFrame -// - -// VideoFrameBuffer holds information about a memory block which is used -// for video data. For efficiency, instances of this class are not deleted -// when the refcount reaches zero; instead they're stored in a linked list -// to be reused. The instances are deleted when the corresponding AVS -// file is closed. - -// AVS_VideoFrameBuffer is layed out identicly to VideoFrameBuffer -// DO NOT USE THIS STRUCTURE DIRECTLY -typedef struct AVS_VideoFrameBuffer { - unsigned char * data; - int data_size; - // sequence_number is incremented every time the buffer is changed, so - // that stale views can tell they're no longer valid. - long sequence_number; - - long refcount; -} AVS_VideoFrameBuffer; - -// VideoFrame holds a "window" into a VideoFrameBuffer. - -// AVS_VideoFrame is layed out identicly to IVideoFrame -// DO NOT USE THIS STRUCTURE DIRECTLY -typedef struct AVS_VideoFrame { - int refcount; - AVS_VideoFrameBuffer * vfb; - int offset, pitch, row_size, height, offsetU, offsetV, pitchUV; // U&V offsets are from top of picture. -} AVS_VideoFrame; - -// Access functions for AVS_VideoFrame -AVSC_INLINE int avs_get_pitch(const AVS_VideoFrame * p) { - return p->pitch;} - -AVSC_INLINE int avs_get_pitch_p(const AVS_VideoFrame * p, int plane) { - switch (plane) { - case AVS_PLANAR_U: case AVS_PLANAR_V: return p->pitchUV;} - return p->pitch;} - -AVSC_INLINE int avs_get_row_size(const AVS_VideoFrame * p) { - return p->row_size; } - -AVSC_INLINE int avs_get_row_size_p(const AVS_VideoFrame * p, int plane) { - int r; - switch (plane) { - case AVS_PLANAR_U: case AVS_PLANAR_V: - if (p->pitchUV) return p->row_size>>1; - else return 0; - case AVS_PLANAR_U_ALIGNED: case AVS_PLANAR_V_ALIGNED: - if (p->pitchUV) { - r = ((p->row_size+AVS_FRAME_ALIGN-1)&(~(AVS_FRAME_ALIGN-1)) )>>1; // Aligned rowsize - if (r < p->pitchUV) - return r; - return p->row_size>>1; - } else return 0; - case AVS_PLANAR_Y_ALIGNED: - r = (p->row_size+AVS_FRAME_ALIGN-1)&(~(AVS_FRAME_ALIGN-1)); // Aligned rowsize - if (r <= p->pitch) - return r; - return p->row_size; - } - return p->row_size; -} - -AVSC_INLINE int avs_get_height(const AVS_VideoFrame * p) { - return p->height;} - -AVSC_INLINE int avs_get_height_p(const AVS_VideoFrame * p, int plane) { - switch (plane) { - case AVS_PLANAR_U: case AVS_PLANAR_V: - if (p->pitchUV) return p->height>>1; - return 0; - } - return p->height;} - -AVSC_INLINE const unsigned char* avs_get_read_ptr(const AVS_VideoFrame * p) { - return p->vfb->data + p->offset;} - -AVSC_INLINE const unsigned char* avs_get_read_ptr_p(const AVS_VideoFrame * p, int plane) -{ - switch (plane) { - case AVS_PLANAR_U: return p->vfb->data + p->offsetU; - case AVS_PLANAR_V: return p->vfb->data + p->offsetV; - default: return p->vfb->data + p->offset;} -} - -AVSC_INLINE int avs_is_writable(const AVS_VideoFrame * p) { - return (p->refcount == 1 && p->vfb->refcount == 1);} - -AVSC_INLINE unsigned char* avs_get_write_ptr(const AVS_VideoFrame * p) -{ - if (avs_is_writable(p)) { - ++p->vfb->sequence_number; - return p->vfb->data + p->offset; - } else - return 0; -} - -AVSC_INLINE unsigned char* avs_get_write_ptr_p(const AVS_VideoFrame * p, int plane) -{ - if (plane==AVS_PLANAR_Y && avs_is_writable(p)) { - ++p->vfb->sequence_number; - return p->vfb->data + p->offset; - } else if (plane==AVS_PLANAR_Y) { - return 0; - } else { - switch (plane) { - case AVS_PLANAR_U: return p->vfb->data + p->offsetU; - case AVS_PLANAR_V: return p->vfb->data + p->offsetV; - default: return p->vfb->data + p->offset; - } - } -} - -#if defined __cplusplus -extern "C" -{ -#endif // __cplusplus -AVSC_API(void, avs_release_video_frame)(AVS_VideoFrame *); -// makes a shallow copy of a video frame -AVSC_API(AVS_VideoFrame *, avs_copy_video_frame)(AVS_VideoFrame *); -#if defined __cplusplus -} -#endif // __cplusplus - -#ifndef AVSC_NO_DECLSPEC -AVSC_INLINE void avs_release_frame(AVS_VideoFrame * f) - {avs_release_video_frame(f);} -AVSC_INLINE AVS_VideoFrame * avs_copy_frame(AVS_VideoFrame * f) - {return avs_copy_video_frame(f);} -#endif - -///////////////////////////////////////////////////////////////////// -// -// AVS_Value -// - -// Treat AVS_Value as a fat pointer. That is use avs_copy_value -// and avs_release_value appropiaty as you would if AVS_Value was -// a pointer. - -// To maintain source code compatibility with future versions of the -// avisynth_c API don't use the AVS_Value directly. Use the helper -// functions below. - -// AVS_Value is layed out identicly to AVSValue -typedef struct AVS_Value AVS_Value; -struct AVS_Value { - short type; // 'a'rray, 'c'lip, 'b'ool, 'i'nt, 'f'loat, 's'tring, 'v'oid, or 'l'ong - // for some function e'rror - short array_size; - union { - void * clip; // do not use directly, use avs_take_clip - char boolean; - int integer; - INT64 integer64; // match addition of __int64 to avxplugin.h - float floating_pt; - const char * string; - const AVS_Value * array; - } d; -}; - -// AVS_Value should be initilized with avs_void. -// Should also set to avs_void after the value is released -// with avs_copy_value. Consider it the equalvent of setting -// a pointer to NULL -static const AVS_Value avs_void = {'v'}; - -AVSC_API(void, avs_copy_value)(AVS_Value * dest, AVS_Value src); -AVSC_API(void, avs_release_value)(AVS_Value); - -AVSC_INLINE int avs_defined(AVS_Value v) { return v.type != 'v'; } -AVSC_INLINE int avs_is_clip(AVS_Value v) { return v.type == 'c'; } -AVSC_INLINE int avs_is_bool(AVS_Value v) { return v.type == 'b'; } -AVSC_INLINE int avs_is_int(AVS_Value v) { return v.type == 'i'; } -AVSC_INLINE int avs_is_float(AVS_Value v) { return v.type == 'f' || v.type == 'i'; } -AVSC_INLINE int avs_is_string(AVS_Value v) { return v.type == 's'; } -AVSC_INLINE int avs_is_array(AVS_Value v) { return v.type == 'a'; } -AVSC_INLINE int avs_is_error(AVS_Value v) { return v.type == 'e'; } - -#if defined __cplusplus -extern "C" -{ -#endif // __cplusplus -AVSC_API(AVS_Clip *, avs_take_clip)(AVS_Value, AVS_ScriptEnvironment *); -AVSC_API(void, avs_set_to_clip)(AVS_Value *, AVS_Clip *); -#if defined __cplusplus -} -#endif // __cplusplus - -AVSC_INLINE int avs_as_bool(AVS_Value v) - { return v.d.boolean; } -AVSC_INLINE int avs_as_int(AVS_Value v) - { return v.d.integer; } -AVSC_INLINE const char * avs_as_string(AVS_Value v) - { return avs_is_error(v) || avs_is_string(v) ? v.d.string : 0; } -AVSC_INLINE double avs_as_float(AVS_Value v) - { return avs_is_int(v) ? v.d.integer : v.d.floating_pt; } -AVSC_INLINE const char * avs_as_error(AVS_Value v) - { return avs_is_error(v) ? v.d.string : 0; } -AVSC_INLINE const AVS_Value * avs_as_array(AVS_Value v) - { return v.d.array; } -AVSC_INLINE int avs_array_size(AVS_Value v) - { return avs_is_array(v) ? v.array_size : 1; } -AVSC_INLINE AVS_Value avs_array_elt(AVS_Value v, int index) - { return avs_is_array(v) ? v.d.array[index] : v; } - -// only use these functions on am AVS_Value that does not already have -// an active value. Remember, treat AVS_Value as a fat pointer. -AVSC_INLINE AVS_Value avs_new_value_bool(int v0) - { AVS_Value v = {0}; v.type = 'b'; v.d.boolean = v0 == 0 ? 0 : 1; return v; } -AVSC_INLINE AVS_Value avs_new_value_int(int v0) - { AVS_Value v = {0}; v.type = 'i'; v.d.integer = v0; return v; } -AVSC_INLINE AVS_Value avs_new_value_string(const char * v0) - { AVS_Value v = {0}; v.type = 's'; v.d.string = v0; return v; } -AVSC_INLINE AVS_Value avs_new_value_float(float v0) - { AVS_Value v = {0}; v.type = 'f'; v.d.floating_pt = v0; return v;} -AVSC_INLINE AVS_Value avs_new_value_error(const char * v0) - { AVS_Value v = {0}; v.type = 'e'; v.d.string = v0; return v; } -#ifndef AVSC_NO_DECLSPEC -AVSC_INLINE AVS_Value avs_new_value_clip(AVS_Clip * v0) - { AVS_Value v = {0}; avs_set_to_clip(&v, v0); return v; } -#endif -AVSC_INLINE AVS_Value avs_new_value_array(AVS_Value * v0, int size) - { AVS_Value v = {0}; v.type = 'a'; v.d.array = v0; v.array_size = size; return v; } - -///////////////////////////////////////////////////////////////////// -// -// AVS_Clip -// -#if defined __cplusplus -extern "C" -{ -#endif // __cplusplus -AVSC_API(void, avs_release_clip)(AVS_Clip *); -AVSC_API(AVS_Clip *, avs_copy_clip)(AVS_Clip *); - -AVSC_API(const char *, avs_clip_get_error)(AVS_Clip *); // return 0 if no error - -AVSC_API(const AVS_VideoInfo *, avs_get_video_info)(AVS_Clip *); - -AVSC_API(int, avs_get_version)(AVS_Clip *); - -AVSC_API(AVS_VideoFrame *, avs_get_frame)(AVS_Clip *, int n); -// The returned video frame must be released with avs_release_video_frame - -AVSC_API(int, avs_get_parity)(AVS_Clip *, int n); -// return field parity if field_based, else parity of first field in frame - -AVSC_API(int, avs_get_audio)(AVS_Clip *, void * buf, - INT64 start, INT64 count); -// start and count are in samples - -AVSC_API(int, avs_set_cache_hints)(AVS_Clip *, - int cachehints, size_t frame_range); -#if defined __cplusplus -} -#endif // __cplusplus - -// This is the callback type used by avs_add_function -typedef AVS_Value (AVSC_CC * AVS_ApplyFunc) - (AVS_ScriptEnvironment *, AVS_Value args, void * user_data); - -typedef struct AVS_FilterInfo AVS_FilterInfo; -struct AVS_FilterInfo -{ - // these members should not be modified outside of the AVS_ApplyFunc callback - AVS_Clip * child; - AVS_VideoInfo vi; - AVS_ScriptEnvironment * env; - AVS_VideoFrame * (AVSC_CC * get_frame)(AVS_FilterInfo *, int n); - int (AVSC_CC * get_parity)(AVS_FilterInfo *, int n); - int (AVSC_CC * get_audio)(AVS_FilterInfo *, void * buf, - INT64 start, INT64 count); - int (AVSC_CC * set_cache_hints)(AVS_FilterInfo *, int cachehints, - int frame_range); - void (AVSC_CC * free_filter)(AVS_FilterInfo *); - - // Should be set when ever there is an error to report. - // It is cleared before any of the above methods are called - const char * error; - // this is to store whatever and may be modified at will - void * user_data; -}; - -// Create a new filter -// fi is set to point to the AVS_FilterInfo so that you can -// modify it once it is initilized. -// store_child should generally be set to true. If it is not -// set than ALL methods (the function pointers) must be defined -// If it is set than you do not need to worry about freeing the child -// clip. -#if defined __cplusplus -extern "C" -{ -#endif // __cplusplus -AVSC_API(AVS_Clip *, avs_new_c_filter)(AVS_ScriptEnvironment * e, - AVS_FilterInfo * * fi, - AVS_Value child, int store_child); -#if defined __cplusplus -} -#endif // __cplusplus - - -///////////////////////////////////////////////////////////////////// -// -// AVS_ScriptEnvironment -// - -// For GetCPUFlags. These are backwards-compatible with those in VirtualDub. -enum { - /* slowest CPU to support extension */ - AVS_CPU_FORCE = 0x01, // N/A - AVS_CPU_FPU = 0x02, // 386/486DX - AVS_CPU_MMX = 0x04, // P55C, K6, PII - AVS_CPU_INTEGER_SSE = 0x08, // PIII, Athlon - AVS_CPU_SSE = 0x10, // PIII, Athlon XP/MP - AVS_CPU_SSE2 = 0x20, // PIV, Hammer - AVS_CPU_3DNOW = 0x40, // K6-2 - AVS_CPU_3DNOW_EXT = 0x80, // Athlon - AVS_CPU_X86_64 = 0xA0, // Hammer (note: equiv. to 3DNow + SSE2, - // which only Hammer will have anyway) -}; - -#if defined __cplusplus -extern "C" -{ -#endif // __cplusplus -AVSC_API(const char *, avs_get_error)(AVS_ScriptEnvironment *); // return 0 if no error - -AVSC_API(long, avs_get_cpu_flags)(AVS_ScriptEnvironment *); -AVSC_API(int, avs_check_version)(AVS_ScriptEnvironment *, int version); - -AVSC_API(char *, avs_save_string)(AVS_ScriptEnvironment *, const char* s, int length); -AVSC_API(char *, avs_sprintf)(AVS_ScriptEnvironment *, const char * fmt, ...); - -AVSC_API(char *, avs_vsprintf)(AVS_ScriptEnvironment *, const char * fmt, va_list val); - // note: val is really a va_list; I hope everyone typedefs va_list to a pointer - -AVSC_API(int, avs_add_function)(AVS_ScriptEnvironment *, - const char * name, const char * params, - AVS_ApplyFunc apply, void * user_data); - -AVSC_API(int, avs_function_exists)(AVS_ScriptEnvironment *, const char * name); - -AVSC_API(AVS_Value, avs_invoke)(AVS_ScriptEnvironment *, const char * name, - AVS_Value args, const char** arg_names); -// The returned value must be be released with avs_release_value - -AVSC_API(AVS_Value, avs_get_var)(AVS_ScriptEnvironment *, const char* name); -// The returned value must be be released with avs_release_value - -AVSC_API(int, avs_set_var)(AVS_ScriptEnvironment *, const char* name, AVS_Value val); - -AVSC_API(int, avs_set_global_var)(AVS_ScriptEnvironment *, const char* name, const AVS_Value val); - -//void avs_push_context(AVS_ScriptEnvironment *, int level=0); -//void avs_pop_context(AVS_ScriptEnvironment *); - -AVSC_API(AVS_VideoFrame *, avs_new_video_frame_a)(AVS_ScriptEnvironment *, - const AVS_VideoInfo * vi, int align); -// align should be at least 16 -#if defined __cplusplus -} -#endif // __cplusplus - -#ifndef AVSC_NO_DECLSPEC -AVSC_INLINE -AVS_VideoFrame * avs_new_video_frame(AVS_ScriptEnvironment * env, - const AVS_VideoInfo * vi) - {return avs_new_video_frame_a(env,vi,AVS_FRAME_ALIGN);} - -AVSC_INLINE -AVS_VideoFrame * avs_new_frame(AVS_ScriptEnvironment * env, - const AVS_VideoInfo * vi) - {return avs_new_video_frame_a(env,vi,AVS_FRAME_ALIGN);} -#endif - -#if defined __cplusplus -extern "C" -{ -#endif // __cplusplus -AVSC_API(int, avs_make_writable)(AVS_ScriptEnvironment *, AVS_VideoFrame * * pvf); - -AVSC_API(void, avs_bit_blt)(AVS_ScriptEnvironment *, unsigned char* dstp, int dst_pitch, const unsigned char* srcp, int src_pitch, int row_size, int height); - -typedef void (AVSC_CC *AVS_ShutdownFunc)(void* user_data, AVS_ScriptEnvironment * env); -AVSC_API(void, avs_at_exit)(AVS_ScriptEnvironment *, AVS_ShutdownFunc function, void * user_data); - -AVSC_API(AVS_VideoFrame *, avs_subframe)(AVS_ScriptEnvironment *, AVS_VideoFrame * src, int rel_offset, int new_pitch, int new_row_size, int new_height); -// The returned video frame must be be released - -AVSC_API(int, avs_set_memory_max)(AVS_ScriptEnvironment *, int mem); - -AVSC_API(int, avs_set_working_dir)(AVS_ScriptEnvironment *, const char * newdir); - -// avisynth.dll exports this; it's a way to use it as a library, without -// writing an AVS script or without going through AVIFile. -AVSC_API(AVS_ScriptEnvironment *, avs_create_script_environment)(int version); -#if defined __cplusplus -} -#endif // __cplusplus - -// this symbol is the entry point for the plugin and must -// be defined -AVSC_EXPORT -const char * AVSC_CC avisynth_c_plugin_init(AVS_ScriptEnvironment* env); - - -#if defined __cplusplus -extern "C" -{ -#endif // __cplusplus -AVSC_API(void, avs_delete_script_environment)(AVS_ScriptEnvironment *); - - -AVSC_API(AVS_VideoFrame *, avs_subframe_planar)(AVS_ScriptEnvironment *, AVS_VideoFrame * src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV); -// The returned video frame must be be released -#if defined __cplusplus -} -#endif // __cplusplus - -#endif //__AVXSYNTH_C__ diff --git a/compat/avisynth/windowsPorts/basicDataTypeConversions.h b/compat/avisynth/windowsPorts/basicDataTypeConversions.h deleted file mode 100644 index ff367d5a2a7..00000000000 --- a/compat/avisynth/windowsPorts/basicDataTypeConversions.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef __DATA_TYPE_CONVERSIONS_H__ -#define __DATA_TYPE_CONVERSIONS_H__ - -#include -#include - -#ifdef __cplusplus -namespace avxsynth { -#endif // __cplusplus - -typedef int64_t __int64; -typedef int32_t __int32; -#ifdef __cplusplus -typedef bool BOOL; -#else -typedef uint32_t BOOL; -#endif // __cplusplus -typedef void* HMODULE; -typedef void* LPVOID; -typedef void* PVOID; -typedef PVOID HANDLE; -typedef HANDLE HWND; -typedef HANDLE HINSTANCE; -typedef void* HDC; -typedef void* HBITMAP; -typedef void* HICON; -typedef void* HFONT; -typedef void* HGDIOBJ; -typedef void* HBRUSH; -typedef void* HMMIO; -typedef void* HACMSTREAM; -typedef void* HACMDRIVER; -typedef void* HIC; -typedef void* HACMOBJ; -typedef HACMSTREAM* LPHACMSTREAM; -typedef void* HACMDRIVERID; -typedef void* LPHACMDRIVER; -typedef unsigned char BYTE; -typedef BYTE* LPBYTE; -typedef char TCHAR; -typedef TCHAR* LPTSTR; -typedef const TCHAR* LPCTSTR; -typedef char* LPSTR; -typedef LPSTR LPOLESTR; -typedef const char* LPCSTR; -typedef LPCSTR LPCOLESTR; -typedef wchar_t WCHAR; -typedef unsigned short WORD; -typedef unsigned int UINT; -typedef UINT MMRESULT; -typedef uint32_t DWORD; -typedef DWORD COLORREF; -typedef DWORD FOURCC; -typedef DWORD HRESULT; -typedef DWORD* LPDWORD; -typedef DWORD* DWORD_PTR; -typedef int32_t LONG; -typedef int32_t* LONG_PTR; -typedef LONG_PTR LRESULT; -typedef uint32_t ULONG; -typedef uint32_t* ULONG_PTR; -//typedef __int64_t intptr_t; -typedef uint64_t _fsize_t; - - -// -// Structures -// - -typedef struct _GUID { - DWORD Data1; - WORD Data2; - WORD Data3; - BYTE Data4[8]; -} GUID; - -typedef GUID REFIID; -typedef GUID CLSID; -typedef CLSID* LPCLSID; -typedef GUID IID; - -#ifdef __cplusplus -}; // namespace avxsynth -#endif // __cplusplus -#endif // __DATA_TYPE_CONVERSIONS_H__ diff --git a/compat/avisynth/windowsPorts/windows2linux.h b/compat/avisynth/windowsPorts/windows2linux.h deleted file mode 100644 index 7cf46002665..00000000000 --- a/compat/avisynth/windowsPorts/windows2linux.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef __WINDOWS2LINUX_H__ -#define __WINDOWS2LINUX_H__ - -/* - * LINUX SPECIFIC DEFINITIONS -*/ -// -// Data types conversions -// -#include -#include -#include "basicDataTypeConversions.h" - -#ifdef __cplusplus -namespace avxsynth { -#endif // __cplusplus -// -// purposefully define the following MSFT definitions -// to mean nothing (as they do not mean anything on Linux) -// -#define __stdcall -#define __cdecl -#define noreturn -#define __declspec(x) -#define STDAPI extern "C" HRESULT -#define STDMETHODIMP HRESULT __stdcall -#define STDMETHODIMP_(x) x __stdcall - -#define STDMETHOD(x) virtual HRESULT x -#define STDMETHOD_(a, x) virtual a x - -#ifndef TRUE -#define TRUE true -#endif - -#ifndef FALSE -#define FALSE false -#endif - -#define S_OK (0x00000000) -#define S_FALSE (0x00000001) -#define E_NOINTERFACE (0X80004002) -#define E_POINTER (0x80004003) -#define E_FAIL (0x80004005) -#define E_OUTOFMEMORY (0x8007000E) - -#define INVALID_HANDLE_VALUE ((HANDLE)((LONG_PTR)-1)) -#define FAILED(hr) ((hr) & 0x80000000) -#define SUCCEEDED(hr) (!FAILED(hr)) - - -// -// Functions -// -#define MAKEDWORD(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) -#define MAKEWORD(a,b) (((a) << 8) | (b)) - -#define lstrlen strlen -#define lstrcpy strcpy -#define lstrcmpi strcasecmp -#define _stricmp strcasecmp -#define InterlockedIncrement(x) __sync_fetch_and_add((x), 1) -#define InterlockedDecrement(x) __sync_fetch_and_sub((x), 1) -// Windows uses (new, old) ordering but GCC has (old, new) -#define InterlockedCompareExchange(x,y,z) __sync_val_compare_and_swap(x,z,y) - -#define UInt32x32To64(a, b) ( (uint64_t) ( ((uint64_t)((uint32_t)(a))) * ((uint32_t)(b)) ) ) -#define Int64ShrlMod32(a, b) ( (uint64_t) ( (uint64_t)(a) >> (b) ) ) -#define Int32x32To64(a, b) ((__int64)(((__int64)((long)(a))) * ((long)(b)))) - -#define MulDiv(nNumber, nNumerator, nDenominator) (int32_t) (((int64_t) (nNumber) * (int64_t) (nNumerator) + (int64_t) ((nDenominator)/2)) / (int64_t) (nDenominator)) - -#ifdef __cplusplus -}; // namespace avxsynth -#endif // __cplusplus - -#endif // __WINDOWS2LINUX_H__ diff --git a/compat/cuda/ptx2c.sh b/compat/cuda/ptx2c.sh index 0750e7a3b78..48452379c2b 100755 --- a/compat/cuda/ptx2c.sh +++ b/compat/cuda/ptx2c.sh @@ -27,10 +27,8 @@ IN="$2" NAME="$(basename "$IN" | sed 's/\..*//')" printf "const char %s_ptx[] = \\" "$NAME" > "$OUT" -while IFS= read -r LINE -do - printf "\n\t\"%s\\\n\"" "$(printf "%s" "$LINE" | sed -e 's/\r//g' -e 's/["\\]/\\&/g')" >> "$OUT" -done < "$IN" -printf ";\n" >> "$OUT" +echo >> "$OUT" +sed -e "$(printf 's/\r//g')" -e 's/["\\]/\\&/g' -e "$(printf 's/^/\t"/')" -e 's/$/\\n"/' < "$IN" >> "$OUT" +echo ";" >> "$OUT" exit 0 diff --git a/compat/os2threads.h b/compat/os2threads.h index 2177a033ecb..a061eaa63de 100644 --- a/compat/os2threads.h +++ b/compat/os2threads.h @@ -27,15 +27,19 @@ #define COMPAT_OS2THREADS_H #define INCL_DOS +#define INCL_DOSERRORS #include #undef __STRICT_ANSI__ /* for _beginthread() */ #include +#include #include #include #include "libavutil/attributes.h" +#include "libavutil/common.h" +#include "libavutil/time.h" typedef struct { TID tid; @@ -163,6 +167,28 @@ static av_always_inline int pthread_cond_broadcast(pthread_cond_t *cond) return 0; } +static av_always_inline int pthread_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + int64_t abs_milli = abstime->tv_sec * 1000LL + abstime->tv_nsec / 1000000; + ULONG t = av_clip64(abs_milli - av_gettime() / 1000, 0, ULONG_MAX); + + __atomic_increment(&cond->wait_count); + + pthread_mutex_unlock(mutex); + + APIRET ret = DosWaitEventSem(cond->event_sem, t); + + __atomic_decrement(&cond->wait_count); + + DosPostEventSem(cond->ack_sem); + + pthread_mutex_lock(mutex); + + return (ret == ERROR_TIMEOUT) ? ETIMEDOUT : 0; +} + static av_always_inline int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { diff --git a/compat/w32pthreads.h b/compat/w32pthreads.h index 21acfd2ba1a..6405e72b64f 100644 --- a/compat/w32pthreads.h +++ b/compat/w32pthreads.h @@ -38,11 +38,13 @@ #define WIN32_LEAN_AND_MEAN #include #include +#include #include "libavutil/attributes.h" #include "libavutil/common.h" #include "libavutil/internal.h" #include "libavutil/mem.h" +#include "libavutil/time.h" typedef struct pthread_t { void *handle; @@ -61,6 +63,9 @@ typedef CONDITION_VARIABLE pthread_cond_t; #define InitializeCriticalSection(x) InitializeCriticalSectionEx(x, 0, 0) #define WaitForSingleObject(a, b) WaitForSingleObjectEx(a, b, FALSE) +#define PTHREAD_CANCEL_ENABLE 1 +#define PTHREAD_CANCEL_DISABLE 0 + static av_unused unsigned __stdcall attribute_align_arg win32thread_worker(void *arg) { pthread_t *h = (pthread_t*)arg; @@ -156,10 +161,31 @@ static inline int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex return 0; } +static inline int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + int64_t abs_milli = abstime->tv_sec * 1000LL + abstime->tv_nsec / 1000000; + DWORD t = av_clip64(abs_milli - av_gettime() / 1000, 0, UINT32_MAX); + + if (!SleepConditionVariableSRW(cond, mutex, t, 0)) { + DWORD err = GetLastError(); + if (err == ERROR_TIMEOUT) + return ETIMEDOUT; + else + return EINVAL; + } + return 0; +} + static inline int pthread_cond_signal(pthread_cond_t *cond) { WakeConditionVariable(cond); return 0; } +static inline int pthread_setcancelstate(int state, int *oldstate) +{ + return 0; +} + #endif /* COMPAT_W32PTHREADS_H */ diff --git a/configure b/configure index 34c2adb4a4f..8569a60bf82 100755 --- a/configure +++ b/configure @@ -236,6 +236,7 @@ External library support: --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-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] --enable-libiec61883 enable iec61883 via libiec61883 [no] @@ -254,6 +255,8 @@ External library support: --enable-libopenmpt enable decoding tracked files via libopenmpt [no] --enable-libopus enable Opus de/encoding via libopus [no] --enable-libpulse enable Pulseaudio input via libpulse [no] + --enable-librabbitmq enable RabbitMQ library [no] + --enable-librav1e enable AV1 encoding via rav1e [no] --enable-librsvg enable SVG rasterization via librsvg [no] --enable-librubberband enable rubberband needed for rubberband filter [no] --enable-librtmp enable RTMP[E] support via librtmp [no] @@ -301,6 +304,7 @@ External library support: --enable-mbedtls enable mbedTLS, needed for https support if openssl, gnutls or libtls is not used [no] --enable-mediacodec enable Android MediaCodec support [no] + --enable-mediafoundation enable encoding via MediaFoundation [auto] --enable-libmysofa enable libmysofa, needed for sofalizer filter [no] --enable-openal enable OpenAL 1.1 capture support [no] --enable-opencl enable OpenCL processing [no] @@ -315,6 +319,7 @@ 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] + --enable-vulkan enable Vulkan code [no] --disable-xlib disable xlib [autodetect] --disable-zlib disable zlib [autodetect] @@ -481,6 +486,7 @@ Developer options (useful when working on FFmpeg itself): --ignore-tests=TESTS comma-separated list (without "fate-" prefix in the name) of tests whose result is ignored --enable-linux-perf enable Linux Performance Monitor API + --disable-large-tests disable tests that use a large amount of memory NOTE: Object files are built at the place where configure is launched. EOF @@ -1547,11 +1553,11 @@ require_cc(){ } require_cpp(){ - name="$1" - headers="$2" - classes="$3" - shift 3 - check_lib_cpp "$headers" "$classes" "$@" || die "ERROR: $name not found" + log require_cpp "$@" + name_version="$1" + name="${1%% *}" + shift + check_lib_cpp "$name" "$@" || die "ERROR: $name_version not found" } require_headers(){ @@ -1662,7 +1668,7 @@ COMPONENT_LIST=" " EXAMPLE_LIST=" - avio_dir_cmd_example + avio_list_dir_example avio_reading_example decode_audio_example decode_video_example @@ -1699,6 +1705,7 @@ EXTERNAL_AUTODETECT_LIBRARY_LIST=" libxcb_shape libxcb_xfixes lzma + mediafoundation schannel sdl2 securetransport @@ -1768,6 +1775,7 @@ EXTERNAL_LIBRARY_LIST=" libfontconfig libfreetype libfribidi + libglslang libgme libgsm libiec61883 @@ -1784,6 +1792,8 @@ EXTERNAL_LIBRARY_LIST=" libopenmpt libopus libpulse + librabbitmq + librav1e librsvg librtmp libshine @@ -1851,6 +1861,7 @@ HWACCEL_LIBRARY_LIST=" mmal omx opencl + vulkan " DOCUMENT_LIST=" @@ -1929,6 +1940,7 @@ CONFIG_LIST=" $SUBSYSTEM_LIST autodetect fontconfig + large_tests linux_perf memory_poisoning neon_clobber_test @@ -2192,10 +2204,12 @@ SYSTEM_FUNCS=" getaddrinfo gethrtime getopt + GetModuleHandle GetProcessAffinityMask GetProcessMemoryInfo GetProcessTimes getrusage + GetStdHandle GetSystemTimeAsFileTime gettimeofday glob @@ -2221,6 +2235,7 @@ SYSTEM_FUNCS=" SecItemImport SetConsoleTextAttribute SetConsoleCtrlHandler + SetDllDirectory setmode setrlimit Sleep @@ -2268,6 +2283,9 @@ TOOLCHAIN_FEATURES=" TYPES_LIST=" kCMVideoCodecType_HEVC kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange + kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ + kCVImageBufferTransferFunction_ITU_R_2100_HLG + kCVImageBufferTransferFunction_Linear socklen_t struct_addrinfo struct_group_source_req @@ -2632,6 +2650,7 @@ ac3_decoder_select="ac3_parser ac3dsp bswapdsp fmtconvert mdct" ac3_fixed_decoder_select="ac3_parser ac3dsp bswapdsp mdct" ac3_encoder_select="ac3dsp audiodsp mdct me_cmp" ac3_fixed_encoder_select="ac3dsp audiodsp mdct me_cmp" +acelp_kelvin_decoder_select="audiodsp" adpcm_g722_decoder_select="g722dsp" adpcm_g722_encoder_select="g722dsp" aic_decoder_select="golomb idctdsp" @@ -2655,7 +2674,9 @@ asv2_decoder_select="blockdsp bswapdsp idctdsp" asv2_encoder_select="aandcttables bswapdsp fdctdsp pixblockdsp" atrac1_decoder_select="mdct sinewin" atrac3_decoder_select="mdct" +atrac3al_decoder_select="mdct" atrac3p_decoder_select="mdct sinewin" +atrac3pal_decoder_select="mdct sinewin" atrac9_decoder_select="mdct" avrn_decoder_select="exif jpegtables" bink_decoder_select="blockdsp hpeldsp" @@ -2669,6 +2690,7 @@ cook_decoder_select="audiodsp mdct sinewin" cscd_decoder_select="lzo" cscd_decoder_suggest="zlib" dca_decoder_select="mdct" +dca_encoder_select="mdct" dds_decoder_select="texturedsp" dirac_decoder_select="dirac_parse dwt golomb videodsp mpegvideoenc" dnxhd_decoder_select="blockdsp idctdsp" @@ -2720,6 +2742,8 @@ huffyuv_encoder_select="bswapdsp huffman huffyuvencdsp llvidencdsp" hymt_decoder_select="huffyuv_decoder" iac_decoder_select="imc_decoder" imc_decoder_select="bswapdsp fft mdct sinewin" +imm4_decoder_select="bswapdsp" +imm5_decoder_select="h264_decoder hevc_decoder" indeo3_decoder_select="hpeldsp" indeo4_decoder_select="ividsp" indeo5_decoder_select="ividsp" @@ -2731,7 +2755,7 @@ ljpeg_encoder_select="idctdsp jpegtables mpegvideoenc" lscr_decoder_deps="zlib" magicyuv_decoder_select="llviddsp" magicyuv_encoder_select="llvidencdsp" -mdec_decoder_select="blockdsp idctdsp mpegvideo" +mdec_decoder_select="blockdsp bswapdsp idctdsp mpegvideo" metasound_decoder_select="lsp mdct sinewin" mimic_decoder_select="blockdsp bswapdsp hpeldsp idctdsp" mjpeg_decoder_select="blockdsp hpeldsp exif idctdsp jpegtables" @@ -2768,10 +2792,14 @@ msmpeg4v3_decoder_select="h263_decoder" msmpeg4v3_encoder_select="h263_encoder" mss2_decoder_select="mpegvideo qpeldsp vc1_decoder" mts2_decoder_select="mss34dsp" +mv30_decoder_select="aandcttables blockdsp" +mvha_decoder_deps="zlib" +mvha_decoder_select="llviddsp" mwsc_decoder_deps="zlib" mxpeg_decoder_select="mjpeg_decoder" nellymoser_decoder_select="mdct sinewin" nellymoser_encoder_select="audio_frame_queue mdct sinewin" +notchlc_decoder_select="lzf" nuv_decoder_select="idctdsp lzo" on2avc_decoder_select="mdct" opus_decoder_deps="swresample" @@ -2817,6 +2845,7 @@ tdsc_decoder_deps="zlib" tdsc_decoder_select="mjpeg_decoder" theora_decoder_select="vp3_decoder" thp_decoder_select="mjpeg_decoder" +tiff_decoder_select="mjpeg_decoder" tiff_decoder_suggest="zlib lzma" tiff_encoder_suggest="zlib" truehd_decoder_select="mlp_parser" @@ -2859,6 +2888,7 @@ wmv3_decoder_select="vc1_decoder" wmv3image_decoder_select="wmv3_decoder" xma1_decoder_select="wmapro_decoder" xma2_decoder_select="wmapro_decoder" +ylc_decoder_select="bswapdsp" zerocodec_decoder_deps="zlib" zlib_decoder_deps="zlib" zlib_encoder_deps="zlib" @@ -2974,6 +3004,8 @@ vp9_nvdec_hwaccel_deps="nvdec" vp9_nvdec_hwaccel_select="vp9_decoder" vp9_vaapi_hwaccel_deps="vaapi VADecPictureParameterBufferVP9_bit_depth" vp9_vaapi_hwaccel_select="vp9_decoder" +vp9_vdpau_hwaccel_deps="vdpau VdpPictureInfoVP9" +vp9_vdpau_hwaccel_select="vp9_decoder" wmv3_d3d11va_hwaccel_select="vc1_d3d11va_hwaccel" wmv3_d3d11va2_hwaccel_select="vc1_d3d11va2_hwaccel" wmv3_dxva2_hwaccel_select="vc1_dxva2_hwaccel" @@ -2982,6 +3014,8 @@ wmv3_vaapi_hwaccel_select="vc1_vaapi_hwaccel" wmv3_vdpau_hwaccel_select="vc1_vdpau_hwaccel" # hardware-accelerated codecs +mediafoundation_deps="mftransform_h MFCreateAlignedMemoryBuffer" +mediafoundation_extralibs="-lmfplat -lmfuuid -lole32 -lstrmiids" omx_deps="libdl pthreads" omx_rpi_select="omx" qsv_deps="libmfx" @@ -2998,12 +3032,16 @@ scale_cuda_filter_deps_any="cuda_nvcc cuda_llvm" thumbnail_cuda_filter_deps="ffnvcodec" thumbnail_cuda_filter_deps_any="cuda_nvcc cuda_llvm" transpose_npp_filter_deps="ffnvcodec libnpp" +overlay_cuda_filter_deps="ffnvcodec" +overlay_cuda_filter_deps_any="cuda_nvcc cuda_llvm" amf_deps_any="libdl LoadLibrary" nvenc_deps="ffnvcodec" nvenc_deps_any="libdl LoadLibrary" nvenc_encoder_deps="nvenc" +aac_mf_encoder_deps="mediafoundation" +ac3_mf_encoder_deps="mediafoundation" h263_v4l2m2m_decoder_deps="v4l2_m2m h263_v4l2_m2m" h263_v4l2m2m_encoder_deps="v4l2_m2m h263_v4l2_m2m" h264_amf_encoder_deps="amf" @@ -3012,10 +3050,11 @@ h264_cuvid_decoder_deps="cuvid" h264_cuvid_decoder_select="h264_mp4toannexb_bsf" h264_mediacodec_decoder_deps="mediacodec" h264_mediacodec_decoder_select="h264_mp4toannexb_bsf h264_parser" +h264_mf_encoder_deps="mediafoundation" h264_mmal_decoder_deps="mmal" h264_nvenc_encoder_deps="nvenc" h264_omx_encoder_deps="omx" -h264_qsv_decoder_select="h264_mp4toannexb_bsf h264_parser qsvdec" +h264_qsv_decoder_select="h264_mp4toannexb_bsf qsvdec" h264_qsv_encoder_select="qsvenc" h264_rkmpp_decoder_deps="rkmpp" h264_rkmpp_decoder_select="h264_mp4toannexb_bsf" @@ -3028,8 +3067,9 @@ hevc_cuvid_decoder_deps="cuvid" hevc_cuvid_decoder_select="hevc_mp4toannexb_bsf" hevc_mediacodec_decoder_deps="mediacodec" hevc_mediacodec_decoder_select="hevc_mp4toannexb_bsf hevc_parser" +hevc_mf_encoder_deps="mediafoundation" hevc_nvenc_encoder_deps="nvenc" -hevc_qsv_decoder_select="hevc_mp4toannexb_bsf hevc_parser qsvdec" +hevc_qsv_decoder_select="hevc_mp4toannexb_bsf qsvdec" hevc_qsv_encoder_select="hevcparse qsvenc" hevc_rkmpp_decoder_deps="rkmpp" hevc_rkmpp_decoder_select="hevc_mp4toannexb_bsf" @@ -3039,17 +3079,19 @@ hevc_v4l2m2m_decoder_deps="v4l2_m2m hevc_v4l2_m2m" hevc_v4l2m2m_decoder_select="hevc_mp4toannexb_bsf" hevc_v4l2m2m_encoder_deps="v4l2_m2m hevc_v4l2_m2m" mjpeg_cuvid_decoder_deps="cuvid" +mjpeg_qsv_decoder_select="qsvdec" mjpeg_qsv_encoder_deps="libmfx" mjpeg_qsv_encoder_select="qsvenc" mjpeg_vaapi_encoder_deps="VAEncPictureParameterBufferJPEG" 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" -mpeg2_qsv_decoder_select="qsvdec mpegvideo_parser" +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" @@ -3066,11 +3108,11 @@ nvenc_hevc_encoder_select="hevc_nvenc_encoder" vc1_crystalhd_decoder_select="crystalhd" vc1_cuvid_decoder_deps="cuvid" vc1_mmal_decoder_deps="mmal" -vc1_qsv_decoder_select="qsvdec vc1_parser" +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_qsv_decoder_select="qsvdec vp8_parser" +vp8_qsv_decoder_select="qsvdec" vp8_rkmpp_decoder_deps="rkmpp" vp8_vaapi_encoder_deps="VAEncPictureParameterBufferVP8" vp8_vaapi_encoder_select="vaapi_encode" @@ -3078,9 +3120,12 @@ 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_qsv_decoder_select="qsvdec" vp9_rkmpp_decoder_deps="rkmpp" vp9_vaapi_encoder_deps="VAEncPictureParameterBufferVP9" 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" @@ -3096,6 +3141,7 @@ vc1_parser_select="vc1dsp" # bitstream_filters aac_adtstoasc_bsf_select="adts_header" +av1_frame_merge_bsf_select="cbs_av1" av1_frame_split_bsf_select="cbs_av1" av1_metadata_bsf_select="cbs_av1" eac3_core_bsf_select="ac3_parser" @@ -3132,6 +3178,7 @@ mp2_at_decoder_select="mpegaudioheader" mp3_at_decoder_select="mpegaudioheader" pcm_alaw_at_decoder_deps="audiotoolbox" pcm_mulaw_at_decoder_deps="audiotoolbox" +qdmc_decoder_select="fft" qdmc_at_decoder_deps="audiotoolbox" qdm2_at_decoder_deps="audiotoolbox" aac_at_encoder_deps="audiotoolbox" @@ -3185,6 +3232,8 @@ libopenmpt_demuxer_deps="libopenmpt" libopus_decoder_deps="libopus" libopus_encoder_deps="libopus" libopus_encoder_select="audio_frame_queue" +librav1e_encoder_deps="librav1e" +librav1e_encoder_select="extract_extradata_bsf" librsvg_decoder_deps="librsvg" libshine_encoder_deps="libshine" libshine_encoder_select="audio_frame_queue" @@ -3221,11 +3270,13 @@ videotoolbox_encoder_deps="videotoolbox VTCompressionSessionPrepareToEncodeFrame # demuxers / muxers ac3_demuxer_select="ac3_parser" +act_demuxer_select="riffdec" aiff_muxer_select="iso_media" asf_demuxer_select="riffdec" asf_o_demuxer_select="riffdec" asf_muxer_select="riffenc" asf_stream_muxer_select="asf_muxer" +av1_demuxer_select="av1_frame_merge_bsf av1_parser" avi_demuxer_select="iso_media riffdec exif" avi_muxer_select="riffenc" caf_demuxer_select="iso_media riffdec" @@ -3242,6 +3293,8 @@ eac3_demuxer_select="ac3_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_muxer_select="mpegts_muxer" hls_muxer_suggest="gcrypt openssl" @@ -3250,20 +3303,23 @@ image2_brender_pix_demuxer_select="image2_demuxer" ipod_muxer_select="mov_muxer" ismv_muxer_select="mov_muxer" ivf_muxer_select="av1_metadata_bsf vp9_superframe_bsf" +latm_muxer_select="aac_adtstoasc_bsf" matroska_audio_muxer_select="matroska_muxer" matroska_demuxer_select="iso_media riffdec" matroska_demuxer_suggest="bzlib lzo zlib" -matroska_muxer_select="iso_media riffenc" +matroska_muxer_select="iso_media riffenc vp9_superframe_bsf aac_adtstoasc_bsf" +mlp_demuxer_select="mlp_parser" mmf_muxer_select="riffenc" mov_demuxer_select="iso_media riffdec" mov_demuxer_suggest="zlib" -mov_muxer_select="iso_media riffenc rtpenc_chain" +mov_muxer_select="iso_media riffenc rtpenc_chain vp9_superframe_bsf aac_adtstoasc_bsf" mp3_demuxer_select="mpegaudio_parser" mp3_muxer_select="mpegaudioheader" mp4_muxer_select="mov_muxer" mpegts_demuxer_select="iso_media" -mpegts_muxer_select="adts_muxer latm_muxer" +mpegts_muxer_select="adts_muxer latm_muxer h264_mp4toannexb_bsf hevc_mp4toannexb_bsf" mpegtsraw_demuxer_select="mpegts_demuxer" +mxf_muxer_select="golomb pcm_rechunk_bsf" mxf_d10_muxer_select="mxf_muxer" mxf_opatom_muxer_select="mxf_muxer" nut_muxer_select="riffenc" @@ -3274,7 +3330,7 @@ ogv_muxer_select="ogg_muxer" opus_muxer_select="ogg_muxer" psp_muxer_select="mov_muxer" rtp_demuxer_select="sdp_demuxer" -rtp_muxer_select="golomb" +rtp_muxer_select="golomb jpegtables" rtpdec_select="asf_demuxer jpegtables mov_demuxer mpegts_demuxer rm_demuxer rtp_protocol srtp" rtsp_demuxer_select="http_protocol rtpdec" rtsp_muxer_select="rtp_muxer http_protocol rtp_protocol rtpenc_chain" @@ -3287,6 +3343,7 @@ spdif_muxer_select="adts_header" spx_muxer_select="ogg_muxer" swf_demuxer_suggest="zlib" tak_demuxer_select="tak_parser" +truehd_demuxer_select="mlp_parser" tg2_muxer_select="mov_muxer" tgp_muxer_select="mov_muxer" vobsub_demuxer_select="mpegps_demuxer" @@ -3294,6 +3351,7 @@ w64_demuxer_select="wav_demuxer" w64_muxer_select="wav_muxer" wav_demuxer_select="riffdec" wav_muxer_select="riffenc" +webm_chunk_muxer_select="webm_muxer" webm_muxer_select="iso_media riffenc" webm_dash_manifest_demuxer_select="matroska_demuxer" wtv_demuxer_select="mpegts_demuxer riffdec" @@ -3398,6 +3456,8 @@ unix_protocol_deps="sys_un_h" unix_protocol_select="network" # external library protocols +libamqp_protocol_deps="librabbitmq" +libamqp_protocol_select="network" librtmp_protocol_deps="librtmp" librtmpe_protocol_deps="librtmp" librtmps_protocol_deps="librtmp" @@ -3408,6 +3468,8 @@ libsrt_protocol_deps="libsrt" libsrt_protocol_select="network" libssh_protocol_deps="libssh" libtls_conflict="openssl gnutls mbedtls" +libzmq_protocol_deps="libzmq" +libzmq_protocol_select="network" # filters afftdn_filter_deps="avcodec" @@ -3415,7 +3477,7 @@ afftdn_filter_select="fft" afftfilt_filter_deps="avcodec" afftfilt_filter_select="fft" afir_filter_deps="avcodec" -afir_filter_select="fft" +afir_filter_select="rdft" amovie_filter_deps="avcodec avformat" aresample_filter_deps="swresample" asr_filter_deps="pocketsphinx" @@ -3423,6 +3485,7 @@ ass_filter_deps="libass" atempo_filter_deps="avcodec" atempo_filter_select="rdft" avgblur_opencl_filter_deps="opencl" +avgblur_vulkan_filter_deps="vulkan libglslang" azmq_filter_deps="libzmq" blackframe_filter_deps="gpl" bm3d_filter_deps="avcodec" @@ -3430,6 +3493,7 @@ bm3d_filter_select="dct" boxblur_filter_deps="gpl" boxblur_opencl_filter_deps="opencl gpl" bs2b_filter_deps="libbs2b" +chromaber_vulkan_filter_deps="vulkan libglslang" colorkey_opencl_filter_deps="opencl" colormatrix_filter_deps="gpl" convolution_opencl_filter_deps="opencl" @@ -3449,7 +3513,10 @@ delogo_filter_deps="gpl" denoise_vaapi_filter_deps="vaapi" derain_filter_select="dnn" deshake_filter_select="pixelutils" +deshake_opencl_filter_deps="opencl" dilation_opencl_filter_deps="opencl" +dnn_processing_filter_deps="swscale" +dnn_processing_filter_select="dnn" drawtext_filter_deps="libfreetype" drawtext_filter_suggest="libfontconfig libfribidi" elbg_filter_deps="avcodec" @@ -3468,7 +3535,7 @@ freezedetect_filter_select="scene_sad" frei0r_filter_deps="frei0r libdl" frei0r_src_filter_deps="frei0r libdl" fspp_filter_deps="gpl" -geq_filter_deps="gpl" +headphone_filter_select="fft" histeq_filter_deps="gpl" hqdn3d_filter_deps="gpl" interlace_filter_deps="gpl" @@ -3491,7 +3558,9 @@ openclsrc_filter_deps="opencl" overlay_opencl_filter_deps="opencl" overlay_qsv_filter_deps="libmfx" overlay_qsv_filter_select="qsvvpp" +overlay_vulkan_filter_deps="vulkan libglslang" owdenoise_filter_deps="gpl" +pad_opencl_filter_deps="opencl" pan_filter_deps="swresample" perspective_filter_deps="gpl" phase_filter_deps="gpl" @@ -3510,6 +3579,7 @@ sab_filter_deps="gpl swscale" scale2ref_filter_deps="swscale" scale_filter_deps="swscale" scale_qsv_filter_deps="libmfx" +scdet_filter_select="scene_sad" select_filter_select="scene_sad" sharpness_vaapi_filter_deps="vaapi" showcqt_filter_deps="avcodec avformat swscale" @@ -3517,11 +3587,13 @@ showcqt_filter_suggest="libfontconfig libfreetype" showcqt_filter_select="fft" showfreqs_filter_deps="avcodec" showfreqs_filter_select="fft" +showspatial_filter_select="fft" showspectrum_filter_deps="avcodec" showspectrum_filter_select="fft" showspectrumpic_filter_deps="avcodec" showspectrumpic_filter_select="fft" signature_filter_deps="gpl avcodec avformat" +sinc_filter_select="rdft" smartblur_filter_deps="gpl swscale" sobel_opencl_filter_deps="opencl" sofalizer_filter_deps="libmysofa avcodec" @@ -3536,10 +3608,13 @@ stereo3d_filter_deps="gpl" subtitles_filter_deps="avformat avcodec libass" super2xsai_filter_deps="gpl" pixfmts_super2xsai_test_deps="super2xsai_filter" +superequalizer_filter_select="rdft" +surround_filter_select="rdft" tinterlace_filter_deps="gpl" tinterlace_merge_test_deps="tinterlace_filter" tinterlace_pad_test_deps="tinterlace_filter" tonemap_filter_deps="const_nan" +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" @@ -3553,13 +3628,15 @@ zmq_filter_deps="libzmq" zoompan_filter_deps="swscale" zscale_filter_deps="libzimg const_nan" scale_vaapi_filter_deps="vaapi" +scale_vulkan_filter_deps="vulkan libglslang" vpp_qsv_filter_deps="libmfx" vpp_qsv_filter_select="qsvvpp" +xfade_opencl_filter_deps="opencl" yadif_cuda_filter_deps="ffnvcodec" yadif_cuda_filter_deps_any="cuda_nvcc cuda_llvm" # examples -avio_dir_cmd_deps="avformat avutil" +avio_list_dir_deps="avformat avutil" avio_reading_deps="avformat avcodec avutil" decode_audio_example_deps="avcodec avutil" decode_video_example_deps="avcodec avutil" @@ -3599,7 +3676,7 @@ avformat_deps="avcodec avutil" avformat_suggest="libm network zlib" avresample_deps="avutil" avresample_suggest="libm" -avutil_suggest="clock_gettime ffnvcodec libm libdrm libmfx opencl user32 vaapi videotoolbox corefoundation corevideo coremedia bcrypt" +avutil_suggest="clock_gettime ffnvcodec libm libdrm libmfx opencl user32 vaapi vulkan videotoolbox corefoundation corevideo coremedia bcrypt" postproc_deps="avutil gpl" postproc_suggest="libm" swresample_deps="avutil" @@ -3688,6 +3765,7 @@ enable asm enable debug enable doc enable faan faandct faanidct +enable large_tests enable optimizations enable runtime_cpudetect enable safe_bitstream_reader @@ -4363,7 +4441,7 @@ msvc_common_flags(){ # generic catch all at the bottom will print the original flag. -Wall) ;; -Wextra) ;; - -std=c99) ;; + -std=c*) ;; # Common flags -fomit-frame-pointer) ;; -g) echo -Z7 ;; @@ -4379,6 +4457,7 @@ msvc_common_flags(){ -l*) echo ${flag#-l}.lib ;; -LARGEADDRESSAWARE) echo $flag ;; -L*) echo -libpath:${flag#-L} ;; + -Wl,*) ;; *) echo $flag ;; esac done @@ -4606,7 +4685,11 @@ probe_cc(){ _ld_path='-libpath:' elif $_cc -nologo- 2>&1 | grep -q Microsoft || { $_cc -v 2>&1 | grep -q clang && $_cc -? > /dev/null 2>&1; }; then _type=msvc - _ident=$($_cc 2>&1 | head -n1 | tr -d '\r') + if $_cc -nologo- 2>&1 | grep -q Microsoft; then + _ident=$($_cc 2>&1 | head -n1 | tr -d '\r') + else + _ident=$($_cc --version 2>/dev/null | head -n1 | tr -d '\r') + fi _DEPCMD='$(DEP$(1)) $(DEP$(1)FLAGS) $($(1)DEP_FLAGS) $< 2>&1 | awk '\''/including/ { sub(/^.*file: */, ""); gsub(/\\/, "/"); if (!match($$0, / /)) print "$@:", $$0 }'\'' > $(@:.o=.d)' _DEPFLAGS='$(CPPFLAGS) $(CFLAGS) -showIncludes -Zs' _cflags_speed="-O2" @@ -4734,7 +4817,7 @@ fi if test "$cpu" = host; then enabled cross_compile && - die "--cpu=host makes no sense when cross-compiling." + warn "--cpu=host makes no sense when cross-compiling." case "$cc_type" in gcc|llvm_gcc) @@ -5253,6 +5336,7 @@ case $target_os in ;; openbsd|bitrig) disable symver + enable section_data_rel_ro striptype="" SHFLAGS='-shared' SLIB_INSTALL_NAME='$(SLIBNAME).$(LIBMAJOR).$(LIBMINOR)' @@ -5291,6 +5375,11 @@ case $target_os in fi version_script='-exported_symbols_list' VERSION_SCRIPT_POSTPROCESS_CMD='tr " " "\n" | sed -n /global:/,/local:/p | grep ";" | tr ";" "\n" | sed -E "s/(.+)/_\1/g" | sed -E "s/(.+[^*])$$$$/\1*/"' + # Workaround for Xcode 11 -fstack-check bug + if enabled clang; then + clang_version=$($cc -dumpversion) + test ${clang_version%%.*} -eq 11 && add_cflags -fno-stack-check + fi ;; msys*) die "Native MSYS builds are discouraged, please use the MINGW environment." @@ -5883,10 +5972,10 @@ EOF elf*) enabled debug && append X86ASMFLAGS $x86asm_debug ;; esac - check_x86asm avx512_external "vmovdqa32 [eax]{k1}{z}, zmm0" - check_x86asm avx2_external "vextracti128 xmm0, ymm0, 0" - check_x86asm xop_external "vpmacsdd xmm0, xmm1, xmm2, xmm3" - check_x86asm fma4_external "vfmaddps ymm0, ymm1, ymm2, ymm3" + enabled avx512 && check_x86asm avx512_external "vmovdqa32 [eax]{k1}{z}, zmm0" + 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 @@ -5996,14 +6085,17 @@ 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 windows.h GetModuleHandle check_func_headers windows.h GetProcessAffinityMask check_func_headers windows.h GetProcessTimes +check_func_headers windows.h GetStdHandle check_func_headers windows.h GetSystemTimeAsFileTime check_func_headers windows.h LoadLibrary check_func_headers windows.h MapViewOfFile check_func_headers windows.h PeekNamedPipe check_func_headers windows.h SetConsoleTextAttribute check_func_headers windows.h SetConsoleCtrlHandler +check_func_headers windows.h SetDllDirectory check_func_headers windows.h Sleep check_func_headers windows.h VirtualAlloc check_func_headers glob.h glob @@ -6019,6 +6111,7 @@ check_headers io.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 sys/param.h @@ -6069,6 +6162,9 @@ enabled videotoolbox && { check_lib coreservices CoreServices/CoreServices.h UTGetOSTypeFromString "-framework CoreServices" check_func_headers CoreMedia/CMFormatDescription.h kCMVideoCodecType_HEVC "-framework CoreMedia" check_func_headers CoreVideo/CVPixelBuffer.h kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange "-framework CoreVideo" + check_func_headers CoreVideo/CVImageBuffer.h kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ "-framework CoreVideo" + check_func_headers CoreVideo/CVImageBuffer.h kCVImageBufferTransferFunction_ITU_R_2100_HLG "-framework CoreVideo" + check_func_headers CoreVideo/CVImageBuffer.h kCVImageBufferTransferFunction_Linear "-framework CoreVideo" } check_struct "sys/time.h sys/resource.h" "struct rusage" ru_maxrss @@ -6078,8 +6174,10 @@ check_type "windows.h dxva.h" "DXVA_PicParams_VP9" -DWINAPI_FAMILY=WINAPI_FAMILY check_type "windows.h d3d11.h" "ID3D11VideoDecoder" check_type "windows.h d3d11.h" "ID3D11VideoContext" check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602 +check_func_headers mfapi.h MFCreateAlignedMemoryBuffer -lmfplat check_type "vdpau/vdpau.h" "VdpPictureInfoHEVC" +check_type "vdpau/vdpau.h" "VdpPictureInfoVP9" if [ -z "$nvccflags" ]; then nvccflags=$nvccflags_default @@ -6100,10 +6198,10 @@ 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 >= 9.0.18.0" "$ffnv_hdr_list" "" || \ - check_pkg_config ffnvcodec "ffnvcodec >= 8.2.15.8 ffnvcodec < 8.3" "$ffnv_hdr_list" "" || \ - check_pkg_config ffnvcodec "ffnvcodec >= 8.1.24.9 ffnvcodec < 8.2" "$ffnv_hdr_list" "" || \ - check_pkg_config ffnvcodec "ffnvcodec >= 8.0.14.9 ffnvcodec < 8.1" "$ffnv_hdr_list" "" + check_pkg_config ffnvcodec "ffnvcodec >= 9.1.23.1" "$ffnv_hdr_list" "" || \ + check_pkg_config ffnvcodec "ffnvcodec >= 9.0.18.3 ffnvcodec < 9.1" "$ffnv_hdr_list" "" || \ + check_pkg_config ffnvcodec "ffnvcodec >= 8.2.15.10 ffnvcodec < 8.3" "$ffnv_hdr_list" "" || \ + check_pkg_config ffnvcodec "ffnvcodec >= 8.1.24.11 ffnvcodec < 8.2" "$ffnv_hdr_list" "" fi check_cpp_condition winrt windows.h "!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)" @@ -6173,6 +6271,7 @@ for func in $COMPLEX_FUNCS; do done # these are off by default, so fail if requested and not available +enabled avisynth && require_headers "avisynth/avisynth_c.h" enabled cuda_nvcc && { check_nvcc cuda_nvcc || die "ERROR: failed checking for nvcc."; } enabled chromaprint && require chromaprint chromaprint.h chromaprint_get_version -lchromaprint enabled decklink && { require_headers DeckLinkAPI.h && @@ -6196,7 +6295,7 @@ enabled libcelt && require libcelt celt/celt.h celt_decode -lcelt0 && die "ERROR: libcelt must be installed and version must be >= 0.11.0."; } enabled libcaca && require_pkg_config libcaca caca caca.h caca_create_canvas enabled libcodec2 && require libcodec2 codec2/codec2.h codec2_create -lcodec2 -enabled libdav1d && require_pkg_config libdav1d "dav1d >= 0.2.1" "dav1d/dav1d.h" dav1d_version +enabled libdav1d && require_pkg_config libdav1d "dav1d >= 0.4.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 @@ -6209,6 +6308,7 @@ 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 libglslang && require_cpp libglslang glslang/SPIRV/GlslangToSpv.h "glslang::TIntermediate*" -lglslang -lOSDependent -lHLSL -lOGLCompiler -lSPVRemapper -lSPIRV -lSPIRV-Tools-opt -lSPIRV-Tools -lpthread -lstdc++ 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 @@ -6226,10 +6326,14 @@ enabled liblensfun && require_pkg_config liblensfun lensfun lensfun.h lf_ # can find the libraries and headers through other means. enabled libmfx && { check_pkg_config libmfx libmfx "mfx/mfxvideo.h" MFXInit || { require libmfx "mfx/mfxvideo.h" MFXInit "-llibmfx $advapi32_extralibs" && warn "using libmfx without pkg-config"; } } +if enabled libmfx; then + check_cc MFX_CODEC_VP9 "mfx/mfxvp9.h mfx/mfxstructures.h" "MFX_CODEC_VP9" +fi + enabled libmodplug && require_pkg_config libmodplug libmodplug libmodplug/modplug.h ModPlug_Load enabled libmp3lame && require "libmp3lame >= 3.98.3" lame/lame.h lame_set_VBR_quality -lmp3lame $libm_extralibs -enabled libmysofa && { check_pkg_config libmysofa libmysofa mysofa.h mysofa_load || - require libmysofa mysofa.h mysofa_load -lmysofa $zlib_extralibs; } +enabled libmysofa && { check_pkg_config libmysofa libmysofa mysofa.h mysofa_neighborhood_init_withstepdefine || + require libmysofa mysofa.h mysofa_neighborhood_init_withstepdefine -lmysofa $zlib_extralibs; } enabled libnpp && { check_lib libnpp npp.h nppGetLibVersion -lnppig -lnppicc -lnppc -lnppidei || check_lib libnpp npp.h nppGetLibVersion -lnppi -lnppc -lnppidei || die "ERROR: libnpp not found"; } @@ -6252,6 +6356,8 @@ enabled libopus && { } } enabled libpulse && require_pkg_config libpulse libpulse pulse/pulseaudio.h pa_context_new +enabled librabbitmq && require_pkg_config librabbitmq "librabbitmq >= 0.7.1" amqp.h amqp_new_connection +enabled librav1e && require_pkg_config librav1e "rav1e >= 0.1.0" rav1e.h rav1e_context_new enabled librsvg && require_pkg_config librsvg librsvg-2.0 librsvg-2.0/librsvg/rsvg.h rsvg_handle_render_cairo 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++" @@ -6309,12 +6415,12 @@ enabled libx264 && { check_pkg_config libx264 x264 "stdint.h x264.h" x require_cpp_condition libx264 x264.h "X264_BUILD >= 118" && 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 >= 68" + require_cpp_condition libx265 x265.h "X265_BUILD >= 70" 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 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 zmq.h zmq_ctx_new +enabled libzmq && require_pkg_config libzmq "libzmq >= 4.2.1" zmq.h zmq_ctx_new enabled libzvbi && require_pkg_config libzvbi zvbi-0.2 libzvbi.h vbi_decoder_new && { test_cpp_condition libzvbi.h "VBI_VERSION_MAJOR > 0 || VBI_VERSION_MINOR > 2 || VBI_VERSION_MINOR == 2 && VBI_VERSION_MICRO >= 28" || enabled gpl || die "ERROR: libzvbi requires version 0.2.28 or --enable-gpl."; } @@ -6349,12 +6455,16 @@ enabled opengl && { check_lib opengl GL/glx.h glXGetProcAddress "-lGL check_lib opengl ES2/gl.h glGetError "-isysroot=${sysroot} -Wl,-framework,OpenGLES" || die "ERROR: opengl not found." } +enabled omx_rpi && { test_code cc OMX_Core.h OMX_IndexConfigBrcmVideoRequestIFrame || + { ! enabled cross_compile && + add_cflags -isystem/opt/vc/include/IL && + test_code cc OMX_Core.h OMX_IndexConfigBrcmVideoRequestIFrame; } || + die "ERROR: OpenMAX IL headers from raspberrypi/firmware not found"; } && + enable omx enabled omx && require_headers OMX_Core.h -enabled omx_rpi && { check_headers OMX_Core.h || - { ! enabled cross_compile && add_cflags -isystem/opt/vc/include/IL && check_headers OMX_Core.h ; } || - die "ERROR: OpenMAX IL headers not found"; } && enable omx enabled openssl && { check_pkg_config openssl openssl openssl/ssl.h OPENSSL_init_ssl || check_pkg_config openssl openssl openssl/ssl.h SSL_library_init || + check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto || check_lib openssl openssl/ssl.h SSL_library_init -lssl -lcrypto || check_lib openssl openssl/ssl.h SSL_library_init -lssl32 -leay32 || check_lib openssl openssl/ssl.h SSL_library_init -lssl -lcrypto -lws2_32 -lgdi32 || @@ -6430,19 +6540,21 @@ pod2man --help > /dev/null 2>&1 && enable pod2man || disable pod2man rsync --help 2> /dev/null | grep -q 'contimeout' && enable rsync_contimeout || disable rsync_contimeout # check V4L2 codecs available in the API -check_headers linux/fb.h -check_headers linux/videodev2.h -test_code cc linux/videodev2.h "struct v4l2_frmsizeenum vfse; vfse.discrete.width = 0;" && enable_sanitized struct_v4l2_frmivalenum_discrete -check_cc v4l2_m2m linux/videodev2.h "int i = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_VIDEO_M2M | V4L2_BUF_FLAG_LAST;" -check_cc vc1_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VC1_ANNEX_G;" -check_cc mpeg1_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_MPEG1;" -check_cc mpeg2_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_MPEG2;" -check_cc mpeg4_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_MPEG4;" -check_cc hevc_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_HEVC;" -check_cc h263_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_H263;" -check_cc h264_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_H264;" -check_cc vp8_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VP8;" -check_cc vp9_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VP9;" +if enabled v4l2_m2m; then + check_headers linux/fb.h + check_headers linux/videodev2.h + test_code cc linux/videodev2.h "struct v4l2_frmsizeenum vfse; vfse.discrete.width = 0;" && enable_sanitized struct_v4l2_frmivalenum_discrete + check_cc v4l2_m2m linux/videodev2.h "int i = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_VIDEO_M2M | V4L2_BUF_FLAG_LAST;" + check_cc vc1_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VC1_ANNEX_G;" + check_cc mpeg1_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_MPEG1;" + check_cc mpeg2_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_MPEG2;" + check_cc mpeg4_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_MPEG4;" + check_cc hevc_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_HEVC;" + check_cc h263_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_H263;" + check_cc h264_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_H264;" + check_cc vp8_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VP8;" + check_cc vp9_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VP9;" +fi check_headers sys/videoio.h test_code cc sys/videoio.h "struct v4l2_frmsizeenum vfse; vfse.discrete.width = 0;" && enable_sanitized struct_v4l2_frmivalenum_discrete @@ -6470,8 +6582,8 @@ else EOF fi -enabled alsa && check_pkg_config alsa alsa "alsa/asoundlib.h" snd_pcm_htimestamp || - check_lib alsa alsa/asoundlib.h snd_pcm_htimestamp -lasound +enabled alsa && { check_pkg_config alsa alsa "alsa/asoundlib.h" snd_pcm_htimestamp || + check_lib alsa alsa/asoundlib.h snd_pcm_htimestamp -lasound; } enabled libjack && require_pkg_config libjack jack jack/jack.h jack_port_get_latency_range @@ -6526,6 +6638,7 @@ if enabled vaapi; then check_type "va/va.h va/va_dec_hevc.h" "VAPictureParameterBufferHEVC" check_struct "va/va.h" "VADecPictureParameterBufferVP9" bit_depth + check_type "va/va.h va/va_vpp.h" "VAProcFilterParameterBufferHDRToneMapping" check_struct "va/va.h va/va_vpp.h" "VAProcPipelineCaps" rotation_flags check_type "va/va.h va/va_enc_hevc.h" "VAEncPictureParameterBufferHEVC" check_type "va/va.h va/va_enc_jpeg.h" "VAEncPictureParameterBufferJPEG" @@ -6567,6 +6680,9 @@ enabled vdpau && enabled crystalhd && check_lib crystalhd "stdint.h libcrystalhd/libcrystalhd_if.h" DtsCrystalHDVersion -lcrystalhd +enabled vulkan && + require_pkg_config vulkan "vulkan >= 1.1.97" "vulkan/vulkan.h" vkCreateInstance + if enabled x86; then case $target_os in mingw32*|mingw64*|win32|win64|linux|cygwin*) @@ -6575,7 +6691,7 @@ if enabled x86; then disable ffnvcodec cuvid nvdec nvenc ;; esac -elif enabled ppc64 && ! enabled bigendian; then +elif enabled_any aarch64 ppc64 && ! enabled bigendian; then case $target_os in linux) ;; @@ -6599,7 +6715,7 @@ EOF 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) >= 0x0001000400040001" + "(AMF_VERSION_MAJOR << 48 | AMF_VERSION_MINOR << 32 | AMF_VERSION_RELEASE << 16 | AMF_VERSION_BUILD_NUM) >= 0x0001000400090000" # Funny iconv installations are not unusual, so check it after all flags have been set if enabled libc_iconv; then @@ -7397,7 +7513,7 @@ cat > $TMPH < Wed, 15 Jul 2020 18:24:21 +0800 + jellyfin-ffmpeg (4.2.1-7) unstable; urgency=medium * Integrate free libva and intel-vaapi-driver with MIT license in the deb diff --git a/debian/control b/debian/control index 10b7a2df2d0..bae40818e12 100644 --- a/debian/control +++ b/debian/control @@ -5,7 +5,7 @@ Maintainer: Jellyfin Packaging Team Uploaders: Jellyfin Packaging Team Rules-Requires-Root: no Homepage: https://ffmpeg.org/ -Standards-Version: 4.2.1 +Standards-Version: 4.3.1 Vcs-Git: https://github.com/jellyfin/jellyfin-ffmpeg.git Vcs-Browser: https://github.com/jellyfin/jellyfin-ffmpeg Build-Depends: @@ -43,8 +43,14 @@ Build-Depends: libx265-dev, # --enable-libzvbi libzvbi-dev, +# --enable-vdpau + libvdpau-dev, +# --enable-cuda-llvm + clang [!armhf !arm64], +# --enable-opencl + ocl-icd-opencl-dev [!armhf !arm64], # --enable-omx - libomxil-bellagio-dev, +# libomxil-bellagio-dev # omx headers are fully included in raspberrypi/firmware. # libomxil-bellagio-dev is missing some functions required in ffmpeg 4.3+. # Drop this package when building ffmpeg 4.3+. diff --git a/debian/copyright b/debian/copyright index 8e39ea36988..b82de823ccf 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1021,6 +1021,24 @@ License: Expat Comment: VA-API (Video Acceleration API) user mode driver for Intel GEN Graphics family. +Files: gmmlib/* +Copyright: 2017, Intel Corporation +License: Expat +Comment: + The Intel(R) Graphics Memory Management Library provides device specific and buffer management + for the Intel(R) Graphics Compute Runtime for OpenCL(TM) and the Intel(R) Media Driver for VAAPI. + +Files: gmmlib/Source/GmmLib/Utility/GmmLog/spdlog/* +Copyright: 2016, Gabi Melman +License: Expat + +Files: MediaSDK/* +Copyright: 2017, Intel Corporation +License: Expat +Comment: + Intel® Media SDK provides a plain C API to access hardware-accelerated video decode, encode and filtering + on Intel® Gen graphics hardware platforms. Implementation written in C++ 11 with parts in C-for-Media (CM). + Files: debian/* Copyright: 2014-2017, Andreas Cadhalpun diff --git a/debian/patches/0002-Update-AMF-files-to-support-Linux.patch b/debian/patches/0002-Update-AMF-files-to-support-Linux.patch deleted file mode 100644 index 58dc1d135ef..00000000000 --- a/debian/patches/0002-Update-AMF-files-to-support-Linux.patch +++ /dev/null @@ -1,100 +0,0 @@ -Index: jellyfin-ffmpeg/libavcodec/amfenc.c -=================================================================== ---- jellyfin-ffmpeg.orig/libavcodec/amfenc.c -+++ jellyfin-ffmpeg/libavcodec/amfenc.c -@@ -213,6 +213,7 @@ static int amf_init_from_dxva2_device(AV - static int amf_init_context(AVCodecContext *avctx) - { - AmfContext *ctx = avctx->priv_data; -+ AMFContext1 *context1 = NULL; - AMF_RESULT res; - av_unused int ret; - -@@ -311,8 +312,20 @@ static int amf_init_context(AVCodecConte - if (res == AMF_OK) { - av_log(avctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D9.\n"); - } else { -- av_log(avctx, AV_LOG_ERROR, "AMF initialisation failed via D3D9: error %d.\n", res); -- return AVERROR(ENOSYS); -+ AMFGuid guid = IID_AMFContext1(); -+ res = ctx->context->pVtbl->QueryInterface(ctx->context, &guid, (void**)&context1); -+ AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext1() failed with error %d\n", res); -+ -+ res = context1->pVtbl->InitVulkan(context1, NULL); -+ context1->pVtbl->Release(context1); -+ if (res != AMF_OK) { -+ if (res == AMF_NOT_SUPPORTED) -+ av_log(avctx, AV_LOG_ERROR, "AMF via Vulkan is not supported on the given device.\n"); -+ else -+ av_log(avctx, AV_LOG_ERROR, "AMF failed to initialise on the given Vulkan device: %d.\n", res); -+ return AVERROR(ENOSYS); -+ } -+ av_log(avctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via Vulkan.\n"); - } - } - } -@@ -438,7 +451,7 @@ static int amf_copy_buffer(AVCodecContex - int64_t timestamp = AV_NOPTS_VALUE; - int64_t size = buffer->pVtbl->GetSize(buffer); - -- if ((ret = ff_alloc_packet2(avctx, pkt, size, 0)) < 0) { -+ if ((ret = av_new_packet(pkt, size)) < 0) { - return ret; - } - memcpy(pkt->data, buffer->pVtbl->GetNative(buffer), size); -Index: jellyfin-ffmpeg/libavcodec/amfenc_h264.c -=================================================================== ---- jellyfin-ffmpeg.orig/libavcodec/amfenc_h264.c -+++ jellyfin-ffmpeg/libavcodec/amfenc_h264.c -@@ -366,6 +366,7 @@ static const AVCodecDefault defaults[] = - { "b", "2M" }, - { "g", "250" }, - { "slices", "1" }, -+ { "flags", "+loop"}, - { NULL }, - }; - -Index: jellyfin-ffmpeg/libavcodec/amfenc_hevc.c -=================================================================== ---- jellyfin-ffmpeg.orig/libavcodec/amfenc_hevc.c -+++ jellyfin-ffmpeg/libavcodec/amfenc_hevc.c -@@ -69,7 +69,7 @@ static const AVOption options[] = { - { "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 = 60 }, 0, INT_MAX, 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 }, - { "preanalysis", "Enable preanalysis", OFFSET(preanalysis), 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}, -@@ -136,7 +136,7 @@ static av_cold int amf_encode_init_hevc( - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_TIER, ctx->tier); - - profile_level = avctx->level; -- if (profile_level == 0) { -+ if (profile_level == FF_LEVEL_UNKNOWN) { - profile_level = ctx->level; - } - if (profile_level != 0) { -@@ -144,7 +144,7 @@ static av_cold int amf_encode_init_hevc( - } - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET, ctx->quality); - // Maximum Reference Frames -- if (avctx->refs != 0) { -+ if (avctx->refs != -1) { - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_MAX_NUM_REFRAMES, avctx->refs); - } - // Aspect Ratio -@@ -254,10 +254,10 @@ static av_cold int amf_encode_init_hevc( - } - - if (ctx->qp_p != -1) { -- AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QP_I, ctx->qp_p); -+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QP_P, ctx->qp_p); - } - if (ctx->qp_i != -1) { -- AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QP_P, ctx->qp_i); -+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QP_I, ctx->qp_i); - } - AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_SKIP_FRAME_ENABLE, ctx->skip_frame); - diff --git a/debian/patches/series b/debian/patches/series index 6c56c1f60b4..736c9d4354d 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,2 +1 @@ 0001_fix-segment-muxer.patch -0002-Update-AMF-files-to-support-Linux.patch diff --git a/debian/rules b/debian/rules index d19e8d905e0..a682c228894 100755 --- a/debian/rules +++ b/debian/rules @@ -16,7 +16,6 @@ CONFIG := --prefix=${TARGET_DIR} \ --disable-ffplay \ --disable-shared \ --disable-libxcb \ - --disable-vdpau \ --disable-sdl2 \ --disable-xlib \ --enable-gpl \ @@ -55,12 +54,19 @@ CONFIG_ARM64 := ${CONFIG_ARM_COMMON} \ --cross-prefix=/usr/bin/aarch64-linux-gnu- \ CONFIG_x86 := --arch=amd64 \ + --enable-libzimg \ + --enable-opencl \ + --enable-vaapi \ --enable-amf \ + --enable-libmfx \ + --enable-vdpau \ + --enable-cuda \ + --enable-cuda-llvm \ + --enable-cuvid \ --enable-nvenc \ --enable-nvdec \ - --enable-vaapi \ + --enable-ffnvcodec \ --enable-libdav1d -# --enable-libmfx # uncomment for non-free QSV HOST_ARCH := $(shell arch) BUILD_ARCH := ${DEB_HOST_MULTIARCH} diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 00000000000..7628cecb996 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,9 @@ +/*.1 +/*.3 +/*.html +/*.pod +/config.texi +/avoptions_codec.texi +/avoptions_format.texi +/fate.txt +/print_options diff --git a/doc/APIchanges b/doc/APIchanges index 07331b16e79..70579df21a4 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -15,6 +15,123 @@ libavutil: 2017-10-21 API changes, most recent first: +2020-06-05 - ec39c2276a - lavu 56.50.100 - buffer.h + Passing NULL as alloc argument to av_buffer_pool_init2() is now allowed. + +2020-05-27 - ba6cada92e - lavc 58.88.100 - avcodec.h codec.h + Move AVCodec-related public API to new header codec.h. + +2020-05-23 - 064b875e89 - lavu 56.49.100 - video_enc_params.h + Add AV_VIDEO_ENC_PARAMS_H264. + +2020-05-23 - 2e08b39444 - lavu 56.48.100 - hwcontext.h + Add av_hwdevice_ctx_create_derived_opts. + +2020-05-23 - 6b65c4ec54 - lavu 56.47.100 - rational.h + Add av_gcd_q(). + +2020-05-22 - af9e622776 - lavu 56.46.101 - opt.h + Add AV_OPT_FLAG_CHILD_CONSTS. + +2020-05-22 - 9d443c3e68 - lavc 58.87.100 - avcodec.h codec_par.h + Move AVBitstreamFilter-related public API to new header bsf.h. + Move AVCodecParameters-related public API to new header codec_par.h. + +2020-05-21 - 13b1bbff0b - lavc 58.86.101 - avcodec.h + Deprecated AV_CODEC_CAP_INTRA_ONLY and AV_CODEC_CAP_LOSSLESS. + +2020-05-17 - 84af196c65 - lavu 56.46.100 - common.h + Add av_sat_add64() and av_sat_sub64() + +2020-05-12 - 991d417692 - lavu 56.45.100 - video_enc_params.h + lavc 58.84.100 - avcodec.h + Add a new API for exporting video encoding information. + Replaces the deprecated API for exporting QP tables from decoders. + Add AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS to request this information from + decoders. + +2020-05-10 - dccd07f66d - lavu 56.44.100 - hwcontext_vulkan.h + Add enabled_inst_extensions, num_enabled_inst_extensions, enabled_dev_extensions + and num_enabled_dev_extensions fields to AVVulkanDeviceContext + +2020-04-22 - 0e1db79e37 - lavc 58.81.100 - packet.h + - lavu 56.43.100 - dovi_meta.h + Add AV_PKT_DATA_DOVI_CONF and AVDOVIDecoderConfigurationRecord. + +2020-04-15 - 22b25b3ea5 - lavc 58.79.100 - avcodec.h + Add formal support for calling avcodec_flush_buffers() on encoders. + Encoders that set the cap AV_CODEC_CAP_ENCODER_FLUSH will be flushed. + For all other encoders, the call is now a no-op rather than undefined + behaviour. + +2020-04-10 - 672946c7fe - lavc 58.78.100 - avcodec.h codec_desc.h codec_id.h packet.h + Move AVCodecDesc-related public API to new header codec_desc.h. + Move AVCodecID enum to new header codec_id.h. + Move AVPacket-related public API to new header packet.h. + +2020-03-29 - 4cb0dda555 - lavf 58.42.100 - avformat.h + av_read_frame() now guarantees to handle uninitialized input packets + and to return refcounted packets on success. + +2020-03-27 - c52ec0367d - lavc 58.77.100 - avcodec.h + av_packet_ref() now guarantees to return the destination packet + in a blank state on error. + +2020-03-10 - 05d27f342b - lavc 58.75.100 - avcodec.h + Add AV_PKT_DATA_ICC_PROFILE. + +2020-02-21 - d005a7cdfd - lavc 58.73.101 - avcodec.h + Add AV_CODEC_EXPORT_DATA_PRFT. + +2020-02-21 - c666689491 - lavc 58.73.100 - avcodec.h + Add AVCodecContext.export_side_data and AV_CODEC_EXPORT_DATA_MVS. + +2020-02-13 - e8f054b095 - lavu 56.41.100 - tx.h + Add AV_TX_INT32_FFT and AV_TX_INT32_MDCT + +2020-02-12 - 3182114f88 - lavu 56.40.100 - log.h + Add av_log_once(). + +2020-02-04 - a88449ffb2 - lavu 56.39.100 - hwcontext.h + Add AV_PIX_FMT_VULKAN + Add AV_HWDEVICE_TYPE_VULKAN and implementation. + +2020-01-30 - 27529eeb27 - lavf 58.37.100 - avio.h + Add avio_protocol_get_class(). + +2020-01-15 - 717b2074ec - lavc 58.66.100 - avcodec.h + Add AV_PKT_DATA_PRFT and AVProducerReferenceTime. + +2019-12-27 - 45259a0ee4 - lavu 56.38.100 - eval.h + Add av_expr_count_func(). + +2019-12-26 - 16685114d5 - lavu 56.37.100 - buffer.h + Add av_buffer_pool_buffer_get_opaque(). + +2019-11-17 - 1c23abc88f - lavu 56.36.100 - eval API + Add av_expr_count_vars(). + +2019-10-14 - f3746d31f9 - lavu 56.35.101 - opt.h + Add AV_OPT_FLAG_RUNTIME_PARAM. + +2019-09-25 - f8406ab4b9 - lavc 58.59.100 - avcodec.h + Add max_samples + +2019-09-04 - 2a9d461abc - lavu 56.35.100 - hwcontext_videotoolbox.h + Add av_map_videotoolbox_format_from_pixfmt2() for full range pixfmt + +2019-09-01 - 8821d1f56e - lavu 56.34.100 - pixfmt.h + Add EBU Tech. 3213-E AVColorPrimaries value + +2019-08-17 - 95fa73a2b4 - lavf 58.31.101 - avio.h + 4K limit removed from avio_printf. + +2019-08-17 - a82f8f2f10 - lavf 58.31.100 - avio.h + Add avio_print_string_array and avio_print. + +2019-07-27 - 42e2319ba9 - lavu 56.33.100 - tx.h + Add AV_TX_DOUBLE_FFT and AV_TX_DOUBLE_MDCT + -------- 8< --------- FFmpeg 4.2 was cut here -------- 8< --------- 2019-06-21 - a30e44098a - lavu 56.30.100 - frame.h diff --git a/doc/Doxyfile b/doc/Doxyfile index eedfdf40d6c..a53a48907ed 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = FFmpeg # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 4.2.1 +PROJECT_NUMBER = 4.3.1 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi index 14f35893d3b..8a2f55cc41c 100644 --- a/doc/bitstream_filters.texi +++ b/doc/bitstream_filters.texi @@ -224,6 +224,10 @@ Insert or remove AUD NAL units in all access units of the stream. @item sample_aspect_ratio Set the sample aspect ratio of the stream in the VUI parameters. +@item overscan_appropriate_flag +Set whether the stream is suitable for display using overscan +or not (see H.264 section E.2.1). + @item video_format @item video_full_range_flag Set the video format in the stream (see H.264 section E.2.1 and @@ -544,6 +548,36 @@ ffmpeg -i INPUT -c copy -bsf noise[=1] output.mkv @section null This bitstream filter passes the packets through unchanged. +@section pcm_rechunk + +Repacketize PCM audio to a fixed number of samples per packet or a fixed packet +rate per second. This is similar to the @ref{asetnsamples,,asetnsamples audio +filter,ffmpeg-filters} but works on audio packets instead of audio frames. + +@table @option +@item nb_out_samples, n +Set the number of samples per each output audio packet. The number is intended +as the number of samples @emph{per each channel}. Default value is 1024. + +@item pad, p +If set to 1, the filter will pad the last audio packet with silence, so that it +will contain the same number of samples (or roughly the same number of samples, +see @option{frame_rate}) as the previous ones. Default value is 1. + +@item frame_rate, r +This option makes the filter output a fixed number of packets per second instead +of a fixed number of samples per packet. If the audio sample rate is not +divisible by the frame rate then the number of samples will not be constant but +will vary slightly so that each packet will start as close to the frame +boundary as possible. Using this option has precedence over @option{nb_out_samples}. +@end table + +You can generate the well known 1602-1601-1602-1601-1602 pattern of 48kHz audio +for NTSC frame rate using the @option{frame_rate} option. +@example +ffmpeg -f lavfi -i sine=r=48000:d=1 -c pcm_s16le -bsf pcm_rechunk=r=30000/1001 -f framecrc - +@end example + @section prores_metadata Modify color property metadata embedded in prores stream. @@ -585,6 +619,10 @@ Keep the same transfer characteristics property (default). @item unknown @item bt709 BT 601, BT 709, BT 2020 +@item smpte2084 +SMPTE ST 2084 +@item arib-std-b67 +ARIB STD-B67 @end table @@ -594,7 +632,7 @@ Available values are: @table @samp @item auto -Keep the same transfer characteristics property (default). +Keep the same colorspace property (default). @item unknown @item bt709 @@ -610,6 +648,11 @@ Set Rec709 colorspace for each frame of the file ffmpeg -i INPUT -c copy -bsf:v prores_metadata=color_primaries=bt709:color_trc=bt709:colorspace=bt709 output.mov @end example +Set Hybrid Log-Gamma parameters for each frame of the file +@example +ffmpeg -i INPUT -c copy -bsf:v prores_metadata=color_primaries=bt2020:color_trc=arib-std-b67:colorspace=bt2020nc output.mov +@end example + @section remove_extra Remove extradata from packets. @@ -659,7 +702,9 @@ Modify metadata embedded in a VP9 stream. @table @option @item color_space -Set the color space value in the frame header. +Set the color space value in the frame header. Note that any frame +set to RGB will be implicitly set to PC range and that RGB is +incompatible with profiles 0 and 2. @table @samp @item unknown @item bt601 @@ -671,8 +716,8 @@ Set the color space value in the frame header. @end table @item color_range -Set the color range value in the frame header. Note that this cannot -be set in RGB streams. +Set the color range value in the frame header. Note that any value +imposed by the color space will take precedence over this value. @table @samp @item tv @item pc diff --git a/doc/build_system.txt b/doc/build_system.txt index 3d6a21a9f3f..0b1b0c2054c 100644 --- a/doc/build_system.txt +++ b/doc/build_system.txt @@ -48,6 +48,8 @@ config tools/target_dec__fuzzer Build fuzzer to fuzz the specified decoder. +tools/target_bsf__fuzzer + Build fuzzer to fuzz the specified bitstream filter. Useful standard make commands: make -t diff --git a/doc/codecs.texi b/doc/codecs.texi index 0d0de94c549..c092aadc0e9 100644 --- a/doc/codecs.texi +++ b/doc/codecs.texi @@ -55,6 +55,7 @@ Do not draw edges. @item psnr Set error[?] variables during encoding. @item truncated +Input bitstream might be randomly truncated. @item drop_changed Don't output frames whose parameters differ from first decoded frame in stream. Error AVERROR_INPUT_CHANGED is returned when a frame is dropped. @@ -79,6 +80,8 @@ Deprecated, use mpegvideo private options instead. Apply interlaced motion estimation. @item cgop Use closed gop. +@item output_corrupt +Output even potentially corrupted frames. @end table @item me_method @var{integer} (@emph{encoding,video}) @@ -643,6 +646,24 @@ noise preserving sum of squared differences @item dia_size @var{integer} (@emph{encoding,video}) Set diamond type & size for motion estimation. +@table @samp +@item (1024, INT_MAX) +full motion estimation(slowest) +@item (768, 1024] +umh motion estimation +@item (512, 768] +hex motion estimation +@item (256, 512] +l2s diamond motion estimation +@item [2,256] +var diamond motion estimation +@item (-1, 2) +small diamond motion estimation +@item -1 +funny diamond motion estimation +@item (INT_MIN, -1) +sab diamond motion estimation +@end table @item last_pred @var{integer} (@emph{encoding,video}) Set amount of motion predictors from the previous frame. @@ -760,14 +781,12 @@ Set noise reduction. Set number of bits which should be loaded into the rc buffer before decoding starts. -@item flags2 @var{flags} (@emph{decoding/encoding,audio,video}) +@item flags2 @var{flags} (@emph{decoding/encoding,audio,video,subtitles}) Possible values: @table @samp @item fast Allow non spec compliant speedup tricks. -@item sgop -Deprecated, use mpegvideo private options instead. @item noout Skip bitstream encoding. @item ignorecrop @@ -781,6 +800,22 @@ Show all frames before the first keyframe. @item export_mvs Export motion vectors into frame side-data (see @code{AV_FRAME_DATA_MOTION_VECTORS}) for codecs that support it. See also @file{doc/examples/export_mvs.c}. +@item skip_manual +Do not skip samples and export skip information as frame side data. +@item ass_ro_flush_noop +Do not reset ASS ReadOrder field on flush. +@end table + +@item export_side_data @var{flags} (@emph{decoding/encoding,audio,video,subtitles}) + +Possible values: +@table @samp +@item mvs +Export motion vectors into frame side-data (see @code{AV_FRAME_DATA_MOTION_VECTORS}) +for codecs that support it. See also @file{doc/examples/export_mvs.c}. +@item prft +Export encoder Producer Reference Time into packet side-data (see @code{AV_PKT_DATA_PRFT}) +for codecs that support it. @end table @item error @var{integer} (@emph{encoding,video}) @@ -820,49 +855,8 @@ Set number of macroblock rows at the bottom which are skipped. @item profile @var{integer} (@emph{encoding,audio,video}) -Possible values: -@table @samp -@item unknown - -@item aac_main - -@item aac_low - -@item aac_ssr - -@item aac_ltp - -@item aac_he - -@item aac_he_v2 - -@item aac_ld - -@item aac_eld - -@item mpeg2_aac_low - -@item mpeg2_aac_he - -@item mpeg4_sp - -@item mpeg4_core - -@item mpeg4_main - -@item mpeg4_asp - -@item dts - -@item dts_es - -@item dts_96_24 - -@item dts_hd_hra - -@item dts_hd_ma - -@end table +Set encoder codec profile. Default value is @samp{unknown}. Encoder specific +profiles are documented in the relevant encoder documentation. @item level @var{integer} (@emph{encoding,audio,video}) diff --git a/doc/decoders.texi b/doc/decoders.texi index 0582b018b09..9005714e3c8 100644 --- a/doc/decoders.texi +++ b/doc/decoders.texi @@ -57,7 +57,7 @@ You need to explicitly configure the build with @code{--enable-libdav1d}. @subsection Options -The following option is supported by the libdav1d wrapper. +The following options are supported by the libdav1d wrapper. @table @option @@ -68,8 +68,15 @@ Set amount of frame threads to use during decoding. The default value is 0 (auto Set amount of tile threads to use during decoding. The default value is 0 (autodetect). @item filmgrain -Apply film grain to the decoded video if present in the bitstream. The default value -is true. +Apply film grain to the decoded video if present in the bitstream. Defaults to the +internal default of the library. + +@item oppoint +Select an operating point of a scalable AV1 bitstream (0 - 31). Defaults to the +internal default of the library. + +@item alllayers +Output all spatial layers of a scalable AV1 bitstream. The default value is false. @end table @@ -280,7 +287,7 @@ palette is stored in the IFO file, and therefore not available when reading from dumped VOB files. The format for this option is a string containing 16 24-bits hexadecimal -numbers (without 0x prefix) separated by comas, for example @code{0d00ee, +numbers (without 0x prefix) separated by commas, for example @code{0d00ee, ee450d, 101010, eaeaea, 0ce60b, ec14ed, ebff0b, 0d617a, 7b7b7b, d1d1d1, 7b2a0e, 0d950c, 0f007b, cf0dec, cfa80c, 7c127b}. @@ -309,6 +316,11 @@ List of teletext page numbers to decode. Pages that do not match the specified list are dropped. You may use the special @code{*} string to match all pages, or @code{subtitle} to match all subtitle pages. Default value is *. +@item txt_default_region +Set default character set used for decoding, a value between 0 and 87 (see +ETS 300 706, Section 15, Table 32). Default value is -1, which does not +override the libzvbi default. This option is needed for some legacy level 1.0 +transmissions which cannot signal the proper charset. @item txt_chop_top Discards the top teletext line. Default value is 1. @item txt_format diff --git a/doc/demuxers.texi b/doc/demuxers.texi index 57d1532212f..3c15ab9eeec 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -331,6 +331,10 @@ segment index to start live streams at (negative values are from the end). Maximum number of times a insufficient list is attempted to be reloaded. Default value is 1000. +@item m3u8_hold_counters +The maximum number of times to load m3u8 when it refreshes without new segments. +Default value is 1000. + @item http_persistent Use persistent HTTP connections. Applicable only for HTTP streams. Enabled by default. @@ -338,6 +342,10 @@ Enabled by default. @item http_multiple Use multiple HTTP connections for downloading HTTP segments. Enabled by default for HTTP/1.1 servers. + +@item http_seekable +Use HTTP partial requests for downloading HTTP segments. +0 = disable, 1 = enable, -1 = auto, Default is auto. @end table @section image2 @@ -448,6 +456,17 @@ nanosecond precision. @item video_size Set the video size of the images to read. If not specified the video size is guessed from the first image file in the sequence. +@item export_path_metadata +If set to 1, will add two extra fields to the metadata found in input, making them +also available for other filters (see @var{drawtext} filter for examples). Default +value is 0. The extra fields are described below: +@table @option +@item lavf.image2dec.source_path +Corresponds to the full path to the input file being read. +@item lavf.image2dec.source_basename +Corresponds to the name of the file being read. +@end table + @end table @subsection Examples @@ -585,9 +604,13 @@ Set the sample rate for libopenmpt to output. Range is from 1000 to INT_MAX. The value default is 48000. @end table -@section mov/mp4/3gp/QuickTime +@section mov/mp4/3gp + +Demuxer for Quicktime File Format & ISO/IEC Base Media File Format (ISO/IEC 14496-12 or MPEG-4 Part 12, ISO/IEC 15444-12 or JPEG 2000 Part 12). -QuickTime / MP4 demuxer. +Registered extensions: mov, mp4, m4a, 3gp, 3g2, mj2, psp, m4b, ism, ismv, isma, f4v + +@subsection Options This demuxer accepts the following options: @table @option @@ -598,10 +621,73 @@ Enabling this can theoretically leak information in some use cases. @item use_absolute_path Allows loading of external tracks via absolute paths, disabled by default. Enabling this poses a security risk. It should only be enabled if the source -is known to be non malicious. +is known to be non-malicious. + +@item seek_streams_individually +When seeking, identify the closest point in each stream individually and demux packets in +that stream from identified point. This can lead to a different sequence of packets compared +to demuxing linearly from the beginning. Default is true. + +@item ignore_editlist +Ignore any edit list atoms. The demuxer, by default, modifies the stream index to reflect the +timeline described by the edit list. Default is false. + +@item advanced_editlist +Modify the stream index to reflect the timeline described by the edit list. @code{ignore_editlist} +must be set to false for this option to be effective. +If both @code{ignore_editlist} and this option are set to false, then only the +start of the stream index is modified to reflect initial dwell time or starting timestamp +described by the edit list. Default is true. + +@item ignore_chapters +Don't parse chapters. This includes GoPro 'HiLight' tags/moments. Note that chapters are +only parsed when input is seekable. Default is false. + +@item use_mfra_for +For seekable fragmented input, set fragment's starting timestamp from media fragment random access box, if present. +Following options are available: +@table @samp +@item auto +Auto-detect whether to set mfra timestamps as PTS or DTS @emph{(default)} + +@item dts +Set mfra timestamps as DTS + +@item pts +Set mfra timestamps as PTS + +@item 0 +Don't use mfra box to set timestamps @end table +@item export_all +Export unrecognized boxes within the @var{udta} box as metadata entries. The first four +characters of the box type are set as the key. Default is false. + +@item export_xmp +Export entire contents of @var{XMP_} box and @var{uuid} box as a string with key @code{xmp}. Note that +if @code{export_all} is set and this option isn't, the contents of @var{XMP_} box are still exported +but with key @code{XMP_}. Default is false. + +@item activation_bytes +4-byte key required to decrypt Audible AAX and AAX+ files. See Audible AAX subsection below. + +@item audible_fixed_key +Fixed key used for handling Audible AAX/AAX+ files. It has been pre-set so should not be necessary to +specify. + +@item decryption_key +16-byte key, in hex, to decrypt files encrypted using ISO Common Encryption (CENC/AES-128 CTR; ISO/IEC 23001-7). +@end table + +@subsection Audible AAX + +Audible AAX files are encrypted M4B files, and they can be decrypted by specifying a 4 byte activation secret. +@example +ffmpeg -activation_bytes 1CEB00DA -i test.aax -vn -c:a copy output.mp4 +@end example + @section mpegts MPEG-2 transport stream demuxer. diff --git a/doc/developer.texi b/doc/developer.texi index 5c342c91064..b33cab0fc7e 100644 --- a/doc/developer.texi +++ b/doc/developer.texi @@ -131,6 +131,9 @@ compound literals (@samp{x = (struct s) @{ 17, 23 @};}). @item for loops with variable definition (@samp{for (int i = 0; i < 8; i++)}); +@item +Variadic macros (@samp{#define ARRAY(nb, ...) (int[nb + 1])@{ nb, __VA_ARGS__ @}}); + @item Implementation defined behavior for signed integers is assumed to match the expected behavior for two's complement. Non representable values in integer @@ -622,7 +625,7 @@ If the patch fixes a bug, did you provide a verbose analysis of the bug? If the patch fixes a bug, did you provide enough information, including a sample, so the bug can be reproduced and the fix can be verified? Note please do not attach samples >100k to mails but rather provide a -URL, you can upload to ftp://upload.ffmpeg.org. +URL, you can upload to @url{https://streams.videolan.org/upload/}. @item Did you provide a verbose summary about what the patch does change? diff --git a/doc/doxy/.gitignore b/doc/doxy/.gitignore new file mode 100644 index 00000000000..ac7af2e80e3 --- /dev/null +++ b/doc/doxy/.gitignore @@ -0,0 +1 @@ +/html/ diff --git a/doc/encoders.texi b/doc/encoders.texi index eefd124751f..1331b794589 100644 --- a/doc/encoders.texi +++ b/doc/encoders.texi @@ -30,11 +30,7 @@ follows. Advanced Audio Coding (AAC) encoder. -This encoder is the default AAC encoder, natively implemented into FFmpeg. Its -quality is on par or better than libfdk_aac at the default bitrate of 128kbps. -This encoder also implements more options, profiles and samplerates than -other encoders (with only the AAC-HE profile pending to be implemented) so this -encoder has become the default and is the recommended choice. +This encoder is the default AAC encoder, natively implemented into FFmpeg. @subsection Options @@ -651,10 +647,7 @@ configuration. You need to explicitly configure the build with so if you allow the use of GPL, you should configure with @code{--enable-gpl --enable-nonfree --enable-libfdk-aac}. -This encoder is considered to produce output on par or worse at 128kbps to the -@ref{aacenc,,the native FFmpeg AAC encoder} but can often produce better -sounding audio at identical or lower bitrates and has support for the -AAC-HE profiles. +This encoder has support for the AAC-HE profiles. VBR encoding, enabled through the @option{vbr} or @option{flags +qscale} options, is experimental and only works with some @@ -1378,6 +1371,49 @@ makes it possible to store non-rgb pix_fmts. @end table +@section librav1e + +rav1e AV1 encoder wrapper. + +Requires the presence of the rav1e headers and library during configuration. +You need to explicitly configure the build with @code{--enable-librav1e}. + +@subsection Options + +@table @option +@item qmax +Sets the maximum quantizer to use when using bitrate mode. + +@item qmin +Sets the minimum quantizer to use when using bitrate mode. + +@item qp +Uses quantizer mode to encode at the given quantizer (0-255). + +@item speed +Selects the speed preset (0-10) to encode with. + +@item tiles +Selects how many tiles to encode with. + +@item tile-rows +Selects how many rows of tiles to encode with. + +@item tile-columns +Selects how many columns of tiles to encode with. + +@item rav1e-params +Set rav1e options using a list of @var{key}=@var{value} pairs separated +by ":". See @command{rav1e --help} for a list of options. + +For example to specify librav1e encoding options with @option{-rav1e-params}: + +@example +ffmpeg -i input -c:v librav1e -b:v 500K -rav1e-params speed=5:low_latency=true output.mp4 +@end example + +@end table + @section libaom-av1 libaom AV1 encoder wrapper. @@ -1465,6 +1501,15 @@ Complexity-based. Cyclic refresh. @end table +@item tune (@emph{tune}) +Set the distortion metric the encoder is tuned with. Default is @code{psnr}. + +@table @samp +@item psnr (@emph{0}) + +@item ssim (@emph{1}) +@end table + @item lag-in-frames Set the maximum number of frames which the encoder may keep in flight at any one time for lookahead purposes. Defaults to the internal @@ -1544,6 +1589,9 @@ Enable row based multi-threading. Disabled by default. Enable Constrained Directional Enhancement Filter. The libaom-av1 encoder enables CDEF by default. +@item enable-restoration (@emph{boolean}) +Enable Loop Restoration Filter. Default is true for libaom-av1. + @item enable-global-motion (@emph{boolean}) Enable the use of global motion for block prediction. Default is true. @@ -1842,16 +1890,14 @@ Enable error resiliency features. Increase sharpness at the expense of lower PSNR. The valid range is [0, 7]. -@item VP8-specific options -@table @option @item ts-parameters Sets the temporal scalability configuration using a :-separated list of key=value pairs. For example, to specify temporal scalability parameters with @code{ffmpeg}: @example ffmpeg -i INPUT -c:v libvpx -ts-parameters ts_number_layers=3:\ -ts_target_bitrate=250000,500000,1000000:ts_rate_decimator=4,2,1:\ -ts_periodicity=4:ts_layer_id=0,2,1,2 OUTPUT +ts_target_bitrate=250,500,1000:ts_rate_decimator=4,2,1:\ +ts_periodicity=4:ts_layer_id=0,2,1,2:ts_layering_mode=3 OUTPUT @end example Below is a brief explanation of each of the parameters, please refer to @code{struct vpx_codec_enc_cfg} in @code{vpx/vpx_encoder.h} for more @@ -1860,13 +1906,38 @@ details. @item ts_number_layers Number of temporal coding layers. @item ts_target_bitrate -Target bitrate for each temporal layer. +Target bitrate for each temporal layer (in kbps). +(bitrate should be inclusive of the lower temporal layer). @item ts_rate_decimator Frame rate decimation factor for each temporal layer. @item ts_periodicity Length of the sequence defining frame temporal layer membership. @item ts_layer_id Template defining the membership of frames to temporal layers. +@item ts_layering_mode +(optional) Selecting the temporal structure from a set of pre-defined temporal layering modes. +Currently supports the following options. +@table @option +@item 0 +No temporal layering flags are provided internally, +relies on flags being passed in using @code{metadata} field in @code{AVFrame} +with following keys. +@table @option +@item vp8-flags +Sets the flags passed into the encoder to indicate the referencing scheme for +the current frame. +Refer to function @code{vpx_codec_encode} in @code{vpx/vpx_encoder.h} for more +details. +@item temporal_id +Explicitly sets the temporal id of the current frame to encode. +@end table +@item 2 +Two temporal layers. 0-1... +@item 3 +Three temporal layers. 0-2-1-2...; with single reference frame. +@item 4 +Same as option "3", except there is a dependency between +the two temporal layer 2 frames within the temporal period. @end table @end table @@ -2371,6 +2442,20 @@ during configuration. You need to explicitly configure the build with @subsection Options @table @option +@item b +Sets target video bitrate. + +@item bf + +@item g +Set the GOP size. + +@item keyint_min +Minimum GOP size. + +@item refs +Number of reference frames each P-frame can use. The range is from @var{1-16}. + @item preset Set the x265 preset. @@ -2383,6 +2468,28 @@ Set profile restrictions. @item crf Set the quality for constant quality mode. +@item qp +Set constant quantization rate control method parameter. + +@item qmin +Minimum quantizer scale. + +@item qmax +Maximum quantizer scale. + +@item qdiff +Maximum difference between quantizer scales. + +@item qblur +Quantizer curve blur + +@item qcomp +Quantizer curve compression factor + +@item i_qfactor + +@item b_qfactor + @item forced-idr Normally, when forcing a I-frame type, the encoder can select any type of I-frame. This option forces it to choose an IDR-frame. @@ -2618,6 +2725,14 @@ fastest. @end table +@section MediaFoundation + +This provides wrappers to encoders (both audio and video) in the +MediaFoundation framework. It can access both SW and HW encoders. +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 mpeg2 MPEG-2 video encoder. @@ -2625,6 +2740,20 @@ MPEG-2 video encoder. @subsection Options @table @option +@item profile @var{integer} +Select the mpeg2 profile to encode: + +@table @samp +@item 422 +@item main +@item ss +Spatially Scalable +@item snr +SNR Scalable +@item high +@item simple +@end table + @item seq_disp_ext @var{integer} Specifies if the encoder should write a sequence_display_extension to the output. @@ -2733,7 +2862,7 @@ recommended value) and do not set a size constraint. @section QSV encoders -The family of Intel QuickSync Video encoders (MPEG-2, H.264 and HEVC) +The family of Intel QuickSync Video encoders (MPEG-2, H.264, HEVC, JPEG/MJPEG and VP9) The ratecontrol method is selected as follows: @@ -3116,6 +3245,14 @@ and they can also be used in Matroska files. @subsection Options @table @option +@item palette +Specify the global palette used by the bitmaps. + +The format for this option is a string containing 16 24-bits hexadecimal +numbers (without 0x prefix) separated by commas, for example @code{0d00ee, +ee450d, 101010, eaeaea, 0ce60b, ec14ed, ebff0b, 0d617a, 7b7b7b, d1d1d1, +7b2a0e, 0d950c, 0f007b, cf0dec, cfa80c, 7c127b}. + @item even_rows_fix When set to 1, enable a work-around that makes the number of pixel rows even in all subtitles. This fixes a problem with some players that diff --git a/doc/examples/.gitignore b/doc/examples/.gitignore new file mode 100644 index 00000000000..44960e1de7f --- /dev/null +++ b/doc/examples/.gitignore @@ -0,0 +1,24 @@ +/avio_list_dir +/avio_reading +/decode_audio +/decode_video +/demuxing_decoding +/encode_audio +/encode_video +/extract_mvs +/filter_audio +/filtering_audio +/filtering_video +/http_multiclient +/hw_decode +/metadata +/muxing +/pc-uninstalled +/qsvdec +/remuxing +/resampling_audio +/scaling_video +/transcode_aac +/transcoding +/vaapi_encode +/vaapi_transcode diff --git a/doc/examples/Makefile b/doc/examples/Makefile index 2935424e545..81bfd34d5d0 100644 --- a/doc/examples/Makefile +++ b/doc/examples/Makefile @@ -1,4 +1,4 @@ -EXAMPLES-$(CONFIG_AVIO_DIR_CMD_EXAMPLE) += avio_dir_cmd +EXAMPLES-$(CONFIG_AVIO_LIST_DIR_EXAMPLE) += avio_list_dir EXAMPLES-$(CONFIG_AVIO_READING_EXAMPLE) += avio_reading EXAMPLES-$(CONFIG_DECODE_AUDIO_EXAMPLE) += decode_audio EXAMPLES-$(CONFIG_DECODE_VIDEO_EXAMPLE) += decode_video diff --git a/doc/examples/Makefile.example b/doc/examples/Makefile.example index 6428154c51d..a232d97f987 100644 --- a/doc/examples/Makefile.example +++ b/doc/examples/Makefile.example @@ -11,7 +11,7 @@ CFLAGS += -Wall -g CFLAGS := $(shell pkg-config --cflags $(FFMPEG_LIBS)) $(CFLAGS) LDLIBS := $(shell pkg-config --libs $(FFMPEG_LIBS)) $(LDLIBS) -EXAMPLES= avio_dir_cmd \ +EXAMPLES= avio_list_dir \ avio_reading \ decode_audio \ decode_video \ diff --git a/doc/examples/avio_dir_cmd.c b/doc/examples/avio_list_dir.c similarity index 69% rename from doc/examples/avio_dir_cmd.c rename to doc/examples/avio_list_dir.c index 0722bd9ab15..3073baaefa9 100644 --- a/doc/examples/avio_dir_cmd.c +++ b/doc/examples/avio_list_dir.c @@ -102,38 +102,15 @@ static int list_op(const char *input_dir) return ret; } -static int del_op(const char *url) -{ - int ret = avpriv_io_delete(url); - if (ret < 0) - av_log(NULL, AV_LOG_ERROR, "Cannot delete '%s': %s.\n", url, av_err2str(ret)); - return ret; -} - -static int move_op(const char *src, const char *dst) -{ - int ret = avpriv_io_move(src, dst); - if (ret < 0) - av_log(NULL, AV_LOG_ERROR, "Cannot move '%s' into '%s': %s.\n", src, dst, av_err2str(ret)); - return ret; -} - - static void usage(const char *program_name) { - fprintf(stderr, "usage: %s OPERATION entry1 [entry2]\n" - "API example program to show how to manipulate resources " - "accessed through AVIOContext.\n" - "OPERATIONS:\n" - "list list content of the directory\n" - "move rename content in directory\n" - "del delete content in directory\n", - program_name); + fprintf(stderr, "usage: %s input_dir\n" + "API example program to show how to list files in directory " + "accessed through AVIOContext.\n", program_name); } int main(int argc, char *argv[]) { - const char *op = NULL; int ret; av_log_set_level(AV_LOG_DEBUG); @@ -145,32 +122,7 @@ int main(int argc, char *argv[]) avformat_network_init(); - op = argv[1]; - if (strcmp(op, "list") == 0) { - if (argc < 3) { - av_log(NULL, AV_LOG_INFO, "Missing argument for list operation.\n"); - ret = AVERROR(EINVAL); - } else { - ret = list_op(argv[2]); - } - } else if (strcmp(op, "del") == 0) { - if (argc < 3) { - av_log(NULL, AV_LOG_INFO, "Missing argument for del operation.\n"); - ret = AVERROR(EINVAL); - } else { - ret = del_op(argv[2]); - } - } else if (strcmp(op, "move") == 0) { - if (argc < 4) { - av_log(NULL, AV_LOG_INFO, "Missing argument for move operation.\n"); - ret = AVERROR(EINVAL); - } else { - ret = move_op(argv[2], argv[3]); - } - } else { - av_log(NULL, AV_LOG_INFO, "Invalid operation %s\n", op); - ret = AVERROR(EINVAL); - } + ret = list_op(argv[1]); avformat_network_deinit(); diff --git a/doc/examples/decode_audio.c b/doc/examples/decode_audio.c index 19dcafd2c82..6c2a8ed5508 100644 --- a/doc/examples/decode_audio.c +++ b/doc/examples/decode_audio.c @@ -39,6 +39,35 @@ #define AUDIO_INBUF_SIZE 20480 #define AUDIO_REFILL_THRESH 4096 +static int get_format_from_sample_fmt(const char **fmt, + enum AVSampleFormat sample_fmt) +{ + int i; + struct sample_fmt_entry { + enum AVSampleFormat sample_fmt; const char *fmt_be, *fmt_le; + } sample_fmt_entries[] = { + { AV_SAMPLE_FMT_U8, "u8", "u8" }, + { AV_SAMPLE_FMT_S16, "s16be", "s16le" }, + { AV_SAMPLE_FMT_S32, "s32be", "s32le" }, + { AV_SAMPLE_FMT_FLT, "f32be", "f32le" }, + { AV_SAMPLE_FMT_DBL, "f64be", "f64le" }, + }; + *fmt = NULL; + + for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) { + struct sample_fmt_entry *entry = &sample_fmt_entries[i]; + if (sample_fmt == entry->sample_fmt) { + *fmt = AV_NE(entry->fmt_be, entry->fmt_le); + return 0; + } + } + + fprintf(stderr, + "sample format %s is not supported as output format\n", + av_get_sample_fmt_name(sample_fmt)); + return -1; +} + static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame, FILE *outfile) { @@ -86,6 +115,9 @@ int main(int argc, char **argv) size_t data_size; AVPacket *pkt; AVFrame *decoded_frame = NULL; + enum AVSampleFormat sfmt; + int n_channels = 0; + const char *fmt; if (argc <= 2) { fprintf(stderr, "Usage: %s \n", argv[0]); @@ -172,6 +204,26 @@ int main(int argc, char **argv) pkt->size = 0; decode(c, pkt, decoded_frame, outfile); + /* print output pcm infomations, because there have no metadata of pcm */ + sfmt = c->sample_fmt; + + if (av_sample_fmt_is_planar(sfmt)) { + const char *packed = av_get_sample_fmt_name(sfmt); + printf("Warning: the sample format the decoder produced is planar " + "(%s). This example will output the first channel only.\n", + packed ? packed : "?"); + sfmt = av_get_packed_sample_fmt(sfmt); + } + + n_channels = c->channels; + if ((ret = get_format_from_sample_fmt(&fmt, sfmt)) < 0) + goto end; + + printf("Play the output audio file with the command:\n" + "ffplay -f %s -ac %d -ar %d %s\n", + fmt, n_channels, c->sample_rate, + outfilename); +end: fclose(outfile); fclose(f); diff --git a/doc/examples/decode_video.c b/doc/examples/decode_video.c index 5a9d43f6897..169188a4b93 100644 --- a/doc/examples/decode_video.c +++ b/doc/examples/decode_video.c @@ -95,7 +95,8 @@ int main(int argc, char **argv) AVPacket *pkt; if (argc <= 2) { - fprintf(stderr, "Usage: %s \n", argv[0]); + fprintf(stderr, "Usage: %s \n" + "And check your input file is encoded by mpeg1video please.\n", argv[0]); exit(0); } filename = argv[1]; diff --git a/doc/examples/demuxing_decoding.c b/doc/examples/demuxing_decoding.c index 69a31a89351..803e35d25c3 100644 --- a/doc/examples/demuxing_decoding.c +++ b/doc/examples/demuxing_decoding.c @@ -55,95 +55,93 @@ static AVPacket pkt; static int video_frame_count = 0; static int audio_frame_count = 0; -/* Enable or disable frame reference counting. You are not supposed to support - * both paths in your application but pick the one most appropriate to your - * needs. Look for the use of refcount in this example to see what are the - * differences of API usage between them. */ -static int refcount = 0; +static int output_video_frame(AVFrame *frame) +{ + if (frame->width != width || frame->height != height || + frame->format != pix_fmt) { + /* To handle this change, one could call av_image_alloc again and + * decode the following frames into another rawvideo file. */ + fprintf(stderr, "Error: Width, height and pixel format have to be " + "constant in a rawvideo file, but the width, height or " + "pixel format of the input video changed:\n" + "old: width = %d, height = %d, format = %s\n" + "new: width = %d, height = %d, format = %s\n", + width, height, av_get_pix_fmt_name(pix_fmt), + frame->width, frame->height, + av_get_pix_fmt_name(frame->format)); + return -1; + } + + printf("video_frame n:%d coded_n:%d\n", + video_frame_count++, frame->coded_picture_number); + + /* 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); -static int decode_packet(int *got_frame, int cached) + /* write to rawvideo file */ + fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file); + return 0; +} + +static int output_audio_frame(AVFrame *frame) +{ + size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample(frame->format); + printf("audio_frame n:%d nb_samples:%d pts:%s\n", + audio_frame_count++, frame->nb_samples, + av_ts2timestr(frame->pts, &audio_dec_ctx->time_base)); + + /* Write the raw audio data samples of the first plane. This works + * fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However, + * most audio decoders output planar audio, which uses a separate + * plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P). + * In other words, this code will write only the first audio channel + * in these cases. + * You should use libswresample or libavfilter to convert the frame + * to packed data. */ + fwrite(frame->extended_data[0], 1, unpadded_linesize, audio_dst_file); + + return 0; +} + +static int decode_packet(AVCodecContext *dec, const AVPacket *pkt) { int ret = 0; - int decoded = pkt.size; - *got_frame = 0; + // submit the packet to the decoder + ret = avcodec_send_packet(dec, pkt); + if (ret < 0) { + fprintf(stderr, "Error submitting a packet for decoding (%s)\n", av_err2str(ret)); + return ret; + } - if (pkt.stream_index == video_stream_idx) { - /* decode video frame */ - ret = avcodec_decode_video2(video_dec_ctx, frame, got_frame, &pkt); + // get all the available frames from the decoder + while (ret >= 0) { + ret = avcodec_receive_frame(dec, frame); if (ret < 0) { - fprintf(stderr, "Error decoding video frame (%s)\n", av_err2str(ret)); - return ret; - } + // 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; - if (*got_frame) { - - if (frame->width != width || frame->height != height || - frame->format != pix_fmt) { - /* To handle this change, one could call av_image_alloc again and - * decode the following frames into another rawvideo file. */ - fprintf(stderr, "Error: Width, height and pixel format have to be " - "constant in a rawvideo file, but the width, height or " - "pixel format of the input video changed:\n" - "old: width = %d, height = %d, format = %s\n" - "new: width = %d, height = %d, format = %s\n", - width, height, av_get_pix_fmt_name(pix_fmt), - frame->width, frame->height, - av_get_pix_fmt_name(frame->format)); - return -1; - } - - printf("video_frame%s n:%d coded_n:%d\n", - cached ? "(cached)" : "", - video_frame_count++, frame->coded_picture_number); - - /* 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); - - /* write to rawvideo file */ - fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file); - } - } else if (pkt.stream_index == audio_stream_idx) { - /* decode audio frame */ - ret = avcodec_decode_audio4(audio_dec_ctx, frame, got_frame, &pkt); - if (ret < 0) { - fprintf(stderr, "Error decoding audio frame (%s)\n", av_err2str(ret)); + fprintf(stderr, "Error during decoding (%s)\n", av_err2str(ret)); return ret; } - /* Some audio decoders decode only part of the packet, and have to be - * called again with the remainder of the packet data. - * Sample: fate-suite/lossless-audio/luckynight-partial.shn - * Also, some decoders might over-read the packet. */ - decoded = FFMIN(ret, pkt.size); - - if (*got_frame) { - size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample(frame->format); - printf("audio_frame%s n:%d nb_samples:%d pts:%s\n", - cached ? "(cached)" : "", - audio_frame_count++, frame->nb_samples, - av_ts2timestr(frame->pts, &audio_dec_ctx->time_base)); - - /* Write the raw audio data samples of the first plane. This works - * fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However, - * most audio decoders output planar audio, which uses a separate - * plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P). - * In other words, this code will write only the first audio channel - * in these cases. - * You should use libswresample or libavfilter to convert the frame - * to packed data. */ - fwrite(frame->extended_data[0], 1, unpadded_linesize, audio_dst_file); - } - } - /* If we use frame reference counting, we own the data and need - * to de-reference it when we don't use it anymore */ - if (*got_frame && refcount) + // write the frame data to output file + if (dec->codec->type == AVMEDIA_TYPE_VIDEO) + ret = output_video_frame(frame); + else + ret = output_audio_frame(frame); + av_frame_unref(frame); + if (ret < 0) + return ret; + } - return decoded; + return 0; } static int open_codec_context(int *stream_idx, @@ -186,8 +184,7 @@ static int open_codec_context(int *stream_idx, return ret; } - /* Init the decoders, with or without reference counting */ - av_dict_set(&opts, "refcounted_frames", refcount ? "1" : "0", 0); + /* Init the decoders */ if ((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) { fprintf(stderr, "Failed to open %s codec\n", av_get_media_type_string(type)); @@ -230,24 +227,17 @@ static int get_format_from_sample_fmt(const char **fmt, int main (int argc, char **argv) { - int ret = 0, got_frame; + int ret = 0; - if (argc != 4 && argc != 5) { - fprintf(stderr, "usage: %s [-refcount] input_file video_output_file audio_output_file\n" + if (argc != 4) { + fprintf(stderr, "usage: %s input_file video_output_file audio_output_file\n" "API example program to show how to read frames from an input file.\n" "This program reads frames from a file, decodes them, and writes decoded\n" "video frames to a rawvideo file named video_output_file, and decoded\n" - "audio frames to a rawaudio file named audio_output_file.\n\n" - "If the -refcount option is specified, the program use the\n" - "reference counting frame system which allows keeping a copy of\n" - "the data for longer than one decode call.\n" - "\n", argv[0]); + "audio frames to a rawaudio file named audio_output_file.\n", + argv[0]); exit(1); } - if (argc == 5 && !strcmp(argv[1], "-refcount")) { - refcount = 1; - argv++; - } src_filename = argv[1]; video_dst_filename = argv[2]; audio_dst_filename = argv[3]; @@ -325,23 +315,22 @@ int main (int argc, char **argv) /* read frames from the file */ while (av_read_frame(fmt_ctx, &pkt) >= 0) { - AVPacket orig_pkt = pkt; - do { - ret = decode_packet(&got_frame, 0); - if (ret < 0) - break; - pkt.data += ret; - pkt.size -= ret; - } while (pkt.size > 0); - av_packet_unref(&orig_pkt); + // check if the packet belongs to a stream we are interested in, otherwise + // skip it + if (pkt.stream_index == video_stream_idx) + ret = decode_packet(video_dec_ctx, &pkt); + else if (pkt.stream_index == audio_stream_idx) + ret = decode_packet(audio_dec_ctx, &pkt); + av_packet_unref(&pkt); + if (ret < 0) + break; } - /* flush cached frames */ - pkt.data = NULL; - pkt.size = 0; - do { - decode_packet(&got_frame, 1); - } while (got_frame); + /* flush the decoders */ + if (video_dec_ctx) + decode_packet(video_dec_ctx, NULL); + if (audio_dec_ctx) + decode_packet(audio_dec_ctx, NULL); printf("Demuxing succeeded.\n"); diff --git a/doc/examples/encode_video.c b/doc/examples/encode_video.c index 6731b2ad19b..908eb203d52 100644 --- a/doc/examples/encode_video.c +++ b/doc/examples/encode_video.c @@ -145,7 +145,7 @@ int main(int argc, char **argv) frame->width = c->width; frame->height = c->height; - ret = av_frame_get_buffer(frame, 32); + ret = av_frame_get_buffer(frame, 0); if (ret < 0) { fprintf(stderr, "Could not allocate the video frame data\n"); exit(1); @@ -186,7 +186,8 @@ int main(int argc, char **argv) encode(c, NULL, pkt, f); /* add sequence end code to have a real MPEG file */ - fwrite(endcode, 1, sizeof(endcode), f); + if (codec->id == AV_CODEC_ID_MPEG1VIDEO || codec->id == AV_CODEC_ID_MPEG2VIDEO) + fwrite(endcode, 1, sizeof(endcode), f); fclose(f); avcodec_free_context(&c); diff --git a/doc/examples/muxing.c b/doc/examples/muxing.c index 08da98e574a..bd16486a242 100644 --- a/doc/examples/muxing.c +++ b/doc/examples/muxing.c @@ -78,15 +78,45 @@ static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt) pkt->stream_index); } -static int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt) +static int write_frame(AVFormatContext *fmt_ctx, AVCodecContext *c, + AVStream *st, AVFrame *frame) { - /* rescale output packet timestamp values from codec to stream timebase */ - av_packet_rescale_ts(pkt, *time_base, st->time_base); - pkt->stream_index = st->index; + int ret; + + // send the frame to the encoder + ret = avcodec_send_frame(c, frame); + if (ret < 0) { + fprintf(stderr, "Error sending a frame to the encoder: %s\n", + av_err2str(ret)); + exit(1); + } + + while (ret >= 0) { + AVPacket pkt = { 0 }; + + ret = avcodec_receive_packet(c, &pkt); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + break; + else if (ret < 0) { + fprintf(stderr, "Error encoding a frame: %s\n", av_err2str(ret)); + exit(1); + } + + /* rescale output packet timestamp values from codec to stream timebase */ + av_packet_rescale_ts(&pkt, c->time_base, st->time_base); + pkt.stream_index = st->index; - /* Write the compressed frame to the media file. */ - log_packet(fmt_ctx, pkt); - return av_interleaved_write_frame(fmt_ctx, pkt); + /* Write the compressed frame to the media file. */ + log_packet(fmt_ctx, &pkt); + ret = av_interleaved_write_frame(fmt_ctx, &pkt); + av_packet_unref(&pkt); + if (ret < 0) { + fprintf(stderr, "Error while writing output packet: %s\n", av_err2str(ret)); + exit(1); + } + } + + return ret == AVERROR_EOF ? 1 : 0; } /* Add an output stream. */ @@ -285,7 +315,7 @@ static AVFrame *get_audio_frame(OutputStream *ost) /* check if we want to generate more frames */ if (av_compare_ts(ost->next_pts, ost->enc->time_base, - STREAM_DURATION, (AVRational){ 1, 1 }) >= 0) + STREAM_DURATION, (AVRational){ 1, 1 }) > 0) return NULL; for (j = 0; j nb_samples; j++) { @@ -309,13 +339,10 @@ static AVFrame *get_audio_frame(OutputStream *ost) static int write_audio_frame(AVFormatContext *oc, OutputStream *ost) { AVCodecContext *c; - AVPacket pkt = { 0 }; // data and size must be 0; AVFrame *frame; int ret; - int got_packet; int dst_nb_samples; - av_init_packet(&pkt); c = ost->enc; frame = get_audio_frame(ost); @@ -349,22 +376,7 @@ static int write_audio_frame(AVFormatContext *oc, OutputStream *ost) ost->samples_count += dst_nb_samples; } - ret = avcodec_encode_audio2(c, &pkt, frame, &got_packet); - if (ret < 0) { - fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret)); - exit(1); - } - - if (got_packet) { - ret = write_frame(oc, &c->time_base, ost->st, &pkt); - if (ret < 0) { - fprintf(stderr, "Error while writing audio frame: %s\n", - av_err2str(ret)); - exit(1); - } - } - - return (frame || got_packet) ? 0 : 1; + return write_frame(oc, c, ost->st, frame); } /**************************************************************/ @@ -384,7 +396,7 @@ static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height) picture->height = height; /* allocate the buffers for the frame data */ - ret = av_frame_get_buffer(picture, 32); + ret = av_frame_get_buffer(picture, 0); if (ret < 0) { fprintf(stderr, "Could not allocate frame data.\n"); exit(1); @@ -464,7 +476,7 @@ static AVFrame *get_video_frame(OutputStream *ost) /* check if we want to generate more frames */ if (av_compare_ts(ost->next_pts, c->time_base, - STREAM_DURATION, (AVRational){ 1, 1 }) >= 0) + STREAM_DURATION, (AVRational){ 1, 1 }) > 0) return NULL; /* when we pass a frame to the encoder, it may keep a reference to it @@ -506,37 +518,8 @@ static AVFrame *get_video_frame(OutputStream *ost) */ static int write_video_frame(AVFormatContext *oc, OutputStream *ost) { - int ret; - AVCodecContext *c; - AVFrame *frame; - int got_packet = 0; - AVPacket pkt = { 0 }; - - c = ost->enc; - - frame = get_video_frame(ost); - - av_init_packet(&pkt); - - /* encode the image */ - ret = avcodec_encode_video2(c, &pkt, frame, &got_packet); - if (ret < 0) { - fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret)); - exit(1); - } - - if (got_packet) { - ret = write_frame(oc, &c->time_base, ost->st, &pkt); - } else { - ret = 0; - } - - if (ret < 0) { - fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret)); - exit(1); - } + return write_frame(oc, ost->enc, ost->st, get_video_frame(ost)); - return (frame || got_packet) ? 0 : 1; } static void close_stream(AVFormatContext *oc, OutputStream *ost) diff --git a/doc/examples/vaapi_encode.c b/doc/examples/vaapi_encode.c index 98fd5d3b512..707939db374 100644 --- a/doc/examples/vaapi_encode.c +++ b/doc/examples/vaapi_encode.c @@ -172,7 +172,7 @@ int main(int argc, char *argv[]) sw_frame->width = width; sw_frame->height = height; sw_frame->format = AV_PIX_FMT_NV12; - if ((err = av_frame_get_buffer(sw_frame, 32)) < 0) + if ((err = av_frame_get_buffer(sw_frame, 0)) < 0) goto close; if ((err = fread((uint8_t*)(sw_frame->data[0]), size, 1, fin)) <= 0) break; diff --git a/doc/fate.texi b/doc/fate.texi index 2be61d639c9..c3550785a18 100644 --- a/doc/fate.texi +++ b/doc/fate.texi @@ -149,6 +149,8 @@ the synchronisation of the samples directory. @chapter Uploading new samples to the fate suite +If you need a sample uploaded send a mail to samples-request. + This is for developers who have an account on the fate suite server. If you upload new samples, please make sure they are as small as possible, space on each client, network bandwidth and so on benefit from smaller test cases. @@ -157,6 +159,8 @@ practice generally do not replace, remove or overwrite files as it likely would break older checkouts or releases. Also all needed samples for a commit should be uploaded, ideally 24 hours, before the push. +If you need an account for frequently uploading samples or you wish to help +others by doing that send a mail to ffmpeg-devel. @example #First update your local samples copy: diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index cd35eb49c82..76fafdcf7ed 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -879,12 +879,19 @@ 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}) -Force key frames at the specified timestamps, more precisely at the first -frames after each specified time. +@item -force_key_frames[:@var{stream_specifier}] source (@emph{output,per-stream}) -If the argument is prefixed with @code{expr:}, the string @var{expr} -is interpreted like an expression and is evaluated for each frame. A -key frame is forced in case the evaluation is non-zero. +@var{force_key_frames} can take arguments of the following form: + +@table @option + +@item @var{time}[,@var{time}...] +If the argument consists of timestamps, ffmpeg will round the specified times to the nearest +output timestamp as per the encoder time base and force a keyframe at the first frame having +timestamp equal or greater than the computed timestamp. Note that if the encoder time base is too +coarse, then the keyframes may be forced on frames with timestamps lower than the specified time. +The default encoder time base is the inverse of the output framerate but may be set otherwise +via @code{-enc_time_base}. If one of the times is "@code{chapters}[@var{delta}]", it is expanded into the time of the beginning of all chapters in the file, shifted by @@ -898,6 +905,11 @@ before the beginning of every chapter: -force_key_frames 0:05:00,chapters-0.1 @end example +@item expr:@var{expr} +If the argument is prefixed with @code{expr:}, the string @var{expr} +is interpreted like an expression and is evaluated for each frame. A +key frame is forced in case the evaluation is non-zero. + The expression in @var{expr} can contain the following constants: @table @option @item n @@ -925,6 +937,12 @@ starting from second 13: -force_key_frames expr:if(isnan(prev_forced_t),gte(t,13),gte(t,prev_forced_t+5)) @end example +@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. + +@end table + Note that forcing too many keyframes is very harmful for the lookahead algorithms of certain encoders: using fixed-GOP options or similar would be more efficient. @@ -1011,6 +1029,35 @@ Choose the GPU device on the second platform supporting the @emph{cl_khr_fp16} extension. @end table +@item vulkan +If @var{device} is an integer, it selects the device by its index in a +system-dependent list of devices. If @var{device} is any other string, it +selects the first device with a name containing that string as a substring. + +The following options are recognized: +@table @option +@item debug +If set to 1, enables the validation layer, if installed. +@item linear_images +If set to 1, images allocated by the hwcontext will be linear and locally mappable. +@item instance_extensions +A plus separated list of additional instance extensions to enable. +@item device_extensions +A plus separated list of additional device extensions to enable. +@end table + +Examples: +@table @emph +@item -init_hw_device vulkan:1 +Choose the second device on the system. + +@item -init_hw_device vulkan:RADV +Choose the first device with a name containing the string @emph{RADV}. + +@item -init_hw_device vulkan:0,instance_extensions=VK_KHR_wayland_surface+VK_KHR_xcb_surface +Choose the first device and enable the Wayland and XCB instance extensions. +@end table + @end table @item -init_hw_device @var{type}[=@var{name}]@@@var{source} @@ -1383,7 +1430,7 @@ it will usually display as 0 if not supported. Show benchmarking information during the encode. Shows real, system and user time used in various steps (audio/video encode/decode). @item -timelimit @var{duration} (@emph{global}) -Exit after ffmpeg has been running for @var{duration} seconds. +Exit after ffmpeg has been running for @var{duration} seconds in CPU user time. @item -dump (@emph{global}) Dump each input packet to stderr. @item -hex (@emph{global}) @@ -1515,6 +1562,10 @@ Enable bitexact mode for (de)muxer and (de/en)coder Finish encoding when the shortest input stream ends. @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 -muxdelay @var{seconds} (@emph{output}) Set the maximum demux-decode delay. @item -muxpreload @var{seconds} (@emph{output}) @@ -1670,6 +1721,8 @@ Stop and abort on various conditions. The following flags are available: @table @option @item empty_output No packets were passed to the muxer, the output is empty. +@item empty_output_stream +No packets were passed to the muxer in some of the output streams. @end table @item -xerror (@emph{global}) diff --git a/doc/ffplay.texi b/doc/ffplay.texi index a487c0de8d0..f3761bb12e4 100644 --- a/doc/ffplay.texi +++ b/doc/ffplay.texi @@ -133,8 +133,9 @@ This option has been deprecated in favor of private options, try -pixel_format. @item -stats Print several playback statistics, in particular show the stream duration, the codec parameters, the current position in the stream and -the audio/video synchronisation drift. It is on by default, to -explicitly disable it you need to specify @code{-nostats}. +the audio/video synchronisation drift. It is shown by default, unless the +log level is lower than @code{info}. Its display can be forced by manually +specifying this option. To disable it, you need to specify @code{-nostats}. @item -fast Non-spec-compliant optimizations. diff --git a/doc/ffprobe.xsd b/doc/ffprobe.xsd index 97dc67def6d..71cbd23ec6f 100644 --- a/doc/ffprobe.xsd +++ b/doc/ffprobe.xsd @@ -226,6 +226,7 @@ + diff --git a/doc/fftools-common-opts.texi b/doc/fftools-common-opts.texi index 1234de850e9..f339e0d7661 100644 --- a/doc/fftools-common-opts.texi +++ b/doc/fftools-common-opts.texi @@ -236,13 +236,11 @@ ffmpeg [...] -loglevel +repeat By default the program logs to stderr. If coloring is supported by the terminal, colors are used to mark errors and warnings. Log coloring can be disabled setting the environment variable -@env{AV_LOG_FORCE_NOCOLOR} or @env{NO_COLOR}, or can be forced setting +@env{AV_LOG_FORCE_NOCOLOR}, or can be forced setting the environment variable @env{AV_LOG_FORCE_COLOR}. -The use of the environment variable @env{NO_COLOR} is deprecated and -will be dropped in a future FFmpeg version. @item -report -Dump full command line and console output to a file named +Dump full command line and log output to a file named @code{@var{program}-@var{YYYYMMDD}-@var{HHMMSS}.log} in the current directory. This file can be useful for bug reports. diff --git a/doc/filters.texi b/doc/filters.texi index 604e44d5697..84567dec16a 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -312,6 +312,15 @@ See @code{ffmpeg -filters} to view which filters have timeline support. @c man end FILTERGRAPH DESCRIPTION +@anchor{commands} +@chapter Changing options at runtime with a command + +Some options can be changed during the operation of the filter using +a command. These options are marked 'T' on the output of +@command{ffmpeg} @option{-h filter=}. +The name of the command is the name of the option and the argument is +the new value. + @anchor{framesync} @chapter Options for filters with several inputs (framesync) @c man begin OPTIONS FOR FILTERS WITH SEVERAL INPUTS @@ -434,6 +443,10 @@ How much to use compressed signal in output. Default is 1. Range is between 0 and 1. @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section acontrast Simple audio dynamic range compression/expansion filter. @@ -688,6 +701,10 @@ Unused delays will be silently ignored. If number of given delays is smaller than number of channels all remaining channels will not be delayed. If you want to delay exact number of samples, append 'S' to number. If you want instead to delay in seconds, append 's' to number. + +@item all +Use last set delay for all remaining channels. By default is disabled. +This option if enabled changes how option @code{delays} is interpreted. @end table @subsection Examples @@ -706,6 +723,12 @@ the first channel (and any other channels that may be present) unchanged. @example adelay=0|500S|700S @end example + +@item +Delay all channels by same number of samples: +@example +adelay=delays=64S:all=1 +@end example @end itemize @section aderivative, aintegral @@ -756,7 +779,7 @@ aecho=0.8:0.88:60:0.4 @end example @item -If delay is very short, then it sound like a (metallic) robot playing music: +If delay is very short, then it sounds like a (metallic) robot playing music: @example aecho=0.8:0.88:6:0.4 @end example @@ -1143,12 +1166,24 @@ Leave almost only low frequencies in audio: @example afftfilt="'real=re * (1-clip((b/nb)*b,0,1))':imag='im * (1-clip((b/nb)*b,0,1))'" @end example + +@item +Apply robotize effect: +@example +afftfilt="real='hypot(re,im)*sin(0)':imag='hypot(re,im)*cos(0)':win_size=512:overlap=0.75" +@end example + +@item +Apply whisper effect: +@example +afftfilt="real='hypot(re,im)*cos((random(0)*2-1)*2*3.14)':imag='hypot(re,im)*sin((random(1)*2-1)*2*3.14)':win_size=128:overlap=0.8" +@end example @end itemize @anchor{afir} @section afir -Apply an arbitrary Frequency Impulse Response filter. +Apply an arbitrary Finite Impulse Response filter. This filter is designed for applying long FIR filters, up to 60 seconds long. @@ -1157,11 +1192,11 @@ It can be used as component for digital crossover filters, room equalization, cross talk cancellation, wavefield synthesis, auralization, ambiophonics, ambisonics and spatialization. -This filter uses second stream as FIR coefficients. -If second stream holds single channel, it will be used -for all input channels in first stream, otherwise -number of channels in second stream must be same as -number of channels in first stream. +This filter uses the streams higher than first one as FIR coefficients. +If the non-first stream holds a single channel, it will be used +for all input channels in the first stream, otherwise +the number of channels in the non-first stream must be same as +the number of channels in the first stream. It accepts the following parameters: @@ -1222,13 +1257,22 @@ Set video stream frame rate. This option is used only when @var{response} is ena @item minp Set minimal partition size used for convolution. Default is @var{8192}. -Allowed range is from @var{8} to @var{32768}. +Allowed range is from @var{1} to @var{32768}. Lower values decreases latency at cost of higher CPU usage. @item maxp Set maximal partition size used for convolution. Default is @var{8192}. Allowed range is from @var{8} to @var{32768}. Lower values may increase CPU usage. + +@item nbirs +Set number of input impulse responses streams which will be switchable at runtime. +Allowed range is from @var{1} to @var{32}. Default is @var{1}. + +@item ir +Set IR stream which will be used for convolution, starting from @var{0}, should always be +lower than supplied value by @code{nbirs} option. Default is @var{0}. +This option can be changed at runtime via @ref{commands}. @end table @subsection Examples @@ -1250,13 +1294,13 @@ negotiate the most appropriate format to minimize conversions. It accepts the following parameters: @table @option -@item sample_fmts +@item sample_fmts, f A '|'-separated list of requested sample formats. -@item sample_rates +@item sample_rates, r A '|'-separated list of requested sample rates. -@item channel_layouts +@item channel_layouts, cl A '|'-separated list of requested channel layouts. See @ref{channel layout syntax,,the Channel Layout section in the ffmpeg-utils(1) manual,ffmpeg-utils} @@ -1345,13 +1389,13 @@ Apply an arbitrary Infinite Impulse Response filter. It accepts the following parameters: @table @option -@item z +@item zeros, z Set numerator/zeros coefficients. -@item p +@item poles, p Set denominator/poles coefficients. -@item k +@item gains, k Set channels gains. @item dry_gain @@ -1360,25 +1404,27 @@ Set input gain. @item wet_gain Set output gain. -@item f +@item format, f Set coefficients format. @table @samp @item tf -transfer function +digital transfer function @item zp Z-plane zeros/poles, cartesian (default) @item pr Z-plane zeros/poles, polar radians @item pd Z-plane zeros/poles, polar degrees +@item sp +S-plane zeros/poles @end table -@item r +@item process, r Set kind of processing. Can be @code{d} - direct or @code{s} - serial cascading. Default is @code{s}. -@item e +@item precision, e Set filtering precision. @table @samp @@ -1392,6 +1438,10 @@ single-precision floating-point 16-bit integers @end table +@item normalize, n +Normalize filter coefficients, by default is enabled. +Enabling it will normalize magnitude response at DC to 0dB. + @item mix How much to use filtered signal in output. Default is 1. Range is between 0 and 1. @@ -1516,6 +1566,13 @@ Range is between 0 and 1. @item channels, c Specify which channels to filter, by default all available are filtered. + +@item normalize, n +Normalize biquad coefficients, by default is disabled. +Enabling it will normalize magnitude response at DC to 0dB. + +@item order, o +Set the filter order, can be 1 or 2. Default is 2. @end table @subsection Commands @@ -1655,6 +1712,14 @@ Specify weight of each input audio stream as sequence. Each weight is separated by space. By default all inputs have same weight. @end table +@subsection Commands + +This filter supports the following commands: +@table @option +@item weights +Syntax is same as option with same name. +@end table + @section amultiply Multiply first audio stream with second audio stream and store result @@ -1766,7 +1831,7 @@ Each sample is adjusted by looking for other samples with similar contexts. This context similarity is defined by comparing their surrounding patches of size @option{p}. Patches are searched in an area of @option{r} around the sample. -The filter accepts the following options. +The filter accepts the following options: @table @option @item s @@ -1814,6 +1879,62 @@ Change output mode. Syntax for the command is : "i", "o" or "n" string. @end table +@section anlms +Apply Normalized Least-Mean-Squares algorithm to the first audio stream using the second audio stream. + +This adaptive filter is used to mimic a desired filter by finding the filter coefficients that +relate to producing the least mean square of the error signal (difference between the desired, +2nd input audio stream and the actual signal, the 1st input audio stream). + +A description of the accepted options follows. + +@table @option +@item order +Set filter order. + +@item mu +Set filter mu. + +@item eps +Set the filter eps. + +@item leakage +Set the filter leakage. + +@item out_mode +It accepts the following values: +@table @option +@item i +Pass the 1st input. + +@item d +Pass the 2nd input. + +@item o +Pass filtered samples. + +@item n +Pass difference between desired and filtered samples. + +Default value is @var{o}. +@end table +@end table + +@subsection Examples + +@itemize +@item +One of many usages of this filter is noise reduction, input audio is filtered +with same samples that are delayed by fixed amount, one such example for stereo audio is: +@example +asplit[a][b],[a]adelay=32S|32S[a],[b][a]anlms=order=128:leakage=0.0005:mu=.5:out_mode=o +@end example +@end itemize + +@subsection Commands + +This filter supports the same commands as options, excluding option @code{order}. + @section anull Pass the audio source unchanged to the output. @@ -2029,6 +2150,17 @@ atrim=end=5,areverse @end example @end itemize +@section arnndn + +Reduce noise from speech using Recurrent Neural Networks. + +This filter accepts the following options: + +@table @option +@item model, m +Set train model file to load. This option is always required. +@end table + @section asetnsamples Set the number of samples per each output audio frame. @@ -2142,6 +2274,10 @@ It accepts the following values: Set additional parameter which controls sigmoid function. @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section asr Automatic Speech Recognition @@ -2210,6 +2346,8 @@ RMS_trough Crest_factor Flat_factor Peak_count +Noise_floor +Noise_floor_count Bit_depth Dynamic_range Zero_crossings @@ -2232,6 +2370,8 @@ RMS_peak RMS_trough Flat_factor Peak_count +Noise_floor +Noise_floor_count Bit_depth Number_of_samples Number_of_NaNs @@ -2303,6 +2443,13 @@ Flatness (i.e. consecutive samples with the same value) of the signal at its pea Number of occasions (not the number of samples) that the signal attained either @var{Min level} or @var{Max level}. +@item Noise floor dB +Minimum local peak measured in dBFS over a short window. + +@item Noise floor count +Number of occasions (not the number of samples) that the signal attained +@var{Noise floor}. + @item Bit depth Overall bit depth of audio. Number of bits used for each sample. @@ -2316,6 +2463,45 @@ Number of points where the waveform crosses the zero level axis. Rate of Zero crossings and number of audio samples. @end table +@section asubboost +Boost subwoofer frequencies. + +The filter accepts the following options: + +@table @option +@item dry +Set dry gain, how much of original signal is kept. Allowed range is from 0 to 1. +Default value is 0.5. + +@item wet +Set wet gain, how much of filtered signal is kept. Allowed range is from 0 to 1. +Default value is 0.8. + +@item decay +Set delay line decay gain value. Allowed range is from 0 to 1. +Default value is 0.7. + +@item feedback +Set delay line feedback gain value. Allowed range is from 0 to 1. +Default value is 0.5. + +@item cutoff +Set cutoff frequency in herz. Allowed range is 50 to 900. +Default value is 100. + +@item slope +Set slope amount for cutoff frequency. Allowed range is 0.0001 to 1. +Default value is 0.5. + +@item delay +Set delay. Allowed range is from 1 to 100. +Default value is 20. +@end table + +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section atempo Adjust audio tempo. @@ -2351,6 +2537,15 @@ atempo=sqrt(3),atempo=sqrt(3) @end example @end itemize +@subsection Commands + +This filter supports the following commands: +@table @option +@item tempo +Change filter tempo scale factor. +Syntax for the command is : "@var{tempo}" +@end table + @section atrim Trim the input so that the output contains one continuous subpart of the input. @@ -2420,6 +2615,39 @@ ffmpeg -i INPUT -af atrim=end_sample=1000 @end itemize +@section axcorrelate +Calculate normalized cross-correlation between two input audio streams. + +Resulted samples are always between -1 and 1 inclusive. +If result is 1 it means two input samples are highly correlated in that selected segment. +Result 0 means they are not correlated at all. +If result is -1 it means two input samples are out of phase, which means they cancel each +other. + +The filter accepts the following options: + +@table @option +@item size +Set size of segment over which cross-correlation is calculated. +Default is 256. Allowed range is from 2 to 131072. + +@item algo +Set algorithm for cross-correlation. Can be @code{slow} or @code{fast}. +Default is @code{slow}. Fast algorithm assumes mean values over any given segment +are always zero and thus need much less calculations to make. +This is generally not true, but is valid for typical audio streams. +@end table + +@subsection Examples + +@itemize +@item +Calculate correlation between channels in stereo audio stream: +@example +ffmpeg -i stereo.wav -af channelsplit,axcorrelate=size=1024:algo=fast correlation.wav +@end example +@end itemize + @section bandpass Apply a two-pole Butterworth band-pass filter with central @@ -2461,6 +2689,10 @@ Range is between 0 and 1. @item channels, c Specify which channels to filter, by default all available are filtered. + +@item normalize, n +Normalize biquad coefficients, by default is disabled. +Enabling it will normalize magnitude response at DC to 0dB. @end table @subsection Commands @@ -2520,6 +2752,10 @@ Range is between 0 and 1. @item channels, c Specify which channels to filter, by default all available are filtered. + +@item normalize, n +Normalize biquad coefficients, by default is disabled. +Enabling it will normalize magnitude response at DC to 0dB. @end table @subsection Commands @@ -2586,6 +2822,10 @@ Range is between 0 and 1. @item channels, c Specify which channels to filter, by default all available are filtered. + +@item normalize, n +Normalize biquad coefficients, by default is disabled. +Enabling it will normalize magnitude response at DC to 0dB. @end table @subsection Commands @@ -2637,6 +2877,13 @@ Syntax for the command is : "@var{value}" @item mix, m How much to use filtered signal in output. Default is 1. Range is between 0 and 1. + +@item channels, c +Specify which channels to filter, by default all available are filtered. + +@item normalize, n +Normalize biquad coefficients, by default is disabled. +Enabling it will normalize magnitude response at DC to 0dB. @end table @section bs2b @@ -2965,12 +3212,12 @@ Compensation Delay Line is a metric based delay to compensate differing positions of microphones or speakers. For example, you have recorded guitar with two microphones placed in -different location. Because the front of sound wave has fixed speed in +different locations. Because the front of sound wave has fixed speed in normal conditions, the phasing of microphones can vary and depends on their location and interposition. The best sound mix can be achieved when -these microphones are in phase (synchronized). Note that distance of -~30 cm between microphones makes one microphone to capture signal in -antiphase to another microphone. That makes the final mix sounding moody. +these microphones are in phase (synchronized). Note that a distance of +~30 cm between microphones makes one microphone capture the signal in +antiphase to the other microphone. That makes the final mix sound moody. This filter helps to solve phasing problems by adding different delays to each microphone track and make them synchronized. @@ -2979,7 +3226,7 @@ synchronize other tracks one by one with it. Remember that synchronization/delay tolerance depends on sample rate, too. Higher sample rates will give more tolerance. -It accepts the following parameters: +The filter accepts the following parameters: @table @option @item mm @@ -3003,7 +3250,7 @@ Set wet amount. Amount of processed (wet) signal. Default is 1. @item temp -Set temperature degree in Celsius. This is the temperature of the environment. +Set temperature in degrees Celsius. This is the temperature of the environment. Default is 20. @end table @@ -3029,6 +3276,10 @@ Set soundstage wideness. Default is 0.5. Allowed range is from 0 to 1. This sets cut off frequency of low shelf filter. Default is cut off near 1550 Hz. With range set to 1 cut off frequency is set to 2100 Hz. +@item slope +Set curve slope of low shelf filter. Default is 0.5. +Allowed range is from 0.01 to 1. + @item level_in Set input gain. Default is 0.9. @@ -3036,6 +3287,10 @@ Set input gain. Default is 0.9. Set output gain. Default is 1. @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section crystalizer Simple algorithm to expand audio dynamic range. @@ -3050,6 +3305,10 @@ Sets the intensity of effect (default: 2.0). Must be in range between 0.0 Enable clipping. By default is enabled. @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section dcshift Apply a DC shift to the audio. @@ -3135,7 +3394,7 @@ this goal *without* applying "dynamic range compressing". It will retain 100% of the dynamic range *within* each section of the audio file. @table @option -@item f +@item framelen, f Set the frame length in milliseconds. In range from 10 to 8000 milliseconds. Default is 500 milliseconds. The Dynamic Audio Normalizer processes the input audio in small chunks, @@ -3150,7 +3409,7 @@ been found to give good results with most files. Note that the exact frame length, in number of samples, will be determined automatically, based on the sampling rate of the individual input audio file. -@item g +@item gausssize, g Set the Gaussian filter window size. In range from 3 to 301, must be odd number. Default is 31. Probably the most important parameter of the Dynamic Audio Normalizer is the @@ -3167,7 +3426,7 @@ Normalizer will behave like a "traditional" normalization filter. On the contrary, the more you decrease this value, the more the Dynamic Audio Normalizer will behave like a dynamic range compressor. -@item p +@item peak, p Set the target peak value. This specifies the highest permissible magnitude level for the normalized audio input. This filter will try to approach the target peak magnitude as closely as possible, but at the same time it also @@ -3176,7 +3435,7 @@ A frame's maximum local gain factor is imposed directly by the target peak magnitude. The default value is 0.95 and thus leaves a headroom of 5%*. It is not recommended to go above this value. -@item m +@item maxgain, m Set the maximum gain factor. In range from 1.0 to 100.0. Default is 10.0. The Dynamic Audio Normalizer determines the maximum possible (local) gain factor for each input frame, i.e. the maximum gain factor that does not @@ -3194,7 +3453,7 @@ Instead, a "sigmoid" threshold function will be applied. This way, the gain factors will smoothly approach the threshold value, but never exceed that value. -@item r +@item targetrms, r Set the target RMS. In range from 0.0 to 1.0. Default is 0.0 - disabled. By default, the Dynamic Audio Normalizer performs "peak" normalization. This means that the maximum local gain factor for each frame is defined @@ -3212,7 +3471,7 @@ factor is defined as the factor that would result in exactly that RMS value. Note, however, that the maximum local gain factor is still restricted by the frame's highest magnitude sample, in order to prevent clipping. -@item n +@item coupling, n Enable channels coupling. By default is enabled. By default, the Dynamic Audio Normalizer will amplify all channels by the same amount. This means the same gain factor will be applied to all channels, i.e. @@ -3224,7 +3483,7 @@ the gain factor will be determined independently for each channel, depending only on the individual channel's highest magnitude sample. This allows for harmonizing the volume of the different channels. -@item c +@item correctdc, c Enable DC bias correction. By default is disabled. An audio signal (in the time domain) is a sequence of sample values. In the Dynamic Audio Normalizer these sample values are represented in the @@ -3243,7 +3502,7 @@ are centered around 0.0 again. Also, in order to avoid "gaps" at the frame boundaries, the DC correction offset values will be interpolated smoothly between neighbouring frames. -@item b +@item altboundary, b Enable alternative boundary mode. By default is disabled. The Dynamic Audio Normalizer takes into account a certain neighbourhood around each frame. This includes the preceding frames as well as the @@ -3258,7 +3517,7 @@ to deal with this situation. The default boundary mode assumes a gain factor of exactly 1.0 for the missing frames, resulting in a smooth "fade in" and "fade out" at the beginning and at the end of the input, respectively. -@item s +@item compress, s Set the compress factor. In range from 0.0 to 30.0. Default is 0.0. By default, the Dynamic Audio Normalizer does not apply "traditional" compression. This means that signal peaks will not be pruned and thus the @@ -3275,8 +3534,20 @@ value. Instead, the threshold value will be adjusted for each individual frame. In general, smaller parameters result in stronger compression, and vice versa. Values below 3.0 are not recommended, because audible distortion may appear. + +@item threshold, t +Set the target threshold value. This specifies the lowest permissible +magnitude level for the audio input which will be normalized. +If input frame volume is above this value frame will be normalized. +Otherwise frame may not be normalized at all. The default value is set +to 0, which means all input frames will be normalized. +This option is mostly useful if digital noise is not wanted to be amplified. @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section earwax Make audio easier to listen to on headphones. @@ -3332,6 +3603,10 @@ Range is between 0 and 1. @item channels, c Specify which channels to filter, by default all available are filtered. + +@item normalize, n +Normalize biquad coefficients, by default is disabled. +Enabling it will normalize magnitude response at DC to 0dB. @end table @subsection Examples @@ -3391,6 +3666,10 @@ Sets the difference coefficient (default: 2.5). 0.0 means mono sound Enable clipping. By default is enabled. @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section firequalizer Apply FIR Equalization using arbitrary frequency response. @@ -3801,6 +4080,10 @@ Range is between 0 and 1. @item channels, c Specify which channels to filter, by default all available are filtered. + +@item normalize, n +Normalize biquad coefficients, by default is disabled. +Enabling it will normalize magnitude response at DC to 0dB. @end table @subsection Commands @@ -4019,8 +4302,8 @@ If the specified value is not valid, it is ignored and prior one is kept. EBU R128 loudness normalization. Includes both dynamic and linear normalization modes. Support for both single pass (livestreams, files) and double pass (files) modes. -This algorithm can target IL, LRA, and maximum true peak. To accurately detect true peaks, -the audio stream will be upsampled to 192 kHz unless the normalization mode is linear. +This algorithm can target IL, LRA, and maximum true peak. In dynamic mode, to accurately +detect true peaks, the audio stream will be upsampled to 192 kHz. Use the @code{-ar} option or @code{aresample} filter to explicitly set an output sample rate. The filter accepts the following options: @@ -4059,10 +4342,13 @@ Set offset gain. Gain is applied before the true-peak limiter. Range is -99.0 - +99.0. Default is +0.0. @item linear -Normalize linearly if possible. -measured_I, measured_LRA, measured_TP, and measured_thresh must also -to be specified in order to use this mode. -Options are true or false. Default is true. +Normalize by linearly scaling the source audio. +@code{measured_I}, @code{measured_LRA}, @code{measured_TP}, +and @code{measured_thresh} must all be specified. Target LRA shouldn't +be lower than source LRA and the change in integrated loudness shouldn't +result in a true peak which exceeds the target TP. If any of these +conditions aren't met, normalization mode will revert to @var{dynamic}. +Options are @code{true} or @code{false}. Default is @code{true}. @item dual_mono Treat mono input files as "dual-mono". If a mono file is intended for playback @@ -4117,6 +4403,10 @@ Range is between 0 and 1. @item channels, c Specify which channels to filter, by default all available are filtered. + +@item normalize, n +Normalize biquad coefficients, by default is disabled. +Enabling it will normalize magnitude response at DC to 0dB. @end table @subsection Examples @@ -4410,6 +4700,19 @@ Possible values are: @end table @end table +@subsection Commands + +This filter supports the following commands: +@table @option +@item tempo +Change filter tempo scale factor. +Syntax for the command is : "@var{tempo}" + +@item pitch +Change filter pitch scale factor. +Syntax for the command is : "@var{pitch}" +@end table + @section sidechaincompress This filter acts like normal compressor but has the ability to compress @@ -4472,6 +4775,10 @@ How much to use compressed signal in output. Default is 1. Range is between 0 and 1. @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @subsection Examples @itemize @@ -4563,7 +4870,16 @@ This filter logs a message when it detects that the input audio volume is less or equal to a noise tolerance value for a duration greater or equal to the minimum detected noise duration. -The printed times and duration are expressed in seconds. +The printed times and duration are expressed in seconds. The +@code{lavfi.silence_start} or @code{lavfi.silence_start.X} metadata key +is set on the first frame whose timestamp equals or exceeds the detection +duration and it contains the timestamp of the first frame of the silence. + +The @code{lavfi.silence_duration} or @code{lavfi.silence_duration.X} +and @code{lavfi.silence_end} or @code{lavfi.silence_end.X} metadata +keys are set on the first frame after the silence. If @option{mono} is +enabled, and each channel is evaluated separately, the @code{.X} +suffixed keys are used, and @code{X} corresponds to the channel number. The filter accepts the following options: @@ -4573,7 +4889,9 @@ Set noise tolerance. Can be specified in dB (in case "dB" is appended to the specified value) or amplitude ratio. Default is -60dB, or 0.001. @item duration, d -Set silence duration until notification (default is 2 seconds). +Set silence duration until notification (default is 2 seconds). See +@ref{time duration syntax,,the Time duration section in the ffmpeg-utils(1) manual,ffmpeg-utils} +for the accepted syntax. @item mono, m Process each channel separately, instead of combined. By default is disabled. @@ -4699,6 +5017,14 @@ second of silence in audio: @example silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-90dB @end example + +@item +Trim all digital silence samples, using peak detection, from beginning to end +where there is more than 0 samples of digital silence in audio and digital +silence is detected in all channels at same positions in stream: +@example +silenceremove=window=0:detection=peak:stop_mode=all:start_mode=all:stop_periods=-1:stop_threshold=0 +@end example @end itemize @section sofalizer @@ -4960,6 +5286,10 @@ channels. Default is 0.3. Set level of input signal of original channel. Default is 0.8. @end table +@subsection Commands + +This filter supports the all above options except @code{delay} as @ref{commands}. + @section superequalizer Apply 18 band equalizer. @@ -5193,6 +5523,10 @@ Range is between 0 and 1. @item channels, c Specify which channels to filter, by default all available are filtered. + +@item normalize, n +Normalize biquad coefficients, by default is disabled. +Enabling it will normalize magnitude response at DC to 0dB. @end table @subsection Commands @@ -5311,6 +5645,11 @@ Pre-amplification gain in dB to apply to the selected replaygain gain. Default value for @var{replaygain_preamp} is 0.0. +@item replaygain_noclip +Prevent clipping by limiting the gain applied. + +Default value for @var{replaygain_noclip} is 1. + @item eval Set when the volume expression is evaluated. @@ -5370,11 +5709,6 @@ The command accepts the same syntax of the corresponding option. If the specified expression is not valid, it is kept at its current value. -@item replaygain_noclip -Prevent clipping by limiting the gain applied. - -Default value for @var{replaygain_noclip} is 1. - @end table @subsection Examples @@ -5602,6 +5936,44 @@ aevalsrc="0.1*sin(2*PI*(360-2.5/2)*t) | 0.1*sin(2*PI*(360+2.5/2)*t)" @end itemize +@section afirsrc + +Generate a FIR coefficients using frequency sampling method. + +The resulting stream can be used with @ref{afir} filter for filtering the audio signal. + +The filter accepts the following options: + +@table @option +@item taps, t +Set number of filter coefficents in output audio stream. +Default value is 1025. + +@item frequency, f +Set frequency points from where magnitude and phase are set. +This must be in non decreasing order, and first element must be 0, while last element +must be 1. Elements are separated by white spaces. + +@item magnitude, m +Set magnitude value for every frequency point set by @option{frequency}. +Number of values must be same as number of frequency points. +Values are separated by white spaces. + +@item phase, p +Set phase value for every frequency point set by @option{frequency}. +Number of values must be same as number of frequency points. +Values are separated by white spaces. + +@item sample_rate, r +Set sample rate, default is 44100. + +@item nb_samples, n +Set number of samples per each frame. Default is 1024. + +@item win_func, w +Set window function. Default is blackman. +@end table + @section anullsrc The null audio source, return unprocessed audio frames. It is mainly useful @@ -5733,7 +6105,7 @@ results in noise with an infinite length. @item color, colour, c Specify the color of noise. Available noise colors are white, pink, brown, -blue and violet. Default color is white. +blue, violet and velvet. Default color is white. @item seed, s Specify a value used to seed the PRNG. @@ -5931,6 +6303,79 @@ build. Below is a description of the currently available video filters. +@section addroi + +Mark a region of interest in a video frame. + +The frame data is passed through unchanged, but metadata is attached +to the frame indicating regions of interest which can affect the +behaviour of later encoding. Multiple regions can be marked by +applying the filter multiple times. + +@table @option +@item x +Region distance in pixels from the left edge of the frame. +@item y +Region distance in pixels from the top edge of the frame. +@item w +Region width in pixels. +@item h +Region height in pixels. + +The parameters @var{x}, @var{y}, @var{w} and @var{h} are expressions, +and may contain the following variables: +@table @option +@item iw +Width of the input frame. +@item ih +Height of the input frame. +@end table + +@item qoffset +Quantisation offset to apply within the region. + +This must be a real value in the range -1 to +1. A value of zero +indicates no quality change. A negative value asks for better quality +(less quantisation), while a positive value asks for worse quality +(greater quantisation). + +The range is calibrated so that the extreme values indicate the +largest possible offset - if the rest of the frame is encoded with the +worst possible quality, an offset of -1 indicates that this region +should be encoded with the best possible quality anyway. Intermediate +values are then interpolated in some codec-dependent way. + +For example, in 10-bit H.264 the quantisation parameter varies between +-12 and 51. A typical qoffset value of -1/10 therefore indicates that +this region should be encoded with a QP around one-tenth of the full +range better than the rest of the frame. So, if most of the frame +were to be encoded with a QP of around 30, this region would get a QP +of around 24 (an offset of approximately -1/10 * (51 - -12) = -6.3). +An extreme value of -1 would indicate that this region should be +encoded with the best possible quality regardless of the treatment of +the rest of the frame - that is, should be encoded at a QP of -12. +@item clear +If set to true, remove any existing regions of interest marked on the +frame before adding the new one. +@end table + +@subsection Examples + +@itemize +@item +Mark the centre quarter of the frame as interesting. +@example +addroi=iw/4:ih/4:iw/2:ih/2:-1/10 +@end example +@item +Mark the 100-pixel-wide region on the left edge of the frame as very +uninteresting (to be encoded at much lower quality than the rest of +the frame). +@example +addroi=0:0:100:ih:+1/5 +@end example +@end itemize + @section alphaextract Extract the alpha component from the input as a grayscale video. This @@ -5993,7 +6438,19 @@ This option controls maximum possible value that will increase source pixel valu Set which planes to filter. Default is all. Allowed range is from 0 to 15. @end table -@section ass +@subsection Commands + +This filter supports the following @ref{commands} that corresponds to option of same name: +@table @option +@item factor +@item threshold +@item tolerance +@item low +@item high +@item planes +@end table + +@section ass Same as the @ref{subtitles} filter, except that it doesn't require libavcodec and libavformat to work. On the other hand, it is limited to ASS (Advanced @@ -6058,8 +6515,20 @@ number in range [5, 129]. @item p Set what planes of frame filter will use for averaging. Default is all. + +@item a +Set what variant of algorithm filter will use for averaging. Default is @code{p} parallel. +Alternatively can be set to @code{s} serial. + +Parallel can be faster then serial, while other way around is never true. +Parallel will abort early on first change being greater then thresholds, while serial +will continue processing other side of frames if they are equal or bellow thresholds. @end table +@subsection Commands +This filter supports same @ref{commands} as options except option @code{s}. +The command accepts the same syntax of the corresponding option. + @section avgblur Apply average blur filter. @@ -6078,6 +6547,13 @@ Set vertical radius size, if zero it will be same as @code{sizeX}. Default is @code{0}. @end table +@subsection Commands +This filter supports same commands as options. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + @section bbox Compute the bounding box for the non-black pixels in the input frame @@ -6095,6 +6571,23 @@ The filter accepts the following option: Set the minimal luminance value. Default is @code{16}. @end table +@section bilateral +Apply bilateral filter, spatial smoothing while preserving edges. + +The filter accepts the following options: +@table @option +@item sigmaS +Set sigma of gaussian function to calculate spatial weight. +Allowed range is 0 to 10. Default is 0.1. + +@item sigmaR +Set sigma of gaussian function to calculate range weight. +Allowed range is 0 to 1. Default is 0.1. + +@item planes +Set planes to filter. Default is first only. +@end table + @section bitplanenoise Show and measure bit plane noise. @@ -6114,11 +6607,20 @@ Default is disabled. Detect video intervals that are (almost) completely black. Can be useful to detect chapter transitions, commercials, or invalid -recordings. Output lines contains the time for the start, end and -duration of the detected black interval expressed in seconds. +recordings. -In order to display the output lines, you need to set the loglevel at -least to the AV_LOG_INFO value. +The filter outputs its detection analysis to both the log as well as +frame metadata. If a black segment of at least the specified minimum +duration is found, a line with the start and end timestamps as well +as duration is printed to the log with level @code{info}. In addition, +a log line with level @code{debug} is printed per frame showing the +black amount detected for that frame. + +The filter also attaches metadata to the first frame of a black +segment with key @code{lavfi.black_start} and to the first frame +after the black segment ends with key @code{lavfi.black_end}. The +value is the frame's timestamp. This metadata is added regardless +of the minimum duration specified. The filter accepts the following options: @@ -6190,7 +6692,8 @@ The threshold below which a pixel value is considered black; it defaults to @end table -@section blend, tblend +@anchor{blend} +@section blend Blend two video frames into each other. @@ -6565,7 +7068,7 @@ 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. Accept one of the following +Specify which frames to deinterlace. Accepts one of the following values: @table @option @@ -6578,6 +7081,21 @@ Only deinterlace frames marked as interlaced. The default value is @code{all}. @end table +@section cas + +Apply Contrast Adaptive Sharpen filter to video stream. + +The filter accepts the following options: + +@table @option +@item strength +Set the sharpening strength. Default value is 0. + +@item planes +Set planes to filter. Default value is to filter all +planes except alpha plane. +@end table + @section chromahold Remove all color information for all colors except for certain one. @@ -6603,6 +7121,13 @@ Literal colors like "green" or "red" don't make sense with this enabled anymore. This can be used to pass exact YUV values as hexadecimal numbers. @end table +@subsection Commands +This filter supports same @ref{commands} as options. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + @section chromakey YUV colorspace color/chroma keying. @@ -6632,6 +7157,13 @@ Literal colors like "green" or "red" don't make sense with this enabled anymore. This can be used to pass exact YUV values as hexadecimal numbers. @end table +@subsection Commands +This filter supports same @ref{commands} as options. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + @subsection Examples @itemize @@ -6665,6 +7197,10 @@ Set amount to shift chroma-red vertically. Set edge mode, can be @var{smear}, default, or @var{warp}. @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section ciescope Display CIE color diagram with pixels overlaid onto it. @@ -6685,6 +7221,7 @@ Set color system. @item cie1931 @item rec709, hdtv @item uhdtv, rec2020 +@item dcip3 @end table @item cie @@ -6819,6 +7356,9 @@ Adjust red, green and blue midtones (medium pixels). Adjust red, green and blue highlights (brightest pixels). Allowed ranges for options are @code{[-1.0, 1.0]}. Defaults are @code{0}. + +@item pl +Preserve lightness when changing color balance. Default is disabled. @end table @subsection Examples @@ -6831,6 +7371,74 @@ colorbalance=rs=.3 @end example @end itemize +@subsection Commands + +This filter supports the all above options as @ref{commands}. + +@section colorchannelmixer + +Adjust video input frames by re-mixing color channels. + +This filter modifies a color channel by adding the values associated to +the other channels of the same pixels. For example if the value to +modify is red, the output value will be: +@example +@var{red}=@var{red}*@var{rr} + @var{blue}*@var{rb} + @var{green}*@var{rg} + @var{alpha}*@var{ra} +@end example + +The filter accepts the following options: + +@table @option +@item rr +@item rg +@item rb +@item ra +Adjust contribution of input red, green, blue and alpha channels for output red channel. +Default is @code{1} for @var{rr}, and @code{0} for @var{rg}, @var{rb} and @var{ra}. + +@item gr +@item gg +@item gb +@item ga +Adjust contribution of input red, green, blue and alpha channels for output green channel. +Default is @code{1} for @var{gg}, and @code{0} for @var{gr}, @var{gb} and @var{ga}. + +@item br +@item bg +@item bb +@item ba +Adjust contribution of input red, green, blue and alpha channels for output blue channel. +Default is @code{1} for @var{bb}, and @code{0} for @var{br}, @var{bg} and @var{ba}. + +@item ar +@item ag +@item ab +@item aa +Adjust contribution of input red, green, blue and alpha channels for output alpha channel. +Default is @code{1} for @var{aa}, and @code{0} for @var{ar}, @var{ag} and @var{ab}. + +Allowed ranges for options are @code{[-2.0, 2.0]}. +@end table + +@subsection Examples + +@itemize +@item +Convert source to grayscale: +@example +colorchannelmixer=.3:.4:.3:0:.3:.4:.3:0:.3:.4:.3 +@end example +@item +Simulate sepia tones: +@example +colorchannelmixer=.393:.769:.189:0:.349:.686:.168:0:.272:.534:.131 +@end example +@end itemize + +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section colorkey RGB colorspace color keying. @@ -6870,6 +7478,13 @@ ffmpeg -i background.png -i video.mp4 -filter_complex "[1:v]colorkey=0x3BBD1E:0. @end example @end itemize +@subsection Commands +This filter supports same @ref{commands} as options. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + @section colorhold Remove all color information for all RGB colors except for certain one. @@ -6888,6 +7503,13 @@ Blend percentage. 0.0 makes pixels fully gray. Higher values result in more preserved color. @end table +@subsection Commands +This filter supports same @ref{commands} as options. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + @section colorlevels Adjust video input frames using levels. @@ -6957,65 +7579,9 @@ colorlevels=romin=0.5:gomin=0.5:bomin=0.5 @end example @end itemize -@section colorchannelmixer - -Adjust video input frames by re-mixing color channels. - -This filter modifies a color channel by adding the values associated to -the other channels of the same pixels. For example if the value to -modify is red, the output value will be: -@example -@var{red}=@var{red}*@var{rr} + @var{blue}*@var{rb} + @var{green}*@var{rg} + @var{alpha}*@var{ra} -@end example - -The filter accepts the following options: - -@table @option -@item rr -@item rg -@item rb -@item ra -Adjust contribution of input red, green, blue and alpha channels for output red channel. -Default is @code{1} for @var{rr}, and @code{0} for @var{rg}, @var{rb} and @var{ra}. - -@item gr -@item gg -@item gb -@item ga -Adjust contribution of input red, green, blue and alpha channels for output green channel. -Default is @code{1} for @var{gg}, and @code{0} for @var{gr}, @var{gb} and @var{ga}. - -@item br -@item bg -@item bb -@item ba -Adjust contribution of input red, green, blue and alpha channels for output blue channel. -Default is @code{1} for @var{bb}, and @code{0} for @var{br}, @var{bg} and @var{ba}. - -@item ar -@item ag -@item ab -@item aa -Adjust contribution of input red, green, blue and alpha channels for output alpha channel. -Default is @code{1} for @var{aa}, and @code{0} for @var{ar}, @var{ag} and @var{ab}. - -Allowed ranges for options are @code{[-2.0, 2.0]}. -@end table - -@subsection Examples +@subsection Commands -@itemize -@item -Convert source to grayscale: -@example -colorchannelmixer=.3:.4:.3:0:.3:.4:.3:0:.3:.4:.3 -@end example -@item -Simulate sepia tones: -@example -colorchannelmixer=.393:.769:.189:0:.349:.686:.168:0:.272:.534:.131 -@end example -@end itemize +This filter supports the all above options as @ref{commands}. @section colormatrix @@ -7538,6 +8104,40 @@ ffmpeg -f lavfi -i nullsrc=s=100x100,coreimage=filter=CIQRCodeGenerator@@inputMe @end example @end itemize +@section cover_rect + +Cover a rectangular object + +It accepts the following options: + +@table @option +@item cover +Filepath of the optional cover image, needs to be in yuv420. + +@item mode +Set covering mode. + +It accepts the following values: +@table @samp +@item cover +cover it by the supplied image +@item blur +cover it by interpolating the surrounding pixels +@end table + +Default value is @var{blur}. +@end table + +@subsection Examples + +@itemize +@item +Cover a rectangular object by the supplied image of a given video using @command{ffmpeg}: +@example +ffmpeg -i file.ts -vf find_rect=newref.pgm,cover_rect=cover.jpg:mode=cover new.mkv +@end example +@end itemize + @section crop Crop the input video to given dimensions. @@ -7958,8 +8558,34 @@ Draw rows and columns numbers on left and top of video. @item opacity Set background opacity. + +@item format +Set display number format. Can be @code{hex}, or @code{dec}. Default is @code{hex}. +@end table + +@section dblur +Apply Directional blur filter. + +The filter accepts the following options: + +@table @option +@item angle +Set angle of directional blur. Default is @code{45}. + +@item radius +Set radius of directional blur. Default is @code{5}. + +@item planes +Set which planes to filter. By default all planes are filtered. @end table +@subsection Commands +This filter supports same @ref{commands} as options. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + @section dctdnoiz Denoise frames using 2D DCT (frequency domain filtering). @@ -8233,6 +8859,10 @@ Limit the maximum change for each plane, default is 65535. If 0, plane will remain unchanged. @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section deflicker Remove temporal frame luminance variations. @@ -8355,6 +8985,7 @@ delogo=x=0:y=0:w=100:h=77:band=10 @end itemize +@anchor{derain} @section derain Remove the rain in the input image/video by applying the derain methods based on @@ -8366,12 +8997,27 @@ Recurrent Squeeze-and-Excitation Context Aggregation Net (RESCAN). See @url{http://openaccess.thecvf.com/content_ECCV_2018/papers/Xia_Li_Recurrent_Squeeze-and-Excitation_Context_ECCV_2018_paper.pdf}. @end itemize -Training scripts as well as scripts for model generation are provided in +Training as well as model generation scripts are provided in the repository at @url{https://github.com/XueweiMeng/derain_filter.git}. -The filter accepts the following options: +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 +@item filter_type +Specify which filter to use. This option accepts the following values: + +@table @samp +@item derain +Derain filter. To conduct derain filter, you need to use a derain model. + +@item dehaze +Dehaze filter. To conduct dehaze filter, you need to use a dehaze model. +@end table +Default value is @samp{derain}. + @item dnn_backend Specify which DNN backend to use for model loading and execution. This option accepts the following values: @@ -8379,16 +9025,23 @@ 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 +@url{https://www.tensorflow.org/install/install_c}) and configure FFmpeg with +@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 and native +backend can load files for only its format. @end table +It can also be finished with @ref{dnn_processing} filter. + @section deshake Attempt to fix small changes in horizontal and/or vertical shift. This @@ -8554,6 +9207,10 @@ Flags to local 3x3 coordinates maps like this: 6 7 8 @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section displace Displace pixels as indicated by second and third input stream. @@ -8609,6 +9266,76 @@ ffmpeg -i INPUT -f lavfi -i nullsrc=hd720,geq='r=128+80*(sin(sqrt((X-W/2)*(X-W/2 @end example @end itemize +@anchor{dnn_processing} +@section dnn_processing + +Do image processing with deep neural networks. It works together with another filter +which converts the pixel format of the Frame to what the dnn network requires. + +The filter accepts the following options: + +@table @option +@item dnn_backend +Specify which DNN backend to use for model loading and execution. This option accepts +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 +@url{https://www.tensorflow.org/install/install_c}) and configure FFmpeg with +@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 and native +backend can load files for only its format. + +Native model file (.model) can be generated from TensorFlow model file (.pb) by using tools/python/convert.py + +@item input +Set the input name of the dnn network. + +@item output +Set the output name of the dnn network. + +@end table + +@subsection Examples + +@itemize +@item +Remove rain in rgb24 frame with can.pb (see @ref{derain} filter): +@example +./ffmpeg -i rain.jpg -vf format=rgb24,dnn_processing=dnn_backend=tensorflow:model=can.pb:input=x:output=y derain.jpg +@end example + +@item +Halve the pixel value of the frame with format gray32f: +@example +ffmpeg -i input.jpg -vf format=grayf32,dnn_processing=model=halve_gray_float.model:input=dnn_in:output=dnn_out:dnn_backend=native -y out.native.png +@end example + +@item +Handle the Y channel with srcnn.pb (see @ref{sr} filter) for frame with yuv420p (planar YUV formats supported): +@example +./ffmpeg -i 480p.jpg -vf format=yuv420p,scale=w=iw*2:h=ih*2,dnn_processing=dnn_backend=tensorflow:model=srcnn.pb:input=x:output=y -y srcnn.jpg +@end example + +@item +Handle the Y channel with espcn.pb (see @ref{sr} filter), which changes frame size, for format yuv420p (planar YUV formats supported): +@example +./ffmpeg -i 480p.jpg -vf format=yuv420p,dnn_processing=dnn_backend=tensorflow:model=espcn.pb:input=x:output=y -y tmp.espcn.jpg +@end example + +@end itemize + @section drawbox Draw a colored box on the input image. @@ -8711,6 +9438,121 @@ drawbox=x=-t:y=0.5*(ih-iw/2.4)-t:w=iw+t*2:h=iw/2.4+t*2:t=2:c=red @end example @end itemize +@subsection Commands +This filter supports same commands as options. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + +@anchor{drawgraph} +@section drawgraph +Draw a graph using input video metadata. + +It accepts the following parameters: + +@table @option +@item m1 +Set 1st frame metadata key from which metadata values will be used to draw a graph. + +@item fg1 +Set 1st foreground color expression. + +@item m2 +Set 2nd frame metadata key from which metadata values will be used to draw a graph. + +@item fg2 +Set 2nd foreground color expression. + +@item m3 +Set 3rd frame metadata key from which metadata values will be used to draw a graph. + +@item fg3 +Set 3rd foreground color expression. + +@item m4 +Set 4th frame metadata key from which metadata values will be used to draw a graph. + +@item fg4 +Set 4th foreground color expression. + +@item min +Set minimal value of metadata value. + +@item max +Set maximal value of metadata value. + +@item bg +Set graph background color. Default is white. + +@item mode +Set graph mode. + +Available values for mode is: +@table @samp +@item bar +@item dot +@item line +@end table + +Default is @code{line}. + +@item slide +Set slide mode. + +Available values for slide is: +@table @samp +@item frame +Draw new frame when right border is reached. + +@item replace +Replace old columns with new ones. + +@item scroll +Scroll from right to left. + +@item rscroll +Scroll from left to right. + +@item picture +Draw single picture. +@end table + +Default is @code{frame}. + +@item size +Set size of graph video. For the syntax of this option, check the +@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}. +The default value is @code{900x256}. + +@item rate, r +Set the output frame rate. Default value is @code{25}. + +The foreground color expressions can use the following variables: +@table @option +@item MIN +Minimal value of metadata value. + +@item MAX +Maximal value of metadata value. + +@item VAL +Current metadata key value. +@end table + +The color is defined as 0xAABBGGRR. +@end table + +Example using metadata from @ref{signalstats} filter: +@example +signalstats,drawgraph=lavfi.signalstats.YAVG:min=0:max=255 +@end example + +Example using metadata from @ref{ebur128} filter: +@example +ebur128=metadata=1,adrawgraph=lavfi.r128.M:min=-120:max=5 +@end example + @section drawgrid Draw a grid on the input image. @@ -8796,6 +9638,13 @@ drawgrid=w=iw/3:h=ih/3:t=2:c=white@@0.5 @end example @end itemize +@subsection Commands +This filter supports same commands as options. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + @anchor{drawtext} @section drawtext @@ -9290,6 +10139,15 @@ drawtext=fontfile=FreeSans.ttf:text=DOG:fontsize=24:x=10:y=20+24-max_glyph_a, drawtext=fontfile=FreeSans.ttf:text=cow:fontsize=24:x=80:y=20+24-max_glyph_a @end example +@item +Plot special @var{lavf.image2dec.source_basename} metadata onto each frame if +such metadata exists. Otherwise, plot the string "NA". Note that image2 demuxer +must have option @option{-export_path_metadata 1} for the special metadata fields +to be available for filters. +@example +drawtext="fontsize=20:fontcolor=white:fontfile=FreeSans.ttf:text='%@{metadata\:lavf.image2dec.source_basename\:NA@}':x=10:y=10" +@end example + @end itemize For more information about libfreetype, check: @@ -9358,6 +10216,50 @@ edgedetect=mode=colormix:high=0 @end example @end itemize +@section elbg + +Apply a posterize effect using the ELBG (Enhanced LBG) algorithm. + +For each input image, the filter will compute the optimal mapping from +the input to the output given the codebook length, that is the number +of distinct output colors. + +This filter accepts the following options. + +@table @option +@item codebook_length, l +Set codebook length. The value must be a positive integer, and +represents the number of distinct output colors. Default value is 256. + +@item nb_steps, n +Set the maximum number of iterations to apply for computing the optimal +mapping. The higher the value the better the result and the higher the +computation time. Default value is 1. + +@item seed, s +Set a random seed, must be an integer included between 0 and +UINT32_MAX. If not specified, or if explicitly set to -1, the filter +will try to use a good random seed on a best effort basis. + +@item pal8 +Set pal8 output pixel format. This option does not work with codebook +length greater than 256. +@end table + +@section entropy + +Measure graylevel entropy in histogram of color channels of video frames. + +It accepts the following parameters: + +@table @option +@item mode +Can be either @var{normal} or @var{diff}. Default is @var{normal}. + +@var{diff} mode measures entropy of histogram delta values, absolute differences +between neighbour histogram values. +@end table + @section eq Set brightness, contrast, saturation and approximate gamma adjustment. @@ -9366,7 +10268,7 @@ The filter accepts the following options: @table @option @item contrast Set the contrast expression. The value must be a float value in range -@code{-2.0} to @code{2.0}. The default value is "1". +@code{-1000.0} to @code{1000.0}. The default value is "1". @item brightness Set the brightness expression. The value must be a float value in @@ -9495,6 +10397,10 @@ Flags to local 3x3 coordinates maps like this: 6 7 8 @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section extractplanes Extract color channel components from input video stream into @@ -9533,55 +10439,11 @@ ffmpeg -i video.avi -filter_complex 'extractplanes=y+u+v[y][u][v]' -map '[y]' y. @end example @end itemize -@section elbg - -Apply a posterize effect using the ELBG (Enhanced LBG) algorithm. +@section fade -For each input image, the filter will compute the optimal mapping from -the input to the output given the codebook length, that is the number -of distinct output colors. +Apply a fade-in/out effect to the input video. -This filter accepts the following options. - -@table @option -@item codebook_length, l -Set codebook length. The value must be a positive integer, and -represents the number of distinct output colors. Default value is 256. - -@item nb_steps, n -Set the maximum number of iterations to apply for computing the optimal -mapping. The higher the value the better the result and the higher the -computation time. Default value is 1. - -@item seed, s -Set a random seed, must be an integer included between 0 and -UINT32_MAX. If not specified, or if explicitly set to -1, the filter -will try to use a good random seed on a best effort basis. - -@item pal8 -Set pal8 output pixel format. This option does not work with codebook -length greater than 256. -@end table - -@section entropy - -Measure graylevel entropy in histogram of color channels of video frames. - -It accepts the following parameters: - -@table @option -@item mode -Can be either @var{normal} or @var{diff}. Default is @var{normal}. - -@var{diff} mode measures entropy of histogram delta values, absolute differences -between neighbour histogram values. -@end table - -@section fade - -Apply a fade-in/out effect to the input video. - -It accepts the following parameters: +It accepts the following parameters: @table @option @item type, t @@ -9668,6 +10530,40 @@ fade=t=in:st=5.5:d=0.5 @end itemize +@section fftdnoiz +Denoise frames using 3D FFT (frequency domain filtering). + +The filter accepts the following options: + +@table @option +@item sigma +Set the noise sigma constant. This sets denoising strength. +Default value is 1. Allowed range is from 0 to 30. +Using very high sigma with low overlap may give blocking artifacts. + +@item amount +Set amount of denoising. By default all detected noise is reduced. +Default value is 1. Allowed range is from 0 to 1. + +@item block +Set size of block, Default is 4, can be 3, 4, 5 or 6. +Actual size of block in pixels is 2 to power of @var{block}, so by default +block size in pixels is 2^4 which is 16. + +@item overlap +Set block overlap. Default is 0.5. Allowed range is from 0.2 to 0.8. + +@item prev +Set number of previous frames to use for denoising. By default is set to 0. + +@item next +Set number of next frames to to use for denoising. By default is set to 0. + +@item planes +Set planes which will be filtered, by default are all available filtered +except alpha. +@end table + @section fftfilt Apply arbitrary expressions to samples in frequency domain @@ -9752,40 +10648,6 @@ fftfilt=dc_Y=0:weight_Y='exp(-4 * ((Y+X)/(W+H)))' @end itemize -@section fftdnoiz -Denoise frames using 3D FFT (frequency domain filtering). - -The filter accepts the following options: - -@table @option -@item sigma -Set the noise sigma constant. This sets denoising strength. -Default value is 1. Allowed range is from 0 to 30. -Using very high sigma with low overlap may give blocking artifacts. - -@item amount -Set amount of denoising. By default all detected noise is reduced. -Default value is 1. Allowed range is from 0 to 1. - -@item block -Set size of block, Default is 4, can be 3, 4, 5 or 6. -Actual size of block in pixels is 2 to power of @var{block}, so by default -block size in pixels is 2^4 which is 16. - -@item overlap -Set block overlap. Default is 0.5. Allowed range is from 0.2 to 0.8. - -@item prev -Set number of previous frames to use for denoising. By default is set to 0. - -@item next -Set number of next frames to to use for denoising. By default is set to 0. - -@item planes -Set planes which will be filtered, by default are all available filtered -except alpha. -@end table - @section field Extract a single field from an interlaced image using stride @@ -9820,6 +10682,8 @@ field and second number tells from which frame to pick up bottom field. If optionally followed by @code{+} output frame will be marked as interlaced, else if followed by @code{-} output frame will be marked as progressive, else it will be marked same as input frame. +If optionally followed by @code{t} output frame will use only top field, or in +case of @code{b} it will use only bottom field. If line starts with @code{#} or @code{;} that line is skipped. @item mode @@ -10254,6 +11118,13 @@ Default is @var{smear}. Set color for pixels in fixed mode. Default is @var{black}. @end table +@subsection Commands +This filter supports same @ref{commands} as options. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + @section find_rect Find a rectangular object @@ -10284,40 +11155,6 @@ ffmpeg -i file.ts -vf find_rect=newref.pgm,cover_rect=cover.jpg:mode=cover new.m @end example @end itemize -@section cover_rect - -Cover a rectangular object - -It accepts the following options: - -@table @option -@item cover -Filepath of the optional cover image, needs to be in yuv420. - -@item mode -Set covering mode. - -It accepts the following values: -@table @samp -@item cover -cover it by the supplied image -@item blur -cover it by interpolating the surrounding pixels -@end table - -Default value is @var{blur}. -@end table - -@subsection Examples - -@itemize -@item -Cover a rectangular object by the supplied image of a given video using @command{ffmpeg}: -@example -ffmpeg -i file.ts -vf find_rect=newref.pgm,cover_rect=cover.jpg:mode=cover new.mkv -@end example -@end itemize - @section floodfill Flood area with values of same pixel components with another values. @@ -10589,6 +11426,25 @@ specified value) or as a difference ratio between 0 and 1. Default is -60dB, or Set freeze duration until notification (default is 2 seconds). @end table +@section freezeframes + +Freeze video frames. + +This filter freezes video frames using frame from 2nd input. + +The filter accepts the following options: + +@table @option +@item first +Set number of first frame from which to start freeze. + +@item last +Set number of last frame from which to end freeze. + +@item replace +Set number of frame from 2nd input which will be used instead of replaced frames. +@end table + @anchor{frei0r} @section frei0r @@ -10705,6 +11561,13 @@ Set vertical sigma, if negative it will be same as @code{sigma}. Default is @code{-1}. @end table +@subsection Commands +This filter supports same commands as options. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + @section geq Apply generic equation to each pixel. @@ -10789,11 +11652,28 @@ red/green/blue component. Return 0 if there is no such component. @item alpha(x, y) Return the value of the pixel at location (@var{x},@var{y}) of the alpha plane. Return 0 if there is no such plane. + +@item psum(x,y), lumsum(x, y), cbsum(x,y), crsum(x,y), rsum(x,y), gsum(x,y), bsum(x,y), alphasum(x,y) +Sum of sample values in the rectangle from (0,0) to (x,y), this allows obtaining +sums of samples within a rectangle. See the functions without the sum postfix. + +@item interpolation +Set one of interpolation methods: +@table @option +@item nearest, n +@item bilinear, b +@end table +Default is bilinear. @end table For functions, if @var{x} and @var{y} are outside the area, the value will be automatically clipped to the closer edge. +Please note that this filter can use multiple threads in which case each slice +will have its own expression state. If you want to use only a single expression +state because your expressions depend on previous state then you should limit +the number of filter threads to 1. + @subsection Examples @itemize @@ -10886,7 +11766,8 @@ gradfun=radius=8 @end itemize -@section graphmonitor, agraphmonitor +@anchor{graphmonitor} +@section graphmonitor Show various filtergraph stats. With this filter one can debug complete filtergraph. @@ -11101,6 +11982,7 @@ the histogram. Possible values are @code{none}, @code{weak} or @code{strong}. It defaults to @code{none}. @end table +@anchor{histogram} @section histogram Compute and draw a color distribution histogram for the input video. @@ -11195,6 +12077,13 @@ A floating point number which specifies chroma temporal strength. It defaults to @var{luma_tmp}*@var{chroma_spatial}/@var{luma_spatial}. @end table +@subsection Commands +This filter supports same @ref{commands} as options. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + @anchor{hwdownload} @section hwdownload @@ -11293,7 +12182,18 @@ Upload system memory frames to hardware surfaces. The device to upload to must be supplied when the filter is initialised. If using ffmpeg, select the appropriate device with the @option{-filter_hw_device} -option. +option or with the @option{derive_device} option. The input and output devices +must be of different types and compatible - the exact meaning of this is +system-dependent, but typically it means that they must refer to the same +underlying hardware context (for example, refer to the same graphics card). + +The following additional parameters are accepted: + +@table @option +@item derive_device @var{type} +Rather than using the device supplied at initialisation, instead derive a new +device of type @var{type} from the device the input frames exist on. +@end table @anchor{hwupload_cuda} @section hwupload_cuda @@ -11329,7 +12229,7 @@ All streams must be of same pixel format and of same height. Note that this filter is faster than using @ref{overlay} and @ref{pad} filter to create same output. -The filter accept the following option: +The filter accepts the following option: @table @option @item inputs @@ -11467,6 +12367,8 @@ this value filter algorithm for connecting components is activated. By default value is 0. @end table +The @code{hysteresis} filter also supports the @ref{framesync} options. + @section idet Detect video interlacing type. @@ -11588,6 +12490,10 @@ Default value is @code{none}. Swap luma/chroma/alpha fields. Exchange even & odd lines. Default value is @code{0}. @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section inflate Apply inflate effect to the video. @@ -11606,6 +12512,10 @@ Limit the maximum change for each plane, default is 65535. If 0, plane will remain unchanged. @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section interlace Simple interlacing filter from progressive contents. This interleaves upper (or @@ -11907,7 +12817,7 @@ The filter has following options: @table @option @item model_path Set the model path which is to be used for SVM. -Default value: @code{"vmaf_v0.6.1.pkl"} +Default value: @code{"/usr/local/share/model/vmaf_v0.6.1.pkl"} @item log_path Set the file path to be used to store logs. @@ -11923,31 +12833,42 @@ Default value: @code{false} @item phone_model Invokes the phone model which will generate VMAF scores higher than in the regular model, which is more suitable for laptop, TV, etc. viewing conditions. +Default value: @code{false} @item psnr Enables computing psnr along with vmaf. +Default value: @code{false} @item ssim Enables computing ssim along with vmaf. +Default value: @code{false} @item ms_ssim Enables computing ms_ssim along with vmaf. +Default value: @code{false} @item pool -Set the pool method (mean, min or harmonic mean) to be used for computing vmaf. +Set the pool method to be used for computing vmaf. +Options are @code{min}, @code{harmonic_mean} or @code{mean} (default). @item n_threads Set number of threads to be used when computing vmaf. +Default value: @code{0}, which makes use of all available logical processors. @item n_subsample Set interval for frame subsampling used when computing vmaf. +Default value: @code{1} @item enable_conf_interval Enables confidence interval. +Default value: @code{false} @end table This filter also supports the @ref{framesync} options. +@subsection Examples +@itemize +@item On the below examples the input file @file{main.mpg} being processed is compared with the reference file @file{ref.mpg}. @@ -11955,11 +12876,19 @@ compared with the reference file @file{ref.mpg}. ffmpeg -i main.mpg -i ref.mpg -lavfi libvmaf -f null - @end example +@item Example with options: @example ffmpeg -i main.mpg -i ref.mpg -lavfi libvmaf="psnr=1:log_fmt=json" -f null - @end example +@item +Example with options and different containers: +@example +ffmpeg -i main.mpg -i ref.mkv -lavfi "[0:v]settb=AVTB,setpts=PTS-STARTPTS[main];[1:v]settb=AVTB,setpts=PTS-STARTPTS[ref];[main][ref]libvmaf=psnr=1:log_fmt=json" -f null - +@end example +@end itemize + @section limiter Limits the pixel components values to the specified range [min, max]. @@ -12106,13 +13035,20 @@ Default value is @code{0}. @item tolerance Set the range of luma values to be keyed out. -Default value is @code{0}. +Default value is @code{0.01}. @item softness Set the range of softness. Default value is @code{0}. Use this to control gradual transition from zero to full transparency. @end table +@subsection Commands +This filter supports same @ref{commands} as options. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + @section lut, lutrgb, lutyuv Compute a look-up table for binding each pixel component input value @@ -12285,6 +13221,8 @@ set output bit depth, only available for @code{lut2} filter. By default is 0, which means bit depth is automatically picked from first input format. @end table +The @code{lut2} filter also supports the @ref{framesync} options. + Each of them specifies the expression to use for computing the lookup table for the corresponding pixel component values. @@ -12356,6 +13294,22 @@ copied from first stream. By default value 0xf, all planes will be processed. @end table +@section maskedmax + +Merge the second and third input stream into output stream using absolute differences +between second input stream and first input stream and absolute difference between +third input stream and first input stream. The picked value will be from second input +stream if second absolute difference is greater than first one or from third input stream +otherwise. + +This filter accepts the following options: +@table @option +@item planes +Set which planes will be processed as bitmap, unprocessed planes will be +copied from first stream. +By default value 0xf, all planes will be processed. +@end table + @section maskedmerge Merge the first input stream with the second input stream using per pixel @@ -12375,6 +13329,43 @@ copied from first stream. By default value 0xf, all planes will be processed. @end table +@section maskedmin + +Merge the second and third input stream into output stream using absolute differences +between second input stream and first input stream and absolute difference between +third input stream and first input stream. The picked value will be from second input +stream if second absolute difference is less than first one or from third input stream +otherwise. + +This filter accepts the following options: +@table @option +@item planes +Set which planes will be processed as bitmap, unprocessed planes will be +copied from first stream. +By default value 0xf, all planes will be processed. +@end table + +@section maskedthreshold +Pick pixels comparing absolute difference of two video streams with fixed +threshold. + +If absolute difference between pixel component of first and second video +stream is equal or lower than user supplied threshold than pixel component +from first video stream is picked, otherwise pixel component from second +video stream is picked. + +This filter accepts the following options: +@table @option +@item threshold +Set threshold used when picking pixels from absolute difference from two input +video streams. + +@item planes +Set which planes will be processed as bitmap, unprocessed planes will be +copied from second stream. +By default value 0xf, all planes will be processed. +@end table + @section maskfun Create mask from input video. @@ -12446,17 +13437,49 @@ Higher values should result in a smoother motion vector field but less optimal individual vectors. Default value is 1. @end table -@section mergeplanes - -Merge color channel components from several video streams. +@section median -The filter accepts up to 4 input streams, and merge selected input -planes to the output video. +Pick median pixel from certain rectangle defined by radius. This filter accepts the following options: + @table @option -@item mapping -Set input to output plane mapping. Default is @code{0}. +@item radius +Set horizontal radius size. Default value is @code{1}. +Allowed range is integer from 1 to 127. + +@item planes +Set which planes to process. Default is @code{15}, which is all available planes. + +@item radiusV +Set vertical radius size. Default value is @code{0}. +Allowed range is integer from 0 to 127. +If it is 0, value will be picked from horizontal @code{radius} option. + +@item percentile +Set median percentile. Default value is @code{0.5}. +Default value of @code{0.5} will pick always median values, while @code{0} will pick +minimum values, and @code{1} maximum values. +@end table + +@subsection Commands +This filter supports same @ref{commands} as options. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + +@section mergeplanes + +Merge color channel components from several video streams. + +The filter accepts up to 4 input streams, and merge selected input +planes to the output video. + +This filter accepts the following options: +@table @option +@item mapping +Set input to output plane mapping. Default is @code{0}. The mappings is specified as a bitmap. It should be specified as a hexadecimal number in the form 0xAa[Bb[Cc[Dd]]]. 'Aa' describes the @@ -12649,7 +13672,7 @@ Frame difference. Corresponding pixel values are compared and if it satisfies @v Default method is @samp{fdiff}. @item scd_threshold -Scene change detection threshold. Default is @code{5.0}. +Scene change detection threshold. Default is @code{10.}. @end table @section mix @@ -13014,6 +14037,13 @@ expensive no-op. Defaults to 1.0 (full strength). @end table +@subsection Commands +This filter supports same @ref{commands} as options, excluding @var{smoothing} option. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + @subsection Examples Stretch video contrast to use the full dynamic range, with no temporal @@ -13168,7 +14198,7 @@ the following values: "blur", "blur_no_scale", "median", "gaussian", or "bilateral". The default value is "gaussian". The meaning of @var{param1}, @var{param2}, @var{param3}, and @var{param4} -depend on the smooth type. @var{param1} and +depends on the smooth type. @var{param1} and @var{param2} accept integer positive values or 0. @var{param3} and @var{param4} accept floating point values. @@ -13227,6 +14257,13 @@ Draw some statistics. By default is enabled. Draw scope. By default is enabled. @end table +@subsection Commands +This filter supports same @ref{commands} as options. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. + @subsection Examples @itemize @@ -13481,6 +14518,38 @@ testsrc=s=100x100, split=4 [in0][in1][in2][in3]; @end itemize +@anchor{overlay_cuda} +@section overlay_cuda + +Overlay one video on top of another. + +This is the CUDA cariant of the @ref{overlay} filter. +It only accepts CUDA frames. The underlying input pixel formats have to match. + +It takes two inputs and has one output. The first input is the "main" +video on which the second input is overlaid. + +It accepts the following parameters: + +@table @option +@item x +@item y +Set the x and y coordinates of the overlaid video on the main video. +Default value is "0" for both expressions. + +@item eof_action +See @ref{framesync}. + +@item shortest +See @ref{framesync}. + +@item repeatlast +See @ref{framesync}. + +@end table + +This filter also supports the @ref{framesync} options. + @section owdenoise Apply Overcomplete Wavelet denoiser. @@ -13938,6 +15007,26 @@ Filter selects among @samp{t}, @samp{b} and @samp{p} using image analysis only. @end table @end table +@section photosensitivity +Reduce various flashes in video, so to help users with epilepsy. + +It accepts the following options: +@table @option +@item frames, f +Set how many frames to use when filtering. Default is 30. + +@item threshold, t +Set detection threshold factor. Default is 1. +Lower is stricter. + +@item skip +Set how many pixels to skip when sampling frames. Default is 1. +Allowed range is from 1 to 1024. + +@item bypass +Leave frames unchanged. Default is disabled. +@end table + @section pixdesctest Pixel format descriptor test filter, mainly useful for internal @@ -14215,141 +15304,11 @@ Set value which will be multiplied with filtered result. Set value which will be added to filtered result. @end table -@anchor{program_opencl} -@section program_opencl - -Filter video using an OpenCL program. - -@table @option - -@item source -OpenCL program source file. - -@item kernel -Kernel name in program. - -@item inputs -Number of inputs to the filter. Defaults to 1. - -@item size, s -Size of output frames. Defaults to the same as the first input. - -@end table - -The program source file must contain a kernel function with the given name, -which will be run once for each plane of the output. Each run on a plane -gets enqueued as a separate 2D global NDRange with one work-item for each -pixel to be generated. The global ID offset for each work-item is therefore -the coordinates of a pixel in the destination image. - -The kernel function needs to take the following arguments: -@itemize -@item -Destination image, @var{__write_only image2d_t}. - -This image will become the output; the kernel should write all of it. -@item -Frame index, @var{unsigned int}. - -This is a counter starting from zero and increasing by one for each frame. -@item -Source images, @var{__read_only image2d_t}. - -These are the most recent images on each input. The kernel may read from -them to generate the output, but they can't be written to. -@end itemize - -Example programs: - -@itemize -@item -Copy the input to the output (output must be the same size as the input). -@verbatim -__kernel void copy(__write_only image2d_t destination, - unsigned int index, - __read_only image2d_t source) -{ - const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE; - - int2 location = (int2)(get_global_id(0), get_global_id(1)); - - float4 value = read_imagef(source, sampler, location); - - write_imagef(destination, location, value); -} -@end verbatim - -@item -Apply a simple transformation, rotating the input by an amount increasing -with the index counter. Pixel values are linearly interpolated by the -sampler, and the output need not have the same dimensions as the input. -@verbatim -__kernel void rotate_image(__write_only image2d_t dst, - unsigned int index, - __read_only image2d_t src) -{ - const sampler_t sampler = (CLK_NORMALIZED_COORDS_FALSE | - CLK_FILTER_LINEAR); - - float angle = (float)index / 100.0f; - - float2 dst_dim = convert_float2(get_image_dim(dst)); - float2 src_dim = convert_float2(get_image_dim(src)); - - float2 dst_cen = dst_dim / 2.0f; - float2 src_cen = src_dim / 2.0f; - - int2 dst_loc = (int2)(get_global_id(0), get_global_id(1)); - - float2 dst_pos = convert_float2(dst_loc) - dst_cen; - float2 src_pos = { - cos(angle) * dst_pos.x - sin(angle) * dst_pos.y, - sin(angle) * dst_pos.x + cos(angle) * dst_pos.y - }; - src_pos = src_pos * src_dim / dst_dim; - - float2 src_loc = src_pos + src_cen; - - if (src_loc.x < 0.0f || src_loc.y < 0.0f || - src_loc.x > src_dim.x || src_loc.y > src_dim.y) - write_imagef(dst, dst_loc, 0.5f); - else - write_imagef(dst, dst_loc, read_imagef(src, sampler, src_loc)); -} -@end verbatim - -@item -Blend two inputs together, with the amount of each input used varying -with the index counter. -@verbatim -__kernel void blend_images(__write_only image2d_t dst, - unsigned int index, - __read_only image2d_t src1, - __read_only image2d_t src2) -{ - const sampler_t sampler = (CLK_NORMALIZED_COORDS_FALSE | - CLK_FILTER_LINEAR); - - float blend = (cos((float)index / 50.0f) + 1.0f) / 2.0f; - - int2 dst_loc = (int2)(get_global_id(0), get_global_id(1)); - int2 src1_loc = dst_loc * get_image_dim(src1) / get_image_dim(dst); - int2 src2_loc = dst_loc * get_image_dim(src2) / get_image_dim(dst); - - float4 val1 = read_imagef(src1, sampler, src1_loc); - float4 val2 = read_imagef(src2, sampler, src2_loc); - - write_imagef(dst, dst_loc, val1 * blend + val2 * (1.0f - blend)); -} -@end verbatim - -@end itemize - @section pseudocolor Alter frame colors in video with pseudocolors. -This filter accept the following options: +This filter accepts the following options: @table @option @item c0 @@ -14489,6 +15448,9 @@ Maximum allowed value for each channel, and average over all channels. @end table +@subsection Examples +@itemize +@item For example: @example movie=ref_movie.mpg, setpts=PTS-STARTPTS [main]; @@ -14499,6 +15461,13 @@ On this example the input file being processed is compared with the reference file @file{ref_movie.mpg}. The PSNR of each individual frame is stored in @file{stats.log}. +@item +Another example with different containers: +@example +ffmpeg -i main.mpg -i ref.mkv -lavfi "[0:v]settb=AVTB,setpts=PTS-STARTPTS[main];[1:v]settb=AVTB,setpts=PTS-STARTPTS[ref];[main][ref]psnr" -f null - +@end example +@end itemize + @anchor{pullup} @section pullup @@ -14639,42 +15608,16 @@ Set the line to start scanning for EIA-608 data. Default is @code{0}. @item scan_max Set the line to end scanning for EIA-608 data. Default is @code{29}. -@item mac -Set minimal acceptable amplitude change for sync codes detection. -Default is @code{0.2}. Allowed range is @code{[0.001 - 1]}. - @item spw Set the ratio of width reserved for sync code detection. -Default is @code{0.27}. Allowed range is @code{[0.01 - 0.7]}. - -@item mhd -Set the max peaks height difference for sync code detection. -Default is @code{0.1}. Allowed range is @code{[0.0 - 0.5]}. - -@item mpd -Set max peaks period difference for sync code detection. -Default is @code{0.1}. Allowed range is @code{[0.0 - 0.5]}. - -@item msd -Set the first two max start code bits differences. -Default is @code{0.02}. Allowed range is @code{[0.0 - 0.5]}. - -@item bhd -Set the minimum ratio of bits height compared to 3rd start code bit. -Default is @code{0.75}. Allowed range is @code{[0.01 - 1]}. - -@item th_w -Set the white color threshold. Default is @code{0.35}. Allowed range is @code{[0.1 - 1]}. - -@item th_b -Set the black color threshold. Default is @code{0.15}. Allowed range is @code{[0.0 - 0.5]}. +Default is @code{0.27}. Allowed range is @code{[0.1 - 0.7]}. @item chp Enable checking the parity bit. In the event of a parity error, the filter will output @code{0x00} for that character. Default is false. @item lp -Lowpass lines prior further proccessing. Default is disabled. +Lowpass lines prior to further processing. Default is enabled. @end table @subsection Examples @@ -14740,6 +15683,11 @@ Xmap and Ymap input video streams are 16bit depth, single channel. @item format Specify pixel format of output from this filter. Can be @code{color} or @code{gray}. Default is @code{color}. + +@item fill +Specify the color of the unmapped pixels. For the syntax of this option, +check the @ref{color syntax,,"Color" section in the ffmpeg-utils +manual,ffmpeg-utils}. Default color is @code{black}. @end table @section removegrain @@ -14923,6 +15871,10 @@ Set amount to shift alpha vertically. Set edge mode, can be @var{smear}, default, or @var{warp}. @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @section roberts Apply roberts cross operator to input video stream. @@ -15295,6 +16247,19 @@ Please note that this is a different thing than specifying -1 for @option{w} or @option{h}, you still need to specify the output resolution for this option to work. +@item force_divisible_by +Ensures that both the output dimensions, width and height, are divisible by the +given integer when used together with @option{force_original_aspect_ratio}. This +works similar to using @code{-n} in the @option{w} and @option{h} options. + +This option respects the value set for @option{force_original_aspect_ratio}, +increasing or decreasing the resolution accordingly. The video's aspect ratio +may be slightly modified. + +This option can be handy if you need to have a video fit within or exceed +a defined resolution using @option{force_original_aspect_ratio} but also have +encoder restrictions on width or height divisibility. + @end table The values of the @option{w} and @option{h} options are expressions @@ -15335,6 +16300,19 @@ pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1. @item ovsub horizontal and vertical output chroma subsample values. For example for the pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1. + +@item n +The (sequential) number of the input frame, starting from 0. +Only available with @code{eval=frame}. + +@item t +The presentation timestamp of the input frame, expressed as a number of +seconds. Only available with @code{eval=frame}. + +@item pos +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}. @end table @subsection Examples @@ -15487,23 +16465,63 @@ Supersampling @item lanczos @end table -@end table +@item force_original_aspect_ratio +Enable decreasing or increasing output video width or height if necessary to +keep the original aspect ratio. Possible values: -@section scale2ref +@table @samp +@item disable +Scale the video as specified and disable this feature. -Scale (resize) the input video, based on a reference video. +@item decrease +The output video dimensions will automatically be decreased if needed. -See the scale filter for available options, scale2ref supports the same but -uses the reference video instead of the main input as basis. scale2ref also -supports the following additional constants for the @option{w} and -@option{h} options: +@item increase +The output video dimensions will automatically be increased if needed. -@table @var -@item main_w -@item main_h -The main input video's width and height +@end table -@item main_a +One useful instance of this option is that when you know a specific device's +maximum allowed resolution, you can use this to limit the output video to +that, while retaining the aspect ratio. For example, device A allows +1280x720 playback, and your video is 1920x800. Using this option (set it to +decrease) and specifying 1280x720 to the command line makes the output +1280x533. + +Please note that this is a different thing than specifying -1 for @option{w} +or @option{h}, you still need to specify the output resolution for this option +to work. + +@item force_divisible_by +Ensures that both the output dimensions, width and height, are divisible by the +given integer when used together with @option{force_original_aspect_ratio}. This +works similar to using @code{-n} in the @option{w} and @option{h} options. + +This option respects the value set for @option{force_original_aspect_ratio}, +increasing or decreasing the resolution accordingly. The video's aspect ratio +may be slightly modified. + +This option can be handy if you need to have a video fit within or exceed +a defined resolution using @option{force_original_aspect_ratio} but also have +encoder restrictions on width or height divisibility. + +@end table + +@section scale2ref + +Scale (resize) the input video, based on a reference video. + +See the scale filter for available options, scale2ref supports the same but +uses the reference video instead of the main input as basis. scale2ref also +supports the following additional constants for the @option{w} and +@option{h} options: + +@table @var +@item main_w +@item main_h +The main input video's width and height + +@item main_a The same as @var{main_w} / @var{main_h} @item main_sar @@ -15518,6 +16536,19 @@ The main input video's display aspect ratio. Calculated from The main input video's horizontal and vertical chroma subsample values. For example for the pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1. + +@item main_n +The (sequential) number of the main input frame, starting from 0. +Only available with @code{eval=frame}. + +@item main_t +The presentation timestamp of the main input frame, expressed as a number of +seconds. Only available with @code{eval=frame}. + +@item main_pos +The position (byte offset) of the frame in the main 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}. @end table @subsection Examples @@ -15536,6 +16567,84 @@ Scale a logo to 1/10th the height of a video, while preserving its display aspec @end example @end itemize +@subsection Commands + +This filter supports the following commands: +@table @option +@item width, w +@item height, h +Set the output video dimension expression. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. +@end table + +@section scroll +Scroll input video horizontally and/or vertically by constant speed. + +The filter accepts the following options: +@table @option +@item horizontal, h +Set the horizontal scrolling speed. Default is 0. Allowed range is from -1 to 1. +Negative values changes scrolling direction. + +@item vertical, v +Set the vertical scrolling speed. Default is 0. Allowed range is from -1 to 1. +Negative values changes scrolling direction. + +@item hpos +Set the initial horizontal scrolling position. Default is 0. Allowed range is from 0 to 1. + +@item vpos +Set the initial vertical scrolling position. Default is 0. Allowed range is from 0 to 1. +@end table + +@subsection Commands + +This filter supports the following @ref{commands}: +@table @option +@item horizontal, h +Set the horizontal scrolling speed. +@item vertical, v +Set the vertical scrolling speed. +@end table + +@anchor{scdet} +@section scdet + +Detect video scene change. + +This filter sets frame metadata with mafd between frame, the scene score, and +forward the frame to the next filter, so they can use these metadata to detect +scene change or others. + +In addition, this filter logs a message and sets frame metadata when it detects +a scene change by @option{threshold}. + +@code{lavfi.scd.mafd} metadata keys are set with mafd for every frame. + +@code{lavfi.scd.score} metadata keys are set with scene change score for every frame +to detect scene change. + +@code{lavfi.scd.time} metadata keys are set with current filtered frame time which +detect scene change with @option{threshold}. + +The filter accepts the following options: + +@table @option +@item threshold, t +Set the scene change detection threshold as a percentage of maximum change. Good +values are in the @code{[8.0, 14.0]} range. The range for @option{threshold} is +@code{[0., 100.]}. + +Default value is @code{10.}. + +@item sc_pass, s +Set the flag to pass scene change frames to the next filter. Default value is @code{0} +You can enable it if you want to get snapshot of scene change frames only. +@end table + @anchor{selectivecolor} @section selectivecolor @@ -15930,6 +17039,15 @@ The Adler-32 checksum (printed in hexadecimal) of all the planes of the input fr @item plane_checksum The Adler-32 checksum (printed in hexadecimal) of each plane of the input frame, expressed in the form "[@var{c0} @var{c1} @var{c2} @var{c3}]". + +@item mean +The mean value of pixels in each plane of the input frame, expressed in the form +"[@var{mean0} @var{mean1} @var{mean2} @var{mean3}]". + +@item stdev +The standard deviation of pixel values in each plane of the input frame, expressed +in the form "[@var{stdev0} @var{stdev1} @var{stdev2} @var{stdev3}]". + @end table @section showpalette @@ -16343,6 +17461,126 @@ in [-30,0] will filter edges. Default value is @option{luma_threshold}. If a chroma option is not explicitly set, the corresponding luma value is set. +@section sobel +Apply sobel operator to input video stream. + +The filter accepts the following option: + +@table @option +@item planes +Set which planes will be processed, unprocessed planes will be copied. +By default value 0xf, all planes will be processed. + +@item scale +Set value which will be multiplied with filtered result. + +@item delta +Set value which will be added to filtered result. +@end table + +@anchor{spp} +@section spp + +Apply a simple postprocessing filter that compresses and decompresses the image +at several (or - in the case of @option{quality} level @code{6} - all) shifts +and average the results. + +The filter accepts the following options: + +@table @option +@item quality +Set quality. This option defines the number of levels for averaging. It accepts +an integer in the range 0-6. If set to @code{0}, the filter will have no +effect. A value of @code{6} means the higher quality. For each increment of +that value the speed drops by a factor of approximately 2. Default value is +@code{3}. + +@item qp +Force a constant quantization parameter. If not set, the filter will use the QP +from the video stream (if available). + +@item mode +Set thresholding mode. Available modes are: + +@table @samp +@item hard +Set hard thresholding (default). +@item soft +Set soft thresholding (better de-ringing effect, but likely blurrier). +@end table + +@item use_bframe_qp +Enable the use of the QP from the B-Frames if set to @code{1}. Using this +option may cause flicker since the B-Frames have often larger QP. Default is +@code{0} (not enabled). +@end table + +@subsection Commands + +This filter supports the following commands: +@table @option +@item quality, level +Set quality level. The value @code{max} can be used to set the maximum level, +currently @code{6}. +@end table + +@anchor{sr} +@section sr + +Scale the input by applying one of the super-resolution methods based on +convolutional neural networks. Supported models: + +@itemize +@item +Super-Resolution Convolutional Neural Network model (SRCNN). +See @url{https://arxiv.org/abs/1501.00092}. + +@item +Efficient Sub-Pixel Convolutional Neural Network model (ESPCN). +See @url{https://arxiv.org/abs/1609.05158}. +@end itemize + +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 +@item dnn_backend +Specify which DNN backend to use for model loading and execution. This option accepts +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 +@url{https://www.tensorflow.org/install/install_c}) and configure FFmpeg with +@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. + +@item scale_factor +Set scale factor for SRCNN model. Allowed values are @code{2}, @code{3} and @code{4}. +Default value is @code{2}. Scale factor is necessary for SRCNN model, because it accepts +input upscaled using bicubic upscaling with proper scale factor. +@end table + +This feature can also be finished with @ref{dnn_processing} filter. + @section ssim Obtain the SSIM (Structural SImilarity Metric) between two input videos. @@ -16389,6 +17627,9 @@ Same as above but in dB representation. This filter also supports the @ref{framesync} options. +@subsection Examples +@itemize +@item For example: @example movie=ref_movie.mpg, setpts=PTS-STARTPTS [main]; @@ -16399,11 +17640,19 @@ On this example the input file being processed is compared with the reference file @file{ref_movie.mpg}. The SSIM of each individual frame is stored in @file{stats.log}. +@item Another example with both psnr and ssim at same time: @example ffmpeg -i main.mpg -i ref.mpg -lavfi "ssim;[0:v][1:v]psnr" -f null - @end example +@item +Another example with different containers: +@example +ffmpeg -i main.mpg -i ref.mkv -lavfi "[0:v]settb=AVTB,setpts=PTS-STARTPTS[main];[1:v]settb=AVTB,setpts=PTS-STARTPTS[ref];[main][ref]ssim" -f null - +@end example +@end itemize + @section stereo3d Convert between different stereoscopic image formats. @@ -16431,16 +17680,20 @@ side by side crosseye with half width resolution (right eye left, left eye right) @item abl +@item tbl above-below (left eye above, right eye below) @item abr +@item tbr above-below (right eye above, left eye below) @item ab2l +@item tb2l above-below with half height resolution (left eye above, right eye below) @item ab2r +@item tb2r above-below with half height resolution (right eye above, left eye below) @@ -16484,16 +17737,20 @@ side by side crosseye with half width resolution (right eye left, left eye right) @item abl +@item tbl above-below (left eye above, right eye below) @item abr +@item tbr above-below (right eye above, left eye below) @item ab2l +@item tb2l above-below with half height resolution (left eye above, right eye below) @item ab2r +@item tb2r above-below with half height resolution (right eye above, left eye below) @@ -16645,150 +17902,42 @@ asendcmd='5.0 astreamselect map 1',astreamselect=inputs=2:map=0 @end example @end itemize -@section sobel -Apply sobel operator to input video stream. +@anchor{subtitles} +@section subtitles -The filter accepts the following option: +Draw subtitles on top of input video using the libass library. -@table @option -@item planes -Set which planes will be processed, unprocessed planes will be copied. -By default value 0xf, all planes will be processed. +To enable compilation of this filter you need to configure FFmpeg with +@code{--enable-libass}. This filter also requires a build with libavcodec and +libavformat to convert the passed subtitles file to ASS (Advanced Substation +Alpha) subtitles format. -@item scale -Set value which will be multiplied with filtered result. +The filter accepts the following options: -@item delta -Set value which will be added to filtered result. -@end table +@table @option +@item filename, f +Set the filename of the subtitle file to read. It must be specified. -@anchor{spp} -@section spp +@item original_size +Specify the size of the original video, the video for which the ASS file +was composed. For the syntax of this option, check the +@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}. +Due to a misdesign in ASS aspect ratio arithmetic, this is necessary to +correctly scale the fonts if the aspect ratio has been changed. -Apply a simple postprocessing filter that compresses and decompresses the image -at several (or - in the case of @option{quality} level @code{6} - all) shifts -and average the results. +@item fontsdir +Set a directory path containing fonts that can be used by the filter. +These fonts will be used in addition to whatever the font provider uses. -The filter accepts the following options: +@item alpha +Process alpha channel, by default alpha channel is untouched. -@table @option -@item quality -Set quality. This option defines the number of levels for averaging. It accepts -an integer in the range 0-6. If set to @code{0}, the filter will have no -effect. A value of @code{6} means the higher quality. For each increment of -that value the speed drops by a factor of approximately 2. Default value is -@code{3}. +@item charenc +Set subtitles input character encoding. @code{subtitles} filter only. Only +useful if not UTF-8. -@item qp -Force a constant quantization parameter. If not set, the filter will use the QP -from the video stream (if available). - -@item mode -Set thresholding mode. Available modes are: - -@table @samp -@item hard -Set hard thresholding (default). -@item soft -Set soft thresholding (better de-ringing effect, but likely blurrier). -@end table - -@item use_bframe_qp -Enable the use of the QP from the B-Frames if set to @code{1}. Using this -option may cause flicker since the B-Frames have often larger QP. Default is -@code{0} (not enabled). -@end table - -@section sr - -Scale the input by applying one of the super-resolution methods based on -convolutional neural networks. Supported models: - -@itemize -@item -Super-Resolution Convolutional Neural Network model (SRCNN). -See @url{https://arxiv.org/abs/1501.00092}. - -@item -Efficient Sub-Pixel Convolutional Neural Network model (ESPCN). -See @url{https://arxiv.org/abs/1609.05158}. -@end itemize - -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 -@item dnn_backend -Specify which DNN backend to use for model loading and execution. This option accepts -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 -@url{https://www.tensorflow.org/install/install_c}) and configure FFmpeg with -@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. - -@item scale_factor -Set scale factor for SRCNN model. Allowed values are @code{2}, @code{3} and @code{4}. -Default value is @code{2}. Scale factor is necessary for SRCNN model, because it accepts -input upscaled using bicubic upscaling with proper scale factor. -@end table - -@anchor{subtitles} -@section subtitles - -Draw subtitles on top of input video using the libass library. - -To enable compilation of this filter you need to configure FFmpeg with -@code{--enable-libass}. This filter also requires a build with libavcodec and -libavformat to convert the passed subtitles file to ASS (Advanced Substation -Alpha) subtitles format. - -The filter accepts the following options: - -@table @option -@item filename, f -Set the filename of the subtitle file to read. It must be specified. - -@item original_size -Specify the size of the original video, the video for which the ASS file -was composed. For the syntax of this option, check the -@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}. -Due to a misdesign in ASS aspect ratio arithmetic, this is necessary to -correctly scale the fonts if the aspect ratio has been changed. - -@item fontsdir -Set a directory path containing fonts that can be used by the filter. -These fonts will be used in addition to whatever the font provider uses. - -@item alpha -Process alpha channel, by default alpha channel is untouched. - -@item charenc -Set subtitles input character encoding. @code{subtitles} filter only. Only -useful if not UTF-8. - -@item stream_index, si -Set subtitles stream index. @code{subtitles} filter only. +@item stream_index, si +Set subtitles stream index. @code{subtitles} filter only. @item force_style Override default style or script info parameters of the subtitles. It accepts a @@ -16889,6 +18038,11 @@ the position in the file of the input frame, NAN if unknown @section swapuv Swap U & V plane. +@section tblend +Blend successive video frames. + +See @ref{blend} + @section telecine Apply telecine process to the video. @@ -16928,6 +18082,61 @@ PAL output (25i): 16p: 33333334 @end example +@section thistogram + +Compute and draw a color distribution histogram for the input video across time. + +Unlike @ref{histogram} video filter which only shows histogram of single input frame +at certain time, this filter shows also past histograms of number of frames defined +by @code{width} option. + +The computed histogram is a representation of the color component +distribution in an image. + +The filter accepts the following options: + +@table @option +@item width, w +Set width of single color component output. Default value is @code{0}. +Value of @code{0} means width will be picked from input video. +This also set number of passed histograms to keep. +Allowed range is [0, 8192]. + +@item display_mode, d +Set display mode. +It accepts the following values: +@table @samp +@item stack +Per color component graphs are placed below each other. + +@item parade +Per color component graphs are placed side by side. + +@item overlay +Presents information identical to that in the @code{parade}, except +that the graphs representing color components are superimposed directly +over one another. +@end table +Default is @code{stack}. + +@item levels_mode, m +Set mode. Can be either @code{linear}, or @code{logarithmic}. +Default is @code{linear}. + +@item components, c +Set what color components to display. +Default is @code{7}. + +@item bgopacity, b +Set background opacity. Default is @code{0.9}. + +@item envelope, e +Show envelope. Default is disabled. + +@item ecolor, ec +Set envelope color. Default is @code{gold}. +@end table + @section threshold Apply threshold effect to video stream. @@ -17016,10 +18225,13 @@ ffmpeg -i in.avi -vf thumbnail,scale=300:200 -frames:v 1 out.png @end example @end itemize +@anchor{tile} @section tile Tile several successive frames together. +The @ref{untile} filter can do the reverse. + The filter accepts the following options: @table @option @@ -17307,11 +18519,32 @@ Enable complex vertical low-pass filtering. This will slightly less reduce interlace 'twitter' and Moire patterning but better retain detail and subjective sharpness impression. +@item bypass_il +Bypass already interlaced frames, only adjust the frame rate. +@end table + +Vertical low-pass filtering and bypassing already interlaced frames can only be +enabled for @option{mode} @var{interleave_top} and @var{interleave_bottom}. + @end table -Vertical low-pass filtering can only be enabled for @option{mode} -@var{interleave_top} and @var{interleave_bottom}. +@section tmedian +Pick median pixels from several successive input video frames. + +The filter accepts the following options: + +@table @option +@item radius +Set radius of median filter. +Default is 1. Allowed range is from 1 to 127. + +@item planes +Set which planes to filter. Default value is @code{15}, by which all planes are processed. +@item percentile +Set median percentile. Default value is @code{0.5}. +Default value of @code{0.5} will pick always median values, while @code{0} will pick +minimum values, and @code{1} maximum values. @end table @section tmix @@ -17474,29 +18707,31 @@ The filter accepts the following options: @table @option @item start -Specify number of delay frames before input video stream. +Specify number of delay frames before input video stream. Default is 0. @item stop Specify number of padding frames after input video stream. -Set to -1 to pad indefinitely. +Set to -1 to pad indefinitely. Default is 0. @item start_mode Set kind of frames added to beginning of stream. Can be either @var{add} or @var{clone}. With @var{add} frames of solid-color are added. With @var{clone} frames are clones of first frame. +Default is @var{add}. @item stop_mode Set kind of frames added to end of stream. Can be either @var{add} or @var{clone}. With @var{add} frames of solid-color are added. With @var{clone} frames are clones of last frame. +Default is @var{add}. @item start_duration, stop_duration Specify the duration of the start/stop delay. See @ref{time duration syntax,,the Time duration section in the ffmpeg-utils(1) manual,ffmpeg-utils} for the accepted syntax. -These options override @var{start} and @var{stop}. +These options override @var{start} and @var{stop}. Default is 0. @item color Specify the color of the padded area. For the syntax of this option, @@ -17780,6 +19015,37 @@ unsharp=7:7:-2:7:7:-2 @end example @end itemize +@anchor{untile} +@section untile + +Decompose a video made of tiled images into the individual images. + +The frame rate of the output video is the frame rate of the input video +multiplied by the number of tiles. + +This filter does the reverse of @ref{tile}. + +The filter accepts the following options: + +@table @option + +@item layout +Set the grid size (i.e. the number of lines and columns). For the syntax of +this option, check the +@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}. +@end table + +@subsection Examples + +@itemize +@item +Produce a 1-second video from a still image file made of 25 frames stacked +vertically, like an analogic film reel: +@example +ffmpeg -r 1 -i image.jpg -vf untile=1x25 movie.mkv +@end example +@end itemize + @section uspp Apply ultra slow/simple postprocessing filter that compresses and decompresses @@ -17805,87 +19071,460 @@ Force a constant quantization parameter. If not set, the filter will use the QP from the video stream (if available). @end table -@section vaguedenoiser - -Apply a wavelet based denoiser. +@section v360 -It transforms each frame from the video input into the wavelet domain, -using Cohen-Daubechies-Feauveau 9/7. Then it applies some filtering to -the obtained coefficients. It does an inverse wavelet transform after. -Due to wavelet properties, it should give a nice smoothed result, and -reduced noise, without blurring picture features. +Convert 360 videos between various formats. -This filter accepts the following options: +The filter accepts the following options: @table @option -@item threshold -The filtering strength. The higher, the more filtered the video will be. -Hard thresholding can use a higher threshold than soft thresholding -before the video looks overfiltered. Default value is 2. -@item method -The filtering method the filter will use. +@item input +@item output +Set format of the input/output video. -It accepts the following values: -@table @samp -@item hard -All values under the threshold will be zeroed. +Available formats: -@item soft -All values under the threshold will be zeroed. All values above will be -reduced by the threshold. +@table @samp -@item garrote -Scales or nullifies coefficients - intermediary between (more) soft and -(less) hard thresholding. -@end table +@item e +@item equirect +Equirectangular projection. -Default is garrote. +@item c3x2 +@item c6x1 +@item c1x6 +Cubemap with 3x2/6x1/1x6 layout. -@item nsteps -Number of times, the wavelet will decompose the picture. Picture can't -be decomposed beyond a particular point (typically, 8 for a 640x480 -frame - as 2^9 = 512 > 480). Valid values are integers between 1 and 32. Default value is 6. +Format specific options: -@item percent -Partial of full denoising (limited coefficients shrinking), from 0 to 100. Default value is 85. +@table @option +@item in_pad +@item out_pad +Set padding proportion for the input/output cubemap. Values in decimals. -@item planes -A list of the planes to process. By default all planes are processed. +Example values: +@table @samp +@item 0 +No padding. +@item 0.01 +1% of face is padding. For example, with 1920x1280 resolution face size would be 640x640 and padding would be 3 pixels from each side. (640 * 0.01 = 6 pixels) @end table -@section vectorscope +Default value is @b{@samp{0}}. +Maximum value is @b{@samp{0.1}}. -Display 2 color component values in the two dimensional graph (which is called -a vectorscope). +@item fin_pad +@item fout_pad +Set fixed padding for the input/output cubemap. Values in pixels. -This filter accepts the following options: +Default value is @b{@samp{0}}. If greater than zero it overrides other padding options. -@table @option -@item mode, m -Set vectorscope mode. +@item in_forder +@item out_forder +Set order of faces for the input/output cubemap. Choose one direction for each position. -It accepts the following values: +Designation of directions: @table @samp -@item gray -Gray values are displayed on graph, higher brightness means more pixels have -same component color value on location in graph. This is the default mode. - -@item color -Gray values are displayed on graph. Surrounding pixels values which are not -present in video frame are drawn in gradient of 2 color components which are -set by option @code{x} and @code{y}. The 3rd color component is static. +@item r +right +@item l +left +@item u +up +@item d +down +@item f +forward +@item b +back +@end table -@item color2 -Actual color components values present in video frame are displayed on graph. +Default value is @b{@samp{rludfb}}. -@item color3 -Similar as color2 but higher frequency of same values @code{x} and @code{y} -on graph increases value of another color component, which is luminance by -default values of @code{x} and @code{y}. +@item in_frot +@item out_frot +Set rotation of faces for the input/output cubemap. Choose one angle for each position. -@item color4 -Actual colors present in video frame are displayed on graph. If two different +Designation of angles: +@table @samp +@item 0 +0 degrees clockwise +@item 1 +90 degrees clockwise +@item 2 +180 degrees clockwise +@item 3 +270 degrees clockwise +@end table + +Default value is @b{@samp{000000}}. +@end table + +@item eac +Equi-Angular Cubemap. + +@item flat +@item gnomonic +@item rectilinear +Regular video. + +Format specific options: +@table @option +@item h_fov +@item v_fov +@item d_fov +Set output horizontal/vertical/diagonal field of view. Values in degrees. + +If diagonal field of view is set it overrides horizontal and vertical field of view. + +@item ih_fov +@item iv_fov +@item id_fov +Set input horizontal/vertical/diagonal field of view. Values in degrees. + +If diagonal field of view is set it overrides horizontal and vertical field of view. +@end table + +@item dfisheye +Dual fisheye. + +Format specific options: +@table @option +@item h_fov +@item v_fov +@item d_fov +Set output horizontal/vertical/diagonal field of view. Values in degrees. + +If diagonal field of view is set it overrides horizontal and vertical field of view. + +@item ih_fov +@item iv_fov +@item id_fov +Set input horizontal/vertical/diagonal field of view. Values in degrees. + +If diagonal field of view is set it overrides horizontal and vertical field of view. +@end table + +@item barrel +@item fb +@item barrelsplit +Facebook's 360 formats. + +@item sg +Stereographic format. + +Format specific options: +@table @option +@item h_fov +@item v_fov +@item d_fov +Set output horizontal/vertical/diagonal field of view. Values in degrees. + +If diagonal field of view is set it overrides horizontal and vertical field of view. + +@item ih_fov +@item iv_fov +@item id_fov +Set input horizontal/vertical/diagonal field of view. Values in degrees. + +If diagonal field of view is set it overrides horizontal and vertical field of view. +@end table + +@item mercator +Mercator format. + +@item ball +Ball format, gives significant distortion toward the back. + +@item hammer +Hammer-Aitoff map projection format. + +@item sinusoidal +Sinusoidal map projection format. + +@item fisheye +Fisheye projection. + +Format specific options: +@table @option +@item h_fov +@item v_fov +@item d_fov +Set output horizontal/vertical/diagonal field of view. Values in degrees. + +If diagonal field of view is set it overrides horizontal and vertical field of view. + +@item ih_fov +@item iv_fov +@item id_fov +Set input horizontal/vertical/diagonal field of view. Values in degrees. + +If diagonal field of view is set it overrides horizontal and vertical field of view. +@end table + +@item pannini +Pannini projection. + +Format specific options: +@table @option +@item h_fov +Set output pannini parameter. + +@item ih_fov +Set input pannini parameter. +@end table + +@item cylindrical +Cylindrical projection. + +Format specific options: +@table @option +@item h_fov +@item v_fov +@item d_fov +Set output horizontal/vertical/diagonal field of view. Values in degrees. + +If diagonal field of view is set it overrides horizontal and vertical field of view. + +@item ih_fov +@item iv_fov +@item id_fov +Set input horizontal/vertical/diagonal field of view. Values in degrees. + +If diagonal field of view is set it overrides horizontal and vertical field of view. +@end table + +@item perspective +Perspective projection. @i{(output only)} + +Format specific options: +@table @option +@item v_fov +Set perspective parameter. +@end table + +@item tetrahedron +Tetrahedron projection. + +@item tsp +Truncated square pyramid projection. + +@item he +@item hequirect +Half equirectangular projection. +@end table + +@item interp +Set interpolation method.@* +@i{Note: more complex interpolation methods require much more memory to run.} + +Available methods: + +@table @samp +@item near +@item nearest +Nearest neighbour. +@item line +@item linear +Bilinear interpolation. +@item lagrange9 +Lagrange9 interpolation. +@item cube +@item cubic +Bicubic interpolation. +@item lanc +@item lanczos +Lanczos interpolation. +@item sp16 +@item spline16 +Spline16 interpolation. +@item gauss +@item gaussian +Gaussian interpolation. +@end table + +Default value is @b{@samp{line}}. + +@item w +@item h +Set the output video resolution. + +Default resolution depends on formats. + +@item in_stereo +@item out_stereo +Set the input/output stereo format. + +@table @samp +@item 2d +2D mono +@item sbs +Side by side +@item tb +Top bottom +@end table + +Default value is @b{@samp{2d}} for input and output format. + +@item yaw +@item pitch +@item roll +Set rotation for the output video. Values in degrees. + +@item rorder +Set rotation order for the output video. Choose one item for each position. + +@table @samp +@item y, Y +yaw +@item p, P +pitch +@item r, R +roll +@end table + +Default value is @b{@samp{ypr}}. + +@item h_flip +@item v_flip +@item d_flip +Flip the output video horizontally(swaps left-right)/vertically(swaps up-down)/in-depth(swaps back-forward). Boolean values. + +@item ih_flip +@item iv_flip +Set if input video is flipped horizontally/vertically. Boolean values. + +@item in_trans +Set if input video is transposed. Boolean value, by default disabled. + +@item out_trans +Set if output video needs to be transposed. Boolean value, by default disabled. + +@item alpha_mask +Build mask in alpha plane for all unmapped pixels by marking them fully transparent. Boolean value, by default disabled. +@end table + +@subsection Examples + +@itemize +@item +Convert equirectangular video to cubemap with 3x2 layout and 1% padding using bicubic interpolation: +@example +ffmpeg -i input.mkv -vf v360=e:c3x2:cubic:out_pad=0.01 output.mkv +@end example +@item +Extract back view of Equi-Angular Cubemap: +@example +ffmpeg -i input.mkv -vf v360=eac:flat:yaw=180 output.mkv +@end example +@item +Convert transposed and horizontally flipped Equi-Angular Cubemap in side-by-side stereo format to equirectangular top-bottom stereo format: +@example +v360=eac:equirect:in_stereo=sbs:in_trans=1:ih_flip=1:out_stereo=tb +@end example +@end itemize + +@subsection Commands + +This filter supports subset of above options as @ref{commands}. + +@section vaguedenoiser + +Apply a wavelet based denoiser. + +It transforms each frame from the video input into the wavelet domain, +using Cohen-Daubechies-Feauveau 9/7. Then it applies some filtering to +the obtained coefficients. It does an inverse wavelet transform after. +Due to wavelet properties, it should give a nice smoothed result, and +reduced noise, without blurring picture features. + +This filter accepts the following options: + +@table @option +@item threshold +The filtering strength. The higher, the more filtered the video will be. +Hard thresholding can use a higher threshold than soft thresholding +before the video looks overfiltered. Default value is 2. + +@item method +The filtering method the filter will use. + +It accepts the following values: +@table @samp +@item hard +All values under the threshold will be zeroed. + +@item soft +All values under the threshold will be zeroed. All values above will be +reduced by the threshold. + +@item garrote +Scales or nullifies coefficients - intermediary between (more) soft and +(less) hard thresholding. +@end table + +Default is garrote. + +@item nsteps +Number of times, the wavelet will decompose the picture. Picture can't +be decomposed beyond a particular point (typically, 8 for a 640x480 +frame - as 2^9 = 512 > 480). Valid values are integers between 1 and 32. Default value is 6. + +@item percent +Partial of full denoising (limited coefficients shrinking), from 0 to 100. Default value is 85. + +@item planes +A list of the planes to process. By default all planes are processed. + +@item type +The threshold type the filter will use. + +It accepts the following values: +@table @samp +@item universal +Threshold used is same for all decompositions. + +@item bayes +Threshold used depends also on each decomposition coefficients. +@end table + +Default is universal. +@end table + +@section vectorscope + +Display 2 color component values in the two dimensional graph (which is called +a vectorscope). + +This filter accepts the following options: + +@table @option +@item mode, m +Set vectorscope mode. + +It accepts the following values: +@table @samp +@item gray +@item tint +Gray values are displayed on graph, higher brightness means more pixels have +same component color value on location in graph. This is the default mode. + +@item color +Gray values are displayed on graph. Surrounding pixels values which are not +present in video frame are drawn in gradient of 2 color components which are +set by option @code{x} and @code{y}. The 3rd color component is static. + +@item color2 +Actual color components values present in video frame are displayed on graph. + +@item color3 +Similar as color2 but higher frequency of same values @code{x} and @code{y} +on graph increases value of another color component, which is luminance by +default values of @code{x} and @code{y}. + +@item color4 +Actual colors present in video frame are displayed on graph. If two different colors map to same position on graph then color with higher value of component not present in graph is picked. @@ -17926,6 +19565,7 @@ Set what kind of graticule to draw. @item none @item green @item color +@item invert @end table @item opacity, o @@ -17970,6 +19610,11 @@ Set what kind of colorspace to use when drawing graticule. @item 709 @end table Default is auto. + +@item tint0, t0 +@item tint1, t1 +Set color tint for gray/tint vectorscope mode. By default both options are zero. +This means no tint, and output will remain gray. @end table @anchor{vidstabdetect} @@ -18222,8 +19867,8 @@ This filter tries to detect if the input is variable or constant frame rate. At end it will output number of frames detected as having variable delta pts, and ones with constant delta pts. -If there was frames with variable delta, than it will also show min and max delta -encountered. +If there was frames with variable delta, than it will also show min, max and +average delta encountered. @section vibrance @@ -18258,6 +19903,10 @@ If @code{intensity} is negative and this is set to 1, colors will change, otherwise colors will be less saturated, more towards gray. @end table +@subsection Commands + +This filter supports the all above options as @ref{commands}. + @anchor{vignette} @section vignette @@ -18371,16 +20020,23 @@ vignette='PI/4+random(1)*PI/50':eval=frame @section vmafmotion -Obtain the average vmaf motion score of a video. -It is one of the component filters of VMAF. +Obtain the average VMAF motion score of a video. +It is one of the component metrics of VMAF. The obtained average motion score is printed through the logging system. -In the below example the input file @file{ref.mpg} is being processed and score -is computed. +The filter accepts the following options: + +@table @option +@item stats_file +If specified, the filter will use the named file to save the motion score of +each frame with respect to the previous frame. +When filename equals "-" the data is sent to standard output. +@end table +Example: @example -ffmpeg -i ref.mpg -lavfi vmafmotion -f null - +ffmpeg -i ref.mpg -vf vmafmotion -f null - @end example @section vstack @@ -18391,7 +20047,7 @@ All streams must be of same pixel format and of same width. Note that this filter is faster than using @ref{overlay} and @ref{pad} filter to create same output. -The filter accept the following option: +The filter accepts the following options: @table @option @item inputs @@ -18412,11 +20068,11 @@ implemented based on the de-interlace algorithm written by Jim Easterbrook for BBC R&D, the Weston 3 field deinterlacing filter uses filter coefficients calculated by BBC R&D. -This filter use field-dominance information in frame to decide which +This filter uses field-dominance information in frame to decide which of each pair of fields to place first in the output. If it gets it wrong use @ref{setfield} filter before @code{w3fdif} filter. -There are two sets of filter coefficients, so called "simple": +There are two sets of filter coefficients, so called "simple" and "complex". Which set of filter coefficients is used can be set by passing an optional parameter: @@ -18433,7 +20089,7 @@ More-complex filter coefficient set. Default value is @samp{complex}. @item deint -Specify which frames to deinterlace. Accept one of the following values: +Specify which frames to deinterlace. Accepts one of the following values: @table @samp @item all @@ -18538,6 +20194,9 @@ Similar as above, but shows difference between blue and red chroma. @item xflat Similar as above, but use different colors. +@item yflat +Similar as above, but again with different colors. + @item chroma Displays only chroma. @@ -18560,6 +20219,9 @@ Display green graticule showing legal broadcast ranges. @item orange Display orange graticule showing legal broadcast ranges. + +@item invert +Display invert graticule showing legal broadcast ranges. @end table @item opacity, o @@ -18588,6 +20250,12 @@ Default is digital. @item bgopacity, b Set background opacity. + +@item tint0, t0 +@item tint1, t1 +Set tint for output. +Only used with lowpass filter and when display is not overlay and input +pixel formats are not RGB. @end table @section weave, doubleweave @@ -18638,36 +20306,153 @@ Set the scaling dimension: @code{2} for @code{2xBR}, @code{3} for Default is @code{3}. @end table -@section xmedian -Pick median pixels from several input videos. +@section xfade + +Apply cross fade from one input video stream to another input video stream. +The cross fade is applied for specified duration. -The filter accept the following options: +The filter accepts the following options: @table @option -@item inputs -Set number of inputs. -Default is 3. Allowed range is from 3 to 255. -If number of inputs is even number, than result will be mean value between two median values. +@item transition +Set one of available transition effects: -@item planes -Set which planes to filter. Default value is @code{15}, by which all planes are processed. -@end table +@table @samp +@item custom +@item fade +@item wipeleft +@item wiperight +@item wipeup +@item wipedown +@item slideleft +@item slideright +@item slideup +@item slidedown +@item circlecrop +@item rectcrop +@item distance +@item fadeblack +@item fadewhite +@item radial +@item smoothleft +@item smoothright +@item smoothup +@item smoothdown +@item circleopen +@item circleclose +@item vertopen +@item vertclose +@item horzopen +@item horzclose +@item dissolve +@item pixelize +@item diagtl +@item diagtr +@item diagbl +@item diagbr +@item hlslice +@item hrslice +@item vuslice +@item vdslice +@end table +Default transition effect is fade. -@section xstack -Stack video inputs into custom layout. +@item duration +Set cross fade duration in seconds. +Default duration is 1 second. -All streams must be of same pixel format. +@item offset +Set cross fade start relative to first input stream in seconds. +Default offset is 0. -The filter accept the following option: +@item expr +Set expression for custom transition effect. + +The expressions can use the following variables and functions: @table @option -@item inputs -Set number of input streams. Default is 2. +@item X +@item Y +The coordinates of the current sample. -@item layout -Specify layout of inputs. -This option requires the desired layout configuration to be explicitly set by the user. -This sets position of each video input in output. Each input +@item W +@item H +The width and height of the image. + +@item P +Progress of transition effect. + +@item PLANE +Currently processed plane. + +@item A +Return value of first input at current location and plane. + +@item B +Return value of second input at current location and plane. + +@item a0(x, y) +@item a1(x, y) +@item a2(x, y) +@item a3(x, y) +Return the value of the pixel at location (@var{x},@var{y}) of the +first/second/third/fourth component of first input. + +@item b0(x, y) +@item b1(x, y) +@item b2(x, y) +@item b3(x, y) +Return the value of the pixel at location (@var{x},@var{y}) of the +first/second/third/fourth component of second input. +@end table +@end table + +@subsection Examples + +@itemize +@item +Cross fade from one input video to another input video, with fade transition and duration of transition +of 2 seconds starting at offset of 5 seconds: +@example +ffmpeg -i first.mp4 -i second.mp4 -filter_complex xfade=transition=fade:duration=2:offset=5 output.mp4 +@end example +@end itemize + +@section xmedian +Pick median pixels from several input videos. + +The filter accepts the following options: + +@table @option +@item inputs +Set number of inputs. +Default is 3. Allowed range is from 3 to 255. +If number of inputs is even number, than result will be mean value between two median values. + +@item planes +Set which planes to filter. Default value is @code{15}, by which all planes are processed. + +@item percentile +Set median percentile. Default value is @code{0.5}. +Default value of @code{0.5} will pick always median values, while @code{0} will pick +minimum values, and @code{1} maximum values. +@end table + +@section xstack +Stack video inputs into custom layout. + +All streams must be of same pixel format. + +The filter accepts the following options: + +@table @option +@item inputs +Set number of input streams. Default is 2. + +@item layout +Specify layout of inputs. +This option requires the desired layout configuration to be explicitly set by the user. +This sets position of each video input in output. Each input is separated by '|'. The first number represents the column, and the second number represents the row. Numbers start at 0 and are separated by '_'. Optionally one can use wX and hX, @@ -18675,40 +20460,92 @@ where X is video input from which to take width or height. Multiple values can be used when separated by '+'. In such case values are summed together. +Note that if inputs are of different sizes gaps may appear, as not all of +the output video frame will be filled. Similarly, videos can overlap each +other if their position doesn't leave enough space for the full frame of +adjoining videos. + For 2 inputs, a default layout of @code{0_0|w0_0} is set. In all other cases, a layout must be set by the user. @item shortest If set to 1, force the output to terminate when the shortest input terminates. Default value is 0. + +@item fill +If set to valid color, all unused pixels will be filled with that color. +By default fill is set to none, so it is disabled. @end table @subsection Examples @itemize @item -Display 4 inputs into 2x2 grid, -note that if inputs are of different sizes unused gaps might appear, -as not all of output video is used. +Display 4 inputs into 2x2 grid. + +Layout: +@example +input1(0, 0) | input3(w0, 0) +input2(0, h0) | input4(w0, h0) +@end example + @example xstack=inputs=4:layout=0_0|0_h0|w0_0|w0_h0 @end example +Note that if inputs are of different sizes, gaps or overlaps may occur. + @item -Display 4 inputs into 1x4 grid, -note that if inputs are of different sizes unused gaps might appear, -as not all of output video is used. +Display 4 inputs into 1x4 grid. + +Layout: +@example +input1(0, 0) +input2(0, h0) +input3(0, h0+h1) +input4(0, h0+h1+h2) +@end example + @example xstack=inputs=4:layout=0_0|0_h0|0_h0+h1|0_h0+h1+h2 @end example +Note that if inputs are of different widths, unused space will appear. + +@item +Display 9 inputs into 3x3 grid. + +Layout: +@example +input1(0, 0) | input4(w0, 0) | input7(w0+w3, 0) +input2(0, h0) | input5(w0, h0) | input8(w0+w3, h0) +input3(0, h0+h1) | input6(w0, h0+h1) | input9(w0+w3, h0+h1) +@end example + +@example +xstack=inputs=9:layout=0_0|0_h0|0_h0+h1|w0_0|w0_h0|w0_h0+h1|w0+w3_0|w0+w3_h0|w0+w3_h0+h1 +@end example + +Note that if inputs are of different sizes, gaps or overlaps may occur. + @item -Display 9 inputs into 3x3 grid, -note that if inputs are of different sizes unused gaps might appear, -as not all of output video is used. +Display 16 inputs into 4x4 grid. + +Layout: +@example +input1(0, 0) | input5(w0, 0) | input9 (w0+w4, 0) | input13(w0+w4+w8, 0) +input2(0, h0) | input6(w0, h0) | input10(w0+w4, h0) | input14(w0+w4+w8, h0) +input3(0, h0+h1) | input7(w0, h0+h1) | input11(w0+w4, h0+h1) | input15(w0+w4+w8, h0+h1) +input4(0, h0+h1+h2)| input8(w0, h0+h1+h2)| input12(w0+w4, h0+h1+h2)| input16(w0+w4+w8, h0+h1+h2) +@end example + @example -xstack=inputs=9:layout=w3_0|w3_h0+h2|w3_h0|0_h4|0_0|w3+w1_0|0_h1+h2|w3+w1_h0|w3+w1_h1+h2 +xstack=inputs=16:layout=0_0|0_h0|0_h0+h1|0_h0+h1+h2|w0_0|w0_h0|w0_h0+h1|w0_h0+h1+h2|w0+w4_0| +w0+w4_h0|w0+w4_h0+h1|w0+w4_h0+h1+h2|w0+w4+w8_0|w0+w4+w8_h0|w0+w4+w8_h0+h1|w0+w4+w8_h0+h1+h2 @end example + +Note that if inputs are of different sizes, gaps or overlaps may occur. + @end itemize @anchor{yadif} @@ -18756,7 +20593,7 @@ 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. Accept one of the following +Specify which frames to deinterlace. Accepts one of the following values: @table @option @@ -18814,7 +20651,7 @@ 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. Accept one of the following +Specify which frames to deinterlace. Accepts one of the following values: @table @option @@ -18827,6 +20664,28 @@ Only deinterlace frames marked as interlaced. The default value is @code{all}. @end table +@section yaepblur + +Apply blur filter while preserving edges ("yaepblur" means "yet another edge preserving blur filter"). +The algorithm is described in +"J. S. Lee, Digital image enhancement and noise filtering by use of local statistics, IEEE Trans. Pattern Anal. Mach. Intell. PAMI-2, 1980." + +It accepts the following parameters: + +@table @option +@item radius, r +Set the window radius. Default value is 3. + +@item planes, p +Set which planes to filter. Default is only the first plane. + +@item sigma, s +Set blur strength. Default value is 128. +@end table + +@subsection Commands +This filter supports same @ref{commands} as options. + @section zoompan Apply Zoom & Pan effect. @@ -19191,7 +21050,17 @@ horizontal and vertical output chroma subsample values. For example for the pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1. @end table +@subsection Commands + +This filter supports the following commands: @table @option +@item width, w +@item height, h +Set the output video dimension expression. +The command accepts the same syntax of the corresponding option. + +If the specified expression is not valid, it is kept at its current +value. @end table @c man end VIDEO FILTERS @@ -19346,6 +21215,39 @@ For the alpha plane, a 3x3 box radius will be run 7 times. @end example @end itemize +@section colorkey_opencl +RGB colorspace color keying. + +The filter accepts the following options: + +@table @option +@item color +The color which will be replaced with transparency. + +@item similarity +Similarity percentage with the key color. + +0.01 matches only the exact key color, while 1.0 matches everything. + +@item blend +Blend percentage. + +0.0 makes pixels either fully transparent, or not transparent at all. + +Higher values result in semi-transparent pixels, with a higher transparency +the more similar the pixels color is to the key color. +@end table + +@subsection Examples + +@itemize +@item +Make every semi-green pixel in the input transparent with some slight blending: +@example +-i INPUT -vf "hwupload, colorkey_opencl=green:0.3:0.1, hwdownload" OUTPUT +@end example +@end itemize + @section convolution_opencl Apply convolution of 3x3, 5x5, 7x7 matrix. @@ -19419,11 +21321,11 @@ Apply emboss: @end example @end itemize -@section dilation_opencl +@section erosion_opencl -Apply dilation effect to the video. +Apply erosion effect to the video. -This filter replaces the pixel by the local(3x3) maximum. +This filter replaces the pixel by the local(3x3) minimum. It accepts the following options: @@ -19452,81 +21354,117 @@ Flags to local 3x3 coordinates region centered on @code{x}: @itemize @item -Apply dilation filter with threshold0 set to 30, threshold1 set 40, threshold2 set to 50 and coordinates set to 231, setting each pixel of the output to the local maximum between pixels: 1, 2, 3, 6, 7, 8 of the 3x3 region centered on it in the input. If the difference between input pixel and local maximum is more then threshold of the corresponding plane, output pixel will be set to input pixel + threshold of corresponding plane. +Apply erosion filter with threshold0 set to 30, threshold1 set 40, threshold2 set to 50 and coordinates set to 231, setting each pixel of the output to the local minimum between pixels: 1, 2, 3, 6, 7, 8 of the 3x3 region centered on it in the input. If the difference between input pixel and local minimum is more then threshold of the corresponding plane, output pixel will be set to input pixel - threshold of corresponding plane. @example --i INPUT -vf "hwupload, dilation_opencl=30:40:50:coordinates=231, hwdownload" OUTPUT +-i INPUT -vf "hwupload, erosion_opencl=30:40:50:coordinates=231, hwdownload" OUTPUT @end example @end itemize -@section erosion_opencl +@section deshake_opencl +Feature-point based video stabilization filter. -Apply erosion effect to the video. +The filter accepts the following options: -This filter replaces the pixel by the local(3x3) minimum. +@table @option +@item tripod +Simulates a tripod by preventing any camera movement whatsoever from the original frame. Defaults to @code{0}. -It accepts the following options: +@item debug +Whether or not additional debug info should be displayed, both in the processed output and in the console. -@table @option -@item threshold0 -@item threshold1 -@item threshold2 -@item threshold3 -Limit the maximum change for each plane. Range is @code{[0, 65535]} and default value is @code{65535}. -If @code{0}, plane will remain unchanged. +Note that in order to see console debug output you will also need to pass @code{-v verbose} to ffmpeg. -@item coordinates -Flag which specifies the pixel to refer to. -Range is @code{[0, 255]} and default value is @code{255}, i.e. all eight pixels are used. +Viewing point matches in the output video is only supported for RGB input. -Flags to local 3x3 coordinates region centered on @code{x}: +Defaults to @code{0}. - 1 2 3 +@item adaptive_crop +Whether or not to do a tiny bit of cropping at the borders to cut down on the amount of mirrored pixels. - 4 x 5 +Defaults to @code{1}. + +@item refine_features +Whether or not feature points should be refined at a sub-pixel level. + +This can be turned off for a slight performance gain at the cost of precision. + +Defaults to @code{1}. + +@item smooth_strength +The strength of the smoothing applied to the camera path from @code{0.0} to @code{1.0}. + +@code{1.0} is the maximum smoothing strength while values less than that result in less smoothing. + +@code{0.0} causes the filter to adaptively choose a smoothing strength on a per-frame basis. + +Defaults to @code{0.0}. + +@item smooth_window_multiplier +Controls the size of the smoothing window (the number of frames buffered to determine motion information from). + +The size of the smoothing window is determined by multiplying the framerate of the video by this number. + +Acceptable values range from @code{0.1} to @code{10.0}. + +Larger values increase the amount of motion data available for determining how to smooth the camera path, +potentially improving smoothness, but also increase latency and memory usage. + +Defaults to @code{2.0}. - 6 7 8 @end table -@subsection Example +@subsection Examples @itemize @item -Apply erosion filter with threshold0 set to 30, threshold1 set 40, threshold2 set to 50 and coordinates set to 231, setting each pixel of the output to the local minimum between pixels: 1, 2, 3, 6, 7, 8 of the 3x3 region centered on it in the input. If the difference between input pixel and local minimum is more then threshold of the corresponding plane, output pixel will be set to input pixel - threshold of corresponding plane. +Stabilize a video with a fixed, medium smoothing strength: @example --i INPUT -vf "hwupload, erosion_opencl=30:40:50:coordinates=231, hwdownload" OUTPUT +-i INPUT -vf "hwupload, deshake_opencl=smooth_strength=0.5, hwdownload" OUTPUT +@end example + +@item +Stabilize a video with debugging (both in console and in rendered video): +@example +-i INPUT -filter_complex "[0:v]format=rgba, hwupload, deshake_opencl=debug=1, hwdownload, format=rgba, format=yuv420p" -v verbose OUTPUT @end example @end itemize -@section colorkey_opencl -RGB colorspace color keying. +@section dilation_opencl -The filter accepts the following options: +Apply dilation effect to the video. + +This filter replaces the pixel by the local(3x3) maximum. + +It accepts the following options: @table @option -@item color -The color which will be replaced with transparency. +@item threshold0 +@item threshold1 +@item threshold2 +@item threshold3 +Limit the maximum change for each plane. Range is @code{[0, 65535]} and default value is @code{65535}. +If @code{0}, plane will remain unchanged. -@item similarity -Similarity percentage with the key color. +@item coordinates +Flag which specifies the pixel to refer to. +Range is @code{[0, 255]} and default value is @code{255}, i.e. all eight pixels are used. -0.01 matches only the exact key color, while 1.0 matches everything. +Flags to local 3x3 coordinates region centered on @code{x}: -@item blend -Blend percentage. + 1 2 3 -0.0 makes pixels either fully transparent, or not transparent at all. + 4 x 5 -Higher values result in semi-transparent pixels, with a higher transparency -the more similar the pixels color is to the key color. + 6 7 8 @end table -@subsection Examples +@subsection Example @itemize @item -Make every semi-green pixel in the input transparent with some slight blending: +Apply dilation filter with threshold0 set to 30, threshold1 set 40, threshold2 set to 50 and coordinates set to 231, setting each pixel of the output to the local maximum between pixels: 1, 2, 3, 6, 7, 8 of the 3x3 region centered on it in the input. If the difference between input pixel and local maximum is more then threshold of the corresponding plane, output pixel will be set to input pixel + threshold of corresponding plane. @example --i INPUT -vf "hwupload, colorkey_opencl=green:0.3:0.1, hwdownload" OUTPUT +-i INPUT -vf "hwupload, dilation_opencl=30:40:50:coordinates=231, hwdownload" OUTPUT @end example @end itemize @@ -19550,7 +21488,7 @@ Set the x coordinate of the overlaid video on the main video. Default value is @code{0}. @item y -Set the x coordinate of the overlaid video on the main video. +Set the y coordinate of the overlaid video on the main video. Default value is @code{0}. @end table @@ -19571,28 +21509,105 @@ The inputs have same memory layout for color channels , the overlay has addition @end itemize -@section prewitt_opencl +@section pad_opencl -Apply the Prewitt operator (@url{https://en.wikipedia.org/wiki/Prewitt_operator}) to input video stream. +Add paddings to the input image, and place the original input at the +provided @var{x}, @var{y} coordinates. -The filter accepts the following option: +It accepts the following options: @table @option -@item planes -Set which planes to filter. Default value is @code{0xf}, by which all planes are processed. - -@item scale -Set value which will be multiplied with filtered result. -Range is @code{[0.0, 65535]} and default value is @code{1.0}. +@item width, w +@item height, h +Specify an expression for the size of the output image with the +paddings added. If the value for @var{width} or @var{height} is 0, the +corresponding input size is used for the output. -@item delta -Set value which will be added to filtered result. -Range is @code{[-65535, 65535]} and default value is @code{0.0}. -@end table +The @var{width} expression can reference the value set by the +@var{height} expression, and vice versa. -@subsection Example +The default value of @var{width} and @var{height} is 0. -@itemize +@item x +@item y +Specify the offsets to place the input image at within the padded area, +with respect to the top/left border of the output image. + +The @var{x} expression can reference the value set by the @var{y} +expression, and vice versa. + +The default value of @var{x} and @var{y} is 0. + +If @var{x} or @var{y} evaluate to a negative number, they'll be changed +so the input image is centered on the padded area. + +@item color +Specify the color of the padded area. For the syntax of this option, +check the @ref{color syntax,,"Color" section in the ffmpeg-utils +manual,ffmpeg-utils}. + +@item aspect +Pad to an aspect instead to a resolution. +@end table + +The value for the @var{width}, @var{height}, @var{x}, and @var{y} +options are expressions containing the following constants: + +@table @option +@item in_w +@item in_h +The input video width and height. + +@item iw +@item ih +These are the same as @var{in_w} and @var{in_h}. + +@item out_w +@item out_h +The output width and height (the size of the padded area), as +specified by the @var{width} and @var{height} expressions. + +@item ow +@item oh +These are the same as @var{out_w} and @var{out_h}. + +@item x +@item y +The x and y offsets as specified by the @var{x} and @var{y} +expressions, or NAN if not yet specified. + +@item a +same as @var{iw} / @var{ih} + +@item sar +input sample aspect ratio + +@item dar +input display aspect ratio, it is the same as (@var{iw} / @var{ih}) * @var{sar} +@end table + +@section prewitt_opencl + +Apply the Prewitt operator (@url{https://en.wikipedia.org/wiki/Prewitt_operator}) to input video stream. + +The filter accepts the following option: + +@table @option +@item planes +Set which planes to filter. Default value is @code{0xf}, by which all planes are processed. + +@item scale +Set value which will be multiplied with filtered result. +Range is @code{[0.0, 65535]} and default value is @code{1.0}. + +@item delta +Set value which will be added to filtered result. +Range is @code{[-65535, 65535]} and default value is @code{0.0}. +@end table + +@subsection Example + +@itemize @item Apply the Prewitt operator with scale set to 2 and delta set to 10. @example @@ -19600,6 +21615,138 @@ Apply the Prewitt operator with scale set to 2 and delta set to 10. @end example @end itemize +@anchor{program_opencl} +@section program_opencl + +Filter video using an OpenCL program. + +@table @option + +@item source +OpenCL program source file. + +@item kernel +Kernel name in program. + +@item inputs +Number of inputs to the filter. Defaults to 1. + +@item size, s +Size of output frames. Defaults to the same as the first input. + +@end table + +The @code{program_opencl} filter also supports the @ref{framesync} options. + +The program source file must contain a kernel function with the given name, +which will be run once for each plane of the output. Each run on a plane +gets enqueued as a separate 2D global NDRange with one work-item for each +pixel to be generated. The global ID offset for each work-item is therefore +the coordinates of a pixel in the destination image. + +The kernel function needs to take the following arguments: +@itemize +@item +Destination image, @var{__write_only image2d_t}. + +This image will become the output; the kernel should write all of it. +@item +Frame index, @var{unsigned int}. + +This is a counter starting from zero and increasing by one for each frame. +@item +Source images, @var{__read_only image2d_t}. + +These are the most recent images on each input. The kernel may read from +them to generate the output, but they can't be written to. +@end itemize + +Example programs: + +@itemize +@item +Copy the input to the output (output must be the same size as the input). +@verbatim +__kernel void copy(__write_only image2d_t destination, + unsigned int index, + __read_only image2d_t source) +{ + const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE; + + int2 location = (int2)(get_global_id(0), get_global_id(1)); + + float4 value = read_imagef(source, sampler, location); + + write_imagef(destination, location, value); +} +@end verbatim + +@item +Apply a simple transformation, rotating the input by an amount increasing +with the index counter. Pixel values are linearly interpolated by the +sampler, and the output need not have the same dimensions as the input. +@verbatim +__kernel void rotate_image(__write_only image2d_t dst, + unsigned int index, + __read_only image2d_t src) +{ + const sampler_t sampler = (CLK_NORMALIZED_COORDS_FALSE | + CLK_FILTER_LINEAR); + + float angle = (float)index / 100.0f; + + float2 dst_dim = convert_float2(get_image_dim(dst)); + float2 src_dim = convert_float2(get_image_dim(src)); + + float2 dst_cen = dst_dim / 2.0f; + float2 src_cen = src_dim / 2.0f; + + int2 dst_loc = (int2)(get_global_id(0), get_global_id(1)); + + float2 dst_pos = convert_float2(dst_loc) - dst_cen; + float2 src_pos = { + cos(angle) * dst_pos.x - sin(angle) * dst_pos.y, + sin(angle) * dst_pos.x + cos(angle) * dst_pos.y + }; + src_pos = src_pos * src_dim / dst_dim; + + float2 src_loc = src_pos + src_cen; + + if (src_loc.x < 0.0f || src_loc.y < 0.0f || + src_loc.x > src_dim.x || src_loc.y > src_dim.y) + write_imagef(dst, dst_loc, 0.5f); + else + write_imagef(dst, dst_loc, read_imagef(src, sampler, src_loc)); +} +@end verbatim + +@item +Blend two inputs together, with the amount of each input used varying +with the index counter. +@verbatim +__kernel void blend_images(__write_only image2d_t dst, + unsigned int index, + __read_only image2d_t src1, + __read_only image2d_t src2) +{ + const sampler_t sampler = (CLK_NORMALIZED_COORDS_FALSE | + CLK_FILTER_LINEAR); + + float blend = (cos((float)index / 50.0f) + 1.0f) / 2.0f; + + int2 dst_loc = (int2)(get_global_id(0), get_global_id(1)); + int2 src1_loc = dst_loc * get_image_dim(src1) / get_image_dim(dst); + int2 src2_loc = dst_loc * get_image_dim(src2) / get_image_dim(dst); + + float4 val1 = read_imagef(src1, sampler, src1_loc); + float4 val2 = read_imagef(src2, sampler, src2_loc); + + write_imagef(dst, dst_loc, val1 * blend + val2 * (1.0f - blend)); +} +@end verbatim + +@end itemize + @section roberts_opencl Apply the Roberts cross operator (@url{https://en.wikipedia.org/wiki/Roberts_cross}) to input video stream. @@ -19738,79 +21885,235 @@ Possible value are: @item bt2020 @end table -Default is same as input. +Default is same as input. + +@end table + +@subsection Example + +@itemize +@item +Convert HDR(PQ/HLG) video to bt2020-transfer-characteristic p010 format using linear operator. +@example +-i INPUT -vf "format=p010,hwupload,tonemap_opencl=t=bt2020:tonemap=linear:format=p010,hwdownload,format=p010" OUTPUT +@end example +@end itemize + +@section unsharp_opencl + +Sharpen or blur the input video. + +It accepts the following parameters: + +@table @option +@item luma_msize_x, lx +Set the luma matrix horizontal size. +Range is @code{[1, 23]} and default value is @code{5}. + +@item luma_msize_y, ly +Set the luma matrix vertical size. +Range is @code{[1, 23]} and default value is @code{5}. + +@item luma_amount, la +Set the luma effect strength. +Range is @code{[-10, 10]} and default value is @code{1.0}. + +Negative values will blur the input video, while positive values will +sharpen it, a value of zero will disable the effect. + +@item chroma_msize_x, cx +Set the chroma matrix horizontal size. +Range is @code{[1, 23]} and default value is @code{5}. + +@item chroma_msize_y, cy +Set the chroma matrix vertical size. +Range is @code{[1, 23]} and default value is @code{5}. + +@item chroma_amount, ca +Set the chroma effect strength. +Range is @code{[-10, 10]} and default value is @code{0.0}. + +Negative values will blur the input video, while positive values will +sharpen it, a value of zero will disable the effect. + +@end table + +All parameters are optional and default to the equivalent of the +string '5:5:1.0:5:5:0.0'. + +@subsection Examples + +@itemize +@item +Apply strong luma sharpen effect: +@example +-i INPUT -vf "hwupload, unsharp_opencl=luma_msize_x=7:luma_msize_y=7:luma_amount=2.5, hwdownload" OUTPUT +@end example + +@item +Apply a strong blur of both luma and chroma parameters: +@example +-i INPUT -vf "hwupload, unsharp_opencl=7:7:-2:7:7:-2, hwdownload" OUTPUT +@end example +@end itemize + +@section xfade_opencl + +Cross fade two videos with custom transition effect by using OpenCL. + +It accepts the following options: + +@table @option +@item transition +Set one of possible transition effects. + +@table @option +@item custom +Select custom transition effect, the actual transition description +will be picked from source and kernel options. + +@item fade +@item wipeleft +@item wiperight +@item wipeup +@item wipedown +@item slideleft +@item slideright +@item slideup +@item slidedown + +Default transition is fade. +@end table + +@item source +OpenCL program source file for custom transition. + +@item kernel +Set name of kernel to use for custom transition from program source file. + +@item duration +Set duration of video transition. + +@item offset +Set time of start of transition relative to first video. +@end table + +The program source file must contain a kernel function with the given name, +which will be run once for each plane of the output. Each run on a plane +gets enqueued as a separate 2D global NDRange with one work-item for each +pixel to be generated. The global ID offset for each work-item is therefore +the coordinates of a pixel in the destination image. + +The kernel function needs to take the following arguments: +@itemize +@item +Destination image, @var{__write_only image2d_t}. + +This image will become the output; the kernel should write all of it. + +@item +First Source image, @var{__read_only image2d_t}. +Second Source image, @var{__read_only image2d_t}. + +These are the most recent images on each input. The kernel may read from +them to generate the output, but they can't be written to. + +@item +Transition progress, @var{float}. This value is always between 0 and 1 inclusive. +@end itemize + +Example programs: + +@itemize +@item +Apply dots curtain transition effect: +@verbatim +__kernel void blend_images(__write_only image2d_t dst, + __read_only image2d_t src1, + __read_only image2d_t src2, + float progress) +{ + const sampler_t sampler = (CLK_NORMALIZED_COORDS_FALSE | + CLK_FILTER_LINEAR); + int2 p = (int2)(get_global_id(0), get_global_id(1)); + float2 rp = (float2)(get_global_id(0), get_global_id(1)); + float2 dim = (float2)(get_image_dim(src1).x, get_image_dim(src1).y); + rp = rp / dim; + + float2 dots = (float2)(20.0, 20.0); + float2 center = (float2)(0,0); + float2 unused; -@end table + float4 val1 = read_imagef(src1, sampler, p); + float4 val2 = read_imagef(src2, sampler, p); + bool next = distance(fract(rp * dots, &unused), (float2)(0.5, 0.5)) < (progress / distance(rp, center)); -@subsection Example + write_imagef(dst, p, next ? val1 : val2); +} +@end verbatim -@itemize -@item -Convert HDR(PQ/HLG) video to bt2020-transfer-characteristic p010 format using linear operator. -@example --i INPUT -vf "format=p010,hwupload,tonemap_opencl=t=bt2020:tonemap=linear:format=p010,hwdownload,format=p010" OUTPUT -@end example @end itemize -@section unsharp_opencl +@c man end OPENCL VIDEO FILTERS -Sharpen or blur the input video. +@chapter VAAPI Video Filters +@c man begin VAAPI VIDEO FILTERS + +VAAPI Video filters are usually used with VAAPI decoder and VAAPI encoder. Below is a description of VAAPI video filters. + +To enable compilation of these filters you need to configure FFmpeg with +@code{--enable-vaapi}. + +To use vaapi filters, you need to setup the vaapi device correctly. For more information, please read @url{https://trac.ffmpeg.org/wiki/Hardware/VAAPI} + +@section tonemap_vaapi + +Perform HDR(High Dynamic Range) to SDR(Standard Dynamic Range) conversion with tone-mapping. +It maps the dynamic range of HDR10 content to the SDR content. +It currently only accepts HDR10 as input. It accepts the following parameters: @table @option -@item luma_msize_x, lx -Set the luma matrix horizontal size. -Range is @code{[1, 23]} and default value is @code{5}. +@item format +Specify the output pixel format. -@item luma_msize_y, ly -Set the luma matrix vertical size. -Range is @code{[1, 23]} and default value is @code{5}. +Currently supported formats are: +@table @var +@item p010 +@item nv12 +@end table -@item luma_amount, la -Set the luma effect strength. -Range is @code{[-10, 10]} and default value is @code{1.0}. +Default is nv12. -Negative values will blur the input video, while positive values will -sharpen it, a value of zero will disable the effect. +@item primaries, p +Set the output color primaries. -@item chroma_msize_x, cx -Set the chroma matrix horizontal size. -Range is @code{[1, 23]} and default value is @code{5}. +Default is same as input. -@item chroma_msize_y, cy -Set the chroma matrix vertical size. -Range is @code{[1, 23]} and default value is @code{5}. +@item transfer, t +Set the output transfer characteristics. -@item chroma_amount, ca -Set the chroma effect strength. -Range is @code{[-10, 10]} and default value is @code{0.0}. +Default is bt709. -Negative values will blur the input video, while positive values will -sharpen it, a value of zero will disable the effect. +@item matrix, m +Set the output colorspace matrix. -@end table +Default is same as input. -All parameters are optional and default to the equivalent of the -string '5:5:1.0:5:5:0.0'. +@end table -@subsection Examples +@subsection Example @itemize @item -Apply strong luma sharpen effect: -@example --i INPUT -vf "hwupload, unsharp_opencl=luma_msize_x=7:luma_msize_y=7:luma_amount=2.5, hwdownload" OUTPUT -@end example - -@item -Apply a strong blur of both luma and chroma parameters: +Convert HDR(HDR10) video to bt2020-transfer-characteristic p010 format @example --i INPUT -vf "hwupload, unsharp_opencl=7:7:-2:7:7:-2, hwdownload" OUTPUT +tonemap_vaapi=format=p010:t=bt2020-10 @end example @end itemize -@c man end OPENCL VIDEO FILTERS +@c man end VAAPI VIDEO FILTERS @chapter Video Sources @c man begin VIDEO SOURCES @@ -19854,9 +22157,9 @@ Specify the frame rate expected for the video stream. The sample (pixel) aspect ratio of the input video. @item sws_param -Specify the optional parameters to be used for the scale filter which -is automatically inserted when an input change is detected in the -input size or format. +This option is deprecated and ignored. Prepend @code{sws_flags=@var{flags};} +to the filtergraph description to specify swscale flags for automatically +inserted scalers. See @ref{Filtergraph syntax}. @item hw_frames_ctx When using a hardware pixel format, this should be a reference to an @@ -19881,7 +22184,7 @@ buffer=size=320x240:pixfmt=6:time_base=1/24:pixel_aspect=1/1 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}[:@var{sws_param}] +@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 @@ -20058,6 +22361,33 @@ need for a nullsrc video source. @end itemize +@section gradients +Generate several gradients. + +@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 "640x480". + +@item rate, r +Set frame rate, expressed as number of frames per second. Default +value is "25". + +@item c0, c1, c2, c3, c4, c5, c6, c7 +Set 8 colors. Default values for colors is to pick random one. + +@item x0, y0, y0, y1 +Set gradient line source and destination points. If negative or out of range, random ones +are picked. + +@item nb_colors, n +Set number of colors to use at once. Allowed range is from 2 to 8. Default value is 2. + +@item seed +Set seed for picking gradient line points. +@end table + + @section mandelbrot Generate a Mandelbrot set fractal, and progressively zoom towards the @@ -20172,6 +22502,9 @@ Set the number or the name of the test to perform. Supported tests are: @item ring2 @item all +@item max_frames, m +Set the maximum number of frames generated for each test, default value is 30. + @end table Default value is "all", which will cycle through the list of all tests. @@ -20567,6 +22900,31 @@ __kernel void sierpinski_carpet(__write_only image2d_t dst, @end itemize +@section sierpinski + +Generate a Sierpinski carpet/triangle fractal, and randomly pan around. + +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 "640x480". + +@item rate, r +Set frame rate, expressed as number of frames per second. Default +value is "25". + +@item seed +Set seed which is used for random panning. + +@item jump +Set max jump for single pan destination. Allowed range is from 1 to 10000. + +@item type +Set fractal type, can be default @code{carpet} or @code{triangle}. +@end table + @c man end VIDEO SOURCES @chapter Video Sinks @@ -20622,6 +22980,15 @@ draw channels. Unrecognized or missing colors will be replaced by white color. @end table +@section adrawgraph +Draw a graph using input audio metadata. + +See @ref{drawgraph} + +@section agraphmonitor + +See @ref{graphmonitor}. + @section ahistogram Convert input audio to a video output, displaying the volume histogram. @@ -20742,7 +23109,7 @@ Convert input audio to a video output, representing the audio vector scope. The filter is used to measure the difference between channels of stereo -audio stream. A monoaural signal, consisting of identical left and right +audio stream. A monaural signal, consisting of identical left and right signal, results in straight vertical line. Any stereo separation is visible as a deviation from this line, creating a Lissajous figure. If the straight (or deviation from it) but horizontal line appears this @@ -20983,111 +23350,6 @@ This filter supports the following commands: Close the current segment and step to the next one @end table -@section drawgraph, adrawgraph - -Draw a graph using input video or audio metadata. - -It accepts the following parameters: - -@table @option -@item m1 -Set 1st frame metadata key from which metadata values will be used to draw a graph. - -@item fg1 -Set 1st foreground color expression. - -@item m2 -Set 2nd frame metadata key from which metadata values will be used to draw a graph. - -@item fg2 -Set 2nd foreground color expression. - -@item m3 -Set 3rd frame metadata key from which metadata values will be used to draw a graph. - -@item fg3 -Set 3rd foreground color expression. - -@item m4 -Set 4th frame metadata key from which metadata values will be used to draw a graph. - -@item fg4 -Set 4th foreground color expression. - -@item min -Set minimal value of metadata value. - -@item max -Set maximal value of metadata value. - -@item bg -Set graph background color. Default is white. - -@item mode -Set graph mode. - -Available values for mode is: -@table @samp -@item bar -@item dot -@item line -@end table - -Default is @code{line}. - -@item slide -Set slide mode. - -Available values for slide is: -@table @samp -@item frame -Draw new frame when right border is reached. - -@item replace -Replace old columns with new ones. - -@item scroll -Scroll from right to left. - -@item rscroll -Scroll from left to right. - -@item picture -Draw single picture. -@end table - -Default is @code{frame}. - -@item size -Set size of graph video. For the syntax of this option, check the -@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}. -The default value is @code{900x256}. - -The foreground color expressions can use the following variables: -@table @option -@item MIN -Minimal value of metadata value. - -@item MAX -Maximal value of metadata value. - -@item VAL -Current metadata key value. -@end table - -The color is defined as 0xAABBGGRR. -@end table - -Example using metadata from @ref{signalstats} filter: -@example -signalstats,drawgraph=lavfi.signalstats.YAVG:min=0:max=255 -@end example - -Example using metadata from @ref{ebur128} filter: -@example -ebur128=metadata=1,adrawgraph=lavfi.r128.M:min=-120:max=5 -@end example - @anchor{ebur128} @section ebur128 @@ -21252,6 +23514,21 @@ These filters accept the following options: @table @option @item nb_inputs, n Set the number of different inputs, it is 2 by default. + +@item duration +How to determine the end-of-stream. + +@table @option +@item longest +The duration of the longest input. (default) + +@item shortest +The duration of the shortest input. + +@item first +The duration of the first input. +@end table + @end table @subsection Examples @@ -21337,6 +23614,10 @@ Values are interpreted as floats, returns true if metadata value is greater than @item expr Values are interpreted as floats, returns true if expression from option @code{expr} evaluates to true. + +@item ends_with +Values are interpreted as strings, returns true if metadata value ends with +the @code{value} option string. @end table @item expr @@ -21358,6 +23639,9 @@ plain filename any writable url can be specified. Filename ``-'' is a shorthand for standard output. If @code{file} option is not set, output is written to the log with AV_LOG_INFO loglevel. +@item direct +Reduces buffering in print mode when output is written to a URL set using @var{file}. + @end table @subsection Examples @@ -21725,6 +24009,38 @@ The command is sent when the current frame timestamp leaves the specified interval. In other words, the command is sent when the previous frame timestamp was in the given interval, and the current is not. + +@item expr +The command @var{ARG} is interpreted as expression and result of +expression is passed as @var{ARG}. + +The expression is evaluated through the eval API and can contain the following +constants: + +@table @option +@item POS +Original position in the file of the frame, or undefined if undefined +for the current frame. + +@item PTS +The presentation timestamp in input. + +@item N +The count of the input frame for video or audio, starting from 0. + +@item T +The time in seconds of the current frame. + +@item TS +The start time in seconds of the current command interval. + +@item TE +The end time in seconds of the current command interval. + +@item TI +The interpolated time of the current command interval, TI = (T - TS) / (TE - TS). +@end table + @end table If @var{FLAGS} is not specified, a default value of @code{[enter]} is @@ -22148,8 +24464,9 @@ implemented with custom @var{basefreq} and @var{endfreq}, use @var{axisfile} option instead. @item font -Specify fontconfig pattern. This has lower priority than @var{fontfile}. -The : in the pattern may be replaced by | to avoid unnecessary escaping. +Specify fontconfig pattern. This has lower priority than @var{fontfile}. The +@code{:} in the pattern may be replaced by @code{|} to avoid unnecessary +escaping. @item fontcolor Specify font color expression. This is arithmetic expression that should return @@ -23105,7 +25422,7 @@ in @file{libavutil/frame.h}. For example, to choose @section spectrumsynth -Sythesize audio from 2 input video spectrums, first input stream represents +Synthesize audio from 2 input video spectrums, first input stream represents magnitude across time and second represents phase across time. The filter will transform from frequency domain as displayed in videos back to time domain as presented in audio output. diff --git a/doc/formats.texi b/doc/formats.texi index 729c77b01d9..fc80ce1d2b4 100644 --- a/doc/formats.texi +++ b/doc/formats.texi @@ -27,6 +27,10 @@ stream information. A higher value will enable detecting more information in case it is dispersed into the stream, but will increase latency. Must be an integer not lesser than 32. It is 5000000 by default. +@item max_probe_packets @var{integer} (@emph{input}) +Set the maximum number of buffered packets when probing a codec. +Default is 2500 packets. + @item packetsize @var{integer} (@emph{output}) Set packet size. @@ -139,7 +143,7 @@ Consider things that a sane encoder should not do as an error. @item max_interleave_delta @var{integer} (@emph{output}) Set maximum buffering duration for interleaving. The duration is -expressed in microseconds, and defaults to 1000000 (1 second). +expressed in microseconds, and defaults to 10000000 (10 seconds). To ensure all the streams are interleaved correctly, libavformat will wait until it has at least one packet for each stream before actually diff --git a/doc/general.texi b/doc/general.texi index 3c0c8034490..9b0ee967528 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -27,29 +27,41 @@ enable it. @section AMD AMF/VCE -FFmpeg can use the AMD Advanced Media Framework library under Windows -for accelerated H.264 and HEVC encoding on hardware with Video Coding Engine (VCE). +FFmpeg can use the AMD Advanced Media Framework library +for accelerated H.264 and HEVC(only windows) encoding on hardware with Video Coding Engine (VCE). -To enable support you must obtain the AMF framework header files from +To enable support you must obtain the AMF framework header files(version 1.4.9+) from @url{https://github.com/GPUOpen-LibrariesAndSDKs/AMF.git}. Create an @code{AMF/} directory in the system include path. Copy the contents of @code{AMF/amf/public/include/} into that directory. Then configure FFmpeg with @code{--enable-amf}. +Initialization of amf encoder occurs in this order: +1) trying to initialize through dx11(only windows) +2) trying to initialize through dx9(only windows) +3) trying to initialize through vulkan + +To use h.264(AMD VCE) encoder on linux amdgru-pro version 19.20+ and amf-amdgpu-pro +package(amdgru-pro contains, but does not install automatically) are required. + +This driver can be installed using amdgpu-pro-install script in official amd driver archive. + @section AviSynth FFmpeg can read AviSynth scripts as input. To enable support, pass -@code{--enable-avisynth} to configure. The correct headers are -included in compat/avisynth/, which allows the user to enable support -without needing to search for these headers themselves. +@code{--enable-avisynth} to configure after installing the headers +provided by @url{https://github.com/AviSynth/AviSynthPlus, AviSynth+}. +AviSynth+ can be configured to install only the headers by either +passing @code{-DHEADERS_ONLY:bool=on} to the normal CMake-based build +system, or by using the supplied @code{GNUmakefile}. For Windows, supported AviSynth variants are @url{http://avisynth.nl, AviSynth 2.6 RC1 or higher} for 32-bit builds and @url{http://avisynth.nl/index.php/AviSynth+, AviSynth+ r1718 or higher} for 32-bit and 64-bit builds. -For Linux and OS X, the supported AviSynth variant is -@url{https://github.com/avxsynth/avxsynth, AvxSynth}. +For Linux, macOS, and BSD, the only supported AviSynth variant is +@url{https://github.com/AviSynth/AviSynthPlus, AviSynth+}, starting with version 3.5. @float NOTE In 2016, AviSynth+ added support for building with GCC. However, due to @@ -67,10 +79,11 @@ GCC builds of AviSynth+ without any special flags. @end float @float NOTE -AviSynth and AvxSynth are loaded dynamically. Distributors can build FFmpeg -with @code{--enable-avisynth}, and the binaries will work regardless of the -end user having AviSynth or AvxSynth installed - they'll only need to be -installed to use AviSynth scripts (obviously). +AviSynth(+) is loaded dynamically. Distributors can build FFmpeg +with @code{--enable-avisynth}, and the binaries will work regardless +of the end user having AviSynth installed. If/when an end user +would like to use AviSynth scripts, then they can install AviSynth(+) +and FFmpeg will be able to find and use it to open scripts. @end float @section Chromaprint @@ -243,6 +256,13 @@ FFmpeg can use the OpenJPEG libraries for decoding/encoding J2K videos. Go to instructions. To enable using OpenJPEG in FFmpeg, pass @code{--enable-libopenjpeg} to @file{./configure}. +@section rav1e + +FFmpeg can make use of rav1e (Rust AV1 Encoder) via its C bindings to encode videos. +Go to @url{https://github.com/xiph/rav1e/} and follow the instructions to build +the C library. To enable using rav1e in FFmpeg, pass @code{--enable-librav1e} +to @file{./configure}. + @section TwoLAME FFmpeg can make use of the TwoLAME library for MP2 encoding. @@ -399,6 +419,9 @@ library: @tab Contains header with version and mode info, simplifying playback. @item CRI ADX @tab X @tab X @tab Audio-only format used in console video games. +@item CRI AIX @tab @tab X +@item CRI HCA @tab @tab X + @tab Audio-only format used in console video games. @item Discworld II BMV @tab @tab X @item Interplay C93 @tab @tab X @tab Used in the game Cyberia from Interplay. @@ -554,7 +577,6 @@ library: @item raw aptX @tab X @tab X @item raw aptX HD @tab X @tab X @item raw Chinese AVS video @tab X @tab X -@item raw CRI ADX @tab X @tab X @item raw Dirac @tab X @tab X @item raw DNxHD @tab X @tab X @item raw DTS @tab X @tab X @@ -797,11 +819,13 @@ following image formats are supported: @item Autodesk RLE @tab @tab X @tab fourcc: AASC @item AV1 @tab E @tab E - @tab Supported through external libraries libaom and libdav1d + @tab Supported through external libraries libaom, libdav1d and librav1e @item Avid 1:1 10-bit RGB Packer @tab X @tab X @tab fourcc: AVrp @item AVS (Audio Video Standard) video @tab @tab X @tab Video encoding used by the Creature Shock game. +@item AVS2-P2/IEEE1857.4 @tab E @tab E + @tab Supported through external libraries libxavs2 and libdavs2 @item AYUV @tab X @tab X @tab Microsoft uncompressed packed 4:4:4:4 @item Beam Software VB @tab @tab X @@ -827,6 +851,8 @@ following image formats are supported: @tab Codec used in Delphine Software International games. @item Discworld II BMV Video @tab @tab X @item Canopus Lossless Codec @tab @tab X +@item CDToons @tab @tab X + @tab Codec used in various Broderbund games. @item Cinepak @tab @tab X @item Cirrus Logic AccuPak @tab X @tab X @tab fourcc: CLJR @@ -1057,8 +1083,10 @@ following image formats are supported: @item AAC+ @tab E @tab IX @tab encoding supported through external library libfdk-aac @item AC-3 @tab IX @tab IX +@item ACELP.KELVIN @tab @tab X @item ADPCM 4X Movie @tab @tab X @item APDCM Yamaha AICA @tab @tab X +@item ADPCM Argonaut Games @tab @tab X @item ADPCM CDROM XA @tab @tab X @item ADPCM Creative Technology @tab @tab X @tab 16 -> 4, 8 -> 4, 8 -> 3, 8 -> 2 @@ -1074,10 +1102,14 @@ following image formats are supported: @item ADPCM G.726 @tab X @tab X @item ADPCM IMA AMV @tab @tab X @tab Used in AMV files +@item ADPCM IMA Cunning Developments @tab @tab X @item ADPCM IMA Electronic Arts EACS @tab @tab X @item ADPCM IMA Electronic Arts SEAD @tab @tab X @item ADPCM IMA Funcom @tab @tab X +@item ADPCM IMA High Voltage Software ALP @tab @tab X @item ADPCM IMA QuickTime @tab X @tab X +@item ADPCM IMA Simon & Schuster Interactive @tab X @tab X +@item ADPCM IMA Ubisoft APM @tab @tab X @item ADPCM IMA Loki SDL MJPEG @tab @tab X @item ADPCM IMA WAV @tab X @tab X @item ADPCM IMA Westwood @tab @tab X @@ -1107,6 +1139,7 @@ following image formats are supported: @item ADPCM Westwood Studios IMA @tab @tab X @tab Used in Westwood Studios games like Command and Conquer. @item ADPCM Yamaha @tab X @tab X +@item ADPCM Zork @tab @tab X @item AMR-NB @tab E @tab X @tab encoding supported through external library libopencore-amrnb @item AMR-WB @tab E @tab X @@ -1128,6 +1161,7 @@ following image formats are supported: @tab decoding supported through external library libcelt @item codec2 @tab E @tab E @tab en/decoding supported through external library libcodec2 +@item CRI HCA @tab @tab X @item Delphine Software International CIN audio @tab @tab X @tab Codec used in Delphine Software International games. @item Digital Speech Standard - Standard Play mode (DSS SP) @tab @tab X @@ -1137,6 +1171,7 @@ following image formats are supported: @item DCA (DTS Coherent Acoustics) @tab X @tab X @tab supported extensions: XCh, XXCH, X96, XBR, XLL, LBR (partially) @item Dolby E @tab @tab X +@item DPCM Gremlin @tab @tab X @item DPCM id RoQ @tab X @tab X @tab Used in Quake III, Jedi Knight 2 and other computer games. @item DPCM Interplay @tab @tab X @@ -1148,6 +1183,7 @@ following image formats are supported: @item DPCM Sol @tab @tab X @item DPCM Xan @tab @tab X @tab Used in Origin's Wing Commander IV AVI files. +@item DPCM Xilam DERF @tab @tab X @item DSD (Direct Stream Digital), least significant bit first @tab @tab X @item DSD (Direct Stream Digital), most significant bit first @tab @tab X @item DSD (Direct Stream Digital), least significant bit first, planar @tab @tab X @@ -1214,7 +1250,6 @@ following image formats are supported: @item PCM unsigned 24-bit little-endian @tab X @tab X @item PCM unsigned 32-bit big-endian @tab X @tab X @item PCM unsigned 32-bit little-endian @tab X @tab X -@item PCM Zork @tab @tab X @item QCELP / PureVoice @tab @tab X @item QDesign Music Codec 1 @tab @tab X @item QDesign Music Codec 2 @tab @tab X @@ -1305,6 +1340,7 @@ performance on systems without hardware floating point support). @multitable @columnfractions .4 .1 @item Name @tab Support +@item AMQP @tab E @item file @tab X @item FTP @tab X @item Gopher @tab X @@ -1329,6 +1365,7 @@ performance on systems without hardware floating point support). @item TCP @tab X @item TLS @tab X @item UDP @tab X +@item ZMQ @tab E @end multitable @code{X} means that the protocol is supported. diff --git a/doc/indevs.texi b/doc/indevs.texi index 14595774f37..6f5afaf3440 100644 --- a/doc/indevs.texi +++ b/doc/indevs.texi @@ -277,8 +277,8 @@ audio track. @item list_devices If set to @option{true}, print a list of devices and exit. -Defaults to @option{false}. Alternatively you can use the @code{-sources} -option of ffmpeg to list the available input devices. +Defaults to @option{false}. This option is deprecated, please use the +@code{-sources} option of ffmpeg to list the available input devices. @item list_formats If set to @option{true}, print a list of supported formats and exit. @@ -292,11 +292,6 @@ as @option{pal} (3 letters). Default behavior is autodetection of the input video format, if the hardware supports it. -@item bm_v210 -This is a deprecated option, you can use @option{raw_format} instead. -If set to @samp{1}, video is captured in 10 bit v210 instead -of uyvy422. Not all Blackmagic devices support this option. - @item raw_format Set the pixel format of the captured video. Available values are: @@ -395,6 +390,14 @@ Either sync could go wrong by 1 frame or in a rarer case @option{timestamp_align} seconds. Defaults to @samp{0}. +@item wait_for_tc (@emph{bool}) +Drop frames till a frame with timecode is received. Sometimes serial timecode +isn't received with the first input frame. If that happens, the stored stream +timecode will be inaccurate. If this option is set to @option{true}, input frames +are dropped till a frame with timecode is received. +Option @var{timecode_format} must be specified. +Defaults to @option{false}. + @end table @subsection Examples @@ -404,7 +407,7 @@ Defaults to @samp{0}. @item List input devices: @example -ffmpeg -f decklink -list_devices 1 -i dummy +ffmpeg -sources decklink @end example @item @@ -422,7 +425,7 @@ ffmpeg -format_code Hi50 -f decklink -i 'Intensity Pro' -c:a copy -c:v copy outp @item Capture video clip at 1080i50 10 bit: @example -ffmpeg -bm_v210 1 -format_code Hi50 -f decklink -i 'UltraStudio Mini Recorder' -c:a copy -c:v copy output.avi +ffmpeg -raw_format yuv422p10 -format_code Hi50 -f decklink -i 'UltraStudio Mini Recorder' -c:a copy -c:v copy output.avi @end example @item @@ -1524,7 +1527,7 @@ ffmpeg -f x11grab -follow_mouse centered -show_region 1 -framerate 25 -video_siz @end example @item video_size -Set the video frame size. Default value is @code{vga}. +Set the video frame size. Default is the full desktop. @item grab_x @item grab_y diff --git a/doc/multithreading.txt b/doc/multithreading.txt index 83849deacca..4f645dc1479 100644 --- a/doc/multithreading.txt +++ b/doc/multithreading.txt @@ -51,16 +51,14 @@ the decode process starts. Call ff_thread_finish_setup() afterwards. If some code can't be moved, have update_thread_context() run it in the next thread. -If the codec allocates writable tables in its init(), add an init_thread_copy() -which re-allocates them for other threads. - Add AV_CODEC_CAP_FRAME_THREADS to the codec capabilities. There will be very little 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 AVCodecInternal.allocate_progress. The +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(). -Otherwise leave it at zero and decode directly into the user-supplied frames. +Otherwise decode directly into the user-supplied frames. Call ff_thread_report_progress() after some part of the current picture has decoded. A good place to put this is where draw_horiz_band() is called - add this if it isn't diff --git a/doc/muxers.texi b/doc/muxers.texi index b109297963d..c598abbe664 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -105,12 +105,14 @@ It takes a single signed native-endian 16-bit raw audio stream of at most 2 chan @table @option @item silence_threshold -Threshold for detecting silence, ranges from -1 to 32767. -1 disables silence detection and -is required for use with the AcoustID service. Default is -1. +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 2 requires that silence -detection be enabled. Default is 1. +Version of algorithm to fingerprint with. Range is 0 to 4. +Version 3 enables silence detection. Default is 1. @item fp_format Format to output the fingerprint as. Accepts the following options: @@ -234,8 +236,10 @@ This is a deprecated option to set the segment length in microseconds, use @var{ @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 frag_duration @var{duration} +Set the length in seconds of fragments within segments (fractional value can 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} @@ -275,6 +279,15 @@ of the adaptation sets and a,b,c,d and e are the indices of the mapped streams. To map all video (or audio) streams to an AdaptationSet, "v" (or "a") can be used as stream identifier instead of IDs. When no assignment is defined, this defaults to an AdaptationSet for each stream. + +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} @@ -320,9 +333,37 @@ This option will also try to comply with the above open spec, till Apple's spec Applicable only when @var{streaming} and @var{hls_playlist} options are enabled. This is an experimental feature. +@item ldash @var{ldash} +Enable Low-latency Dash by constraining the presence and values of some elements. + @item master_m3u8_publish_rate @var{master_m3u8_publish_rate} Publish master playlist repeatedly every after specified number of segment intervals. +@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. + +@item mpd_profile @var{mpd_profile} +Set one or more manifest profiles. + +@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. + +@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. + +@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. + +@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. + @end table @anchor{framecrc} @@ -607,6 +648,9 @@ Set the starting sequence numbers according to @var{start_number} option value. @item epoch The start number will be the seconds since epoch (1970-01-01 00:00:00) +@item epoch_us +The start number will be the microseconds since epoch (1970-01-01 00:00:00) + @item datetime The start number will be based on the current date/time as YYYYmmddHHMMSS. e.g. 20161231235759. @@ -803,6 +847,9 @@ fmp4 files may be used in HLS version 7 and above. @item hls_fmp4_init_filename @var{filename} Set filename to the fragment files header file, default filename is @file{init.mp4}. +@item hls_fmp4_init_resend +Resend init file after m3u8 file refresh every time, default is @var{0}. + When @code{var_stream_map} is set with two or more variant streams, the @var{filename} pattern must contain the string "%v", this string specifies the position of variant stream index in the generated init file names. @@ -898,8 +945,8 @@ serving up segments can be configured to reject requests to *.tmp to prevent acc before they have been added to the m3u8 playlist. This flag also affects how m3u8 playlist files are created. If this flag is set, all playlist files will written into temporary file and renamed after they are complete, similarly as segments are handled. But playlists with @code{file} protocol and with type (@code{hls_playlist_type}) other than @code{vod} -are always written into temporary file regardles of this flag. Master playlist files (@code{master_pl_name}), if any, with @code{file} protocol, -are always written into temporary file regardles of this flag if @code{master_pl_publish_rate} value is other than zero. +are always written into temporary file regardless of this flag. Master playlist files (@code{master_pl_name}), if any, with @code{file} protocol, +are always written into temporary file regardless of this flag if @code{master_pl_publish_rate} value is other than zero. @end table @@ -1029,6 +1076,21 @@ have and language is named ENG, the other audio language is named CHN. By default, a single hls variant containing all the encoded streams is created. +@example +ffmpeg -y -i input_with_subtitle.mkv \ + -b:v:0 5250k -c:v h264 -pix_fmt yuv420p -profile:v main -level 4.1 \ + -b:a:0 256k \ + -c:s webvtt -c:a mp2 -ar 48000 -ac 2 -map 0:v -map 0:a:0 -map 0:s:0 \ + -f hls -var_stream_map "v:0,a:0,s:0,sgroup:subtitle" \ + -master_pl_name master.m3u8 -t 300 -hls_time 10 -hls_init_time 4 -hls_list_size \ + 10 -master_pl_publish_rate 10 -hls_flags \ + delete_segments+discont_start+split_by_time ./tmp/video.m3u8 +@end example + +This example adds @code{#EXT-X-MEDIA} tag with @code{TYPE=SUBTITLES} in +the master playlist with webvtt subtitle group name 'subtitle'. Please make sure +the input file has one text subtitle stream at least. + @item cc_stream_map Map string which specifies different closed captions groups and their attributes. The closed captions stream groups are separated by space. @@ -1163,6 +1225,37 @@ The pattern "img%%-%d.jpg" will specify a sequence of filenames of the 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 +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. + +@subsection Options + +@table @option +@item frame_pts +If set to 1, expand the filename with pts from pkt->pts. +Default value is 0. + +@item start_number +Start the sequence from the specified number. Default value is 1. + +@item update +If set to 1, the filename will always be interpreted as just a +filename, not a pattern, and the corresponding file will be continuously +overwritten with new images. Default value is 0. + +@item strftime +If set to 1, expand the filename with date and time information from +@code{strftime()}. Default value is 0. + +@item protocol_opts @var{options_list} +Set protocol options as a :-separated list of key=value parameters. Values +containing the @code{:} special character must be escaped. + +@end table + @subsection Examples The following example shows how to use @command{ffmpeg} for creating a @@ -1203,31 +1296,11 @@ You can set the file name with current frame's PTS: ffmpeg -f v4l2 -r 1 -i /dev/video0 -copyts -f image2 -frame_pts true %d.jpg" @end example -@subsection Options - -@table @option -@item frame_pts -If set to 1, expand the filename with pts from pkt->pts. -Default value is 0. - -@item start_number -Start the sequence from the specified number. Default value is 1. - -@item update -If set to 1, the filename will always be interpreted as just a -filename, not a pattern, and the corresponding file will be continuously -overwritten with new images. Default value is 0. - -@item strftime -If set to 1, expand the filename with date and time information from -@code{strftime()}. Default value is 0. -@end table - -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 -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. +A more complex example is to publish contents of your desktop directly to a +WebDAV server every second: +@example +ffmpeg -f x11grab -framerate 1 -i :0.0 -q:v 6 -update 1 -protocol_opts method=PUT http://example.com/desktop.jpg +@end example @section matroska @@ -1241,7 +1314,8 @@ The recognized metadata settings in this muxer are: @table @option @item title -Set title name provided to a single track. +Set title name provided to a single track. This gets mapped to +the FileDescription element for a stream written as attachment. @item language Specify the language of the track in the Matroska languages form. @@ -1308,11 +1382,31 @@ index at the beginning of the file. If this option is set to a non-zero value, the muxer will reserve a given amount of space in the file header and then try to write the cues there when the muxing -finishes. If the available space does not suffice, muxing will fail. A safe size -for most use cases should be about 50kB per hour of video. +finishes. If the reserved space does not suffice, no Cues will be written, the +file will be finalized and writing the trailer will return an error. +A safe size for most use cases should be about 50kB per hour of video. Note that cues are only written if the output is seekable and this option will have no effect if it is not. +@item default_mode +This option controls how the FlagDefault of the output tracks will be set. +It influences which tracks players should play by default. The default mode +is @samp{infer}. +@table @samp +@item infer +In this mode, for each type of track (audio, video or subtitle), if there is +a track with disposition default of this type, then the first such track +(i.e. the one with the lowest index) will be marked as default; if no such +track exists, the first track of this type will be marked as default instead +(if existing). This ensures that the default flag is set in a sensible way even +if the input originated from containers that lack the concept of default tracks. +@item infer_no_subs +This mode is the same as infer except that if no subtitle track with +disposition default exists, no subtitle track will be marked as default. +@item passthrough +In this mode the FlagDefault is set if and only if the AV_DISPOSITION_DEFAULT +flag is set in the disposition of the corresponding stream. +@end table @end table @anchor{md5} @@ -1462,13 +1556,6 @@ point on IIS with this muxer. Example: ffmpeg -re @var{} -movflags isml+frag_keyframe -f ismv http://server/publishingpoint.isml/Streams(Encoder1) @end example -@subsection Audible AAX - -Audible AAX files are encrypted M4B files, and they can be decrypted by specifying a 4 byte activation secret. -@example -ffmpeg -activation_bytes 1CEB00DA -i test.aax -vn -c:a copy output.mp4 -@end example - @section mp3 The MP3 muxer writes a raw MP3 stream with the following optional features: @@ -1576,11 +1663,14 @@ Advanced Codec Digital HDTV service. @end table @item mpegts_pmt_start_pid @var{integer} -Set the first PID for PMT. Default is @code{0x1000}. Max is @code{0x1f00}. +Set the first PID for PMTs. Default is @code{0x1000}, minimum is @code{0x0020}, +maximum is @code{0x1ffa}. This option has no effect in m2ts mode where the PMT +PID is fixed @code{0x0100}. @item mpegts_start_pid @var{integer} -Set the first PID for data packets. Default is @code{0x0100}. Max is -@code{0x0f00}. +Set the first PID for elementary streams. Default is @code{0x0100}, minimum is +@code{0x0020}, maximum is @code{0x1ffa}. This option has no effect in m2ts mode +where the elementary stream PIDs are fixed. @item mpegts_m2ts_mode @var{boolean} Enable m2ts mode if set to @code{1}. Default value is @code{-1} which @@ -1607,10 +1697,6 @@ Conform to System B (DVB) instead of System A (ATSC). Mark the initial packet of each stream as discontinuity. @end table -@item resend_headers @var{integer} -Reemit PAT/PMT before writing the next packet. This option is deprecated: -use @option{mpegts_flags} instead. - @item mpegts_copyts @var{boolean} Preserve original timestamps, if value is set to @code{1}. Default value is @code{-1}, which results in shifting timestamps so that they start from 0. @@ -1619,14 +1705,16 @@ is @code{-1}, which results in shifting timestamps so that they start from 0. Omit the PES packet length for video packets. Default is @code{1} (true). @item pcr_period @var{integer} -Override the default PCR retransmission time in milliseconds. Ignored if -variable muxrate is selected. Default is @code{20}. +Override the default PCR retransmission time in milliseconds. Default is +@code{-1} which means that the PCR interval will be determined automatically: +20 ms is used for CBR streams, the highest multiple of the frame duration which +is less than 100 ms is used for VBR streams. -@item pat_period @var{double} -Maximum time in seconds between PAT/PMT tables. +@item pat_period @var{duration} +Maximum time in seconds between PAT/PMT tables. Default is @code{0.1}. -@item sdt_period @var{double} -Maximum time in seconds between SDT tables. +@item sdt_period @var{duration} +Maximum time in seconds between SDT tables. Default is @code{0.5}. @item tables_version @var{integer} Set PAT, PMT and SDT version (default @code{0}, valid values are from 0 to 31, inclusively). @@ -2062,6 +2150,53 @@ Specify whether to remove all fragments when finished. Default 0 (do not remove) @end table +@anchor{streamhash} +@section streamhash + +Per stream hash testing format. + +This muxer computes and prints a cryptographic hash of all the input frames, +on a per-stream basis. This can be used for equality checks without having +to do a complete binary comparison. + +By default audio frames are converted to signed 16-bit raw audio and +video frames to raw video before computing the hash, but the output +of explicit conversions to other codecs can also be used. Timestamps +are ignored. It uses the SHA-256 cryptographic hash function by default, +but supports several other algorithms. + +The output of the muxer consists of one line per stream of the form: +@var{streamindex},@var{streamtype},@var{algo}=@var{hash}, where +@var{streamindex} is the index of the mapped stream, @var{streamtype} is a +single character indicating the type of stream, @var{algo} is a short string +representing the hash function used, and @var{hash} is a hexadecimal number +representing the computed hash. + +@table @option +@item hash @var{algorithm} +Use the cryptographic hash function specified by the string @var{algorithm}. +Supported values include @code{MD5}, @code{murmur3}, @code{RIPEMD128}, +@code{RIPEMD160}, @code{RIPEMD256}, @code{RIPEMD320}, @code{SHA160}, +@code{SHA224}, @code{SHA256} (default), @code{SHA512/224}, @code{SHA512/256}, +@code{SHA384}, @code{SHA512}, @code{CRC32} and @code{adler32}. + +@end table + +@subsection Examples + +To compute the SHA-256 hash of the input converted to raw audio and +video, and store it in the file @file{out.sha256}: +@example +ffmpeg -i INPUT -f streamhash out.sha256 +@end example + +To print an MD5 hash to stdout use the command: +@example +ffmpeg -i INPUT -f streamhash -hash md5 - +@end example + +See also the @ref{hash} and @ref{framehash} muxers. + @anchor{fifo} @section fifo diff --git a/doc/outdevs.texi b/doc/outdevs.texi index c96d2d0e43b..60606eb6e76 100644 --- a/doc/outdevs.texi +++ b/doc/outdevs.texi @@ -140,8 +140,8 @@ device with @command{-list_formats 1}. Audio sample rate is always 48 kHz. @item list_devices If set to @option{true}, print a list of devices and exit. -Defaults to @option{false}. Alternatively you can use the @code{-sinks} -option of ffmpeg to list the available output devices. +Defaults to @option{false}. This option is deprecated, please use the +@code{-sinks} option of ffmpeg to list the available output devices. @item list_formats If set to @option{true}, print a list of supported formats and exit. @@ -168,7 +168,7 @@ Defaults to @samp{unset}. @item List output devices: @example -ffmpeg -i test.avi -f decklink -list_devices 1 dummy +ffmpeg -sinks decklink @end example @item @@ -329,6 +329,8 @@ ffmpeg -i INPUT -f pulse "stream name" SDL (Simple DirectMedia Layer) output device. +"sdl2" can be used as alias for "sdl". + This output device allows one to show a video stream in an SDL window. Only one SDL window is allowed per application, so you can have only one instance of this output device in an application. diff --git a/doc/protocols.texi b/doc/protocols.texi index 3e4e7af3d45..7aa758541c6 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -51,6 +51,66 @@ in microseconds. A description of the currently available protocols follows. +@section amqp + +Advanced Message Queueing Protocol (AMQP) version 0-9-1 is a broker based +publish-subscribe communication protocol. + +FFmpeg must be compiled with --enable-librabbitmq to support AMQP. A separate +AMQP broker must also be run. An example open-source AMQP broker is RabbitMQ. + +After starting the broker, an FFmpeg client may stream data to the broker using +the command: + +@example +ffmpeg -re -i input -f mpegts amqp://[[user]:[password]@@]hostname[:port] +@end example + +Where hostname and port (default is 5672) is the address of the broker. The +client may also set a user/password for authentication. The default for both +fields is "guest". + +Muliple subscribers may stream from the broker using the command: +@example +ffplay amqp://[[user]:[password]@@]hostname[:port] +@end example + +In RabbitMQ all data published to the broker flows through a specific exchange, +and each subscribing client has an assigned queue/buffer. When a packet arrives +at an exchange, it may be copied to a client's queue depending on the exchange +and routing_key fields. + +The following options are supported: + +@table @option + +@item exchange +Sets the exchange to use on the broker. RabbitMQ has several predefined +exchanges: "amq.direct" is the default exchange, where the publisher and +subscriber must have a matching routing_key; "amq.fanout" is the same as a +broadcast operation (i.e. the data is forwarded to all queues on the fanout +exchange independent of the routing_key); and "amq.topic" is similar to +"amq.direct", but allows for more complex pattern matching (refer to the RabbitMQ +documentation). + +@item routing_key +Sets the routing key. The default value is "amqp". The routing key is used on +the "amq.direct" and "amq.topic" exchanges to decide whether packets are written +to the queue of a subscriber. + +@item pkt_size +Maximum size of each packet sent/received to the broker. Default is 131072. +Minimum is 4096 and max is any large value (representable by an int). When +receiving packets, this sets an internal buffer size in FFmpeg. It should be +equal to or greater than the size of the published packets to the broker. Otherwise +the received message may be truncated causing decoding errors. + +@item connection_timeout +The timeout in seconds during the initial connection to the broker. The +default value is rw_timeout, or 5 seconds if rw_timeout is not set. + +@end table + @section async Asynchronous data filling wrapper for input stream. @@ -228,6 +288,14 @@ Set timeout in microseconds of socket I/O operations used by the underlying low operation. By default it is set to -1, which means that the timeout is not specified. +@item ftp-user +Set a user to be used for authenticating to the FTP server. This is overridden by the +user in the FTP URL. + +@item ftp-password +Set a password to be used for authenticating to the FTP server. This is overridden by +the password in the FTP URL, or by @option{ftp-anonymous-password} if no user is set. + @item ftp-anonymous-password Password used when login as anonymous user. Typically an e-mail address should be used. @@ -1187,7 +1255,7 @@ options. This protocol accepts the following options. @table @option -@item connect_timeout +@item connect_timeout=@var{milliseconds} Connection timeout; SRT cannot connect for RTT > 1500 msec (2 handshake exchanges) with the default connect timeout of 3 seconds. This option applies to the caller and rendezvous @@ -1218,7 +1286,7 @@ IP Type of Service. Applies to sender only. Default value is 0xB8. @item ipttl=@var{ttl} IP Time To Live. Applies to sender only. Default value is 64. -@item latency +@item latency=@var{microseconds} Timestamp-based Packet Delivery Delay. Used to absorb bursts of missed packet retransmissions. This flag sets both @option{rcvlatency} and @option{peerlatency} @@ -1229,7 +1297,7 @@ when side is sender and @option{rcvlatency} when side is receiver, and the bidirectional stream sending is not supported. -@item listen_timeout +@item listen_timeout=@var{microseconds} Set socket listen timeout. @item maxbw=@var{bytes/seconds} @@ -1274,6 +1342,26 @@ only if @option{pbkeylen} is non-zero. It is used on the receiver only if the received data is encrypted. The configured passphrase cannot be recovered (write-only). +@item enforced_encryption=@var{1|0} +If true, both connection parties must have the same password +set (including empty, that is, with no encryption). If the +password doesn't match or only one side is unencrypted, +the connection is rejected. Default is true. + +@item kmrefreshrate=@var{packets} +The number of packets to be transmitted after which the +encryption key is switched to a new key. Default is -1. +-1 means auto (0x1000000 in srt library). The range for +this option is integers in the 0 - @code{INT_MAX}. + +@item kmpreannounce=@var{packets} +The interval between when a new encryption key is sent and +when switchover occurs. This value also applies to the +subsequent interval between when switchover occurs and +when the old encryption key is decommissioned. Default is -1. +-1 means auto (0x1000 in srt library). The range for +this option is integers in the 0 - @code{INT_MAX}. + @item payload_size=@var{bytes} Sets the maximum declared size of a packet transferred during the single call to the sending function in Live @@ -1289,7 +1377,7 @@ use a bigger maximum frame size, though not greater than @item pkt_size=@var{bytes} Alias for @samp{payload_size}. -@item peerlatency +@item peerlatency=@var{microseconds} The latency value (as described in @option{rcvlatency}) that is set by the sender side as a minimum value for the receiver. @@ -1301,7 +1389,7 @@ Not required on receiver (set to 0), key size obtained from sender in HaiCrypt handshake. Default value is 0. -@item rcvlatency +@item rcvlatency=@var{microseconds} The time that should elapse since the moment when the packet was sent and the moment when it's delivered to the receiver application in the receiving function. @@ -1319,12 +1407,10 @@ Set UDP receive buffer size, expressed in bytes. @item send_buffer_size=@var{bytes} Set UDP send buffer size, expressed in bytes. -@item rw_timeout -Set raise error timeout for read/write optations. - -This option is only relevant in read mode: -if no data arrived in more than this time -interval, raise error. +@item timeout=@var{microseconds} +Set raise error timeouts for read, write and connect operations. Note that the +SRT library has internal timeouts which can be controlled separately, the +value set here is only a cap on those. @item tlpktdrop=@var{1|0} Too-late Packet Drop. When enabled on receiver, it skips @@ -1418,6 +1504,12 @@ the overhead transmission (retransmitted and control packets). file: Set options as for non-live transmission. See @option{messageapi} for further explanations +@item linger=@var{seconds} +The number of seconds that the socket waits for unsent data when closing. +Default is -1. -1 means auto (off with 0 seconds in live mode, on with 180 +seconds in file mode). The range for this option is integers in the +0 - @code{INT_MAX}. + @end table For more information see: @url{https://github.com/Haivision/srt}. @@ -1619,7 +1711,7 @@ The list of supported options follows. @item buffer_size=@var{size} Set the UDP maximum socket buffer size in bytes. This is used to set either the receive or send buffer size, depending on what the socket is used for. -Default is 64KB. See also @var{fifo_size}. +Default is 32 KB for output, 384 KB for input. See also @var{fifo_size}. @item bitrate=@var{bitrate} If set to nonzero, the output will have the specified constant bitrate if the @@ -1728,4 +1820,51 @@ Timeout in ms. Create the Unix socket in listening mode. @end table +@section zmq + +ZeroMQ asynchronous messaging using the libzmq library. + +This library supports unicast streaming to multiple clients without relying on +an external server. + +The required syntax for streaming or connecting to a stream is: +@example +zmq:tcp://ip-address:port +@end example + +Example: +Create a localhost stream on port 5555: +@example +ffmpeg -re -i input -f mpegts zmq:tcp://127.0.0.1:5555 +@end example + +Multiple clients may connect to the stream using: +@example +ffplay zmq:tcp://127.0.0.1:5555 +@end example + +Streaming to multiple clients is implemented using a ZeroMQ Pub-Sub pattern. +The server side binds to a port and publishes data. Clients connect to the +server (via IP address/port) and subscribe to the stream. The order in which +the server and client start generally does not matter. + +ffmpeg must be compiled with the --enable-libzmq option to support +this protocol. + +Options can be set on the @command{ffmpeg}/@command{ffplay} command +line. The following options are supported: + +@table @option + +@item pkt_size +Forces the maximum packet size for sending/receiving data. The default value is +131,072 bytes. On the server side, this sets the maximum size of sent packets +via ZeroMQ. On the clients, it sets an internal buffer size for receiving +packets. Note that pkt_size on the clients should be equal to or greater than +pkt_size on the server. Otherwise the received message may be truncated causing +decoding errors. + +@end table + + @c man end PROTOCOLS diff --git a/doc/utils.texi b/doc/utils.texi index d55dd315c34..e7a9b40b354 100644 --- a/doc/utils.texi +++ b/doc/utils.texi @@ -126,6 +126,15 @@ The following examples are all valid time duration: @item 55 55 seconds +@item 0.2 +0.2 seconds + +@item 200ms +200 milliseconds, that's 0.2s + +@item 200000us +200000 microseconds, that's 0.2s + @item 12:03:45 12 hours, 03 minutes and 45 seconds @@ -704,6 +713,8 @@ FL+FR+FC+LFE+BL+BR+FLC+FRC FL+FR+FC+LFE+FLC+FRC+SL+SR @item octagonal FL+FR+FC+BL+BR+BC+SL+SR +@item hexadecagonal +FL+FR+FC+BL+BR+BC+SL+SR+WL+WR+TBL+TBR+TBC+TFC+TFL+TFR @item downmix DL+DR @end table @@ -920,6 +931,9 @@ corresponding input value will be returned. @item round(expr) Round the value of expression @var{expr} to the nearest integer. For example, "round(1.5)" is "2.0". +@item sgn(x) +Compute sign of @var{x}. + @item sin(x) Compute sine of @var{x}. diff --git a/docker-build.sh b/docker-build.sh index b99ed8df840..f6e35ed36a0 100755 --- a/docker-build.sh +++ b/docker-build.sh @@ -8,11 +8,22 @@ set -o xtrace ARCHIVE_ADDR=http://archive.ubuntu.com/ubuntu/ PORTS_ADDR=http://ports.ubuntu.com/ -# Prepare HWA headers, libs and drivers for x86_64-linux-gnu -prepare_hwa_amd64() { +# Prepare extra headers, libs and drivers for x86_64-linux-gnu +prepare_extra_amd64() { + # Download and install zimg for zscale filter + pushd ${SOURCE_DIR} + git clone --depth=1 https://github.com/sekrit-twc/zimg + pushd zimg + ./autogen.sh + ./configure --prefix=${TARGET_DIR} + make -j $(nproc) && make install && make install DESTDIR=${SOURCE_DIR}/zimg + echo "zimg${TARGET_DIR}/lib/libzimg.so* usr/lib/jellyfin-ffmpeg/lib" >> ${SOURCE_DIR}/debian/jellyfin-ffmpeg.install + popd + popd + # Download and install the nvidia headers pushd ${SOURCE_DIR} - git clone --depth=1 https://git.videolan.org/git/ffmpeg/nv-codec-headers.git + git clone -b n9.0.18.3 --depth=1 https://git.videolan.org/git/ffmpeg/nv-codec-headers.git pushd nv-codec-headers make make install @@ -53,7 +64,7 @@ prepare_hwa_amd64() { # Download and install libva pushd ${SOURCE_DIR} - git clone -b v2.6-branch --depth=1 https://github.com/intel/libva + git clone -b v2.8-branch --depth=1 https://github.com/intel/libva pushd libva sed -i 's|getenv("LIBVA_DRIVERS_PATH")|"/usr/lib/jellyfin-ffmpeg/lib/dri:/usr/lib/x86_64-linux-gnu/dri:/usr/lib/dri:/usr/local/lib/dri"|g' va/va.c sed -i 's|getenv("LIBVA_DRIVER_NAME")|NULL|g' va/va.c @@ -78,26 +89,40 @@ prepare_hwa_amd64() { popd popd - # Uncomment for non-free QSV # Download and install gmmlib - #pushd ${SOURCE_DIR} - #git clone -b intel-gmmlib-19.3.4.x --depth=1 https://github.com/intel/gmmlib - #pushd gmmlib - #mkdir build && pushd build - #cmake -DCMAKE_INSTALL_PREFIX=${TARGET_DIR} .. - #make -j$(nproc) && make install && make install DESTDIR=${SOURCE_DIR}/intel - #make install - #echo "intel${TARGET_DIR}/lib/libigdgmm.so* usr/lib/jellyfin-ffmpeg/lib" >> ${SOURCE_DIR}/debian/jellyfin-ffmpeg.install - #popd - #popd - #popd + pushd ${SOURCE_DIR} + git clone -b intel-gmmlib-20.2.2 --depth=1 https://github.com/intel/gmmlib + pushd gmmlib + mkdir build && pushd build + cmake -DCMAKE_INSTALL_PREFIX=${TARGET_DIR} .. + make -j$(nproc) && make install && make install DESTDIR=${SOURCE_DIR}/intel + make install + echo "intel${TARGET_DIR}/lib/libigdgmm.so* usr/lib/jellyfin-ffmpeg/lib" >> ${SOURCE_DIR}/debian/jellyfin-ffmpeg.install + popd + popd + popd - # Uncomment for non-free QSV + # Download and install MediaSDK + pushd ${SOURCE_DIR} + git clone -b intel-mediasdk-20.2 --depth=1 https://github.com/Intel-Media-SDK/MediaSDK + pushd MediaSDK + sed -i 's|MFX_PLUGINS_CONF_DIR "/plugins.cfg"|"/usr/lib/jellyfin-ffmpeg/lib/mfx/plugins.cfg"|g' api/mfx_dispatch/linux/mfxloader.cpp + mkdir build && pushd build + cmake -DCMAKE_INSTALL_PREFIX=${TARGET_DIR} .. + make -j$(nproc) && make install && make install DESTDIR=${SOURCE_DIR}/intel + echo "intel${TARGET_DIR}/lib/libmfx* usr/lib/jellyfin-ffmpeg/lib" >> ${SOURCE_DIR}/debian/jellyfin-ffmpeg.install + echo "intel${TARGET_DIR}/lib/mfx/*.so usr/lib/jellyfin-ffmpeg/lib/mfx" >> ${SOURCE_DIR}/debian/jellyfin-ffmpeg.install + echo "intel${TARGET_DIR}/share/mfx/plugins.cfg usr/lib/jellyfin-ffmpeg/lib/mfx" >> ${SOURCE_DIR}/debian/jellyfin-ffmpeg.install + popd + popd + popd + + # Uncomment for non-free QSV driver # Download and install media-driver # Full Feature Build: ENABLE_KERNELS=ON(Default) ENABLE_NONFREE_KERNELS=ON(Default) # Free Kernel Build: ENABLE_KERNELS=ON ENABLE_NONFREE_KERNELS=OFF #pushd ${SOURCE_DIR} - #git clone -b intel-media-19.4 --depth=1 https://github.com/intel/media-driver + #git clone -b intel-media-20.2 --depth=1 https://github.com/intel/media-driver #pushd media-driver #mkdir build && pushd build #cmake -DCMAKE_INSTALL_PREFIX=${TARGET_DIR} \ @@ -113,22 +138,6 @@ prepare_hwa_amd64() { #popd #popd #popd - - # Uncomment for non-free QSV - # Download and install MediaSDK - #pushd ${SOURCE_DIR} - #git clone -b intel-mediasdk-19.4 --depth=1 https://github.com/Intel-Media-SDK/MediaSDK - #pushd MediaSDK - #sed -i 's|MFX_PLUGINS_CONF_DIR "/plugins.cfg"|"/usr/lib/jellyfin-ffmpeg/lib/mfx/plugins.cfg"|g' api/mfx_dispatch/linux/mfxloader.cpp - #mkdir build && pushd build - #cmake -DCMAKE_INSTALL_PREFIX=${TARGET_DIR} .. - #make -j$(nproc) && make install && make install DESTDIR=${SOURCE_DIR}/intel - #echo "intel${TARGET_DIR}/lib/libmfx* usr/lib/jellyfin-ffmpeg/lib" >> ${SOURCE_DIR}/debian/jellyfin-ffmpeg.install - #echo "intel${TARGET_DIR}/lib/mfx/*.so usr/lib/jellyfin-ffmpeg/lib/mfx" >> ${SOURCE_DIR}/debian/jellyfin-ffmpeg.install - #echo "intel${TARGET_DIR}/share/mfx/plugins.cfg usr/lib/jellyfin-ffmpeg/lib/mfx" >> ${SOURCE_DIR}/debian/jellyfin-ffmpeg.install - #popd - #popd - #popd } # Prepare the cross-toolchain prepare_crossbuild_env_armhf() { @@ -164,13 +173,13 @@ EOF yes | apt-get install -y -o APT::Immediate-Configure=0 gcc-${GCC_VER}-source libstdc++6-armhf-cross binutils-arm-linux-gnueabihf bison flex libtool gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:armhf linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf libfontconfig1-dev:armhf libfreetype6-dev:armhf liblttng-ust0:armhf libstdc++6:armhf popd - # Fetch RasPi headers to build MMAL support + # Fetch RasPi headers to build MMAL and OMX-RPI support pushd ${SOURCE_DIR} - git clone --depth=1 https://github.com/raspberrypi/firmware mmalheaders - git clone --depth=1 https://github.com/raspberrypi/userland piuserland - cp -a mmalheaders/opt/vc/include/* /usr/include/ - cp -a mmalheaders/opt/vc/lib/* /usr/lib/ - cp -a piuserland/interface/* /usr/include/ + svn checkout https://github.com/raspberrypi/firmware/trunk/opt/vc/include rpi/include + svn checkout https://github.com/raspberrypi/firmware/trunk/opt/vc/lib rpi/lib + cp -a rpi/include/* /usr/include + cp -a rpi/include/IL/* /usr/include + cp -a rpi/lib/* /usr/lib popd } prepare_crossbuild_env_arm64() { @@ -205,12 +214,21 @@ EOF ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime yes | apt-get install -y -o APT::Immediate-Configure=0 gcc-${GCC_VER}-source libstdc++6-arm64-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 libfreetype6-dev:arm64 liblttng-ust0:arm64 libstdc++6:arm64 popd + + # Fetch RasPi headers to build MMAL and OMX-RPI support + pushd ${SOURCE_DIR} + svn checkout https://github.com/raspberrypi/firmware/trunk/opt/vc/include rpi/include + svn checkout https://github.com/raspberrypi/firmware/trunk/opt/vc/lib rpi/lib + cp -a rpi/include/* /usr/include + cp -a rpi/include/IL/* /usr/include + cp -a rpi/lib/* /usr/lib + popd } # Set the architecture-specific options case ${ARCH} in 'amd64') - prepare_hwa_amd64 + prepare_extra_amd64 CONFIG_SITE="" DEP_ARCH_OPT="" BUILD_ARCH_OPT="" diff --git a/ffbuild/.gitignore b/ffbuild/.gitignore new file mode 100644 index 00000000000..38ed170752c --- /dev/null +++ b/ffbuild/.gitignore @@ -0,0 +1,5 @@ +/.config +/config.fate +/config.log +/config.mak +/config.sh diff --git a/ffbuild/common.mak b/ffbuild/common.mak index 7355508ea02..a60d27c9bd3 100644 --- a/ffbuild/common.mak +++ b/ffbuild/common.mak @@ -162,7 +162,7 @@ $(TOOLOBJS): | tools OUTDIRS := $(OUTDIRS) $(dir $(OBJS) $(HOBJS) $(HOSTOBJS) $(SLIBOBJS) $(TESTOBJS)) -CLEANSUFFIXES = *.d *.gcda *.gcno *.h.c *.ho *.map *.o *.pc *.ptx *.ptx.c *.ver *.version *$(DEFAULT_X86ASMD).asm *~ +CLEANSUFFIXES = *.d *.gcda *.gcno *.h.c *.ho *.map *.o *.pc *.ptx *.ptx.c *.ver *.version *$(DEFAULT_X86ASMD).asm *~ *.ilk *.pdb LIBSUFFIXES = *.a *.lib *.so *.so.* *.dylib *.dll *.def *.dll.a define RULES diff --git a/fftools/Makefile b/fftools/Makefile index 6cec666dd9e..5affaa3f56e 100644 --- a/fftools/Makefile +++ b/fftools/Makefile @@ -10,7 +10,6 @@ ALLAVPROGS = $(AVBASENAMES:%=%$(PROGSSUF)$(EXESUF)) ALLAVPROGS_G = $(AVBASENAMES:%=%$(PROGSSUF)_g$(EXESUF)) OBJS-ffmpeg += fftools/ffmpeg_opt.o fftools/ffmpeg_filter.o fftools/ffmpeg_hw.o -OBJS-ffmpeg-$(CONFIG_CUVID) += fftools/ffmpeg_cuvid.o OBJS-ffmpeg-$(CONFIG_LIBMFX) += fftools/ffmpeg_qsv.o ifndef CONFIG_VIDEOTOOLBOX OBJS-ffmpeg-$(CONFIG_VDA) += fftools/ffmpeg_videotoolbox.o diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c index 9cfbc45c2bf..13567a777ec 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c @@ -55,9 +55,6 @@ #include "libavutil/ffversion.h" #include "libavutil/version.h" #include "cmdutils.h" -#if CONFIG_NETWORK -#include "libavformat/network.h" -#endif #if HAVE_SYS_RESOURCE_H #include #include @@ -119,7 +116,7 @@ static void log_callback_report(void *ptr, int level, const char *fmt, va_list v void init_dynload(void) { -#ifdef _WIN32 +#if HAVE_SETDLLDIRECTORY && defined(_WIN32) /* Calling SetDllDirectory with the empty string (but not NULL) removes the * current working directory from the DLL search path as a security pre-caution. */ SetDllDirectory(""); @@ -182,7 +179,7 @@ void show_help_options(const OptionDef *options, const char *msg, int req_flags, first = 1; for (po = options; po->name; po++) { - char buf[64]; + char buf[128]; if (((po->flags & req_flags) != req_flags) || (alt_flags && !(po->flags & alt_flags)) || @@ -848,8 +845,8 @@ do { \ } if (octx->cur_group.nb_opts || codec_opts || format_opts || resample_opts) - av_log(NULL, AV_LOG_WARNING, "Trailing options were found on the " - "commandline.\n"); + av_log(NULL, AV_LOG_WARNING, "Trailing option(s) found in the " + "command: may be ignored.\n"); av_log(NULL, AV_LOG_DEBUG, "Finished splitting the commandline.\n"); @@ -980,6 +977,7 @@ static int init_report(const char *env) char *filename_template = NULL; char *key, *val; int ret, count = 0; + int prog_loglevel, envlevel = 0; time_t now; struct tm *tm; AVBPrint filename; @@ -1011,6 +1009,7 @@ static int init_report(const char *env) av_log(NULL, AV_LOG_FATAL, "Invalid report file level\n"); exit_program(1); } + envlevel = 1; } else { av_log(NULL, AV_LOG_ERROR, "Unknown key '%s' in FFREPORT\n", key); } @@ -1027,6 +1026,10 @@ static int init_report(const char *env) return AVERROR(ENOMEM); } + prog_loglevel = av_log_get_level(); + if (!envlevel) + report_file_level = FFMAX(report_file_level, prog_loglevel); + report_file = fopen(filename.str, "w"); if (!report_file) { int ret = AVERROR(errno); @@ -1037,16 +1040,17 @@ static int init_report(const char *env) av_log_set_callback(log_callback_report); av_log(NULL, AV_LOG_INFO, "%s started on %04d-%02d-%02d at %02d:%02d:%02d\n" - "Report written to \"%s\"\n", + "Report written to \"%s\"\n" + "Log level: %d\n", program_name, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, - filename.str); + filename.str, report_file_level); av_bprint_finalize(&filename, NULL); return 0; } -int opt_report(const char *opt) +int opt_report(void *optctx, const char *opt, const char *arg) { return init_report(NULL); } @@ -1416,10 +1420,6 @@ static void print_codec(const AVCodec *c) printf("threads "); if (c->capabilities & AV_CODEC_CAP_AVOID_PROBING) printf("avoidprobe "); - if (c->capabilities & AV_CODEC_CAP_INTRA_ONLY) - printf("intraonly "); - if (c->capabilities & AV_CODEC_CAP_LOSSLESS) - printf("lossless "); if (c->capabilities & AV_CODEC_CAP_HARDWARE) printf("hardware "); if (c->capabilities & AV_CODEC_CAP_HYBRID) @@ -1493,13 +1493,14 @@ static char get_media_type_char(enum AVMediaType type) } } -static const AVCodec *next_codec_for_id(enum AVCodecID id, const AVCodec *prev, +static const AVCodec *next_codec_for_id(enum AVCodecID id, void **iter, int encoder) { - while ((prev = av_codec_next(prev))) { - if (prev->id == id && - (encoder ? av_codec_is_encoder(prev) : av_codec_is_decoder(prev))) - return prev; + const AVCodec *c; + while ((c = av_codec_iterate(iter))) { + if (c->id == id && + (encoder ? av_codec_is_encoder(c) : av_codec_is_decoder(c))) + return c; } return NULL; } @@ -1536,11 +1537,12 @@ static unsigned get_codecs_sorted(const AVCodecDescriptor ***rcodecs) static void print_codecs_for_id(enum AVCodecID id, int encoder) { - const AVCodec *codec = NULL; + void *iter = NULL; + const AVCodec *codec; printf(" (%s: ", encoder ? "encoders" : "decoders"); - while ((codec = next_codec_for_id(id, codec, encoder))) + while ((codec = next_codec_for_id(id, &iter, encoder))) printf("%s ", codec->name); printf(")"); @@ -1563,7 +1565,8 @@ int show_codecs(void *optctx, const char *opt, const char *arg) " -------\n"); for (i = 0; i < nb_codecs; i++) { const AVCodecDescriptor *desc = codecs[i]; - const AVCodec *codec = NULL; + const AVCodec *codec; + void *iter = NULL; if (strstr(desc->name, "_deprecated")) continue; @@ -1581,14 +1584,14 @@ int show_codecs(void *optctx, const char *opt, const char *arg) /* print decoders/encoders when there's more than one or their * names are different from codec name */ - while ((codec = next_codec_for_id(desc->id, codec, 0))) { + while ((codec = next_codec_for_id(desc->id, &iter, 0))) { if (strcmp(codec->name, desc->name)) { print_codecs_for_id(desc->id, 0); break; } } - codec = NULL; - while ((codec = next_codec_for_id(desc->id, codec, 1))) { + iter = NULL; + while ((codec = next_codec_for_id(desc->id, &iter, 1))) { if (strcmp(codec->name, desc->name)) { print_codecs_for_id(desc->id, 1); break; @@ -1619,9 +1622,10 @@ static void print_codecs(int encoder) encoder ? "Encoders" : "Decoders"); for (i = 0; i < nb_codecs; i++) { const AVCodecDescriptor *desc = codecs[i]; - const AVCodec *codec = NULL; + const AVCodec *codec; + void *iter = NULL; - while ((codec = next_codec_for_id(desc->id, codec, encoder))) { + 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" : "."); @@ -1826,9 +1830,10 @@ static void show_help_codec(const char *name, int encoder) if (codec) print_codec(codec); else if ((desc = avcodec_descriptor_get_by_name(name))) { + void *iter = NULL; int printed = 0; - while ((codec = next_codec_for_id(desc->id, codec, encoder))) { + while ((codec = next_codec_for_id(desc->id, &iter, encoder))) { printed = 1; print_codec(codec); } @@ -1863,6 +1868,24 @@ static void show_help_demuxer(const char *name) show_help_children(fmt->priv_class, AV_OPT_FLAG_DECODING_PARAM); } +static void show_help_protocol(const char *name) +{ + const AVClass *proto_class; + + if (!name) { + av_log(NULL, AV_LOG_ERROR, "No protocol name specified.\n"); + return; + } + + proto_class = avio_protocol_get_class(name); + if (!proto_class) { + av_log(NULL, AV_LOG_ERROR, "Unknown protocol '%s'.\n", name); + return; + } + + show_help_children(proto_class, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM); +} + static void show_help_muxer(const char *name) { const AVCodecDescriptor *desc; @@ -1993,6 +2016,8 @@ int show_help(void *optctx, const char *opt, const char *arg) show_help_demuxer(par); } else if (!strcmp(topic, "muxer")) { show_help_muxer(par); + } else if (!strcmp(topic, "protocol")) { + show_help_protocol(par); #if CONFIG_AVFILTER } else if (!strcmp(topic, "filter")) { show_help_filter(par); @@ -2032,7 +2057,7 @@ FILE *get_preset_file(char *filename, size_t filename_size, av_strlcpy(filename, preset_name, filename_size); f = fopen(filename, "r"); } else { -#ifdef _WIN32 +#if HAVE_GETMODULEHANDLE && defined(_WIN32) char datadir[MAX_PATH], *ls; base[2] = NULL; @@ -2185,7 +2210,7 @@ double get_rotation(AVStream *st) if (fabs(theta - 90*round(theta/90)) > 2) av_log(NULL, AV_LOG_WARNING, "Odd rotation angle.\n" "If you want to help, upload a sample " - "of this file to ftp://upload.ffmpeg.org/incoming/ " + "of this file to https://streams.videolan.org/upload/ " "and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)"); return theta; diff --git a/fftools/cmdutils.h b/fftools/cmdutils.h index 6e2e0a2acbc..19175105895 100644 --- a/fftools/cmdutils.h +++ b/fftools/cmdutils.h @@ -99,7 +99,7 @@ int opt_default(void *optctx, const char *opt, const char *arg); */ int opt_loglevel(void *optctx, const char *opt, const char *arg); -int opt_report(const char *opt); +int opt_report(void *optctx, const char *opt, const char *arg); int opt_max_alloc(void *optctx, const char *opt, const char *arg); @@ -236,7 +236,7 @@ void show_help_options(const OptionDef *options, const char *msg, int req_flags, { "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, { (void*)opt_report }, "generate a report" }, \ + { "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" }, \ { "hide_banner", OPT_BOOL | OPT_EXPERT, {&hide_banner}, "do not show program banner", "hide_banner" }, \ diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index 01f04103cfa..2e9448ea2ba 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -182,7 +182,7 @@ static int sub2video_get_blank_frame(InputStream *ist) 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, 32)) < 0) + if ((ret = av_frame_get_buffer(frame, 0)) < 0) return ret; memset(frame->data[0], 0, frame->height * frame->linesize[0]); return 0; @@ -237,7 +237,7 @@ static void sub2video_push_ref(InputStream *ist, int64_t pts) } } -void sub2video_update(InputStream *ist, AVSubtitle *sub) +void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub) { AVFrame *frame = ist->sub2video.frame; int8_t *dst; @@ -254,7 +254,12 @@ void sub2video_update(InputStream *ist, AVSubtitle *sub) AV_TIME_BASE_Q, ist->st->time_base); num_rects = sub->num_rects; } else { - pts = ist->sub2video.end_pts; + /* 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; } @@ -269,6 +274,7 @@ void sub2video_update(InputStream *ist, AVSubtitle *sub) 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) @@ -291,9 +297,11 @@ static void sub2video_heartbeat(InputStream *ist, int64_t pts) /* 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.frame->data[0] && ist2->sub2video.end_pts < INT64_MAX)) - sub2video_update(ist2, NULL); + 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) @@ -307,7 +315,7 @@ static void sub2video_flush(InputStream *ist) int ret; if (ist->sub2video.end_pts < INT64_MAX) - sub2video_update(ist, NULL); + 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) @@ -493,32 +501,37 @@ static void ffmpeg_cleanup(int ret) FilterGraph *fg = filtergraphs[i]; avfilter_graph_free(&fg->graph); for (j = 0; j < fg->nb_inputs; j++) { - while (av_fifo_size(fg->inputs[j]->frame_queue)) { + InputFilter *ifilter = fg->inputs[j]; + struct InputStream *ist = ifilter->ist; + + while (av_fifo_size(ifilter->frame_queue)) { AVFrame *frame; - av_fifo_generic_read(fg->inputs[j]->frame_queue, &frame, + av_fifo_generic_read(ifilter->frame_queue, &frame, sizeof(frame), NULL); av_frame_free(&frame); } - av_fifo_freep(&fg->inputs[j]->frame_queue); - if (fg->inputs[j]->ist->sub2video.sub_queue) { - while (av_fifo_size(fg->inputs[j]->ist->sub2video.sub_queue)) { + av_fifo_freep(&ifilter->frame_queue); + if (ist->sub2video.sub_queue) { + while (av_fifo_size(ist->sub2video.sub_queue)) { AVSubtitle sub; - av_fifo_generic_read(fg->inputs[j]->ist->sub2video.sub_queue, + av_fifo_generic_read(ist->sub2video.sub_queue, &sub, sizeof(sub), NULL); avsubtitle_free(&sub); } - av_fifo_freep(&fg->inputs[j]->ist->sub2video.sub_queue); + av_fifo_freep(&ist->sub2video.sub_queue); } - av_buffer_unref(&fg->inputs[j]->hw_frames_ctx); - av_freep(&fg->inputs[j]->name); + 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++) { - av_freep(&fg->outputs[j]->name); - av_freep(&fg->outputs[j]->formats); - av_freep(&fg->outputs[j]->channel_layouts); - av_freep(&fg->outputs[j]->sample_rates); + OutputFilter *ofilter = fg->outputs[j]; + + av_freep(&ofilter->name); + av_freep(&ofilter->formats); + av_freep(&ofilter->channel_layouts); + av_freep(&ofilter->sample_rates); av_freep(&fg->outputs[j]); } av_freep(&fg->outputs); @@ -550,9 +563,7 @@ static void ffmpeg_cleanup(int ret) if (!ost) continue; - for (j = 0; j < ost->nb_bitstream_filters; j++) - av_bsf_free(&ost->bsf_ctx[j]); - av_freep(&ost->bsf_ctx); + av_bsf_free(&ost->bsf_ctx); av_frame_free(&ost->filtered_frame); av_frame_free(&ost->last_frame); @@ -567,6 +578,7 @@ static void ffmpeg_cleanup(int ret) ost->audio_channels_mapped = 0; av_dict_free(&ost->sws_dict); + av_dict_free(&ost->swr_opts); avcodec_free_context(&ost->enc_ctx); avcodec_parameters_free(&ost->ref_par); @@ -779,6 +791,8 @@ static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int u int64_t max = ost->last_mux_dts + !(s->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; + 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, ost->last_mux_dts, pkt->dts); @@ -848,40 +862,15 @@ static void output_packet(OutputFile *of, AVPacket *pkt, { int ret = 0; - /* apply the output bitstream filters, if any */ - if (ost->nb_bitstream_filters) { - int idx; - - ret = av_bsf_send_packet(ost->bsf_ctx[0], eof ? NULL : pkt); + /* apply the output bitstream filters */ + if (ost->bsf_ctx) { + ret = av_bsf_send_packet(ost->bsf_ctx, eof ? NULL : pkt); if (ret < 0) goto finish; - - eof = 0; - idx = 1; - while (idx) { - /* get a packet from the previous filter up the chain */ - ret = av_bsf_receive_packet(ost->bsf_ctx[idx - 1], pkt); - if (ret == AVERROR(EAGAIN)) { - ret = 0; - idx--; - continue; - } else if (ret == AVERROR_EOF) { - eof = 1; - } else if (ret < 0) - goto finish; - - /* send it to the next filter down the chain or to the muxer */ - if (idx < ost->nb_bitstream_filters) { - ret = av_bsf_send_packet(ost->bsf_ctx[idx], eof ? NULL : pkt); - if (ret < 0) - goto finish; - idx++; - eof = 0; - } else if (eof) - goto finish; - else - write_packet(of, pkt, ost, 0); - } + while ((ret = av_bsf_receive_packet(ost->bsf_ctx, pkt)) >= 0) + write_packet(of, pkt, ost, 0); + if (ret == AVERROR(EAGAIN)) + ret = 0; } else if (!eof) write_packet(of, pkt, ost, 0); @@ -1136,7 +1125,7 @@ static void do_video_out(OutputFile *of, av_log(NULL, AV_LOG_DEBUG, "Not duplicating %d initial frames\n", (int)lrintf(delta0)); delta = duration; delta0 = 0; - ost->sync_opts = lrint(sync_ipts); + ost->sync_opts = llrint(sync_ipts); } case VSYNC_CFR: // FIXME set to 0.5 after we fix some dts/pts bugs like in avidec.c @@ -1147,18 +1136,18 @@ static void do_video_out(OutputFile *of, else if (delta > 1.1) { nb_frames = lrintf(delta); if (delta0 > 1.1) - nb0_frames = lrintf(delta0 - 0.6); + nb0_frames = llrintf(delta0 - 0.6); } break; case VSYNC_VFR: if (delta <= -0.6) nb_frames = 0; else if (delta > 0.6) - ost->sync_opts = lrint(sync_ipts); + ost->sync_opts = llrint(sync_ipts); break; case VSYNC_DROP: case VSYNC_PASSTHROUGH: - ost->sync_opts = lrint(sync_ipts); + ost->sync_opts = llrint(sync_ipts); break; default: av_assert0(0); @@ -1265,7 +1254,8 @@ static void do_video_out(OutputFile *of, ost->forced_keyframes_expr_const_values[FKF_N] += 1; } else if ( ost->forced_keyframes && !strncmp(ost->forced_keyframes, "source", 6) - && in_picture->key_frame==1) { + && in_picture->key_frame==1 + && !i) { forced_keyframe = 1; } @@ -1903,9 +1893,6 @@ static void flush_encoders(void) } } - if (enc->codec_type == AVMEDIA_TYPE_AUDIO && enc->frame_size <= 1) - continue; - if (enc->codec_type != AVMEDIA_TYPE_VIDEO && enc->codec_type != AVMEDIA_TYPE_AUDIO) continue; @@ -1995,12 +1982,13 @@ static void do_streamcopy(InputStream *ist, OutputStream *ost, const AVPacket *p 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 = { 0 }; - - av_init_packet(&opkt); + AVPacket opkt; // EOF: flush output bitstream filters. if (!pkt) { + av_init_packet(&opkt); + opkt.data = NULL; + opkt.size = 0; output_packet(of, &opkt, ost, 1); return; } @@ -2039,40 +2027,29 @@ static void do_streamcopy(InputStream *ist, OutputStream *ost, const AVPacket *p if (ost->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO) ost->sync_opts++; + if (av_packet_ref(&opkt, pkt) < 0) + exit_program(1); + if (pkt->pts != AV_NOPTS_VALUE) opkt.pts = av_rescale_q(pkt->pts, ist->st->time_base, ost->mux_timebase) - ost_tb_start_time; - else - opkt.pts = AV_NOPTS_VALUE; - if (pkt->dts == AV_NOPTS_VALUE) + if (pkt->dts == AV_NOPTS_VALUE) { opkt.dts = av_rescale_q(ist->dts, AV_TIME_BASE_Q, ost->mux_timebase); - else - opkt.dts = av_rescale_q(pkt->dts, ist->st->time_base, ost->mux_timebase); - opkt.dts -= ost_tb_start_time; - - if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && pkt->dts != AV_NOPTS_VALUE) { + } else if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { int duration = av_get_audio_frame_duration(ist->dec_ctx, pkt->size); if(!duration) duration = ist->dec_ctx->frame_size; - opkt.dts = opkt.pts = av_rescale_delta(ist->st->time_base, pkt->dts, - (AVRational){1, ist->dec_ctx->sample_rate}, duration, &ist->filter_in_rescale_delta_last, - ost->mux_timebase) - ost_tb_start_time; - } + opkt.dts = av_rescale_delta(ist->st->time_base, pkt->dts, + (AVRational){1, ist->dec_ctx->sample_rate}, duration, + &ist->filter_in_rescale_delta_last, ost->mux_timebase); + /* 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, ost->mux_timebase); + opkt.dts -= ost_tb_start_time; opkt.duration = av_rescale_q(pkt->duration, ist->st->time_base, ost->mux_timebase); - opkt.flags = pkt->flags; - - if (pkt->buf) { - opkt.buf = av_buffer_ref(pkt->buf); - if (!opkt.buf) - exit_program(1); - } - opkt.data = pkt->data; - opkt.size = pkt->size; - - av_copy_packet_side_data(&opkt, pkt); - output_packet(of, &opkt, ost, 0); } @@ -2393,7 +2370,7 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output, int64_ 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 ftp://upload.ffmpeg.org/incoming/ " + "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->st->codecpar->video_delay); @@ -2515,7 +2492,7 @@ static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output, return ret; if (ist->sub2video.frame) { - sub2video_update(ist, &subtitle); + sub2video_update(ist, INT64_MIN, &subtitle); } else if (ist->nb_filters) { if (!ist->sub2video.sub_queue) ist->sub2video.sub_queue = av_fifo_alloc(8 * sizeof(AVSubtitle)); @@ -2784,7 +2761,7 @@ static void print_sdp(void) if (avio_open2(&sdp_pb, sdp_filename, AVIO_FLAG_WRITE, &int_cb, NULL) < 0) { av_log(NULL, AV_LOG_ERROR, "Failed to open sdp file '%s'\n", sdp_filename); } else { - avio_printf(sdp_pb, "SDP:\n%s", sdp); + avio_print(sdp_pb, sdp); avio_closep(&sdp_pb); av_freep(&sdp_filename); } @@ -3016,35 +2993,28 @@ static int check_init_output_file(OutputFile *of, int file_index) static int init_output_bsfs(OutputStream *ost) { - AVBSFContext *ctx; - int i, ret; + AVBSFContext *ctx = ost->bsf_ctx; + int ret; - if (!ost->nb_bitstream_filters) + if (!ctx) return 0; - for (i = 0; i < ost->nb_bitstream_filters; i++) { - ctx = ost->bsf_ctx[i]; - - ret = avcodec_parameters_copy(ctx->par_in, - i ? ost->bsf_ctx[i - 1]->par_out : ost->st->codecpar); - if (ret < 0) - return ret; + ret = avcodec_parameters_copy(ctx->par_in, ost->st->codecpar); + if (ret < 0) + return ret; - ctx->time_base_in = i ? ost->bsf_ctx[i - 1]->time_base_out : ost->st->time_base; + ctx->time_base_in = ost->st->time_base; - ret = av_bsf_init(ctx); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error initializing bitstream filter: %s\n", - ost->bsf_ctx[i]->filter->name); - return ret; - } + ret = av_bsf_init(ctx); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Error initializing bitstream filter: %s\n", + ctx->filter->name); + return ret; } - ctx = ost->bsf_ctx[ost->nb_bitstream_filters - 1]; ret = avcodec_parameters_copy(ost->st->codecpar, ctx->par_out); if (ret < 0) return ret; - ost->st->time_base = ctx->time_base_out; return 0; @@ -3376,10 +3346,6 @@ static int init_output_stream_encode(OutputStream *ost) av_log(oc, 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 -vsync 2\n"); } - for (j = 0; j < ost->forced_kf_count; j++) - ost->forced_kf_pts[j] = av_rescale_q(ost->forced_kf_pts[j], - AV_TIME_BASE_Q, - enc_ctx->time_base); enc_ctx->width = av_buffersink_get_w(ost->filter->filter); enc_ctx->height = av_buffersink_get_h(ost->filter->filter); @@ -3481,21 +3447,14 @@ static int init_output_stream(OutputStream *ost, char *error, int error_len) !av_dict_get(ost->encoder_opts, "ab", NULL, 0)) av_dict_set(&ost->encoder_opts, "b", "128000", 0); - if (ost->filter && av_buffersink_get_hw_frames_ctx(ost->filter->filter) && - ((AVHWFramesContext*)av_buffersink_get_hw_frames_ctx(ost->filter->filter)->data)->format == - av_buffersink_get_format(ost->filter->filter)) { - ost->enc_ctx->hw_frames_ctx = av_buffer_ref(av_buffersink_get_hw_frames_ctx(ost->filter->filter)); - if (!ost->enc_ctx->hw_frames_ctx) - return AVERROR(ENOMEM); - } else { - 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", + 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; - } + return ret; } + if (ist && ist->dec->type == AVMEDIA_TYPE_SUBTITLE && ost->enc->type == AVMEDIA_TYPE_SUBTITLE) { int input_props = 0, output_props = 0; AVCodecDescriptor const *input_descriptor = @@ -3571,12 +3530,14 @@ static int init_output_stream(OutputStream *ost, char *error, int error_len) int i; for (i = 0; i < ist->st->nb_side_data; i++) { AVPacketSideData *sd = &ist->st->side_data[i]; - 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((uint32_t *)dst, 0); + 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((uint32_t *)dst, 0); + } } } @@ -4195,7 +4156,7 @@ static int seek_to_start(InputFile *ifile, AVFormatContext *is) int i, ret, has_audio = 0; int64_t duration = 0; - ret = av_seek_frame(is, -1, is->start_time, 0); + ret = avformat_seek_file(is, -1, INT64_MIN, is->start_time, is->start_time, 0); if (ret < 0) return ret; @@ -4235,7 +4196,8 @@ static int seek_to_start(InputFile *ifile, AVFormatContext *is) ifile->time_base = ist->st->time_base; /* the total duration of the stream, max_pts - min_pts is * the duration of the stream without the last frame */ - duration += ist->max_pts - ist->min_pts; + if (ist->max_pts > ist->min_pts && ist->max_pts - (uint64_t)ist->min_pts < INT64_MAX - duration) + duration += ist->max_pts - ist->min_pts; ifile->time_base = duration_max(duration, &ifile->duration, ist->st->time_base, ifile->time_base); } @@ -4262,6 +4224,7 @@ static int process_input(int file_index) int ret, thread_ret, i, j; int64_t duration; int64_t pkt_dts; + int disable_discontinuity_correction = copy_ts; is = ifile->ctx; ret = get_input_packet(ifile, &pkt); @@ -4463,10 +4426,20 @@ static int process_input(int file_index) pkt.dts += duration; 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 && pkt_dts != AV_NOPTS_VALUE && ist->next_dts != AV_NOPTS_VALUE && + (is->iformat->flags & AVFMT_TS_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->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO || ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) && pkt_dts != AV_NOPTS_VALUE && ist->next_dts != AV_NOPTS_VALUE && - !copy_ts) { + !disable_discontinuity_correction) { int64_t delta = pkt_dts - ist->next_dts; if (is->iformat->flags & AVFMT_TS_DISCONT) { if (delta < -1LL*dts_delta_threshold*AV_TIME_BASE || @@ -4740,6 +4713,10 @@ static int transcode(void) av_freep(&ost->enc_ctx->stats_in); } total_packets_written += ost->packets_written; + if (!ost->packets_written && (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM)) { + av_log(NULL, AV_LOG_FATAL, "Empty output on stream %d.\n", i); + exit_program(1); + } } if (!total_packets_written && (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT)) { @@ -4757,7 +4734,6 @@ static int transcode(void) } } - av_buffer_unref(&hw_device_ctx); hw_device_free_all(); /* finished ! */ diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 7b6f802082a..828cb2a4ff9 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -61,7 +61,6 @@ enum HWAccelID { HWACCEL_GENERIC, HWACCEL_VIDEOTOOLBOX, HWACCEL_QSV, - HWACCEL_CUVID, }; typedef struct HWAccel { @@ -349,6 +348,7 @@ typedef struct InputStream { AVFifoBuffer *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; int dr1; @@ -430,7 +430,8 @@ enum forced_keyframes_const { FKF_NB }; -#define ABORT_ON_FLAG_EMPTY_OUTPUT (1 << 0) +#define ABORT_ON_FLAG_EMPTY_OUTPUT (1 << 0) +#define ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM (1 << 1) extern const char *const forced_keyframes_const_names[]; @@ -459,8 +460,7 @@ typedef struct OutputStream { AVRational mux_timebase; AVRational enc_timebase; - int nb_bitstream_filters; - AVBSFContext **bsf_ctx; + AVBSFContext *bsf_ctx; AVCodecContext *enc_ctx; AVCodecParameters *ref_par; /* associated input codec parameters with encoders options applied */ @@ -615,7 +615,6 @@ extern const AVIOInterruptCB int_cb; extern const OptionDef options[]; extern const HWAccel hwaccels[]; -extern AVBufferRef *hw_device_ctx; #if CONFIG_QSV extern char *qsv_device; #endif @@ -646,7 +645,7 @@ int filtergraph_is_simple(FilterGraph *fg); int init_simple_filtergraph(InputStream *ist, OutputStream *ost); int init_complex_filtergraph(FilterGraph *fg); -void sub2video_update(InputStream *ist, AVSubtitle *sub); +void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub); int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame); @@ -654,7 +653,6 @@ int ffmpeg_parse_options(int argc, char **argv); int videotoolbox_init(AVCodecContext *s); int qsv_init(AVCodecContext *s); -int cuvid_init(AVCodecContext *s); HWDevice *hw_device_get_by_name(const char *name); int hw_device_init_from_string(const char *arg, HWDevice **dev); @@ -662,6 +660,7 @@ 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); int hwaccel_decode_init(AVCodecContext *avctx); diff --git a/fftools/ffmpeg_cuvid.c b/fftools/ffmpeg_cuvid.c deleted file mode 100644 index 3ff3b40f17d..00000000000 --- a/fftools/ffmpeg_cuvid.c +++ /dev/null @@ -1,73 +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 "libavutil/hwcontext.h" -#include "libavutil/pixdesc.h" - -#include "ffmpeg.h" - -static void cuvid_uninit(AVCodecContext *avctx) -{ - InputStream *ist = avctx->opaque; - av_buffer_unref(&ist->hw_frames_ctx); -} - -int cuvid_init(AVCodecContext *avctx) -{ - InputStream *ist = avctx->opaque; - AVHWFramesContext *frames_ctx; - int ret; - - av_log(avctx, AV_LOG_VERBOSE, "Initializing cuvid hwaccel\n"); - - if (!hw_device_ctx) { - ret = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_CUDA, - ist->hwaccel_device, NULL, 0); - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, "Error creating a CUDA device\n"); - return ret; - } - } - - av_buffer_unref(&ist->hw_frames_ctx); - ist->hw_frames_ctx = av_hwframe_ctx_alloc(hw_device_ctx); - if (!ist->hw_frames_ctx) { - av_log(avctx, AV_LOG_ERROR, "Error creating a CUDA frames context\n"); - return AVERROR(ENOMEM); - } - - frames_ctx = (AVHWFramesContext*)ist->hw_frames_ctx->data; - - frames_ctx->format = AV_PIX_FMT_CUDA; - frames_ctx->sw_format = avctx->sw_pix_fmt; - frames_ctx->width = avctx->width; - frames_ctx->height = avctx->height; - - av_log(avctx, AV_LOG_DEBUG, "Initializing CUDA frames context: sw_format = %s, width = %d, height = %d\n", - av_get_pix_fmt_name(frames_ctx->sw_format), frames_ctx->width, frames_ctx->height); - - ret = av_hwframe_ctx_init(ist->hw_frames_ctx); - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, "Error initializing a CUDA frame pool\n"); - return ret; - } - - ist->hwaccel_uninit = cuvid_uninit; - - return 0; -} diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index 72838de1e2c..422e1268e98 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -99,7 +99,8 @@ void choose_sample_fmt(AVStream *st, AVCodec *codec) break; } if (*p == -1) { - if((codec->capabilities & AV_CODEC_CAP_LOSSLESS) && av_get_sample_fmt_name(st->codecpar->format) > av_get_sample_fmt_name(codec->sample_fmts[0])) + const AVCodecDescriptor *desc = avcodec_descriptor_get(codec->id); + if(desc && (desc->props & AV_CODEC_PROP_LOSSLESS) && av_get_sample_fmt_name(st->codecpar->format) > av_get_sample_fmt_name(codec->sample_fmts[0])) av_log(NULL, AV_LOG_ERROR, "Conversion will not be lossless.\n"); if(av_get_sample_fmt_name(st->codecpar->format)) av_log(NULL, AV_LOG_WARNING, @@ -740,6 +741,12 @@ static int sub2video_prepare(InputStream *ist, InputFilter *ifilter) return AVERROR(ENOMEM); ist->sub2video.last_pts = INT64_MIN; ist->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. */ + ist->sub2video.initialize = 1; + return 0; } @@ -786,10 +793,9 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, 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:sws_param=flags=%d", + "pixel_aspect=%d/%d", ifilter->width, ifilter->height, ifilter->format, - tb.num, tb.den, sar.num, sar.den, - SWS_BILINEAR + ((ist->dec_ctx->flags&AV_CODEC_FLAG_BITEXACT) ? SWS_BITEXACT:0)); + 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, @@ -1056,17 +1062,9 @@ int configure_filtergraph(FilterGraph *fg) if ((ret = avfilter_graph_parse2(fg->graph, graph_desc, &inputs, &outputs)) < 0) goto fail; - if (filter_hw_device || hw_device_ctx) { - AVBufferRef *device = filter_hw_device ? filter_hw_device->device_ref - : hw_device_ctx; - for (i = 0; i < fg->graph->nb_filters; i++) { - fg->graph->filters[i]->hw_device_ctx = av_buffer_ref(device); - if (!fg->graph->filters[i]->hw_device_ctx) { - ret = AVERROR(ENOMEM); - goto fail; - } - } - } + ret = hw_device_setup_for_filter(fg); + if (ret < 0) + goto fail; if (simple && (!inputs || inputs->next || !outputs || outputs->next)) { const char *num_inputs; @@ -1169,7 +1167,7 @@ int configure_filtergraph(FilterGraph *fg) while (av_fifo_size(ist->sub2video.sub_queue)) { AVSubtitle tmp; av_fifo_generic_read(ist->sub2video.sub_queue, &tmp, sizeof(tmp), NULL); - sub2video_update(ist, &tmp); + sub2video_update(ist, INT64_MIN, &tmp); avsubtitle_free(&tmp); } } diff --git a/fftools/ffmpeg_hw.c b/fftools/ffmpeg_hw.c index 962d8f7d5a4..fc4a5d31d6a 100644 --- a/fftools/ffmpeg_hw.c +++ b/fftools/ffmpeg_hw.c @@ -19,6 +19,8 @@ #include #include "libavutil/avstring.h" +#include "libavutil/pixdesc.h" +#include "libavfilter/buffersink.h" #include "ffmpeg.h" @@ -416,18 +418,57 @@ int hw_device_setup_for_decode(InputStream *ist) int hw_device_setup_for_encode(OutputStream *ost) { - HWDevice *dev; + 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, 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->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); + } - dev = hw_device_match_by_codec(ost->enc); 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->name); ost->enc_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref); if (!ost->enc_ctx->hw_device_ctx) return AVERROR(ENOMEM); - return 0; } else { // No device required, or no device available. - return 0; } + return 0; } static int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input) @@ -480,3 +521,31 @@ int hwaccel_decode_init(AVCodecContext *avctx) return 0; } + +int hw_device_setup_for_filter(FilterGraph *fg) +{ + HWDevice *dev; + int i; + + // If the user has supplied exactly one hardware device then just + // give it straight to every filter for convenience. If more than + // one device is available then the user needs to pick one explcitly + // with the filter_hw_device option. + if (filter_hw_device) + dev = filter_hw_device; + else if (nb_hw_devices == 1) + dev = hw_devices[0]; + 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 0; +} diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index f5ca18aa644..2eb4e1c973f 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -1,3 +1,4 @@ + /* * ffmpeg option parsing * @@ -43,16 +44,80 @@ #define DEFAULT_PASS_LOGFILENAME_PREFIX "ffmpeg2pass" +#define SPECIFIER_OPT_FMT_str "%s" +#define SPECIFIER_OPT_FMT_i "%i" +#define SPECIFIER_OPT_FMT_i64 "%"PRId64 +#define SPECIFIER_OPT_FMT_ui64 "%"PRIu64 +#define SPECIFIER_OPT_FMT_f "%f" +#define SPECIFIER_OPT_FMT_dbl "%lf" + +static const char *opt_name_codec_names[] = {"c", "codec", "acodec", "vcodec", "scodec", "dcodec", NULL}; +static const char *opt_name_audio_channels[] = {"ac", NULL}; +static const char *opt_name_audio_sample_rate[] = {"ar", NULL}; +static const char *opt_name_frame_rates[] = {"r", NULL}; +static const char *opt_name_frame_sizes[] = {"s", NULL}; +static const char *opt_name_frame_pix_fmts[] = {"pix_fmt", NULL}; +static const char *opt_name_ts_scale[] = {"itsscale", NULL}; +static const char *opt_name_hwaccels[] = {"hwaccel", NULL}; +static const char *opt_name_hwaccel_devices[] = {"hwaccel_device", NULL}; +static const char *opt_name_hwaccel_output_formats[] = {"hwaccel_output_format", NULL}; +static const char *opt_name_autorotate[] = {"autorotate", NULL}; +static const char *opt_name_max_frames[] = {"frames", "aframes", "vframes", "dframes", NULL}; +static const char *opt_name_bitstream_filters[] = {"bsf", "absf", "vbsf", NULL}; +static const char *opt_name_codec_tags[] = {"tag", "atag", "vtag", "stag", NULL}; +static const char *opt_name_sample_fmts[] = {"sample_fmt", NULL}; +static const char *opt_name_qscale[] = {"q", "qscale", NULL}; +static const char *opt_name_forced_key_frames[] = {"forced_key_frames", NULL}; +static const char *opt_name_force_fps[] = {"force_fps", NULL}; +static const char *opt_name_frame_aspect_ratios[] = {"aspect", NULL}; +static const char *opt_name_rc_overrides[] = {"rc_override", NULL}; +static const char *opt_name_intra_matrices[] = {"intra_matrix", NULL}; +static const char *opt_name_inter_matrices[] = {"inter_matrix", NULL}; +static const char *opt_name_chroma_intra_matrices[] = {"chroma_intra_matrix", NULL}; +static const char *opt_name_top_field_first[] = {"top", NULL}; +static const char *opt_name_presets[] = {"pre", "apre", "vpre", "spre", NULL}; +static const char *opt_name_copy_initial_nonkeyframes[] = {"copyinkfr", NULL}; +static const char *opt_name_copy_prior_start[] = {"copypriorss", NULL}; +static const char *opt_name_filters[] = {"filter", "af", "vf", NULL}; +static const char *opt_name_filter_scripts[] = {"filter_script", NULL}; +static const char *opt_name_reinit_filters[] = {"reinit_filter", NULL}; +static const char *opt_name_fix_sub_duration[] = {"fix_sub_duration", NULL}; +static const char *opt_name_canvas_sizes[] = {"canvas_size", NULL}; +static const char *opt_name_pass[] = {"pass", NULL}; +static const char *opt_name_passlogfiles[] = {"passlogfile", NULL}; +static const char *opt_name_max_muxing_queue_size[] = {"max_muxing_queue_size", NULL}; +static const char *opt_name_guess_layout_max[] = {"guess_layout_max", NULL}; +static const char *opt_name_apad[] = {"apad", NULL}; +static const char *opt_name_discard[] = {"discard", NULL}; +static const char *opt_name_disposition[] = {"disposition", NULL}; +static const char *opt_name_time_bases[] = {"time_base", NULL}; +static const char *opt_name_enc_time_bases[] = {"enc_time_base", NULL}; + +#define WARN_MULTIPLE_OPT_USAGE(name, type, so, st)\ +{\ + char namestr[128] = "";\ + const char *spec = so->specifier && so->specifier[0] ? so->specifier : "";\ + for (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 ") : "");\ + 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);\ +} + #define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st)\ {\ - int i, ret;\ + int i, ret, matches = 0;\ + SpecifierOpt *so;\ for (i = 0; i < o->nb_ ## name; i++) {\ char *spec = o->name[i].specifier;\ - if ((ret = check_stream_specifier(fmtctx, st, spec)) > 0)\ + if ((ret = check_stream_specifier(fmtctx, st, spec)) > 0) {\ outvar = o->name[i].u.type;\ - else if (ret < 0)\ + so = &o->name[i];\ + matches++;\ + } else if (ret < 0)\ exit_program(1);\ }\ + if (matches > 1)\ + WARN_MULTIPLE_OPT_USAGE(name, type, so, st);\ } #define MATCH_PER_TYPE_OPT(name, type, outvar, fmtctx, mediatype)\ @@ -71,13 +136,9 @@ const HWAccel hwaccels[] = { #endif #if CONFIG_LIBMFX { "qsv", qsv_init, HWACCEL_QSV, AV_PIX_FMT_QSV }, -#endif -#if CONFIG_CUVID - { "cuvid", cuvid_init, HWACCEL_CUVID, AV_PIX_FMT_CUDA }, #endif { 0 }, }; -AVBufferRef *hw_device_ctx; HWDevice *filter_hw_device; char *vstats_filename; @@ -171,14 +232,11 @@ static void init_options(OptionsContext *o) static int show_hwaccels(void *optctx, const char *opt, const char *arg) { enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE; - int i; printf("Hardware acceleration methods:\n"); while ((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) printf("%s\n", av_hwdevice_get_type_name(type)); - for (i = 0; hwaccels[i].name; i++) - printf("%s\n", hwaccels[i].name); printf("\n"); return 0; } @@ -204,8 +262,9 @@ static AVDictionary *strip_specifiers(AVDictionary *dict) static int opt_abort_on(void *optctx, const char *opt, const char *arg) { static const AVOption opts[] = { - { "abort_on" , NULL, 0, AV_OPT_TYPE_FLAGS, { .i64 = 0 }, INT64_MIN, INT64_MAX, .unit = "flags" }, - { "empty_output" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = ABORT_ON_FLAG_EMPTY_OUTPUT }, .unit = "flags" }, + { "abort_on" , NULL, 0, AV_OPT_TYPE_FLAGS, { .i64 = 0 }, INT64_MIN, INT64_MAX, .unit = "flags" }, + { "empty_output" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = ABORT_ON_FLAG_EMPTY_OUTPUT }, .unit = "flags" }, + { "empty_output_stream", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM }, .unit = "flags" }, { NULL }, }; static const AVClass class = { @@ -477,21 +536,15 @@ static int opt_sdp_file(void *optctx, const char *opt, const char *arg) #if CONFIG_VAAPI static int opt_vaapi_device(void *optctx, const char *opt, const char *arg) { - HWDevice *dev; const char *prefix = "vaapi:"; char *tmp; int err; tmp = av_asprintf("%s%s", prefix, arg); if (!tmp) return AVERROR(ENOMEM); - err = hw_device_init_from_string(tmp, &dev); + err = hw_device_init_from_string(tmp, NULL); av_free(tmp); - if (err < 0) - return err; - hw_device_ctx = av_buffer_ref(dev->device_ref); - if (!hw_device_ctx) - return AVERROR(ENOMEM); - return 0; + return err; } #endif @@ -819,9 +872,28 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic, st); 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(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) { + 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; + } + if (hwaccel) { // The NVDEC hwaccels use a CUDA device, so remap the name here. - if (!strcmp(hwaccel, "nvdec")) + if (!strcmp(hwaccel, "nvdec") || !strcmp(hwaccel, "cuvid")) hwaccel = "cuda"; if (!strcmp(hwaccel, "none")) @@ -855,8 +927,6 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) AV_HWDEVICE_TYPE_NONE) av_log(NULL, AV_LOG_FATAL, "%s ", av_hwdevice_get_type_name(type)); - for (i = 0; hwaccels[i].name; i++) - av_log(NULL, AV_LOG_FATAL, "%s ", hwaccels[i].name); av_log(NULL, AV_LOG_FATAL, "\n"); exit_program(1); } @@ -870,18 +940,6 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) exit_program(1); } - MATCH_PER_STREAM_OPT(hwaccel_output_formats, str, - hwaccel_output_format, ic, st); - 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; - } - ist->hwaccel_pix_fmt = AV_PIX_FMT_NONE; break; @@ -931,7 +989,7 @@ static void assert_file_overwrite(const char *filename) if (!file_overwrite) { if (proto_name && !strcmp(proto_name, "file") && avio_check(filename, 0) == 0) { if (stdin_interaction && !no_file_overwrite) { - fprintf(stderr,"File '%s' already exists. Overwrite ? [y/N] ", filename); + fprintf(stderr,"File '%s' already exists. Overwrite? [y/N] ", filename); fflush(stderr); term_exit(); signal(SIGINT, SIG_DFL); @@ -1471,54 +1529,12 @@ static OutputStream *new_output_stream(OptionsContext *o, AVFormatContext *oc, e MATCH_PER_STREAM_OPT(copy_prior_start, i, ost->copy_prior_start, oc ,st); MATCH_PER_STREAM_OPT(bitstream_filters, str, bsfs, oc, st); - while (bsfs && *bsfs) { - const AVBitStreamFilter *filter; - char *bsf, *bsf_options_str, *bsf_name; - - bsf = av_get_token(&bsfs, ","); - if (!bsf) - exit_program(1); - bsf_name = av_strtok(bsf, "=", &bsf_options_str); - if (!bsf_name) - exit_program(1); - - filter = av_bsf_get_by_name(bsf_name); - if (!filter) { - av_log(NULL, AV_LOG_FATAL, "Unknown bitstream filter %s\n", bsf_name); - exit_program(1); - } - - ost->bsf_ctx = av_realloc_array(ost->bsf_ctx, - ost->nb_bitstream_filters + 1, - sizeof(*ost->bsf_ctx)); - if (!ost->bsf_ctx) - exit_program(1); - - ret = av_bsf_alloc(filter, &ost->bsf_ctx[ost->nb_bitstream_filters]); + if (bsfs && *bsfs) { + ret = av_bsf_list_parse_str(bsfs, &ost->bsf_ctx); if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error allocating a bitstream filter context\n"); + av_log(NULL, AV_LOG_ERROR, "Error parsing bitstream filter sequence '%s': %s\n", bsfs, av_err2str(ret)); exit_program(1); } - - ost->nb_bitstream_filters++; - - if (bsf_options_str && filter->priv_class) { - const AVOption *opt = av_opt_next(ost->bsf_ctx[ost->nb_bitstream_filters-1]->priv_data, NULL); - const char * shorthand[2] = {NULL}; - - if (opt) - shorthand[0] = opt->name; - - ret = av_opt_set_from_string(ost->bsf_ctx[ost->nb_bitstream_filters-1]->priv_data, bsf_options_str, shorthand, "=", ":"); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error parsing options for bitstream filter %s\n", bsf_name); - exit_program(1); - } - } - av_freep(&bsf); - - if (*bsfs) - bsfs++; } MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, oc, st); @@ -1681,8 +1697,6 @@ static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc, in MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script, oc, st); MATCH_PER_STREAM_OPT(filters, str, ost->filters, oc, st); - if (o->nb_filters > 1) - av_log(NULL, AV_LOG_ERROR, "Only '-vf %s' read, ignoring remaining -vf options: Use ',' to separate filters\n", ost->filters); if (!ost->stream_copy) { const char *p = NULL; @@ -1864,8 +1878,6 @@ static OutputStream *new_audio_stream(OptionsContext *o, AVFormatContext *oc, in MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script, oc, st); MATCH_PER_STREAM_OPT(filters, str, ost->filters, oc, st); - if (o->nb_filters > 1) - av_log(NULL, AV_LOG_ERROR, "Only '-af %s' read, ignoring remaining -af options: Use ',' to separate filters\n", ost->filters); if (!ost->stream_copy) { char *sample_fmt = NULL; @@ -2372,12 +2384,14 @@ static int open_output_file(OptionsContext *o, const char *filename) o->attachments[i]); exit_program(1); } - if (!(attachment = av_malloc(len))) { - av_log(NULL, AV_LOG_FATAL, "Attachment %s too large to fit into memory.\n", + if (len > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE || + !(attachment = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE))) { + av_log(NULL, AV_LOG_FATAL, "Attachment %s too large.\n", o->attachments[i]); exit_program(1); } avio_read(pb, attachment, len); + memset(attachment + len, 0, AV_INPUT_BUFFER_PADDING_SIZE); ost = new_attachment_stream(o, oc, -1); ost->stream_copy = 0; @@ -2769,13 +2783,14 @@ static int opt_target(void *optctx, const char *opt, const char *arg) } else { /* Try to determine PAL/NTSC by peeking in the input files */ if (nb_input_files) { - int i, j, fr; + int i, j; for (j = 0; j < nb_input_files; j++) { for (i = 0; i < input_files[j]->nb_streams; i++) { AVStream *st = input_files[j]->ctx->streams[i]; + int64_t fr; if (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) continue; - fr = st->time_base.den * 1000 / st->time_base.num; + fr = st->time_base.den * 1000LL / st->time_base.num; if (fr == 25000) { norm = PAL; break; @@ -3005,8 +3020,11 @@ static int opt_preset(void *optctx, const char *opt, const char *arg) static int opt_old2new(void *optctx, const char *opt, const char *arg) { OptionsContext *o = optctx; + int ret; char *s = av_asprintf("%s:%c", opt + 1, *opt); - int ret = parse_option(o, s, arg, options); + if (!s) + return AVERROR(ENOMEM); + ret = parse_option(o, s, arg, options); av_free(s); return ret; } @@ -3037,6 +3055,8 @@ static int opt_qscale(void *optctx, const char *opt, const char *arg) return parse_option(o, "q:v", arg, options); } s = av_asprintf("q%s", opt + 6); + if (!s) + return AVERROR(ENOMEM); ret = parse_option(o, s, arg, options); av_free(s); return ret; @@ -3081,8 +3101,11 @@ static int opt_vsync(void *optctx, const char *opt, const char *arg) static int opt_timecode(void *optctx, const char *opt, const char *arg) { OptionsContext *o = optctx; + int ret; char *tcr = av_asprintf("timecode=%s", arg); - int ret = parse_option(o, "metadata:g", tcr, options); + if (!tcr) + return AVERROR(ENOMEM); + ret = parse_option(o, "metadata:g", tcr, options); if (ret >= 0) ret = av_dict_set(&o->g->codec_opts, "gop_timecode", arg, 0); av_free(tcr); @@ -3184,7 +3207,7 @@ void show_help_default(const char *opt, const char *arg) " -h -- print basic options\n" " -h long -- print more options\n" " -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\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", program_name); @@ -3192,7 +3215,7 @@ void show_help_default(const char *opt, const char *arg) OPT_EXIT, 0, 0); show_help_options(options, "Global options (affect whole program " - "instead of just one file:", + "instead of just one file):", 0, per_file | OPT_EXIT | OPT_EXPERT, 0); if (show_advanced) show_help_options(options, "Advanced global options:", OPT_EXPERT, @@ -3268,6 +3291,7 @@ static int open_files(OptionGroupList *l, const char *inout, if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Error parsing options for %s file " "%s.\n", inout, g->arg); + uninit_options(&o); return ret; } @@ -3441,7 +3465,7 @@ const OptionDef options[] = { { "stdin", OPT_BOOL | OPT_EXPERT, { &stdin_interaction }, "enable or disable interaction on standard input" }, { "timelimit", HAS_ARG | OPT_EXPERT, { .func_arg = opt_timelimit }, - "set max runtime in seconds", "limit" }, + "set max runtime in seconds in CPU user time", "limit" }, { "dump", OPT_BOOL | OPT_EXPERT, { &do_pkt_dump }, "dump each input packet" }, { "hex", OPT_BOOL | OPT_EXPERT, { &do_hex_dump }, diff --git a/fftools/ffmpeg_qsv.c b/fftools/ffmpeg_qsv.c index 9c4285b6c72..960c88b69d5 100644 --- a/fftools/ffmpeg_qsv.c +++ b/fftools/ffmpeg_qsv.c @@ -28,6 +28,7 @@ #include "ffmpeg.h" +static AVBufferRef *hw_device_ctx; char *qsv_device = NULL; static int qsv_get_buffer(AVCodecContext *s, AVFrame *frame, int flags) diff --git a/fftools/ffmpeg_videotoolbox.c b/fftools/ffmpeg_videotoolbox.c index ad6174d3c7b..a6b78d0f7d8 100644 --- a/fftools/ffmpeg_videotoolbox.c +++ b/fftools/ffmpeg_videotoolbox.c @@ -51,10 +51,12 @@ static int videotoolbox_retrieve_data(AVCodecContext *s, AVFrame *frame) case kCVPixelFormatType_422YpCbCr8: vt->tmp_frame->format = AV_PIX_FMT_UYVY422; break; case kCVPixelFormatType_32BGRA: vt->tmp_frame->format = AV_PIX_FMT_BGRA; break; #ifdef kCFCoreFoundationVersionNumber10_7 - case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: vt->tmp_frame->format = AV_PIX_FMT_NV12; break; + case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: + case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange: vt->tmp_frame->format = AV_PIX_FMT_NV12; break; #endif #if HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE - case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange: vt->tmp_frame->format = AV_PIX_FMT_P010; break; + case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange: + case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange: vt->tmp_frame->format = AV_PIX_FMT_P010; break; #endif default: av_log(NULL, AV_LOG_ERROR, @@ -65,7 +67,7 @@ static int videotoolbox_retrieve_data(AVCodecContext *s, AVFrame *frame) vt->tmp_frame->width = frame->width; vt->tmp_frame->height = frame->height; - ret = av_frame_get_buffer(vt->tmp_frame, 32); + ret = av_frame_get_buffer(vt->tmp_frame, 0); if (ret < 0) return ret; diff --git a/fftools/ffplay.c b/fftools/ffplay.c index fee0619f7c0..d673b8049a6 100644 --- a/fftools/ffplay.c +++ b/fftools/ffplay.c @@ -40,6 +40,7 @@ #include "libavutil/samplefmt.h" #include "libavutil/avassert.h" #include "libavutil/time.h" +#include "libavutil/bprint.h" #include "libavformat/avformat.h" #include "libavdevice/avdevice.h" #include "libswscale/swscale.h" @@ -326,7 +327,7 @@ static int display_disable; static int borderless; static int alwaysontop; static int startup_volume = 100; -static int show_status = 1; +static int show_status = -1; static int av_sync_type = AV_SYNC_AUDIO_MASTER; static int64_t start_time = AV_NOPTS_VALUE; static int64_t duration = AV_NOPTS_VALUE; @@ -644,7 +645,10 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) { if (packet_queue_get(d->queue, &pkt, 1, &d->pkt_serial) < 0) return -1; } - } while (d->queue->serial != d->pkt_serial); + if (d->queue->serial == d->pkt_serial) + break; + av_packet_unref(&pkt); + } while (1); if (pkt.data == flush_pkt.data) { avcodec_flush_buffers(d->avctx); @@ -1689,6 +1693,7 @@ static void video_refresh(void *opaque, double *remaining_time) } is->force_refresh = 0; if (show_status) { + AVBPrint buf; static int64_t last_time; int64_t cur_time; int aqsize, vqsize, sqsize; @@ -1712,18 +1717,28 @@ static void video_refresh(void *opaque, double *remaining_time) av_diff = get_master_clock(is) - get_clock(&is->vidclk); else if (is->audio_st) av_diff = get_master_clock(is) - get_clock(&is->audclk); - av_log(NULL, AV_LOG_INFO, - "%7.2f %s:%7.3f fd=%4d aq=%5dKB vq=%5dKB sq=%5dB f=%"PRId64"/%"PRId64" \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); - fflush(stdout); + + 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", + 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); + + if (show_status == 1 && AV_LOG_INFO > av_log_get_level()) + fprintf(stderr, "%s", buf.str); + else + av_log(NULL, AV_LOG_INFO, "%s", buf.str); + + fflush(stderr); + av_bprint_finalize(&buf, NULL); + last_time = cur_time; } } @@ -2760,9 +2775,6 @@ static int read_thread(void *arg) } memset(st_index, -1, sizeof(st_index)); - is->last_video_stream = is->video_stream = -1; - is->last_audio_stream = is->audio_stream = -1; - is->last_subtitle_stream = is->subtitle_stream = -1; is->eof = 0; ic = avformat_alloc_context(); @@ -2974,7 +2986,7 @@ static int read_thread(void *arg) } if (is->queue_attachments_req) { if (is->video_st && is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC) { - AVPacket copy = { 0 }; + AVPacket copy; if ((ret = av_packet_ref(©, &is->video_st->attached_pic)) < 0) goto fail; packet_queue_put(&is->videoq, ©); @@ -3068,6 +3080,9 @@ static VideoState *stream_open(const char *filename, AVInputFormat *iformat) is = av_mallocz(sizeof(VideoState)); if (!is) return NULL; + is->last_video_stream = is->video_stream = -1; + is->last_audio_stream = is->audio_stream = -1; + is->last_subtitle_stream = is->subtitle_stream = -1; is->filename = av_strdup(filename); if (!is->filename) goto fail; diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c index 5aaddb03089..5515e1b31b6 100644 --- a/fftools/ffprobe.c +++ b/fftools/ffprobe.c @@ -36,6 +36,7 @@ #include "libavutil/display.h" #include "libavutil/hash.h" #include "libavutil/mastering_display_metadata.h" +#include "libavutil/dovi_meta.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/spherical.h" @@ -254,6 +255,7 @@ static const OptionDef *options; /* FFprobe context */ static const char *input_filename; +static const char *print_input_filename; static AVInputFormat *iformat = NULL; static struct AVHashContext *hash; @@ -1083,12 +1085,12 @@ typedef struct CompactContext { #define OFFSET(x) offsetof(CompactContext, x) static const AVOption compact_options[]= { - {"item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, CHAR_MIN, CHAR_MAX }, - {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, CHAR_MIN, CHAR_MAX }, + {"item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, 0, 0 }, + {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, 0, 0 }, {"nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, {"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, - {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, CHAR_MIN, CHAR_MAX }, - {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, CHAR_MIN, CHAR_MAX }, + {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, 0, 0 }, + {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, 0, 0 }, {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, {"p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, {NULL}, @@ -1199,12 +1201,12 @@ static const Writer compact_writer = { #define OFFSET(x) offsetof(CompactContext, x) static const AVOption csv_options[] = { - {"item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str=","}, CHAR_MIN, CHAR_MAX }, - {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str=","}, CHAR_MIN, CHAR_MAX }, + {"item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str=","}, 0, 0 }, + {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str=","}, 0, 0 }, {"nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, {"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, - {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, CHAR_MIN, CHAR_MAX }, - {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, CHAR_MIN, CHAR_MAX }, + {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, 0, 0 }, + {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, 0, 0 }, {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, {"p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, {NULL}, @@ -1237,8 +1239,8 @@ typedef struct FlatContext { #define OFFSET(x) offsetof(FlatContext, x) static const AVOption flat_options[]= { - {"sep_char", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, CHAR_MIN, CHAR_MAX }, - {"s", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, CHAR_MIN, CHAR_MAX }, + {"sep_char", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, 0, 0 }, + {"s", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, 0, 0 }, {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, {"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, {NULL}, @@ -1535,7 +1537,7 @@ static void json_print_section_header(WriterContext *wctx) if (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES) { if (!json->compact) JSON_INDENT(); - printf("\"type\": \"%s\"%s", section->name, json->item_sep); + printf("\"type\": \"%s\"", section->name); } } av_bprint_finalize(&buf, NULL); @@ -1579,8 +1581,10 @@ static inline void json_print_item_str(WriterContext *wctx, static void json_print_str(WriterContext *wctx, const char *key, const char *value) { JSONContext *json = wctx->priv; + const struct section *parent_section = wctx->level ? + wctx->section[wctx->level-1] : NULL; - if (wctx->nb_item[wctx->level]) + if (wctx->nb_item[wctx->level] || (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES)) printf("%s", json->item_sep); if (!json->compact) JSON_INDENT(); @@ -1590,9 +1594,11 @@ static void json_print_str(WriterContext *wctx, const char *key, const char *val static void json_print_int(WriterContext *wctx, const char *key, long long int value) { JSONContext *json = wctx->priv; + const struct section *parent_section = wctx->level ? + wctx->section[wctx->level-1] : NULL; AVBPrint buf; - if (wctx->nb_item[wctx->level]) + if (wctx->nb_item[wctx->level] || (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES)) printf("%s", json->item_sep); if (!json->compact) JSON_INDENT(); @@ -1923,6 +1929,16 @@ 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_DOVI_CONF) { + AVDOVIDecoderConfigurationRecord *dovi = (AVDOVIDecoderConfigurationRecord *)sd->data; + print_int("dv_version_major", dovi->dv_version_major); + print_int("dv_version_minor", dovi->dv_version_minor); + print_int("dv_profile", dovi->dv_profile); + print_int("dv_level", dovi->dv_level); + print_int("rpu_present_flag", dovi->rpu_present_flag); + print_int("el_present_flag", dovi->el_present_flag); + print_int("bl_present_flag", dovi->bl_present_flag); + print_int("dv_bl_signal_compatibility_id", dovi->dv_bl_signal_compatibility_id); } writer_print_section_footer(w); } @@ -2531,6 +2547,7 @@ static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_id if (dec_ctx) { print_int("coded_width", dec_ctx->coded_width); print_int("coded_height", dec_ctx->coded_height); + print_int("closed_captions", !!(dec_ctx->properties & FF_CODEC_PROPERTY_CLOSED_CAPTIONS)); } #endif print_int("has_b_frames", par->video_delay); @@ -2832,7 +2849,8 @@ static void show_error(WriterContext *w, int err) writer_print_section_footer(w); } -static int open_input_file(InputFile *ifile, const char *filename) +static int open_input_file(InputFile *ifile, const char *filename, + const char *print_filename) { int err, i; AVFormatContext *fmt_ctx = NULL; @@ -2854,6 +2872,10 @@ static int open_input_file(InputFile *ifile, const char *filename) print_error(filename, err); return err; } + if (print_filename) { + av_freep(&fmt_ctx->url); + fmt_ctx->url = av_strdup(print_filename); + } ifile->fmt_ctx = fmt_ctx; if (scan_all_pmts_set) av_dict_set(&format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE); @@ -2930,6 +2952,7 @@ static int open_input_file(InputFile *ifile, const char *filename) ist->dec_ctx->pkt_timebase = stream->time_base; ist->dec_ctx->framerate = stream->avg_frame_rate; #if FF_API_LAVF_AVCTX + ist->dec_ctx->properties = stream->codec->properties; ist->dec_ctx->coded_width = stream->codec->coded_width; ist->dec_ctx->coded_height = stream->codec->coded_height; #endif @@ -2967,7 +2990,8 @@ static void close_input_file(InputFile *ifile) avformat_close_input(&ifile->fmt_ctx); } -static int probe_file(WriterContext *wctx, const char *filename) +static int probe_file(WriterContext *wctx, const char *filename, + const char *print_filename) { InputFile ifile = { 0 }; int ret, i; @@ -2976,7 +3000,7 @@ static int probe_file(WriterContext *wctx, const char *filename) do_read_frames = do_show_frames || do_count_frames; do_read_packets = do_show_packets || do_count_packets; - ret = open_input_file(&ifile, filename); + ret = open_input_file(&ifile, filename, print_filename); if (ret < 0) goto end; @@ -3282,6 +3306,12 @@ static int opt_input_file_i(void *optctx, const char *opt, const char *arg) return 0; } +static int opt_print_filename(void *optctx, const char *opt, const char *arg) +{ + print_input_filename = arg; + return 0; +} + void show_help_default(const char *opt, const char *arg) { av_log_set_callback(log_callback_help); @@ -3471,7 +3501,7 @@ static int opt_sections(void *optctx, const char *opt, const char *arg) return 0; } -static int opt_show_versions(const char *opt, const char *arg) +static int opt_show_versions(void *optctx, const char *opt, const char *arg) { mark_section_show_entries(SECTION_ID_PROGRAM_VERSION, 1, NULL); mark_section_show_entries(SECTION_ID_LIBRARY_VERSION, 1, NULL); @@ -3479,7 +3509,7 @@ static int opt_show_versions(const char *opt, const char *arg) } #define DEFINE_OPT_SHOW_SECTION(section, target_section_id) \ - static int opt_show_##section(const char *opt, const char *arg) \ + static int opt_show_##section(void *optctx, const char *opt, const char *arg) \ { \ mark_section_show_entries(SECTION_ID_##target_section_id, 1, NULL); \ return 0; \ @@ -3507,39 +3537,40 @@ static const OptionDef real_options[] = { "use sexagesimal format HOURS:MM:SS.MICROSECONDS for time units" }, { "pretty", 0, {.func_arg = opt_pretty}, "prettify the format of displayed values, make it more human readable" }, - { "print_format", OPT_STRING | HAS_ARG, {(void*)&print_format}, + { "print_format", OPT_STRING | HAS_ARG, { &print_format }, "set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml)", "format" }, - { "of", OPT_STRING | HAS_ARG, {(void*)&print_format}, "alias for -print_format", "format" }, - { "select_streams", OPT_STRING | HAS_ARG, {(void*)&stream_specifier}, "select the specified streams", "stream_specifier" }, + { "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, {(void*)&do_show_data}, "show packets data" }, - { "show_data_hash", OPT_STRING | HAS_ARG, {(void*)&show_data_hash}, "show packets data hash" }, - { "show_error", 0, {(void*)&opt_show_error}, "show probing error" }, - { "show_format", 0, {(void*)&opt_show_format}, "show format/container info" }, - { "show_frames", 0, {(void*)&opt_show_frames}, "show frames info" }, + { "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_format_entry", HAS_ARG, {.func_arg = opt_show_format_entry}, "show a particular entry from the format/container info", "entry" }, { "show_entries", HAS_ARG, {.func_arg = opt_show_entries}, "show a set of specified entries", "entry_list" }, #if HAVE_THREADS - { "show_log", OPT_INT|HAS_ARG, {(void*)&do_show_log}, "show log" }, + { "show_log", OPT_INT|HAS_ARG, { &do_show_log }, "show log" }, #endif - { "show_packets", 0, {(void*)&opt_show_packets}, "show packets info" }, - { "show_programs", 0, {(void*)&opt_show_programs}, "show programs info" }, - { "show_streams", 0, {(void*)&opt_show_streams}, "show streams info" }, - { "show_chapters", 0, {(void*)&opt_show_chapters}, "show chapters info" }, - { "count_frames", OPT_BOOL, {(void*)&do_count_frames}, "count the number of frames per stream" }, - { "count_packets", OPT_BOOL, {(void*)&do_count_packets}, "count the number of packets per stream" }, - { "show_program_version", 0, {(void*)&opt_show_program_version}, "show ffprobe version" }, - { "show_library_versions", 0, {(void*)&opt_show_library_versions}, "show library versions" }, - { "show_versions", 0, {(void*)&opt_show_versions}, "show program and library versions" }, - { "show_pixel_formats", 0, {(void*)&opt_show_pixel_formats}, "show pixel format descriptions" }, - { "show_private_data", OPT_BOOL, {(void*)&show_private_data}, "show private data" }, - { "private", OPT_BOOL, {(void*)&show_private_data}, "same as show_private_data" }, + { "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_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" }, { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {.func_arg = opt_default}, "generic catch all option", "" }, { "i", HAS_ARG, {.func_arg = opt_input_file_i}, "read specified file", "input_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 }, "read and decode the streams to fill missing information with heuristics" }, { NULL, }, @@ -3688,7 +3719,7 @@ int main(int argc, char **argv) av_log(NULL, AV_LOG_ERROR, "Use -h to get full help or, even better, run 'man %s'.\n", program_name); ret = AVERROR(EINVAL); } else if (input_filename) { - ret = probe_file(wctx, input_filename); + ret = probe_file(wctx, input_filename, print_input_filename); if (ret < 0 && do_show_error) show_error(wctx, ret); } diff --git a/libavcodec/.gitignore b/libavcodec/.gitignore new file mode 100644 index 00000000000..28814f72331 --- /dev/null +++ b/libavcodec/.gitignore @@ -0,0 +1,6 @@ +/*_tablegen +/*_tables.c +/*_tables.h +/bsf_list.c +/codec_list.c +/parser_list.c diff --git a/libavcodec/4xm.c b/libavcodec/4xm.c index 1f4e2aee24c..336c651d317 100644 --- a/libavcodec/4xm.c +++ b/libavcodec/4xm.c @@ -525,6 +525,10 @@ static int decode_i_block(FourXContext *f, int16_t *block) break; if (code == 0xf0) { i += 16; + if (i >= 64) { + av_log(f->avctx, AV_LOG_ERROR, "run %d overflow\n", i); + return 0; + } } else { if (code & 0xf) { level = get_xbits(&f->gb, code & 0xf); diff --git a/libavcodec/8svx.c b/libavcodec/8svx.c index edc945c6973..092dbaae040 100644 --- a/libavcodec/8svx.c +++ b/libavcodec/8svx.c @@ -164,8 +164,7 @@ static av_cold int eightsvx_decode_init(AVCodecContext *avctx) case AV_CODEC_ID_8SVX_FIB: esc->table = fibonacci; break; case AV_CODEC_ID_8SVX_EXP: esc->table = exponential; break; default: - av_log(avctx, AV_LOG_ERROR, "Invalid codec id %d.\n", avctx->codec->id); - return AVERROR_INVALIDDATA; + av_assert1(0); } avctx->sample_fmt = AV_SAMPLE_FMT_U8P; diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 3cd73fbcc60..5a6ea59715a 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -6,12 +6,18 @@ HEADERS = ac3_parser.h \ avcodec.h \ avdct.h \ avfft.h \ + bsf.h \ + codec.h \ + codec_desc.h \ + codec_id.h \ + codec_par.h \ d3d11va.h \ dirac.h \ dv_profile.h \ dxva2.h \ jni.h \ mediacodec.h \ + packet.h \ qsv.h \ vaapi.h \ vdpau.h \ @@ -167,12 +173,15 @@ OBJS-$(CONFIG_AAC_ENCODER) += aacenc.o aaccoder.o aacenctab.o \ aacenc_ltp.o \ aacenc_pred.o \ psymodel.o mpeg4audio.o kbdwin.o cbrt_data.o +OBJS-$(CONFIG_AAC_MF_ENCODER) += mfenc.o mf_utils.o OBJS-$(CONFIG_AASC_DECODER) += aasc.o msrledec.o OBJS-$(CONFIG_AC3_DECODER) += ac3dec_float.o ac3dec_data.o ac3.o kbdwin.o ac3tab.o OBJS-$(CONFIG_AC3_FIXED_DECODER) += ac3dec_fixed.o ac3dec_data.o ac3.o kbdwin.o ac3tab.o OBJS-$(CONFIG_AC3_ENCODER) += ac3enc_float.o ac3enc.o ac3tab.o \ ac3.o kbdwin.o OBJS-$(CONFIG_AC3_FIXED_ENCODER) += ac3enc_fixed.o ac3enc.o ac3tab.o ac3.o +OBJS-$(CONFIG_AC3_MF_ENCODER) += mfenc.o mf_utils.o +OBJS-$(CONFIG_ACELP_KELVIN_DECODER) += g729dec.o lsp.o celp_math.o celp_filters.o acelp_filters.o acelp_pitch_delay.o acelp_vectors.o g729postfilter.o OBJS-$(CONFIG_AGM_DECODER) += agm.o OBJS-$(CONFIG_AIC_DECODER) += aic.o OBJS-$(CONFIG_ALAC_DECODER) += alac.o alac_data.o alacdsp.o @@ -193,10 +202,10 @@ OBJS-$(CONFIG_AMV_ENCODER) += mjpegenc.o mjpegenc_common.o \ OBJS-$(CONFIG_ANM_DECODER) += anm.o OBJS-$(CONFIG_ANSI_DECODER) += ansi.o cga_data.o OBJS-$(CONFIG_APE_DECODER) += apedec.o -OBJS-$(CONFIG_APTX_DECODER) += aptx.o -OBJS-$(CONFIG_APTX_ENCODER) += aptx.o -OBJS-$(CONFIG_APTX_HD_DECODER) += aptx.o -OBJS-$(CONFIG_APTX_HD_ENCODER) += aptx.o +OBJS-$(CONFIG_APTX_DECODER) += aptxdec.o aptx.o +OBJS-$(CONFIG_APTX_ENCODER) += aptxenc.o aptx.o +OBJS-$(CONFIG_APTX_HD_DECODER) += aptxdec.o aptx.o +OBJS-$(CONFIG_APTX_HD_ENCODER) += aptxenc.o aptx.o OBJS-$(CONFIG_APNG_DECODER) += png.o pngdec.o pngdsp.o OBJS-$(CONFIG_APNG_ENCODER) += png.o pngenc.o OBJS-$(CONFIG_ARBC_DECODER) += arbc.o @@ -241,8 +250,9 @@ OBJS-$(CONFIG_BRENDER_PIX_DECODER) += brenderpix.o OBJS-$(CONFIG_C93_DECODER) += c93.o OBJS-$(CONFIG_CAVS_DECODER) += cavs.o cavsdec.o cavsdsp.o \ cavsdata.o -OBJS-$(CONFIG_CCAPTION_DECODER) += ccaption_dec.o +OBJS-$(CONFIG_CCAPTION_DECODER) += ccaption_dec.o ass.o OBJS-$(CONFIG_CDGRAPHICS_DECODER) += cdgraphics.o +OBJS-$(CONFIG_CDTOONS_DECODER) += cdtoons.o OBJS-$(CONFIG_CDXL_DECODER) += cdxl.o OBJS-$(CONFIG_CFHD_DECODER) += cfhd.o cfhddata.o OBJS-$(CONFIG_CINEPAK_DECODER) += cinepak.o @@ -263,6 +273,7 @@ OBJS-$(CONFIG_DCA_DECODER) += dcadec.o dca.o dcadata.o dcahuff.o \ OBJS-$(CONFIG_DCA_ENCODER) += dcaenc.o dca.o dcadata.o dcahuff.o \ dcaadpcm.o OBJS-$(CONFIG_DDS_DECODER) += dds.o +OBJS-$(CONFIG_DERF_DPCM_DECODER) += dpcm.o OBJS-$(CONFIG_DIRAC_DECODER) += diracdec.o dirac.o diracdsp.o diractab.o \ dirac_arith.o dirac_dwt.o dirac_vlc.o OBJS-$(CONFIG_DFA_DECODER) += dfa.o @@ -281,8 +292,8 @@ OBJS-$(CONFIG_DSS_SP_DECODER) += dss_sp.o OBJS-$(CONFIG_DST_DECODER) += dstdec.o dsd.o OBJS-$(CONFIG_DVBSUB_DECODER) += dvbsubdec.o OBJS-$(CONFIG_DVBSUB_ENCODER) += dvbsub.o -OBJS-$(CONFIG_DVDSUB_DECODER) += dvdsubdec.o -OBJS-$(CONFIG_DVDSUB_ENCODER) += dvdsubenc.o +OBJS-$(CONFIG_DVDSUB_DECODER) += dvdsubdec.o dvdsub.o +OBJS-$(CONFIG_DVDSUB_ENCODER) += dvdsubenc.o dvdsub.o OBJS-$(CONFIG_DVAUDIO_DECODER) += dvaudiodec.o OBJS-$(CONFIG_DVVIDEO_DECODER) += dvdec.o dv.o dvdata.o OBJS-$(CONFIG_DVVIDEO_ENCODER) += dvenc.o dv.o dvdata.o @@ -350,6 +361,7 @@ OBJS-$(CONFIG_H264_DECODER) += h264dec.o h264_cabac.o h264_cavlc.o \ OBJS-$(CONFIG_H264_AMF_ENCODER) += amfenc_h264.o OBJS-$(CONFIG_H264_CUVID_DECODER) += cuviddec.o OBJS-$(CONFIG_H264_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_H264_MF_ENCODER) += mfenc.o mf_utils.o OBJS-$(CONFIG_H264_MMAL_DECODER) += mmaldec.o OBJS-$(CONFIG_H264_NVENC_ENCODER) += nvenc_h264.o OBJS-$(CONFIG_NVENC_ENCODER) += nvenc_h264.o @@ -364,6 +376,7 @@ OBJS-$(CONFIG_H264_V4L2M2M_DECODER) += v4l2_m2m_dec.o OBJS-$(CONFIG_H264_V4L2M2M_ENCODER) += v4l2_m2m_enc.o OBJS-$(CONFIG_HAP_DECODER) += hapdec.o hap.o OBJS-$(CONFIG_HAP_ENCODER) += hapenc.o hap.o +OBJS-$(CONFIG_HCA_DECODER) += hcadec.o OBJS-$(CONFIG_HCOM_DECODER) += hcom.o OBJS-$(CONFIG_HEVC_DECODER) += hevcdec.o hevc_mvs.o \ hevc_cabac.o hevc_refs.o hevcpred.o \ @@ -371,6 +384,7 @@ OBJS-$(CONFIG_HEVC_DECODER) += hevcdec.o hevc_mvs.o \ OBJS-$(CONFIG_HEVC_AMF_ENCODER) += amfenc_hevc.o OBJS-$(CONFIG_HEVC_CUVID_DECODER) += cuviddec.o OBJS-$(CONFIG_HEVC_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_HEVC_MF_ENCODER) += mfenc.o mf_utils.o OBJS-$(CONFIG_HEVC_NVENC_ENCODER) += nvenc_hevc.o OBJS-$(CONFIG_NVENC_HEVC_ENCODER) += nvenc_hevc.o OBJS-$(CONFIG_HEVC_QSV_DECODER) += qsvdec_h2645.o @@ -393,6 +407,7 @@ OBJS-$(CONFIG_IFF_ILBM_DECODER) += iff.o OBJS-$(CONFIG_ILBC_DECODER) += ilbcdec.o OBJS-$(CONFIG_IMC_DECODER) += imc.o OBJS-$(CONFIG_IMM4_DECODER) += imm4.o +OBJS-$(CONFIG_IMM5_DECODER) += imm5.o OBJS-$(CONFIG_INDEO2_DECODER) += indeo2.o OBJS-$(CONFIG_INDEO3_DECODER) += indeo3.o OBJS-$(CONFIG_INDEO4_DECODER) += indeo4.o ivi.o @@ -425,6 +440,7 @@ OBJS-$(CONFIG_METASOUND_DECODER) += metasound.o metasound_data.o \ OBJS-$(CONFIG_MICRODVD_DECODER) += microdvddec.o ass.o OBJS-$(CONFIG_MIMIC_DECODER) += mimic.o OBJS-$(CONFIG_MJPEG_DECODER) += mjpegdec.o +OBJS-$(CONFIG_MJPEG_QSV_DECODER) += qsvdec_other.o OBJS-$(CONFIG_MJPEG_ENCODER) += mjpegenc.o mjpegenc_common.o \ mjpegenc_huffman.o OBJS-$(CONFIG_MJPEGB_DECODER) += mjpegbdec.o @@ -446,6 +462,7 @@ OBJS-$(CONFIG_MP2FIXED_ENCODER) += mpegaudioenc_fixed.o mpegaudio.o \ mpegaudiodata.o mpegaudiodsp_data.o OBJS-$(CONFIG_MP2FLOAT_DECODER) += mpegaudiodec_float.o OBJS-$(CONFIG_MP3_DECODER) += mpegaudiodec_fixed.o +OBJS-$(CONFIG_MP3_MF_ENCODER) += mfenc.o mf_utils.o OBJS-$(CONFIG_MP3ADU_DECODER) += mpegaudiodec_fixed.o OBJS-$(CONFIG_MP3ADUFLOAT_DECODER) += mpegaudiodec_float.o OBJS-$(CONFIG_MP3FLOAT_DECODER) += mpegaudiodec_float.o @@ -488,19 +505,23 @@ OBJS-$(CONFIG_MSVIDEO1_DECODER) += msvideo1.o OBJS-$(CONFIG_MSVIDEO1_ENCODER) += msvideo1enc.o elbg.o OBJS-$(CONFIG_MSZH_DECODER) += lcldec.o OBJS-$(CONFIG_MTS2_DECODER) += mss4.o +OBJS-$(CONFIG_MV30_DECODER) += mv30.o OBJS-$(CONFIG_MVC1_DECODER) += mvcdec.o OBJS-$(CONFIG_MVC2_DECODER) += mvcdec.o +OBJS-$(CONFIG_MVDV_DECODER) += midivid.o +OBJS-$(CONFIG_MVHA_DECODER) += mvha.o OBJS-$(CONFIG_MWSC_DECODER) += mwsc.o OBJS-$(CONFIG_MXPEG_DECODER) += mxpegdec.o OBJS-$(CONFIG_NELLYMOSER_DECODER) += nellymoserdec.o nellymoser.o OBJS-$(CONFIG_NELLYMOSER_ENCODER) += nellymoserenc.o nellymoser.o +OBJS-$(CONFIG_NOTCHLC_DECODER) += notchlc.o OBJS-$(CONFIG_NUV_DECODER) += nuv.o rtjpeg.o OBJS-$(CONFIG_ON2AVC_DECODER) += on2avc.o on2avcdata.o OBJS-$(CONFIG_OPUS_DECODER) += opusdec.o opus.o opus_celt.o opus_rc.o \ opus_pvq.o opus_silk.o opustab.o vorbis_data.o \ opusdsp.o OBJS-$(CONFIG_OPUS_ENCODER) += opusenc.o opus.o opus_rc.o opustab.o opus_pvq.o \ - opusenc_psy.o + opusenc_psy.o vorbis_data.o OBJS-$(CONFIG_PAF_AUDIO_DECODER) += pafaudio.o OBJS-$(CONFIG_PAF_VIDEO_DECODER) += pafvideo.o OBJS-$(CONFIG_PAM_DECODER) += pnmdec.o pnm.o @@ -509,6 +530,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_PFM_DECODER) += pnmdec.o pnm.o OBJS-$(CONFIG_PGM_DECODER) += pnmdec.o pnm.o OBJS-$(CONFIG_PGM_ENCODER) += pnmenc.o OBJS-$(CONFIG_PGMYUV_DECODER) += pnmdec.o pnm.o @@ -578,13 +600,14 @@ OBJS-$(CONFIG_SIPR_DECODER) += sipr.o acelp_pitch_delay.o \ celp_math.o acelp_vectors.o \ acelp_filters.o celp_filters.o \ sipr16k.o +OBJS-$(CONFIG_SIREN_DECODER) += siren.o OBJS-$(CONFIG_SMACKAUD_DECODER) += smacker.o OBJS-$(CONFIG_SMACKER_DECODER) += smacker.o OBJS-$(CONFIG_SMC_DECODER) += smc.o OBJS-$(CONFIG_SMVJPEG_DECODER) += smvjpegdec.o OBJS-$(CONFIG_SNOW_DECODER) += snowdec.o snow.o snow_dwt.o OBJS-$(CONFIG_SNOW_ENCODER) += snowenc.o snow.o snow_dwt.o \ - h263.o ituh263enc.o + h263.o h263data.o ituh263enc.o OBJS-$(CONFIG_SOL_DPCM_DECODER) += dpcm.o OBJS-$(CONFIG_SONIC_DECODER) += sonic.o OBJS-$(CONFIG_SONIC_ENCODER) += sonic.o @@ -604,10 +627,10 @@ OBJS-$(CONFIG_SUNRAST_ENCODER) += sunrastenc.o OBJS-$(CONFIG_LIBRSVG_DECODER) += librsvgdec.o OBJS-$(CONFIG_SBC_DECODER) += sbcdec.o sbcdec_data.o sbc.o OBJS-$(CONFIG_SBC_ENCODER) += sbcenc.o sbc.o sbcdsp.o sbcdsp_data.o -OBJS-$(CONFIG_SVQ1_DECODER) += svq1dec.o svq1.o svq13.o h263data.o +OBJS-$(CONFIG_SVQ1_DECODER) += svq1dec.o svq1.o h263data.o OBJS-$(CONFIG_SVQ1_ENCODER) += svq1enc.o svq1.o h263data.o \ h263.o ituh263enc.o -OBJS-$(CONFIG_SVQ3_DECODER) += svq3.o svq13.o mpegutils.o h264data.o +OBJS-$(CONFIG_SVQ3_DECODER) += svq3.o mpegutils.o h264data.o OBJS-$(CONFIG_TEXT_DECODER) += textdec.o ass.o OBJS-$(CONFIG_TEXT_ENCODER) += srtenc.o ass_split.o OBJS-$(CONFIG_TAK_DECODER) += takdec.o tak.o takdsp.o @@ -616,7 +639,7 @@ OBJS-$(CONFIG_TARGA_ENCODER) += targaenc.o rle.o OBJS-$(CONFIG_TARGA_Y216_DECODER) += targa_y216dec.o OBJS-$(CONFIG_TDSC_DECODER) += tdsc.o OBJS-$(CONFIG_TIERTEXSEQVIDEO_DECODER) += tiertexseqv.o -OBJS-$(CONFIG_TIFF_DECODER) += tiff.o lzw.o faxcompr.o tiff_data.o tiff_common.o +OBJS-$(CONFIG_TIFF_DECODER) += tiff.o lzw.o faxcompr.o tiff_data.o tiff_common.o mjpegdec.o OBJS-$(CONFIG_TIFF_ENCODER) += tiffenc.o rle.o lzwenc.o tiff_data.o OBJS-$(CONFIG_TMV_DECODER) += tmv.o cga_data.o OBJS-$(CONFIG_TRUEHD_DECODER) += mlpdec.o mlpdsp.o @@ -682,10 +705,11 @@ OBJS-$(CONFIG_VP9_CUVID_DECODER) += cuviddec.o OBJS-$(CONFIG_VP9_MEDIACODEC_DECODER) += mediacodecdec.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 OBJS-$(CONFIG_VPLAYER_DECODER) += textdec.o ass.o OBJS-$(CONFIG_VP9_V4L2M2M_DECODER) += v4l2_m2m_dec.o OBJS-$(CONFIG_VQA_DECODER) += vqavideo.o -OBJS-$(CONFIG_WAVPACK_DECODER) += wavpack.o +OBJS-$(CONFIG_WAVPACK_DECODER) += wavpack.o dsd.o OBJS-$(CONFIG_WAVPACK_ENCODER) += wavpackenc.o OBJS-$(CONFIG_WCMV_DECODER) += wcmv.o OBJS-$(CONFIG_WEBP_DECODER) += webp.o @@ -701,7 +725,7 @@ OBJS-$(CONFIG_WMAVOICE_DECODER) += wmavoice.o \ celp_filters.o \ acelp_vectors.o acelp_filters.o OBJS-$(CONFIG_WMV1_DECODER) += msmpeg4dec.o msmpeg4.o msmpeg4data.o -OBJS-$(CONFIG_WMV1_ENCODER) += msmpeg4enc.o +OBJS-$(CONFIG_WMV1_ENCODER) += msmpeg4enc.o msmpeg4.o msmpeg4data.o OBJS-$(CONFIG_WMV2_DECODER) += wmv2dec.o wmv2.o wmv2data.o \ msmpeg4dec.o msmpeg4.o msmpeg4data.o OBJS-$(CONFIG_WMV2_ENCODER) += wmv2enc.o wmv2.o wmv2data.o \ @@ -803,7 +827,6 @@ OBJS-$(CONFIG_PCM_U32LE_DECODER) += pcm.o OBJS-$(CONFIG_PCM_U32LE_ENCODER) += pcm.o OBJS-$(CONFIG_PCM_VIDC_DECODER) += pcm.o OBJS-$(CONFIG_PCM_VIDC_ENCODER) += pcm.o -OBJS-$(CONFIG_PCM_ZORK_DECODER) += pcm.o OBJS-$(CONFIG_ADPCM_4XM_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_ADX_DECODER) += adxdec.o adx.o @@ -811,6 +834,7 @@ OBJS-$(CONFIG_ADPCM_ADX_ENCODER) += adxenc.o adx.o OBJS-$(CONFIG_ADPCM_AFC_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_AGM_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_AICA_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_ARGO_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_CT_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_DTK_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_EA_DECODER) += adpcm.o adpcm_data.o @@ -826,17 +850,23 @@ OBJS-$(CONFIG_ADPCM_G726_ENCODER) += g726.o OBJS-$(CONFIG_ADPCM_G726LE_DECODER) += g726.o OBJS-$(CONFIG_ADPCM_G726LE_ENCODER) += g726.o OBJS-$(CONFIG_ADPCM_IMA_AMV_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_ALP_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_APC_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_APM_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_CUNNING_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_DAT4_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_DK3_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_DK4_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_EA_EACS_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_EA_SEAD_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_ISS_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_MTF_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_OKI_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_QT_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_QT_ENCODER) += adpcmenc.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_RAD_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_SSI_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_SSI_ENCODER) += adpcmenc.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_SMJPEG_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_WAV_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_WAV_ENCODER) += adpcmenc.o adpcm_data.o @@ -856,6 +886,7 @@ OBJS-$(CONFIG_ADPCM_VIMA_DECODER) += vima.o adpcm_data.o OBJS-$(CONFIG_ADPCM_XA_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_YAMAHA_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_YAMAHA_ENCODER) += adpcmenc.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_ZORK_DECODER) += adpcm.o adpcm_data.o # hardware accelerators OBJS-$(CONFIG_D3D11VA) += dxva2.o @@ -878,7 +909,7 @@ OBJS-$(CONFIG_HEVC_D3D11VA_HWACCEL) += dxva2_hevc.o OBJS-$(CONFIG_HEVC_DXVA2_HWACCEL) += dxva2_hevc.o OBJS-$(CONFIG_HEVC_NVDEC_HWACCEL) += nvdec_hevc.o OBJS-$(CONFIG_HEVC_QSV_HWACCEL) += qsvdec_h2645.o -OBJS-$(CONFIG_HEVC_VAAPI_HWACCEL) += vaapi_hevc.o +OBJS-$(CONFIG_HEVC_VAAPI_HWACCEL) += vaapi_hevc.o h265_profile_level.o OBJS-$(CONFIG_HEVC_VDPAU_HWACCEL) += vdpau_hevc.o OBJS-$(CONFIG_MJPEG_NVDEC_HWACCEL) += nvdec_mjpeg.o OBJS-$(CONFIG_MJPEG_VAAPI_HWACCEL) += vaapi_mjpeg.o @@ -910,6 +941,7 @@ OBJS-$(CONFIG_VP9_D3D11VA_HWACCEL) += dxva2_vp9.o OBJS-$(CONFIG_VP9_DXVA2_HWACCEL) += dxva2_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 OBJS-$(CONFIG_VP8_QSV_HWACCEL) += qsvdec_other.o # libavformat dependencies @@ -988,6 +1020,7 @@ OBJS-$(CONFIG_LIBOPUS_DECODER) += libopusdec.o libopus.o \ vorbis_data.o OBJS-$(CONFIG_LIBOPUS_ENCODER) += libopusenc.o libopus.o \ vorbis_data.o +OBJS-$(CONFIG_LIBRAV1E_ENCODER) += librav1e.o OBJS-$(CONFIG_LIBSHINE_ENCODER) += libshine.o OBJS-$(CONFIG_LIBSPEEX_DECODER) += libspeexdec.o OBJS-$(CONFIG_LIBSPEEX_ENCODER) += libspeexenc.o @@ -1041,12 +1074,12 @@ OBJS-$(CONFIG_H261_PARSER) += h261_parser.o OBJS-$(CONFIG_H263_PARSER) += h263_parser.o OBJS-$(CONFIG_H264_PARSER) += h264_parser.o h264_sei.o h264data.o OBJS-$(CONFIG_HEVC_PARSER) += hevc_parser.o hevc_data.o +OBJS-$(CONFIG_JPEG2000_PARSER) += jpeg2000_parser.o OBJS-$(CONFIG_MJPEG_PARSER) += mjpeg_parser.o OBJS-$(CONFIG_MLP_PARSER) += mlp_parse.o mlp_parser.o mlp.o OBJS-$(CONFIG_MPEG4VIDEO_PARSER) += mpeg4video_parser.o h263.o \ mpeg4videodec.o mpeg4video.o \ ituh263dec.o h263dec.o h263data.o -OBJS-$(CONFIG_PNG_PARSER) += png_parser.o OBJS-$(CONFIG_MPEGAUDIO_PARSER) += mpegaudio_parser.o OBJS-$(CONFIG_MPEGVIDEO_PARSER) += mpegvideo_parser.o \ mpeg12.o mpeg12data.o @@ -1064,11 +1097,13 @@ 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_WEBP_PARSER) += webp_parser.o OBJS-$(CONFIG_XMA_PARSER) += xma_parser.o # bitstream filters OBJS-$(CONFIG_AAC_ADTSTOASC_BSF) += aac_adtstoasc_bsf.o mpeg4audio.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 @@ -1093,6 +1128,8 @@ OBJS-$(CONFIG_MP3_HEADER_DECOMPRESS_BSF) += mp3_header_decompress_bsf.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_PRORES_METADATA_BSF) += prores_metadata_bsf.o OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF) += remove_extradata_bsf.o OBJS-$(CONFIG_TEXT2MOVSUB_BSF) += movsub_bsf.o @@ -1130,6 +1167,7 @@ SKIPHEADERS-$(CONFIG_JNI) += ffjni.h SKIPHEADERS-$(CONFIG_LIBVPX) += libvpx.h SKIPHEADERS-$(CONFIG_LIBWEBP_ENCODER) += libwebpenc_common.h SKIPHEADERS-$(CONFIG_MEDIACODEC) += mediacodecdec_common.h mediacodec_surface.h mediacodec_wrapper.h mediacodec_sw_buffer.h +SKIPHEADERS-$(CONFIG_MEDIAFOUNDATION) += mf_utils.h SKIPHEADERS-$(CONFIG_NVDEC) += nvdec.h SKIPHEADERS-$(CONFIG_NVENC) += nvenc.h SKIPHEADERS-$(CONFIG_QSV) += qsv.h qsv_internal.h diff --git a/libavcodec/aac.h b/libavcodec/aac.h index c2b9c980cbd..d422ea5b139 100644 --- a/libavcodec/aac.h +++ b/libavcodec/aac.h @@ -356,7 +356,7 @@ struct AACContext { OutputConfiguration oc[2]; int warned_num_aac_frames; int warned_960_sbr; - + unsigned warned_71_wide; int warned_gain_control; /* aacdec functions pointers */ diff --git a/libavcodec/aac_adtstoasc_bsf.c b/libavcodec/aac_adtstoasc_bsf.c index 6541b1189c2..69453c706fb 100644 --- a/libavcodec/aac_adtstoasc_bsf.c +++ b/libavcodec/aac_adtstoasc_bsf.c @@ -21,8 +21,8 @@ #include "adts_header.h" #include "adts_parser.h" -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "put_bits.h" #include "get_bits.h" #include "mpeg4audio.h" @@ -134,8 +134,8 @@ static int aac_adtstoasc_init(AVBSFContext *ctx) /* Validate the extradata if the stream is already MPEG-4 AudioSpecificConfig */ if (ctx->par_in->extradata) { MPEG4AudioConfig mp4ac; - int ret = avpriv_mpeg4audio_get_config(&mp4ac, ctx->par_in->extradata, - ctx->par_in->extradata_size * 8, 1); + int ret = avpriv_mpeg4audio_get_config2(&mp4ac, ctx->par_in->extradata, + ctx->par_in->extradata_size, 1, ctx); if (ret < 0) { av_log(ctx, AV_LOG_ERROR, "Error parsing AudioSpecificConfig extradata!\n"); return ret; diff --git a/libavcodec/aacdec.c b/libavcodec/aacdec.c index 98b6e58be3b..d17852d8ba8 100644 --- a/libavcodec/aacdec.c +++ b/libavcodec/aacdec.c @@ -409,6 +409,8 @@ static int read_stream_mux_config(struct LATMContext *latmctx, } else { int esc; do { + if (get_bits_left(gb) < 9) + return AVERROR_INVALIDDATA; esc = get_bits(gb, 1); skip_bits(gb, 8); } while (esc); diff --git a/libavcodec/aacdec_fixed.c b/libavcodec/aacdec_fixed.c index 1d0142fdb02..9b2145c7290 100644 --- a/libavcodec/aacdec_fixed.c +++ b/libavcodec/aacdec_fixed.c @@ -461,7 +461,7 @@ AVCodec ff_aac_fixed_decoder = { AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_NONE }, .capabilities = AV_CODEC_CAP_CHANNEL_CONF | AV_CODEC_CAP_DR1, - .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .channel_layouts = aac_channel_layout, .profiles = NULL_IF_CONFIG_SMALL(ff_aac_profiles), .flush = flush, diff --git a/libavcodec/aacdec_template.c b/libavcodec/aacdec_template.c index 6e086e00dfa..a473e1bad7f 100644 --- a/libavcodec/aacdec_template.c +++ b/libavcodec/aacdec_template.c @@ -520,7 +520,7 @@ static void flush(AVCodecContext *avctx) * * @return Returns error status. 0 - OK, !0 - error */ -static int set_default_channel_config(AVCodecContext *avctx, +static int set_default_channel_config(AACContext *ac, AVCodecContext *avctx, uint8_t (*layout_map)[3], int *tags, int channel_config) @@ -547,7 +547,7 @@ static int set_default_channel_config(AVCodecContext *avctx, * As actual intended 7.1(wide) streams are very rare, default to assuming a * 7.1 layout was intended. */ - if (channel_config == 7 && avctx->strict_std_compliance < FF_COMPLIANCE_STRICT) { + if (channel_config == 7 && avctx->strict_std_compliance < FF_COMPLIANCE_STRICT && (!ac || !ac->warned_71_wide++)) { av_log(avctx, AV_LOG_INFO, "Assuming an incorrectly encoded 7.1 channel layout" " instead of a spec-compliant 7.1(wide) layout, use -strict %d to decode" " according to the specification instead.\n", FF_COMPLIANCE_STRICT); @@ -573,7 +573,7 @@ static ChannelElement *get_che(AACContext *ac, int type, int elem_id) av_log(ac->avctx, AV_LOG_DEBUG, "mono with CPE\n"); - if (set_default_channel_config(ac->avctx, layout_map, + if (set_default_channel_config(ac, ac->avctx, layout_map, &layout_map_tags, 2) < 0) return NULL; if (output_configure(ac, layout_map, layout_map_tags, @@ -592,7 +592,7 @@ static ChannelElement *get_che(AACContext *ac, int type, int elem_id) av_log(ac->avctx, AV_LOG_DEBUG, "stereo with SCE\n"); - if (set_default_channel_config(ac->avctx, layout_map, + if (set_default_channel_config(ac, ac->avctx, layout_map, &layout_map_tags, 1) < 0) return NULL; if (output_configure(ac, layout_map, layout_map_tags, @@ -841,7 +841,7 @@ static int decode_ga_specific_config(AACContext *ac, AVCodecContext *avctx, if (tags < 0) return tags; } else { - if ((ret = set_default_channel_config(avctx, layout_map, + if ((ret = set_default_channel_config(ac, avctx, layout_map, &tags, channel_config))) return ret; } @@ -937,7 +937,7 @@ static int decode_eld_specific_config(AACContext *ac, AVCodecContext *avctx, skip_bits_long(gb, 8 * len); } - if ((ret = set_default_channel_config(avctx, layout_map, + if ((ret = set_default_channel_config(ac, avctx, layout_map, &tags, channel_config))) return ret; @@ -975,7 +975,7 @@ static int decode_audio_specific_config_gb(AACContext *ac, int i, ret; GetBitContext gbc = *gb; - if ((i = ff_mpeg4audio_get_config_gb(m4ac, &gbc, sync_extension)) < 0) + if ((i = ff_mpeg4audio_get_config_gb(m4ac, &gbc, sync_extension, avctx)) < 0) return AVERROR_INVALIDDATA; if (m4ac->sampling_index > 12) { @@ -1157,6 +1157,9 @@ static av_cold int aac_decode_init(AVCodecContext *avctx) AACContext *ac = avctx->priv_data; int ret; + if (avctx->sample_rate > 96000) + return AVERROR_INVALIDDATA; + ret = ff_thread_once(&aac_table_init, &aac_static_table_init); if (ret != 0) return AVERROR_UNKNOWN; @@ -1197,7 +1200,7 @@ static av_cold int aac_decode_init(AVCodecContext *avctx) ac->oc[1].m4ac.chan_config = i; if (ac->oc[1].m4ac.chan_config) { - int ret = set_default_channel_config(avctx, layout_map, + int ret = set_default_channel_config(ac, avctx, layout_map, &layout_map_tags, ac->oc[1].m4ac.chan_config); if (!ret) output_configure(ac, layout_map, layout_map_tags, @@ -2999,7 +3002,7 @@ static int parse_adts_frame_header(AACContext *ac, GetBitContext *gb) push_output_configuration(ac); if (hdr_info.chan_config) { ac->oc[1].m4ac.chan_config = hdr_info.chan_config; - if ((ret = set_default_channel_config(ac->avctx, + if ((ret = set_default_channel_config(ac, ac->avctx, layout_map, &layout_map_tags, hdr_info.chan_config)) < 0) @@ -3246,9 +3249,15 @@ static int aac_decode_frame_int(AVCodecContext *avctx, void *data, err = AVERROR_INVALIDDATA; goto fail; } - while (elem_id > 0) - elem_id -= decode_extension_payload(ac, gb, elem_id, che_prev, che_prev_type); - err = 0; /* FIXME */ + err = 0; + while (elem_id > 0) { + int ret = decode_extension_payload(ac, gb, elem_id, che_prev, che_prev_type); + if (ret < 0) { + err = ret; + break; + } + elem_id -= ret; + } break; default: diff --git a/libavcodec/aacenc.c b/libavcodec/aacenc.c index 4d0abb107f3..db11e0ca299 100644 --- a/libavcodec/aacenc.c +++ b/libavcodec/aacenc.c @@ -39,6 +39,7 @@ #include "mpeg4audio.h" #include "kbdwin.h" #include "sinewin.h" +#include "profiles.h" #include "aac.h" #include "aactab.h" @@ -1131,6 +1132,7 @@ static const AVOption aacenc_options[] = { {"aac_ltp", "Long term prediction", offsetof(AACEncContext, options.ltp), AV_OPT_TYPE_BOOL, {.i64 = 0}, -1, 1, AACENC_FLAGS}, {"aac_pred", "AAC-Main prediction", offsetof(AACEncContext, options.pred), AV_OPT_TYPE_BOOL, {.i64 = 0}, -1, 1, AACENC_FLAGS}, {"aac_pce", "Forces the use of PCEs", offsetof(AACEncContext, options.pce), AV_OPT_TYPE_BOOL, {.i64 = 0}, -1, 1, AACENC_FLAGS}, + FF_AAC_PROFILE_OPTS {NULL} }; diff --git a/libavcodec/aacps.c b/libavcodec/aacps.c index d5dca64b0f2..22df160fe76 100644 --- a/libavcodec/aacps.c +++ b/libavcodec/aacps.c @@ -414,33 +414,33 @@ static void hybrid_synthesis(PSDSPContext *dsp, INTFLOAT out[2][38][64], memset(out[0][n], 0, 5*sizeof(out[0][n][0])); memset(out[1][n], 0, 5*sizeof(out[1][n][0])); for (i = 0; i < 12; i++) { - out[0][n][0] += in[ i][n][0]; - out[1][n][0] += in[ i][n][1]; + out[0][n][0] += (UINTFLOAT)in[ i][n][0]; + out[1][n][0] += (UINTFLOAT)in[ i][n][1]; } for (i = 0; i < 8; i++) { - out[0][n][1] += in[12+i][n][0]; - out[1][n][1] += in[12+i][n][1]; + out[0][n][1] += (UINTFLOAT)in[12+i][n][0]; + out[1][n][1] += (UINTFLOAT)in[12+i][n][1]; } for (i = 0; i < 4; i++) { - out[0][n][2] += in[20+i][n][0]; - out[1][n][2] += in[20+i][n][1]; - out[0][n][3] += in[24+i][n][0]; - out[1][n][3] += in[24+i][n][1]; - out[0][n][4] += in[28+i][n][0]; - out[1][n][4] += in[28+i][n][1]; + out[0][n][2] += (UINTFLOAT)in[20+i][n][0]; + out[1][n][2] += (UINTFLOAT)in[20+i][n][1]; + out[0][n][3] += (UINTFLOAT)in[24+i][n][0]; + out[1][n][3] += (UINTFLOAT)in[24+i][n][1]; + out[0][n][4] += (UINTFLOAT)in[28+i][n][0]; + out[1][n][4] += (UINTFLOAT)in[28+i][n][1]; } } dsp->hybrid_synthesis_deint(out, in + 27, 5, len); } else { for (n = 0; n < len; n++) { - out[0][n][0] = in[0][n][0] + in[1][n][0] + in[2][n][0] + - in[3][n][0] + in[4][n][0] + in[5][n][0]; - out[1][n][0] = in[0][n][1] + in[1][n][1] + in[2][n][1] + - in[3][n][1] + in[4][n][1] + in[5][n][1]; - out[0][n][1] = in[6][n][0] + in[7][n][0]; - out[1][n][1] = in[6][n][1] + in[7][n][1]; - out[0][n][2] = in[8][n][0] + in[9][n][0]; - out[1][n][2] = in[8][n][1] + in[9][n][1]; + out[0][n][0] = (UINTFLOAT)in[0][n][0] + in[1][n][0] + in[2][n][0] + + (UINTFLOAT)in[3][n][0] + in[4][n][0] + in[5][n][0]; + out[1][n][0] = (UINTFLOAT)in[0][n][1] + in[1][n][1] + in[2][n][1] + + (UINTFLOAT)in[3][n][1] + in[4][n][1] + in[5][n][1]; + out[0][n][1] = (UINTFLOAT)in[6][n][0] + in[7][n][0]; + out[1][n][1] = (UINTFLOAT)in[6][n][1] + in[7][n][1]; + out[0][n][2] = (UINTFLOAT)in[8][n][0] + in[9][n][0]; + out[1][n][2] = (UINTFLOAT)in[8][n][1] + in[9][n][1]; } dsp->hybrid_synthesis_deint(out, in + 7, 3, len); } diff --git a/libavcodec/aarch64/Makefile b/libavcodec/aarch64/Makefile index 00f93bf59fd..f6434e40da5 100644 --- a/libavcodec/aarch64/Makefile +++ b/libavcodec/aarch64/Makefile @@ -6,8 +6,10 @@ OBJS-$(CONFIG_H264DSP) += aarch64/h264dsp_init_aarch64.o OBJS-$(CONFIG_H264PRED) += aarch64/h264pred_init.o OBJS-$(CONFIG_H264QPEL) += aarch64/h264qpel_init_aarch64.o OBJS-$(CONFIG_HPELDSP) += aarch64/hpeldsp_init_aarch64.o +OBJS-$(CONFIG_IDCTDSP) += aarch64/idctdsp_init_aarch64.o OBJS-$(CONFIG_MPEGAUDIODSP) += aarch64/mpegaudiodsp_init.o OBJS-$(CONFIG_NEON_CLOBBER_TEST) += aarch64/neontest.o +OBJS-$(CONFIG_PIXBLOCKDSP) += aarch64/pixblockdsp_init_aarch64.o OBJS-$(CONFIG_VIDEODSP) += aarch64/videodsp_init.o OBJS-$(CONFIG_VP8DSP) += aarch64/vp8dsp_init_aarch64.o @@ -21,6 +23,7 @@ OBJS-$(CONFIG_VC1DSP) += aarch64/vc1dsp_init_aarch64.o OBJS-$(CONFIG_VORBIS_DECODER) += aarch64/vorbisdsp_init.o OBJS-$(CONFIG_VP9_DECODER) += aarch64/vp9dsp_init_10bpp_aarch64.o \ aarch64/vp9dsp_init_12bpp_aarch64.o \ + aarch64/vp9mc_aarch64.o \ aarch64/vp9dsp_init_aarch64.o # ARMv8 optimizations @@ -41,10 +44,10 @@ NEON-OBJS-$(CONFIG_H264PRED) += aarch64/h264pred_neon.o NEON-OBJS-$(CONFIG_H264QPEL) += aarch64/h264qpel_neon.o \ aarch64/hpeldsp_neon.o NEON-OBJS-$(CONFIG_HPELDSP) += aarch64/hpeldsp_neon.o -NEON-OBJS-$(CONFIG_IDCTDSP) += aarch64/idctdsp_init_aarch64.o \ - aarch64/simple_idct_neon.o +NEON-OBJS-$(CONFIG_IDCTDSP) += aarch64/simple_idct_neon.o NEON-OBJS-$(CONFIG_MDCT) += aarch64/mdct_neon.o NEON-OBJS-$(CONFIG_MPEGAUDIODSP) += aarch64/mpegaudiodsp_neon.o +NEON-OBJS-$(CONFIG_PIXBLOCKDSP) += aarch64/pixblockdsp_neon.o NEON-OBJS-$(CONFIG_VP8DSP) += aarch64/vp8dsp_neon.o # decoders/encoders diff --git a/libavcodec/aarch64/idctdsp_init_aarch64.c b/libavcodec/aarch64/idctdsp_init_aarch64.c index 0406e608304..742a3372e3a 100644 --- a/libavcodec/aarch64/idctdsp_init_aarch64.c +++ b/libavcodec/aarch64/idctdsp_init_aarch64.c @@ -21,6 +21,8 @@ */ #include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/arm/cpu.h" #include "libavcodec/avcodec.h" #include "libavcodec/idctdsp.h" #include "idct.h" @@ -28,7 +30,9 @@ av_cold void ff_idctdsp_init_aarch64(IDCTDSPContext *c, AVCodecContext *avctx, unsigned high_bit_depth) { - if (!avctx->lowres && !high_bit_depth) { + int cpu_flags = av_get_cpu_flags(); + + if (have_neon(cpu_flags) && !avctx->lowres && !high_bit_depth) { if (avctx->idct_algo == FF_IDCT_AUTO || avctx->idct_algo == FF_IDCT_SIMPLEAUTO || avctx->idct_algo == FF_IDCT_SIMPLENEON) { diff --git a/libavcodec/aarch64/opusdsp_neon.S b/libavcodec/aarch64/opusdsp_neon.S index 3d805e2c9cb..46c2be0874c 100644 --- a/libavcodec/aarch64/opusdsp_neon.S +++ b/libavcodec/aarch64/opusdsp_neon.S @@ -95,16 +95,16 @@ function ff_opus_postfilter_neon, export=1 fmla v3.4s, v7.4s, v2.4s fadd v6.4s, v6.4s, v4.4s - ld1 {v8.4s}, [x0] - fmla v8.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 - fadd v8.4s, v8.4s, v6.4s + fadd v4.4s, v4.4s, v6.4s fmul v3.4s, v7.4s, v2.4s - st1 {v8.4s}, [x0], #16 + st1 {v4.4s}, [x0], #16 subs w3, w3, #4 b.gt 1b diff --git a/libavcodec/aarch64/pixblockdsp_init_aarch64.c b/libavcodec/aarch64/pixblockdsp_init_aarch64.c new file mode 100644 index 00000000000..e4bac722f88 --- /dev/null +++ b/libavcodec/aarch64/pixblockdsp_init_aarch64.c @@ -0,0 +1,46 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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/aarch64/cpu.h" +#include "libavcodec/avcodec.h" +#include "libavcodec/pixblockdsp.h" + +void ff_get_pixels_neon(int16_t *block, const uint8_t *pixels, + ptrdiff_t stride); +void ff_diff_pixels_neon(int16_t *block, const uint8_t *s1, + const uint8_t *s2, ptrdiff_t stride); + +av_cold void ff_pixblockdsp_init_aarch64(PixblockDSPContext *c, + AVCodecContext *avctx, + unsigned high_bit_depth) +{ + int cpu_flags = av_get_cpu_flags(); + + if (have_neon(cpu_flags)) { + if (!high_bit_depth) { + c->get_pixels_unaligned = + c->get_pixels = ff_get_pixels_neon; + } + c->diff_pixels_unaligned = + c->diff_pixels = ff_diff_pixels_neon; + } +} diff --git a/libavcodec/aarch64/pixblockdsp_neon.S b/libavcodec/aarch64/pixblockdsp_neon.S new file mode 100644 index 00000000000..0277e0476d2 --- /dev/null +++ b/libavcodec/aarch64/pixblockdsp_neon.S @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_get_pixels_neon, export=1 + mov w3, #8 +1: + ld1 {v0.8b}, [x1], x2 + subs w3, w3, #2 + ld1 {v1.8b}, [x1], x2 + uxtl v0.8h, v0.8b + uxtl v1.8h, v1.8b + st1 {v0.8h, v1.8h}, [x0], #32 + b.gt 1b + + ret +endfunc + +function ff_diff_pixels_neon, export=1 + mov w4, #8 +1: + ld1 {v0.8b}, [x1], x3 + ld1 {v1.8b}, [x2], x3 + subs w4, w4, #2 + ld1 {v2.8b}, [x1], x3 + usubl v0.8h, v0.8b, v1.8b + ld1 {v3.8b}, [x2], x3 + usubl v1.8h, v2.8b, v3.8b + st1 {v0.8h, v1.8h}, [x0], #32 + b.gt 1b + + ret +endfunc diff --git a/libavcodec/aarch64/vp9mc_16bpp_neon.S b/libavcodec/aarch64/vp9mc_16bpp_neon.S index cac6428709b..53b372c262d 100644 --- a/libavcodec/aarch64/vp9mc_16bpp_neon.S +++ b/libavcodec/aarch64/vp9mc_16bpp_neon.S @@ -25,31 +25,6 @@ // const uint8_t *ref, ptrdiff_t ref_stride, // int h, int mx, int my); -function ff_vp9_copy128_aarch64, export=1 -1: - ldp x5, x6, [x2] - ldp x7, x8, [x2, #16] - stp x5, x6, [x0] - ldp x9, x10, [x2, #32] - stp x7, x8, [x0, #16] - subs w4, w4, #1 - ldp x11, x12, [x2, #48] - stp x9, x10, [x0, #32] - stp x11, x12, [x0, #48] - ldp x5, x6, [x2, #64] - ldp x7, x8, [x2, #80] - stp x5, x6, [x0, #64] - ldp x9, x10, [x2, #96] - stp x7, x8, [x0, #80] - ldp x11, x12, [x2, #112] - stp x9, x10, [x0, #96] - stp x11, x12, [x0, #112] - add x2, x2, x3 - add x0, x0, x1 - b.ne 1b - ret -endfunc - function ff_vp9_avg64_16_neon, export=1 mov x5, x0 sub x1, x1, #64 diff --git a/libavcodec/aarch64/vp9mc_aarch64.S b/libavcodec/aarch64/vp9mc_aarch64.S new file mode 100644 index 00000000000..f17a8cf04af --- /dev/null +++ b/libavcodec/aarch64/vp9mc_aarch64.S @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016 Google Inc. + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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" + +// All public functions in this file have the following signature: +// typedef void (*vp9_mc_func)(uint8_t *dst, ptrdiff_t dst_stride, +// const uint8_t *ref, ptrdiff_t ref_stride, +// int h, int mx, int my); + +function ff_vp9_copy128_aarch64, export=1 +1: + ldp x5, x6, [x2] + ldp x7, x8, [x2, #16] + stp x5, x6, [x0] + ldp x9, x10, [x2, #32] + stp x7, x8, [x0, #16] + subs w4, w4, #1 + ldp x11, x12, [x2, #48] + stp x9, x10, [x0, #32] + stp x11, x12, [x0, #48] + ldp x5, x6, [x2, #64] + ldp x7, x8, [x2, #80] + stp x5, x6, [x0, #64] + ldp x9, x10, [x2, #96] + stp x7, x8, [x0, #80] + ldp x11, x12, [x2, #112] + stp x9, x10, [x0, #96] + stp x11, x12, [x0, #112] + add x2, x2, x3 + add x0, x0, x1 + b.ne 1b + ret +endfunc + +function ff_vp9_copy64_aarch64, export=1 +1: + ldp x5, x6, [x2] + ldp x7, x8, [x2, #16] + stp x5, x6, [x0] + ldp x9, x10, [x2, #32] + stp x7, x8, [x0, #16] + subs w4, w4, #1 + ldp x11, x12, [x2, #48] + stp x9, x10, [x0, #32] + stp x11, x12, [x0, #48] + add x2, x2, x3 + add x0, x0, x1 + b.ne 1b + ret +endfunc + +function ff_vp9_copy32_aarch64, export=1 +1: + ldp x5, x6, [x2] + ldp x7, x8, [x2, #16] + stp x5, x6, [x0] + subs w4, w4, #1 + stp x7, x8, [x0, #16] + add x2, x2, x3 + add x0, x0, x1 + b.ne 1b + ret +endfunc diff --git a/libavcodec/aarch64/vp9mc_neon.S b/libavcodec/aarch64/vp9mc_neon.S index f67624ca048..abf2bae9db0 100644 --- a/libavcodec/aarch64/vp9mc_neon.S +++ b/libavcodec/aarch64/vp9mc_neon.S @@ -25,23 +25,6 @@ // const uint8_t *ref, ptrdiff_t ref_stride, // int h, int mx, int my); -function ff_vp9_copy64_aarch64, export=1 -1: - ldp x5, x6, [x2] - ldp x7, x8, [x2, #16] - stp x5, x6, [x0] - ldp x9, x10, [x2, #32] - stp x7, x8, [x0, #16] - subs w4, w4, #1 - ldp x11, x12, [x2, #48] - stp x9, x10, [x0, #32] - stp x11, x12, [x0, #48] - add x2, x2, x3 - add x0, x0, x1 - b.ne 1b - ret -endfunc - function ff_vp9_avg64_neon, export=1 mov x5, x0 1: @@ -64,19 +47,6 @@ function ff_vp9_avg64_neon, export=1 ret endfunc -function ff_vp9_copy32_aarch64, export=1 -1: - ldp x5, x6, [x2] - ldp x7, x8, [x2, #16] - stp x5, x6, [x0] - subs w4, w4, #1 - stp x7, x8, [x0, #16] - add x2, x2, x3 - add x0, x0, x1 - b.ne 1b - ret -endfunc - function ff_vp9_avg32_neon, export=1 1: ld1 {v2.16b, v3.16b}, [x2], x3 diff --git a/libavcodec/aasc.c b/libavcodec/aasc.c index 58cc3c85ba8..26570f49e5c 100644 --- a/libavcodec/aasc.c +++ b/libavcodec/aasc.c @@ -91,7 +91,7 @@ static int aasc_decode_frame(AVCodecContext *avctx, return AVERROR_INVALIDDATA; } - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; compr = AV_RL32(buf); diff --git a/libavcodec/ac3_parser.c b/libavcodec/ac3_parser.c index 1e203ae6ac2..ba171653ef6 100644 --- a/libavcodec/ac3_parser.c +++ b/libavcodec/ac3_parser.c @@ -201,6 +201,12 @@ static int ac3_sync(uint64_t state, AACAC3ParseContext *hdr_info, AC3HeaderInfo hdr; GetBitContext gbc; + if (tmp.u8[1] == 0x77 && tmp.u8[2] == 0x0b) { + FFSWAP(uint8_t, tmp.u8[1], tmp.u8[2]); + FFSWAP(uint8_t, tmp.u8[3], tmp.u8[4]); + FFSWAP(uint8_t, tmp.u8[5], tmp.u8[6]); + } + init_get_bits(&gbc, tmp.u8+8-AC3_HEADER_SIZE, 54); err = ff_ac3_parse_header(&gbc, &hdr); diff --git a/libavcodec/ac3dec_fixed.c b/libavcodec/ac3dec_fixed.c index bd66175d50f..336a538cad3 100644 --- a/libavcodec/ac3dec_fixed.c +++ b/libavcodec/ac3dec_fixed.c @@ -107,30 +107,17 @@ static void scale_coefs ( } } else { shift = -shift; + mul <<= shift; for (i=0; inum_blocks; blk++) { @@ -1993,12 +1993,11 @@ int ff_ac3_validate_metadata(AC3EncodeContext *s) /* set bitstream id for alternate bitstream syntax */ if (!s->eac3 && (opt->extended_bsi_1 || opt->extended_bsi_2)) { if (s->bitstream_id > 8 && s->bitstream_id < 11) { - static int warn_once = 1; - if (warn_once) { + if (!s->warned_alternate_bitstream) { av_log(avctx, AV_LOG_WARNING, "alternate bitstream syntax is " "not compatible with reduced samplerates. writing of " "extended bitstream information will be disabled.\n"); - warn_once = 0; + s->warned_alternate_bitstream = 1; } } else { s->bitstream_id = 6; @@ -2051,7 +2050,8 @@ av_cold int ff_ac3_encode_close(AVCodecContext *avctx) av_freep(&block->cpl_coord_mant); } - s->mdct_end(s); + if (s->mdct_end) + s->mdct_end(s); return 0; } @@ -2433,7 +2433,7 @@ av_cold int ff_ac3_encode_init(AVCodecContext *avctx) ret = validate_options(s); if (ret) - return ret; + goto init_fail; avctx->frame_size = AC3_BLOCK_SIZE * s->num_blocks; avctx->initial_padding = AC3_BLOCK_SIZE; diff --git a/libavcodec/ac3enc.h b/libavcodec/ac3enc.h index a2442d0e553..1e4a7405bfb 100644 --- a/libavcodec/ac3enc.h +++ b/libavcodec/ac3enc.h @@ -255,6 +255,8 @@ typedef struct AC3EncodeContext { uint8_t *ref_bap [AC3_MAX_CHANNELS][AC3_MAX_BLOCKS]; ///< bit allocation pointers (bap) int ref_bap_set; ///< indicates if ref_bap pointers have been set + int warned_alternate_bitstream; + /* fixed vs. float function pointers */ void (*mdct_end)(struct AC3EncodeContext *s); int (*mdct_init)(struct AC3EncodeContext *s); diff --git a/libavcodec/ac3enc_fixed.c b/libavcodec/ac3enc_fixed.c index b23fc64776d..e57d0352943 100644 --- a/libavcodec/ac3enc_fixed.c +++ b/libavcodec/ac3enc_fixed.c @@ -155,6 +155,7 @@ AVCodec ff_ac3_fixed_encoder = { .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_NONE }, .priv_class = &ac3enc_class, + .supported_samplerates = ff_ac3_sample_rate_tab, .channel_layouts = ff_ac3_channel_layouts, .defaults = ac3_defaults, }; diff --git a/libavcodec/ac3enc_float.c b/libavcodec/ac3enc_float.c index d6e658b2b42..1f3111af0e5 100644 --- a/libavcodec/ac3enc_float.c +++ b/libavcodec/ac3enc_float.c @@ -150,6 +150,7 @@ AVCodec ff_ac3_encoder = { .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .priv_class = &ac3enc_class, + .supported_samplerates = ff_ac3_sample_rate_tab, .channel_layouts = ff_ac3_channel_layouts, .defaults = ac3_defaults, }; diff --git a/libavcodec/ac3tab.c b/libavcodec/ac3tab.c index bd88f32d92b..ef2a41bc59f 100644 --- a/libavcodec/ac3tab.c +++ b/libavcodec/ac3tab.c @@ -126,7 +126,7 @@ const uint8_t ff_ac3_dec_channel_map[8][2][6] = { }; /* possible frequencies */ -const uint16_t ff_ac3_sample_rate_tab[3] = { 48000, 44100, 32000 }; +const int ff_ac3_sample_rate_tab[] = { 48000, 44100, 32000, 0 }; /* possible bitrates */ const uint16_t ff_ac3_bitrate_tab[19] = { diff --git a/libavcodec/ac3tab.h b/libavcodec/ac3tab.h index aa71acbce12..1d1264e3fcb 100644 --- a/libavcodec/ac3tab.h +++ b/libavcodec/ac3tab.h @@ -33,7 +33,7 @@ extern const uint8_t ff_ac3_channels_tab[8]; extern av_export_avcodec const uint16_t avpriv_ac3_channel_layout_tab[8]; extern const uint8_t ff_ac3_enc_channel_map[8][2][6]; extern const uint8_t ff_ac3_dec_channel_map[8][2][6]; -extern const uint16_t ff_ac3_sample_rate_tab[3]; +extern const int ff_ac3_sample_rate_tab[]; extern const uint16_t ff_ac3_bitrate_tab[19]; extern const uint8_t ff_ac3_rematrix_band_tab[5]; extern const uint8_t ff_eac3_default_cpl_band_struct[18]; diff --git a/libavcodec/adpcm.c b/libavcodec/adpcm.c index e194764374c..79c5d625d18 100644 --- a/libavcodec/adpcm.c +++ b/libavcodec/adpcm.c @@ -12,6 +12,11 @@ * EA ADPCM XAS decoder by Peter Ross (pross@xvid.org) * MAXIS EA ADPCM decoder by Robert Marston (rmarston@gmail.com) * THP ADPCM decoder by Marco Gerards (mgerards@xs4all.nl) + * Argonaut Games ADPCM decoder by Zane van Iperen (zane@zanevaniperen.com) + * Simon & Schuster Interactive ADPCM decoder by Zane van Iperen (zane@zanevaniperen.com) + * Ubisoft ADPCM decoder by Zane van Iperen (zane@zanevaniperen.com) + * High Voltage Software ALP decoder by Zane van Iperen (zane@zanevaniperen.com) + * Cunning Developments decoder by Zane van Iperen (zane@zanevaniperen.com) * * This file is part of FFmpeg. * @@ -81,6 +86,15 @@ static const int8_t swf_index_tables[4][16] = { /*5*/ { -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 4, 6, 8, 10, 13, 16 } }; +static const int8_t zork_index_table[8] = { + -1, -1, -1, 1, 4, 7, 10, 12, +}; + +static const int8_t mtf_index_table[16] = { + 8, 6, 4, 2, -1, -1, -1, -1, + -1, -1, -1, -1, 2, 4, 6, 8, +}; + /* end of tables */ typedef struct ADPCMDecodeContext { @@ -96,6 +110,9 @@ static av_cold int adpcm_decode_init(AVCodecContext * avctx) unsigned int max_channels = 2; switch(avctx->codec->id) { + case AV_CODEC_ID_ADPCM_IMA_CUNNING: + max_channels = 1; + break; case AV_CODEC_ID_ADPCM_DTK: case AV_CODEC_ID_ADPCM_EA: min_channels = 2; @@ -105,11 +122,16 @@ static av_cold int adpcm_decode_init(AVCodecContext * avctx) case AV_CODEC_ID_ADPCM_EA_R2: case AV_CODEC_ID_ADPCM_EA_R3: case AV_CODEC_ID_ADPCM_EA_XAS: + case AV_CODEC_ID_ADPCM_MS: max_channels = 6; break; case AV_CODEC_ID_ADPCM_MTAF: min_channels = 2; max_channels = 8; + if (avctx->channels & 1) { + avpriv_request_sample(avctx, "channel count %d\n", avctx->channels); + return AVERROR_PATCHWELCOME; + } break; case AV_CODEC_ID_ADPCM_PSX: max_channels = 8; @@ -135,43 +157,64 @@ static av_cold int adpcm_decode_init(AVCodecContext * avctx) break; case AV_CODEC_ID_ADPCM_IMA_APC: if (avctx->extradata && avctx->extradata_size >= 8) { - c->status[0].predictor = AV_RL32(avctx->extradata); - c->status[1].predictor = AV_RL32(avctx->extradata + 4); + c->status[0].predictor = av_clip_intp2(AV_RL32(avctx->extradata ), 18); + c->status[1].predictor = av_clip_intp2(AV_RL32(avctx->extradata + 4), 18); + } + break; + case AV_CODEC_ID_ADPCM_IMA_APM: + if (avctx->extradata && avctx->extradata_size >= 16) { + c->status[0].predictor = av_clip_intp2(AV_RL32(avctx->extradata + 0), 18); + c->status[0].step_index = av_clip(AV_RL32(avctx->extradata + 4), 0, 88); + c->status[1].predictor = av_clip_intp2(AV_RL32(avctx->extradata + 8), 18); + c->status[1].step_index = av_clip(AV_RL32(avctx->extradata + 12), 0, 88); } break; case AV_CODEC_ID_ADPCM_IMA_WS: if (avctx->extradata && avctx->extradata_size >= 2) c->vqa_version = AV_RL16(avctx->extradata); break; + case AV_CODEC_ID_ADPCM_ARGO: + if (avctx->bits_per_coded_sample != 4) + return AVERROR_INVALIDDATA; + break; + case AV_CODEC_ID_ADPCM_ZORK: + if (avctx->bits_per_coded_sample != 8) + return AVERROR_INVALIDDATA; + break; default: break; } - switch(avctx->codec->id) { - case AV_CODEC_ID_ADPCM_AICA: - case AV_CODEC_ID_ADPCM_IMA_DAT4: - case AV_CODEC_ID_ADPCM_IMA_QT: - case AV_CODEC_ID_ADPCM_IMA_WAV: - case AV_CODEC_ID_ADPCM_4XM: - case AV_CODEC_ID_ADPCM_XA: - case AV_CODEC_ID_ADPCM_EA_R1: - case AV_CODEC_ID_ADPCM_EA_R2: - case AV_CODEC_ID_ADPCM_EA_R3: - case AV_CODEC_ID_ADPCM_EA_XAS: - case AV_CODEC_ID_ADPCM_THP: - case AV_CODEC_ID_ADPCM_THP_LE: - case AV_CODEC_ID_ADPCM_AFC: - case AV_CODEC_ID_ADPCM_DTK: - case AV_CODEC_ID_ADPCM_PSX: - case AV_CODEC_ID_ADPCM_MTAF: - avctx->sample_fmt = AV_SAMPLE_FMT_S16P; - break; - case AV_CODEC_ID_ADPCM_IMA_WS: - avctx->sample_fmt = c->vqa_version == 3 ? AV_SAMPLE_FMT_S16P : - AV_SAMPLE_FMT_S16; - break; - default: - avctx->sample_fmt = AV_SAMPLE_FMT_S16; + switch (avctx->codec->id) { + case AV_CODEC_ID_ADPCM_AICA: + case AV_CODEC_ID_ADPCM_IMA_DAT4: + case AV_CODEC_ID_ADPCM_IMA_QT: + case AV_CODEC_ID_ADPCM_IMA_WAV: + case AV_CODEC_ID_ADPCM_4XM: + case AV_CODEC_ID_ADPCM_XA: + case AV_CODEC_ID_ADPCM_EA_R1: + case AV_CODEC_ID_ADPCM_EA_R2: + case AV_CODEC_ID_ADPCM_EA_R3: + case AV_CODEC_ID_ADPCM_EA_XAS: + case AV_CODEC_ID_ADPCM_THP: + case AV_CODEC_ID_ADPCM_THP_LE: + case AV_CODEC_ID_ADPCM_AFC: + case AV_CODEC_ID_ADPCM_DTK: + case AV_CODEC_ID_ADPCM_PSX: + case AV_CODEC_ID_ADPCM_MTAF: + case AV_CODEC_ID_ADPCM_ARGO: + avctx->sample_fmt = AV_SAMPLE_FMT_S16P; + break; + case AV_CODEC_ID_ADPCM_IMA_WS: + avctx->sample_fmt = c->vqa_version == 3 ? AV_SAMPLE_FMT_S16P : + AV_SAMPLE_FMT_S16; + break; + case AV_CODEC_ID_ADPCM_MS: + avctx->sample_fmt = avctx->channels > 2 ? AV_SAMPLE_FMT_S16P : + AV_SAMPLE_FMT_S16; + break; + default: + avctx->sample_fmt = AV_SAMPLE_FMT_S16; } return 0; @@ -247,6 +290,65 @@ static inline int16_t adpcm_ima_expand_nibble(ADPCMChannelStatus *c, int8_t nibb return (int16_t)c->predictor; } +static inline int16_t adpcm_ima_alp_expand_nibble(ADPCMChannelStatus *c, int8_t nibble, int shift) +{ + int step_index; + int predictor; + int sign, delta, diff, step; + + step = ff_adpcm_step_table[c->step_index]; + step_index = c->step_index + ff_adpcm_index_table[(unsigned)nibble]; + step_index = av_clip(step_index, 0, 88); + + sign = nibble & 8; + delta = nibble & 7; + diff = (delta * step) >> shift; + predictor = c->predictor; + if (sign) predictor -= diff; + else predictor += diff; + + c->predictor = av_clip_int16(predictor); + c->step_index = step_index; + + return (int16_t)c->predictor; +} + +static inline int16_t adpcm_ima_mtf_expand_nibble(ADPCMChannelStatus *c, int nibble) +{ + int step_index, step, delta, predictor; + + step = ff_adpcm_step_table[c->step_index]; + + delta = step * (2 * nibble - 15); + predictor = c->predictor + delta; + + step_index = c->step_index + mtf_index_table[(unsigned)nibble]; + c->predictor = av_clip_int16(predictor >> 4); + c->step_index = av_clip(step_index, 0, 88); + + return (int16_t)c->predictor; +} + +static inline int16_t adpcm_ima_cunning_expand_nibble(ADPCMChannelStatus *c, int8_t nibble) +{ + int step_index; + int predictor; + int step; + + nibble = sign_extend(nibble & 0xF, 4); + + step = ff_adpcm_ima_cunning_step_table[c->step_index]; + step_index = c->step_index + ff_adpcm_ima_cunning_index_table[abs(nibble)]; + step_index = av_clip(step_index, 0, 60); + + predictor = c->predictor + step * nibble; + + c->predictor = av_clip_int16(predictor); + c->step_index = step_index; + + return c->predictor; +} + static inline int16_t adpcm_ima_wav_expand_nibble(ADPCMChannelStatus *c, GetBitContext *gb, int bps) { int nibble, step_index, predictor, sign, delta, diff, step, shift; @@ -270,7 +372,7 @@ static inline int16_t adpcm_ima_wav_expand_nibble(ADPCMChannelStatus *c, GetBitC return (int16_t)c->predictor; } -static inline int adpcm_ima_qt_expand_nibble(ADPCMChannelStatus *c, int nibble, int shift) +static inline int adpcm_ima_qt_expand_nibble(ADPCMChannelStatus *c, int nibble) { int step_index; int predictor; @@ -333,7 +435,7 @@ static inline int16_t adpcm_ima_oki_expand_nibble(ADPCMChannelStatus *c, int nib c->predictor = av_clip_intp2(predictor, 11); c->step_index = step_index; - return c->predictor << 4; + return c->predictor * 16; } static inline int16_t adpcm_ct_expand_nibble(ADPCMChannelStatus *c, int8_t nibble) @@ -400,6 +502,41 @@ static inline int16_t adpcm_mtaf_expand_nibble(ADPCMChannelStatus *c, uint8_t ni return c->predictor; } +static inline int16_t adpcm_zork_expand_nibble(ADPCMChannelStatus *c, uint8_t nibble) +{ + int16_t index = c->step_index; + uint32_t lookup_sample = ff_adpcm_step_table[index]; + int32_t sample = 0; + + if (nibble & 0x40) + sample += lookup_sample; + if (nibble & 0x20) + sample += lookup_sample >> 1; + if (nibble & 0x10) + sample += lookup_sample >> 2; + if (nibble & 0x08) + sample += lookup_sample >> 3; + if (nibble & 0x04) + sample += lookup_sample >> 4; + if (nibble & 0x02) + sample += lookup_sample >> 5; + if (nibble & 0x01) + sample += lookup_sample >> 6; + if (nibble & 0x80) + sample = -sample; + + sample += c->predictor; + sample = av_clip_int16(sample); + + index += zork_index_table[(nibble >> 4) & 7]; + index = av_clip(index, 0, 88); + + c->predictor = sample; + c->step_index = index; + + return sample; +} + static int xa_decode(AVCodecContext *avctx, int16_t *out0, int16_t *out1, const uint8_t *in, ADPCMChannelStatus *left, ADPCMChannelStatus *right, int channels, int sample_offset) @@ -422,6 +559,10 @@ static int xa_decode(AVCodecContext *avctx, int16_t *out0, int16_t *out1, avpriv_request_sample(avctx, "unknown XA-ADPCM filter %d", filter); filter=0; } + if (shift < 0) { + avpriv_request_sample(avctx, "unknown XA-ADPCM shift %d", shift); + shift = 0; + } f0 = xa_adpcm_table[filter][0]; f1 = xa_adpcm_table[filter][1]; @@ -432,7 +573,7 @@ static int xa_decode(AVCodecContext *avctx, int16_t *out0, int16_t *out1, d = in[16+i+j*4]; t = sign_extend(d, 4); - s = ( t<>6); + s = t*(1<>6); s_2 = s_1; s_1 = av_clip_int16(s); out0[j] = s_1; @@ -447,10 +588,14 @@ static int xa_decode(AVCodecContext *avctx, int16_t *out0, int16_t *out1, shift = 12 - (in[5+i*2] & 15); filter = in[5+i*2] >> 4; - if (filter >= FF_ARRAY_ELEMS(xa_adpcm_table)) { + if (filter >= FF_ARRAY_ELEMS(xa_adpcm_table) || shift < 0) { avpriv_request_sample(avctx, "unknown XA-ADPCM filter %d", filter); filter=0; } + if (shift < 0) { + avpriv_request_sample(avctx, "unknown XA-ADPCM shift %d", shift); + shift = 0; + } f0 = xa_adpcm_table[filter][0]; f1 = xa_adpcm_table[filter][1]; @@ -459,7 +604,7 @@ static int xa_decode(AVCodecContext *avctx, int16_t *out0, int16_t *out1, d = in[16+i+j*4]; t = sign_extend(d >> 4, 4); - s = ( t<>6); + s = t*(1<>6); s_2 = s_1; s_1 = av_clip_int16(s); out1[j] = s_1; @@ -537,8 +682,25 @@ static void adpcm_swf_decode(AVCodecContext *avctx, const uint8_t *buf, int buf_ } } +static inline int16_t adpcm_argo_expand_nibble(ADPCMChannelStatus *cs, int nibble, int control, int shift) +{ + int sample = nibble * (1 << shift); + + if (control & 0x04) + sample += (8 * cs->sample1) - (4 * cs->sample2); + else + sample += 4 * cs->sample1; + + sample = av_clip_int16(sample >> 2); + + cs->sample2 = cs->sample1; + cs->sample1 = sample; + + return sample; +} + /** - * Get the number of samples that will be decoded from the packet. + * Get the number of samples (per channel) that will be decoded from the packet. * In one case, this is actually the maximum number of samples possible to * decode with the given buf_size. * @@ -575,14 +737,24 @@ static int get_nb_samples(AVCodecContext *avctx, GetByteContext *gb, return 0; nb_samples = 64; break; + case AV_CODEC_ID_ADPCM_ARGO: + if (buf_size < 17 * ch) + return 0; + nb_samples = 32; + break; /* simple 4-bit adpcm */ case AV_CODEC_ID_ADPCM_CT: case AV_CODEC_ID_ADPCM_IMA_APC: + case AV_CODEC_ID_ADPCM_IMA_CUNNING: case AV_CODEC_ID_ADPCM_IMA_EA_SEAD: case AV_CODEC_ID_ADPCM_IMA_OKI: case AV_CODEC_ID_ADPCM_IMA_WS: case AV_CODEC_ID_ADPCM_YAMAHA: case AV_CODEC_ID_ADPCM_AICA: + case AV_CODEC_ID_ADPCM_IMA_SSI: + case AV_CODEC_ID_ADPCM_IMA_APM: + case AV_CODEC_ID_ADPCM_IMA_ALP: + case AV_CODEC_ID_ADPCM_IMA_MTF: nb_samples = buf_size * 2 / ch; break; } @@ -741,6 +913,9 @@ static int get_nb_samples(AVCodecContext *avctx, GetByteContext *gb, case AV_CODEC_ID_ADPCM_PSX: nb_samples = buf_size / (16 * ch) * 28; break; + case AV_CODEC_ID_ADPCM_ZORK: + nb_samples = buf_size / ch; + break; } /* validate coded sample count */ @@ -827,8 +1002,8 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, for (m = 0; m < 64; m += 2) { int byte = bytestream2_get_byteu(&gb); - samples[m ] = adpcm_ima_qt_expand_nibble(cs, byte & 0x0F, 3); - samples[m + 1] = adpcm_ima_qt_expand_nibble(cs, byte >> 4 , 3); + samples[m ] = adpcm_ima_qt_expand_nibble(cs, byte & 0x0F); + samples[m + 1] = adpcm_ima_qt_expand_nibble(cs, byte >> 4 ); } } break; @@ -924,42 +1099,66 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, { int block_predictor; - block_predictor = bytestream2_get_byteu(&gb); - if (block_predictor > 6) { - av_log(avctx, AV_LOG_ERROR, "ERROR: block_predictor[0] = %d\n", - block_predictor); - return AVERROR_INVALIDDATA; - } - c->status[0].coeff1 = ff_adpcm_AdaptCoeff1[block_predictor]; - c->status[0].coeff2 = ff_adpcm_AdaptCoeff2[block_predictor]; - if (st) { + if (avctx->channels > 2) { + for (channel = 0; channel < avctx->channels; channel++) { + samples = samples_p[channel]; + block_predictor = bytestream2_get_byteu(&gb); + if (block_predictor > 6) { + av_log(avctx, AV_LOG_ERROR, "ERROR: block_predictor[%d] = %d\n", + channel, block_predictor); + return AVERROR_INVALIDDATA; + } + c->status[channel].coeff1 = ff_adpcm_AdaptCoeff1[block_predictor]; + c->status[channel].coeff2 = ff_adpcm_AdaptCoeff2[block_predictor]; + c->status[channel].idelta = sign_extend(bytestream2_get_le16u(&gb), 16); + c->status[channel].sample1 = sign_extend(bytestream2_get_le16u(&gb), 16); + c->status[channel].sample2 = sign_extend(bytestream2_get_le16u(&gb), 16); + *samples++ = c->status[channel].sample2; + *samples++ = c->status[channel].sample1; + for(n = (nb_samples - 2) >> 1; n > 0; n--) { + int byte = bytestream2_get_byteu(&gb); + *samples++ = adpcm_ms_expand_nibble(&c->status[channel], byte >> 4 ); + *samples++ = adpcm_ms_expand_nibble(&c->status[channel], byte & 0x0F); + } + } + } else { block_predictor = bytestream2_get_byteu(&gb); if (block_predictor > 6) { - av_log(avctx, AV_LOG_ERROR, "ERROR: block_predictor[1] = %d\n", + av_log(avctx, AV_LOG_ERROR, "ERROR: block_predictor[0] = %d\n", block_predictor); return AVERROR_INVALIDDATA; } - c->status[1].coeff1 = ff_adpcm_AdaptCoeff1[block_predictor]; - c->status[1].coeff2 = ff_adpcm_AdaptCoeff2[block_predictor]; - } - c->status[0].idelta = sign_extend(bytestream2_get_le16u(&gb), 16); - if (st){ - c->status[1].idelta = sign_extend(bytestream2_get_le16u(&gb), 16); - } + c->status[0].coeff1 = ff_adpcm_AdaptCoeff1[block_predictor]; + c->status[0].coeff2 = ff_adpcm_AdaptCoeff2[block_predictor]; + if (st) { + block_predictor = bytestream2_get_byteu(&gb); + if (block_predictor > 6) { + av_log(avctx, AV_LOG_ERROR, "ERROR: block_predictor[1] = %d\n", + block_predictor); + return AVERROR_INVALIDDATA; + } + c->status[1].coeff1 = ff_adpcm_AdaptCoeff1[block_predictor]; + c->status[1].coeff2 = ff_adpcm_AdaptCoeff2[block_predictor]; + } + c->status[0].idelta = sign_extend(bytestream2_get_le16u(&gb), 16); + if (st){ + c->status[1].idelta = sign_extend(bytestream2_get_le16u(&gb), 16); + } - c->status[0].sample1 = sign_extend(bytestream2_get_le16u(&gb), 16); - if (st) c->status[1].sample1 = sign_extend(bytestream2_get_le16u(&gb), 16); - c->status[0].sample2 = sign_extend(bytestream2_get_le16u(&gb), 16); - if (st) c->status[1].sample2 = sign_extend(bytestream2_get_le16u(&gb), 16); + c->status[0].sample1 = sign_extend(bytestream2_get_le16u(&gb), 16); + if (st) c->status[1].sample1 = sign_extend(bytestream2_get_le16u(&gb), 16); + c->status[0].sample2 = sign_extend(bytestream2_get_le16u(&gb), 16); + if (st) c->status[1].sample2 = sign_extend(bytestream2_get_le16u(&gb), 16); - *samples++ = c->status[0].sample2; - if (st) *samples++ = c->status[1].sample2; - *samples++ = c->status[0].sample1; - if (st) *samples++ = c->status[1].sample1; - for(n = (nb_samples - 2) >> (1 - st); n > 0; n--) { - int byte = bytestream2_get_byteu(&gb); - *samples++ = adpcm_ms_expand_nibble(&c->status[0 ], byte >> 4 ); - *samples++ = adpcm_ms_expand_nibble(&c->status[st], byte & 0x0F); + *samples++ = c->status[0].sample2; + if (st) *samples++ = c->status[1].sample2; + *samples++ = c->status[0].sample1; + if (st) *samples++ = c->status[1].sample1; + for(n = (nb_samples - 2) >> (1 - st); n > 0; n--) { + int byte = bytestream2_get_byteu(&gb); + *samples++ = adpcm_ms_expand_nibble(&c->status[0 ], byte >> 4 ); + *samples++ = adpcm_ms_expand_nibble(&c->status[st], byte & 0x0F); + } } break; } @@ -1105,14 +1304,48 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, } break; case AV_CODEC_ID_ADPCM_IMA_APC: - while (bytestream2_get_bytes_left(&gb) > 0) { + for (n = nb_samples >> (1 - st); n > 0; n--) { int v = bytestream2_get_byteu(&gb); *samples++ = adpcm_ima_expand_nibble(&c->status[0], v >> 4 , 3); *samples++ = adpcm_ima_expand_nibble(&c->status[st], v & 0x0F, 3); } break; + case AV_CODEC_ID_ADPCM_IMA_SSI: + for (n = nb_samples >> (1 - st); n > 0; n--) { + int v = bytestream2_get_byteu(&gb); + *samples++ = adpcm_ima_qt_expand_nibble(&c->status[0], v >> 4 ); + *samples++ = adpcm_ima_qt_expand_nibble(&c->status[st], v & 0x0F); + } + break; + case AV_CODEC_ID_ADPCM_IMA_APM: + for (n = nb_samples / 2; n > 0; n--) { + for (channel = 0; channel < avctx->channels; channel++) { + int v = bytestream2_get_byteu(&gb); + *samples++ = adpcm_ima_qt_expand_nibble(&c->status[channel], v >> 4 ); + samples[st] = adpcm_ima_qt_expand_nibble(&c->status[channel], v & 0x0F); + } + samples += avctx->channels; + } + break; + case AV_CODEC_ID_ADPCM_IMA_ALP: + for (n = nb_samples / 2; n > 0; n--) { + for (channel = 0; channel < avctx->channels; channel++) { + int v = bytestream2_get_byteu(&gb); + *samples++ = adpcm_ima_alp_expand_nibble(&c->status[channel], v >> 4 , 2); + samples[st] = adpcm_ima_alp_expand_nibble(&c->status[channel], v & 0x0F, 2); + } + samples += avctx->channels; + } + break; + case AV_CODEC_ID_ADPCM_IMA_CUNNING: + for (n = 0; n < nb_samples / 2; n++) { + int v = bytestream2_get_byteu(&gb); + *samples++ = adpcm_ima_cunning_expand_nibble(&c->status[0], v & 0x0F); + *samples++ = adpcm_ima_cunning_expand_nibble(&c->status[0], v >> 4); + } + break; case AV_CODEC_ID_ADPCM_IMA_OKI: - while (bytestream2_get_bytes_left(&gb) > 0) { + for (n = nb_samples >> (1 - st); n > 0; n--) { int v = bytestream2_get_byteu(&gb); *samples++ = adpcm_ima_oki_expand_nibble(&c->status[0], v >> 4 ); *samples++ = adpcm_ima_oki_expand_nibble(&c->status[st], v & 0x0F); @@ -1198,8 +1431,11 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, return AVERROR_INVALIDDATA; } } - for (i=0; i<=st; i++) + for (i=0; i<=st; i++) { c->status[i].predictor = bytestream2_get_le32u(&gb); + if (FFABS((int64_t)c->status[i].predictor) > (1<<16)) + return AVERROR_INVALIDDATA; + } for (n = nb_samples >> (1 - st); n > 0; n--) { int byte = bytestream2_get_byteu(&gb); @@ -1246,8 +1482,8 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, for (count2 = 0; count2 < 28; count2++) { byte = bytestream2_get_byteu(&gb); - next_left_sample = sign_extend(byte >> 4, 4) << shift_left; - next_right_sample = sign_extend(byte, 4) << shift_right; + next_left_sample = sign_extend(byte >> 4, 4) * (1 << shift_left); + next_right_sample = sign_extend(byte, 4) * (1 << shift_right); next_left_sample = (next_left_sample + (current_left_sample * coeff1l) + @@ -1286,7 +1522,7 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, if (st) byte[1] = bytestream2_get_byteu(&gb); for(i = 4; i >= 0; i-=4) { /* Pairwise samples LL RR (st) or LL LL (mono) */ for(channel = 0; channel < avctx->channels; channel++) { - int sample = sign_extend(byte[channel] >> i, 4) << shift[channel]; + int sample = sign_extend(byte[channel] >> i, 4) * (1 << shift[channel]); sample = (sample + c->status[channel].sample1 * coeff[channel][0] + c->status[channel].sample2 * coeff[channel][1] + 0x80) >> 8; @@ -1347,10 +1583,10 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, for (count2=0; count2<28; count2++) { if (count2 & 1) - next_sample = sign_extend(byte, 4) << shift; + next_sample = (unsigned)sign_extend(byte, 4) << shift; else { byte = bytestream2_get_byte(&gb); - next_sample = sign_extend(byte >> 4, 4) << shift; + next_sample = (unsigned)sign_extend(byte >> 4, 4) << shift; } next_sample += (current_sample * coeff1) + @@ -1401,11 +1637,11 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, int level, pred; int byte = bytestream2_get_byteu(&gb); - level = sign_extend(byte >> 4, 4) << shift[n]; + level = sign_extend(byte >> 4, 4) * (1 << shift[n]); pred = s[-1] * coeff[0][n] + s[-2] * coeff[1][n]; s[0] = av_clip_int16((level + pred + 0x80) >> 8); - level = sign_extend(byte, 4) << shift[n]; + level = sign_extend(byte, 4) * (1 << shift[n]); pred = s[0] * coeff[0][n] + s[-1] * coeff[1][n]; s[1] = av_clip_int16((level + pred + 0x80) >> 8); } @@ -1444,8 +1680,8 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, for (n = nb_samples >> (1 - st); n > 0; n--) { int v = bytestream2_get_byteu(&gb); - *samples++ = adpcm_ima_qt_expand_nibble(&c->status[0 ], v >> 4, 3); - *samples++ = adpcm_ima_qt_expand_nibble(&c->status[st], v & 0xf, 3); + *samples++ = adpcm_ima_qt_expand_nibble(&c->status[0 ], v >> 4 ); + *samples++ = adpcm_ima_qt_expand_nibble(&c->status[st], v & 0xf); } break; case AV_CODEC_ID_ADPCM_CT: @@ -1562,8 +1798,8 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, sampledat = sign_extend(byte >> 4, 4); } - sampledat = ((prev1 * factor1 + prev2 * factor2) + - ((sampledat * scale) << 11)) >> 11; + sampledat = ((prev1 * factor1 + prev2 * factor2) >> 11) + + sampledat * scale; *samples = av_clip_int16(sampledat); prev2 = prev1; prev1 = *samples++; @@ -1625,8 +1861,8 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, int byte = bytestream2_get_byteu(&gb); int index = (byte >> 4) & 7; unsigned int exp = byte & 0x0F; - int factor1 = table[ch][index * 2]; - int factor2 = table[ch][index * 2 + 1]; + int64_t factor1 = table[ch][index * 2]; + int64_t factor2 = table[ch][index * 2 + 1]; /* Decode 14 samples. */ for (n = 0; n < 14 && (i * 14 + n < nb_samples); n++) { @@ -1640,7 +1876,7 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, } sampledat = ((c->status[ch].sample1 * factor1 - + c->status[ch].sample2 * factor2) >> 11) + (sampledat << exp); + + c->status[ch].sample2 * factor2) >> 11) + sampledat * (1 << exp); *samples = av_clip_int16(sampledat); c->status[ch].sample2 = c->status[ch].sample1; c->status[ch].sample1 = *samples++; @@ -1687,7 +1923,7 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, else sampledat = sign_extend(byte >> 4, 4); - sampledat = (((sampledat << 12) >> (header & 0xf)) << 6) + prev; + sampledat = ((sampledat * (1 << 12)) >> (header & 0xf)) * (1 << 6) + prev; *samples++ = av_clip_int16(sampledat >> 6); c->status[channel].sample2 = c->status[channel].sample1; c->status[channel].sample1 = sampledat; @@ -1724,7 +1960,7 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, scale = sign_extend(byte, 4); } - scale = scale << 12; + scale = scale * (1 << 12); sample = (int)((scale >> shift) + (c->status[channel].sample1 * xa_adpcm_table[filter][0] + c->status[channel].sample2 * xa_adpcm_table[filter][1]) / 64); } *samples++ = av_clip_int16(sample); @@ -1734,7 +1970,64 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, } } break; + case AV_CODEC_ID_ADPCM_ARGO: + /* + * The format of each block: + * uint8_t left_control; + * uint4_t left_samples[nb_samples]; + * ---- and if stereo ---- + * uint8_t right_control; + * uint4_t right_samples[nb_samples]; + * + * Format of the control byte: + * MSB [SSSSDRRR] LSB + * S = (Shift Amount - 2) + * D = Decoder flag. + * R = Reserved + * + * Each block relies on the previous two samples of each channel. + * They should be 0 initially. + */ + for (channel = 0; channel < avctx->channels; channel++) { + int control, shift; + + samples = samples_p[channel]; + cs = c->status + channel; + /* Get the control byte and decode the samples, 2 at a time. */ + control = bytestream2_get_byteu(&gb); + shift = (control >> 4) + 2; + + for (n = 0; n < nb_samples / 2; n++) { + int sample = bytestream2_get_byteu(&gb); + *samples++ = adpcm_argo_expand_nibble(cs, sign_extend(sample >> 4, 4), control, shift); + *samples++ = adpcm_argo_expand_nibble(cs, sign_extend(sample >> 0, 4), control, shift); + } + } + break; + case AV_CODEC_ID_ADPCM_ZORK: + if (!c->has_status) { + for (channel = 0; channel < avctx->channels; channel++) { + c->status[channel].predictor = 0; + c->status[channel].step_index = 0; + } + c->has_status = 1; + } + for (n = 0; n < nb_samples * avctx->channels; n++) { + int v = bytestream2_get_byteu(&gb); + *samples++ = adpcm_zork_expand_nibble(&c->status[n % avctx->channels], v); + } + break; + case AV_CODEC_ID_ADPCM_IMA_MTF: + for (n = nb_samples / 2; n > 0; n--) { + for (channel = 0; channel < avctx->channels; channel++) { + int v = bytestream2_get_byteu(&gb); + *samples++ = adpcm_ima_mtf_expand_nibble(&c->status[channel], v >> 4); + samples[st] = adpcm_ima_mtf_expand_nibble(&c->status[channel], v & 0x0F); + } + samples += avctx->channels; + } + break; default: av_assert0(0); // unsupported codec_id should not happen } @@ -1788,6 +2081,7 @@ ADPCM_DECODER(AV_CODEC_ID_ADPCM_4XM, sample_fmts_s16p, adpcm_4xm, ADPCM_DECODER(AV_CODEC_ID_ADPCM_AFC, sample_fmts_s16p, adpcm_afc, "ADPCM Nintendo Gamecube AFC"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_AGM, sample_fmts_s16, adpcm_agm, "ADPCM AmuseGraphics Movie"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_AICA, sample_fmts_s16p, adpcm_aica, "ADPCM Yamaha AICA"); +ADPCM_DECODER(AV_CODEC_ID_ADPCM_ARGO, sample_fmts_s16p, adpcm_argo, "ADPCM Argonaut Games"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_CT, sample_fmts_s16, adpcm_ct, "ADPCM Creative Technology"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_DTK, sample_fmts_s16p, adpcm_dtk, "ADPCM Nintendo Gamecube DTK"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_EA, sample_fmts_s16, adpcm_ea, "ADPCM Electronic Arts"); @@ -1798,19 +2092,24 @@ ADPCM_DECODER(AV_CODEC_ID_ADPCM_EA_R3, sample_fmts_s16p, adpcm_ea_r3, ADPCM_DECODER(AV_CODEC_ID_ADPCM_EA_XAS, sample_fmts_s16p, adpcm_ea_xas, "ADPCM Electronic Arts XAS"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_AMV, sample_fmts_s16, adpcm_ima_amv, "ADPCM IMA AMV"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_APC, sample_fmts_s16, adpcm_ima_apc, "ADPCM IMA CRYO APC"); +ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_APM, sample_fmts_s16, adpcm_ima_apm, "ADPCM IMA Ubisoft APM"); +ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_CUNNING, sample_fmts_s16, adpcm_ima_cunning, "ADPCM IMA Cunning Developments"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_DAT4, sample_fmts_s16, adpcm_ima_dat4, "ADPCM IMA Eurocom DAT4"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_DK3, sample_fmts_s16, adpcm_ima_dk3, "ADPCM IMA Duck DK3"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_DK4, sample_fmts_s16, adpcm_ima_dk4, "ADPCM IMA Duck DK4"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_EA_EACS, sample_fmts_s16, adpcm_ima_ea_eacs, "ADPCM IMA Electronic Arts EACS"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_EA_SEAD, sample_fmts_s16, adpcm_ima_ea_sead, "ADPCM IMA Electronic Arts SEAD"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_ISS, sample_fmts_s16, adpcm_ima_iss, "ADPCM IMA Funcom ISS"); +ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_MTF, sample_fmts_s16, adpcm_ima_mtf, "ADPCM IMA Capcom's MT Framework"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_OKI, sample_fmts_s16, adpcm_ima_oki, "ADPCM IMA Dialogic OKI"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_QT, sample_fmts_s16p, adpcm_ima_qt, "ADPCM IMA QuickTime"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_RAD, sample_fmts_s16, adpcm_ima_rad, "ADPCM IMA Radical"); +ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_SSI, sample_fmts_s16, adpcm_ima_ssi, "ADPCM IMA Simon & Schuster Interactive"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_SMJPEG, sample_fmts_s16, adpcm_ima_smjpeg, "ADPCM IMA Loki SDL MJPEG"); +ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_ALP, sample_fmts_s16, adpcm_ima_alp, "ADPCM IMA High Voltage Software ALP"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_WAV, sample_fmts_s16p, adpcm_ima_wav, "ADPCM IMA WAV"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_IMA_WS, sample_fmts_both, adpcm_ima_ws, "ADPCM IMA Westwood"); -ADPCM_DECODER(AV_CODEC_ID_ADPCM_MS, sample_fmts_s16, adpcm_ms, "ADPCM Microsoft"); +ADPCM_DECODER(AV_CODEC_ID_ADPCM_MS, sample_fmts_both, adpcm_ms, "ADPCM Microsoft"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_MTAF, sample_fmts_s16p, adpcm_mtaf, "ADPCM MTAF"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_PSX, sample_fmts_s16p, adpcm_psx, "ADPCM Playstation"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_SBPRO_2, sample_fmts_s16, adpcm_sbpro_2, "ADPCM Sound Blaster Pro 2-bit"); @@ -1821,3 +2120,4 @@ ADPCM_DECODER(AV_CODEC_ID_ADPCM_THP_LE, sample_fmts_s16p, adpcm_thp_le, ADPCM_DECODER(AV_CODEC_ID_ADPCM_THP, sample_fmts_s16p, adpcm_thp, "ADPCM Nintendo THP"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_XA, sample_fmts_s16p, adpcm_xa, "ADPCM CDROM XA"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_YAMAHA, sample_fmts_s16, adpcm_yamaha, "ADPCM Yamaha"); +ADPCM_DECODER(AV_CODEC_ID_ADPCM_ZORK, sample_fmts_s16, adpcm_zork, "ADPCM Zork"); diff --git a/libavcodec/adpcm_data.c b/libavcodec/adpcm_data.c index 4cce0a58572..e34e04d5e98 100644 --- a/libavcodec/adpcm_data.c +++ b/libavcodec/adpcm_data.c @@ -177,3 +177,16 @@ const int16_t ff_adpcm_mtaf_stepsize[32][16] = { { 424, 1273, 2121, 2970, 3819, 4668, 5516, 6365, -424, -1273, -2121, -2970, -3819, -4668, -5516, -6365, }, }; + +const int8_t ff_adpcm_ima_cunning_index_table[9] = { + -1, -1, -1, -1, 1, 2, 3, 4, -1 +}; + +const int16_t ff_adpcm_ima_cunning_step_table[61] = { + 1, 1, 1, 1, 2, 2, 3, 3, 4, 5, + 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, + 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, + 192, 224, 256, 320, 384, 448, 512, 640, 768, 896, + 1024, 1280, 1536, 1792, 2048, 2560, 3072, 3584, 4096, 5120, + 6144, 7168, 8192, 10240, 12288, 14336, 16384, 20480, 24576, 28672, 0 +}; diff --git a/libavcodec/adpcm_data.h b/libavcodec/adpcm_data.h index 5a687131d82..d678bfc71ac 100644 --- a/libavcodec/adpcm_data.h +++ b/libavcodec/adpcm_data.h @@ -42,5 +42,7 @@ extern const int16_t ff_adpcm_yamaha_indexscale[]; extern const int8_t ff_adpcm_yamaha_difflookup[]; extern const int16_t ff_adpcm_afc_coeffs[2][16]; extern const int16_t ff_adpcm_mtaf_stepsize[32][16]; +extern const int8_t ff_adpcm_ima_cunning_index_table[9]; +extern const int16_t ff_adpcm_ima_cunning_step_table[61]; #endif /* AVCODEC_ADPCM_DATA_H */ diff --git a/libavcodec/adpcmenc.c b/libavcodec/adpcmenc.c index 668939c7789..d5fbc0b9a70 100644 --- a/libavcodec/adpcmenc.c +++ b/libavcodec/adpcmenc.c @@ -77,6 +77,15 @@ static av_cold int adpcm_encode_init(AVCodecContext *avctx) return AVERROR(EINVAL); } + if (avctx->trellis && avctx->codec->id == AV_CODEC_ID_ADPCM_IMA_SSI) { + /* + * The current trellis implementation doesn't work for extended + * runs of samples without periodic resets. Disallow it. + */ + av_log(avctx, AV_LOG_ERROR, "trellis not supported\n"); + return AVERROR_PATCHWELCOME; + } + if (avctx->trellis) { int frontier = 1 << avctx->trellis; int max_paths = frontier * FREEZE_INTERVAL; @@ -139,6 +148,10 @@ static av_cold int adpcm_encode_init(AVCodecContext *avctx) } avctx->frame_size = 512 * (avctx->sample_rate / 11025); break; + case AV_CODEC_ID_ADPCM_IMA_SSI: + avctx->frame_size = BLKSIZE * 2 / avctx->channels; + avctx->block_align = BLKSIZE; + break; default: ret = AVERROR(EINVAL); goto error; @@ -146,7 +159,6 @@ static av_cold int adpcm_encode_init(AVCodecContext *avctx) return 0; error: - adpcm_encode_close(avctx); return ret; } @@ -484,6 +496,8 @@ static int adpcm_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, if (avctx->codec_id == AV_CODEC_ID_ADPCM_SWF) pkt_size = (2 + avctx->channels * (22 + 4 * (frame->nb_samples - 1)) + 7) / 8; + else if (avctx->codec_id == AV_CODEC_ID_ADPCM_IMA_SSI) + pkt_size = (frame->nb_samples * avctx->channels) / 2; else pkt_size = avctx->block_align; if ((ret = ff_alloc_packet2(avctx, avpkt, pkt_size, 0)) < 0) @@ -568,6 +582,22 @@ static int adpcm_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, flush_put_bits(&pb); break; } + case AV_CODEC_ID_ADPCM_IMA_SSI: + { + PutBitContext pb; + init_put_bits(&pb, dst, pkt_size); + + av_assert0(avctx->trellis == 0); + + for (i = 0; i < frame->nb_samples; i++) { + for (ch = 0; ch < avctx->channels; ch++) { + put_bits(&pb, 4, adpcm_ima_qt_compress_sample(c->status + ch, *samples++)); + } + } + + flush_put_bits(&pb); + break; + } case AV_CODEC_ID_ADPCM_SWF: { PutBitContext pb; @@ -706,21 +736,24 @@ static const enum AVSampleFormat sample_fmts_p[] = { AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_NONE }; -#define ADPCM_ENCODER(id_, name_, sample_fmts_, long_name_) \ -AVCodec ff_ ## name_ ## _encoder = { \ - .name = #name_, \ - .long_name = NULL_IF_CONFIG_SMALL(long_name_), \ - .type = AVMEDIA_TYPE_AUDIO, \ - .id = id_, \ - .priv_data_size = sizeof(ADPCMEncodeContext), \ - .init = adpcm_encode_init, \ - .encode2 = adpcm_encode_frame, \ - .close = adpcm_encode_close, \ - .sample_fmts = sample_fmts_, \ +#define ADPCM_ENCODER(id_, name_, sample_fmts_, capabilities_, long_name_) \ +AVCodec ff_ ## name_ ## _encoder = { \ + .name = #name_, \ + .long_name = NULL_IF_CONFIG_SMALL(long_name_), \ + .type = AVMEDIA_TYPE_AUDIO, \ + .id = id_, \ + .priv_data_size = sizeof(ADPCMEncodeContext), \ + .init = adpcm_encode_init, \ + .encode2 = adpcm_encode_frame, \ + .close = adpcm_encode_close, \ + .sample_fmts = sample_fmts_, \ + .capabilities = capabilities_, \ + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, \ } -ADPCM_ENCODER(AV_CODEC_ID_ADPCM_IMA_QT, adpcm_ima_qt, sample_fmts_p, "ADPCM IMA QuickTime"); -ADPCM_ENCODER(AV_CODEC_ID_ADPCM_IMA_WAV, adpcm_ima_wav, sample_fmts_p, "ADPCM IMA WAV"); -ADPCM_ENCODER(AV_CODEC_ID_ADPCM_MS, adpcm_ms, sample_fmts, "ADPCM Microsoft"); -ADPCM_ENCODER(AV_CODEC_ID_ADPCM_SWF, adpcm_swf, sample_fmts, "ADPCM Shockwave Flash"); -ADPCM_ENCODER(AV_CODEC_ID_ADPCM_YAMAHA, adpcm_yamaha, sample_fmts, "ADPCM Yamaha"); +ADPCM_ENCODER(AV_CODEC_ID_ADPCM_IMA_QT, adpcm_ima_qt, sample_fmts_p, 0, "ADPCM IMA QuickTime"); +ADPCM_ENCODER(AV_CODEC_ID_ADPCM_IMA_SSI, adpcm_ima_ssi, sample_fmts, AV_CODEC_CAP_SMALL_LAST_FRAME, "ADPCM IMA Simon & Schuster Interactive"); +ADPCM_ENCODER(AV_CODEC_ID_ADPCM_IMA_WAV, adpcm_ima_wav, sample_fmts_p, 0, "ADPCM IMA WAV"); +ADPCM_ENCODER(AV_CODEC_ID_ADPCM_MS, adpcm_ms, sample_fmts, 0, "ADPCM Microsoft"); +ADPCM_ENCODER(AV_CODEC_ID_ADPCM_SWF, adpcm_swf, sample_fmts, 0, "ADPCM Shockwave Flash"); +ADPCM_ENCODER(AV_CODEC_ID_ADPCM_YAMAHA, adpcm_yamaha, sample_fmts, 0, "ADPCM Yamaha"); diff --git a/libavcodec/adxdec.c b/libavcodec/adxdec.c index 178ea99dcfc..40ed8e5ba79 100644 --- a/libavcodec/adxdec.c +++ b/libavcodec/adxdec.c @@ -81,7 +81,7 @@ static int adx_decode(ADXContext *c, int16_t *out, int offset, s2 = prev->s2; for (i = 0; i < BLOCK_SAMPLES; i++) { d = get_sbits(&gb, 4); - s0 = ((d * (1 << COEFF_BITS)) * scale + c->coeff[0] * s1 + c->coeff[1] * s2) >> COEFF_BITS; + s0 = d * scale + ((c->coeff[0] * s1 + c->coeff[1] * s2) >> COEFF_BITS); s2 = s1; s1 = av_clip_int16(s0); *out++ = s1; diff --git a/libavcodec/adxenc.c b/libavcodec/adxenc.c index f1ba5911b33..93b902b0e16 100644 --- a/libavcodec/adxenc.c +++ b/libavcodec/adxenc.c @@ -48,7 +48,7 @@ static void adx_encode(ADXContext *c, uint8_t *adx, const int16_t *wav, s2 = prev->s2; for (i = 0, j = 0; j < 32; i += channels, j++) { s0 = wav[i]; - d = ((s0 << COEFF_BITS) - c->coeff[0] * s1 - c->coeff[1] * s2) >> COEFF_BITS; + d = s0 + ((-c->coeff[0] * s1 - c->coeff[1] * s2) >> COEFF_BITS); if (max < d) max = d; if (min > d) @@ -79,13 +79,13 @@ static void adx_encode(ADXContext *c, uint8_t *adx, const int16_t *wav, s1 = prev->s1; s2 = prev->s2; for (i = 0, j = 0; j < 32; i += channels, j++) { - d = ((wav[i] << COEFF_BITS) - c->coeff[0] * s1 - c->coeff[1] * s2) >> COEFF_BITS; + d = wav[i] + ((-c->coeff[0] * s1 - c->coeff[1] * s2) >> COEFF_BITS); d = av_clip_intp2(ROUNDED_DIV(d, scale), 3); put_sbits(&pb, 4, d); - s0 = ((d << COEFF_BITS) * scale + c->coeff[0] * s1 + c->coeff[1] * s2) >> COEFF_BITS; + s0 = d * scale + ((c->coeff[0] * s1 + c->coeff[1] * s2) >> COEFF_BITS); s2 = s1; s1 = s0; } @@ -141,10 +141,26 @@ static int adx_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr) { ADXContext *c = avctx->priv_data; - const int16_t *samples = (const int16_t *)frame->data[0]; + const int16_t *samples = frame ? (const int16_t *)frame->data[0] : NULL; uint8_t *dst; int ch, out_size, ret; + if (!samples) { + if (c->eof) + return 0; + if ((ret = ff_alloc_packet2(avctx, avpkt, 18, 0)) < 0) + return ret; + c->eof = 1; + dst = avpkt->data; + bytestream_put_be16(&dst, 0x8001); + bytestream_put_be16(&dst, 0x000E); + bytestream_put_be64(&dst, 0x0); + bytestream_put_be32(&dst, 0x0); + bytestream_put_be16(&dst, 0x0); + *got_packet_ptr = 1; + return 0; + } + out_size = BLOCK_SIZE * avctx->channels + !c->header_parsed * HEADER_SIZE; if ((ret = ff_alloc_packet2(avctx, avpkt, out_size, 0)) < 0) return ret; @@ -165,6 +181,8 @@ static int adx_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, dst += BLOCK_SIZE; } + avpkt->pts = frame->pts; + avpkt->duration = frame->nb_samples; *got_packet_ptr = 1; return 0; } @@ -177,6 +195,7 @@ AVCodec ff_adpcm_adx_encoder = { .priv_data_size = sizeof(ADXContext), .init = adx_encode_init, .encode2 = adx_encode_frame, + .capabilities = AV_CODEC_CAP_DELAY, .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, }; diff --git a/libavcodec/agm.c b/libavcodec/agm.c index 2c4c9805e90..bc9dfc02f31 100644 --- a/libavcodec/agm.c +++ b/libavcodec/agm.c @@ -423,8 +423,8 @@ static int decode_inter_plane(AGMContext *s, GetBitContext *gb, int size, int map = s->map[x]; if (orig_mv_x >= -32) { - if (y * 8 + mv_y < 0 || y * 8 + mv_y >= h || - x * 8 + mv_x < 0 || x * 8 + mv_x >= w) + if (y * 8 + mv_y < 0 || y * 8 + mv_y + 8 >= h || + x * 8 + mv_x < 0 || x * 8 + mv_x + 8 >= w) return AVERROR_INVALIDDATA; copy_block8(frame->data[plane] + (s->blocks_h - 1 - y) * 8 * frame->linesize[plane] + x * 8, @@ -460,8 +460,8 @@ static int decode_inter_plane(AGMContext *s, GetBitContext *gb, int size, return ret; if (orig_mv_x >= -32) { - if (y * 8 + mv_y < 0 || y * 8 + mv_y >= h || - x * 8 + mv_x < 0 || x * 8 + mv_x >= w) + if (y * 8 + mv_y < 0 || y * 8 + mv_y + 8 > h || + x * 8 + mv_x < 0 || x * 8 + mv_x + 8 > w) return AVERROR_INVALIDDATA; copy_block8(frame->data[plane] + (s->blocks_h - 1 - y) * 8 * frame->linesize[plane] + x * 8, @@ -573,13 +573,16 @@ static int decode_raw_intra_rgb(AVCodecContext *avctx, GetByteContext *gbyte, AV uint8_t *dst = frame->data[0] + (avctx->height - 1) * frame->linesize[0]; uint8_t r = 0, g = 0, b = 0; + if (bytestream2_get_bytes_left(gbyte) < 3 * avctx->width * avctx->height) + return AVERROR_INVALIDDATA; + for (int y = 0; y < avctx->height; y++) { for (int x = 0; x < avctx->width; x++) { - dst[x*3+0] = bytestream2_get_byte(gbyte) + r; + dst[x*3+0] = bytestream2_get_byteu(gbyte) + r; r = dst[x*3+0]; - dst[x*3+1] = bytestream2_get_byte(gbyte) + g; + dst[x*3+1] = bytestream2_get_byteu(gbyte) + g; g = dst[x*3+1]; - dst[x*3+2] = bytestream2_get_byte(gbyte) + b; + dst[x*3+2] = bytestream2_get_byteu(gbyte) + b; b = dst[x*3+2]; } dst -= frame->linesize[0]; @@ -827,7 +830,7 @@ static int decode_intra(AVCodecContext *avctx, GetBitContext *gb, AVFrame *frame static int decode_motion_vectors(AVCodecContext *avctx, GetBitContext *gb) { AGMContext *s = avctx->priv_data; - int nb_mvs = ((avctx->height + 15) >> 4) * ((avctx->width + 15) >> 4); + int nb_mvs = ((avctx->coded_height + 15) >> 4) * ((avctx->coded_width + 15) >> 4); int ret, skip = 0, value, map; av_fast_padded_malloc(&s->mvectors, &s->mvectors_size, @@ -1117,6 +1120,13 @@ static int decode_frame(AVCodecContext *avctx, void *data, frame->key_frame = s->key_frame; frame->pict_type = s->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + if (!s->key_frame) { + if (!s->prev_frame->data[0]) { + av_log(avctx, AV_LOG_ERROR, "Missing reference frame.\n"); + return AVERROR_INVALIDDATA; + } + } + if (header) { if (avctx->codec_tag == MKTAG('A', 'G', 'M', '0') || avctx->codec_tag == MKTAG('A', 'G', 'M', '1')) @@ -1186,10 +1196,6 @@ static int decode_frame(AVCodecContext *avctx, void *data, else ret = decode_intra(avctx, gb, frame); } else { - if (!s->prev_frame->data[0]) { - av_log(avctx, AV_LOG_ERROR, "Missing reference frame.\n"); - return AVERROR_INVALIDDATA; - } if (s->prev_frame-> width != frame->width || s->prev_frame->height != frame->height) return AVERROR_INVALIDDATA; @@ -1236,6 +1242,11 @@ static av_cold int decode_init(AVCodecContext *avctx) s->dct = avctx->codec_tag != MKTAG('A', 'G', 'M', '4') && avctx->codec_tag != MKTAG('A', 'G', 'M', '5'); + if (!s->rgb && !s->dct) { + if ((avctx->width & 1) || (avctx->height & 1)) + return AVERROR_INVALIDDATA; + } + avctx->idct_algo = FF_IDCT_SIMPLE; ff_idctdsp_init(&s->idsp, avctx); ff_init_scantable(s->idsp.idct_permutation, &s->scantable, ff_zigzag_direct); diff --git a/libavcodec/aic.c b/libavcodec/aic.c index 956d71fcff9..f027fa99ef9 100644 --- a/libavcodec/aic.c +++ b/libavcodec/aic.c @@ -504,6 +504,5 @@ AVCodec ff_aic_decoder = { .close = aic_decode_close, .decode = aic_decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(aic_decode_init), .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; diff --git a/libavcodec/alac.c b/libavcodec/alac.c index 782d461b227..82689da02a2 100644 --- a/libavcodec/alac.c +++ b/libavcodec/alac.c @@ -215,20 +215,20 @@ static void lpc_prediction(int32_t *error_buffer, uint32_t *buffer_out, /* LPC prediction */ for (j = 0; j < lpc_order; j++) val += (pred[j] - d) * lpc_coefs[j]; - val = (val + (1 << (lpc_quant - 1))) >> lpc_quant; + val = (val + (1LL << (lpc_quant - 1))) >> lpc_quant; val += d + error_val; buffer_out[i] = sign_extend(val, bps); /* adapt LPC coefficients */ error_sign = sign_only(error_val); if (error_sign) { - for (j = 0; j < lpc_order && (int)error_val * error_sign > 0; j++) { + for (j = 0; j < lpc_order && (int)(error_val * error_sign) > 0; j++) { int sign; val = d - pred[j]; sign = sign_only(val) * error_sign; lpc_coefs[j] -= sign; - val *= sign; - error_val -= (val >> lpc_quant) * (j + 1); + val *= (unsigned)sign; + error_val -= (val >> lpc_quant) * (j + 1U); } } } @@ -397,13 +397,13 @@ static int decode_element(AVCodecContext *avctx, AVFrame *frame, int ch_index, case 20: { for (ch = 0; ch < channels; ch++) { for (i = 0; i < alac->nb_samples; i++) - alac->output_samples_buffer[ch][i] <<= 12; + alac->output_samples_buffer[ch][i] *= 1U << 12; }} break; case 24: { for (ch = 0; ch < channels; ch++) { for (i = 0; i < alac->nb_samples; i++) - alac->output_samples_buffer[ch][i] <<= 8; + alac->output_samples_buffer[ch][i] *= 1U << 8; }} break; } @@ -601,15 +601,6 @@ static av_cold int alac_decode_init(AVCodecContext * avctx) return 0; } -#if HAVE_THREADS -static int init_thread_copy(AVCodecContext *avctx) -{ - ALACContext *alac = avctx->priv_data; - alac->avctx = avctx; - return allocate_buffers(alac); -} -#endif - static const AVOption options[] = { { "extra_bits_bug", "Force non-standard decoding process", offsetof(ALACContext, extra_bit_bug), AV_OPT_TYPE_BOOL, { .i64 = 0 }, @@ -633,7 +624,6 @@ AVCodec ff_alac_decoder = { .init = alac_decode_init, .close = alac_decode_close, .decode = alac_decode_frame, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(init_thread_copy), .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, .priv_class = &alac_class }; diff --git a/libavcodec/alacdsp.c b/libavcodec/alacdsp.c index ecbaedb0670..9996eb4319d 100644 --- a/libavcodec/alacdsp.c +++ b/libavcodec/alacdsp.c @@ -49,7 +49,7 @@ static void append_extra_bits(int32_t *buffer[2], int32_t *extra_bits_buffer[2], for (ch = 0; ch < channels; ch++) for (i = 0; i < nb_samples; i++) - buffer[ch][i] = (buffer[ch][i] << extra_bits) | extra_bits_buffer[ch][i]; + buffer[ch][i] = ((unsigned)buffer[ch][i] << extra_bits) | extra_bits_buffer[ch][i]; } av_cold void ff_alacdsp_init(ALACDSPContext *c) diff --git a/libavcodec/aliaspixdec.c b/libavcodec/aliaspixdec.c index 087b18fb91f..def7e17c0f8 100644 --- a/libavcodec/aliaspixdec.c +++ b/libavcodec/aliaspixdec.c @@ -62,6 +62,9 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, if (ret < 0) return ret; + if (bytestream2_get_bytes_left(&gb) < width*height / 255) + return AVERROR_INVALIDDATA; + ret = ff_get_buffer(avctx, f, 0); if (ret < 0) return ret; diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index d2f9a39ce57..80f128cade5 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -68,6 +68,7 @@ extern AVCodec ff_brender_pix_decoder; extern AVCodec ff_c93_decoder; extern AVCodec ff_cavs_decoder; extern AVCodec ff_cdgraphics_decoder; +extern AVCodec ff_cdtoons_decoder; extern AVCodec ff_cdxl_decoder; extern AVCodec ff_cfhd_decoder; extern AVCodec ff_cinepak_encoder; @@ -158,6 +159,7 @@ extern AVCodec ff_hymt_decoder; extern AVCodec ff_idcin_decoder; extern AVCodec ff_iff_ilbm_decoder; extern AVCodec ff_imm4_decoder; +extern AVCodec ff_imm5_decoder; extern AVCodec ff_indeo2_decoder; extern AVCodec ff_indeo3_decoder; extern AVCodec ff_indeo4_decoder; @@ -215,10 +217,14 @@ extern AVCodec ff_msvideo1_encoder; extern AVCodec ff_msvideo1_decoder; extern AVCodec ff_mszh_decoder; extern AVCodec ff_mts2_decoder; +extern AVCodec ff_mv30_decoder; extern AVCodec ff_mvc1_decoder; extern AVCodec ff_mvc2_decoder; +extern AVCodec ff_mvdv_decoder; +extern AVCodec ff_mvha_decoder; extern AVCodec ff_mwsc_decoder; extern AVCodec ff_mxpeg_decoder; +extern AVCodec ff_notchlc_decoder; extern AVCodec ff_nuv_decoder; extern AVCodec ff_paf_video_decoder; extern AVCodec ff_pam_encoder; @@ -227,6 +233,7 @@ extern AVCodec ff_pbm_encoder; extern AVCodec ff_pbm_decoder; extern AVCodec ff_pcx_encoder; extern AVCodec ff_pcx_decoder; +extern AVCodec ff_pfm_decoder; extern AVCodec ff_pgm_encoder; extern AVCodec ff_pgm_decoder; extern AVCodec ff_pgmyuv_encoder; @@ -271,7 +278,6 @@ extern AVCodec ff_s302m_decoder; extern AVCodec ff_sanm_decoder; extern AVCodec ff_scpr_decoder; extern AVCodec ff_screenpresso_decoder; -extern AVCodec ff_sdx2_dpcm_decoder; extern AVCodec ff_sgi_encoder; extern AVCodec ff_sgi_decoder; extern AVCodec ff_sgirle_decoder; @@ -387,6 +393,7 @@ extern AVCodec ff_ac3_encoder; extern AVCodec ff_ac3_decoder; extern AVCodec ff_ac3_fixed_encoder; extern AVCodec ff_ac3_fixed_decoder; +extern AVCodec ff_acelp_kelvin_decoder; extern AVCodec ff_alac_encoder; extern AVCodec ff_alac_decoder; extern AVCodec ff_als_decoder; @@ -428,6 +435,7 @@ extern AVCodec ff_g723_1_decoder; extern AVCodec ff_g729_decoder; extern AVCodec ff_gsm_decoder; extern AVCodec ff_gsm_ms_decoder; +extern AVCodec ff_hca_decoder; extern AVCodec ff_hcom_decoder; extern AVCodec ff_iac_decoder; extern AVCodec ff_ilbc_decoder; @@ -469,6 +477,7 @@ extern AVCodec ff_sbc_encoder; extern AVCodec ff_sbc_decoder; extern AVCodec ff_shorten_decoder; extern AVCodec ff_sipr_decoder; +extern AVCodec ff_siren_decoder; extern AVCodec ff_smackaud_decoder; extern AVCodec ff_sonic_encoder; extern AVCodec ff_sonic_decoder; @@ -561,13 +570,14 @@ extern AVCodec ff_pcm_u32le_encoder; extern AVCodec ff_pcm_u32le_decoder; extern AVCodec ff_pcm_vidc_encoder; extern AVCodec ff_pcm_vidc_decoder; -extern AVCodec ff_pcm_zork_decoder; /* DPCM codecs */ +extern AVCodec ff_derf_dpcm_decoder; extern AVCodec ff_gremlin_dpcm_decoder; extern AVCodec ff_interplay_dpcm_decoder; extern AVCodec ff_roq_dpcm_encoder; extern AVCodec ff_roq_dpcm_decoder; +extern AVCodec ff_sdx2_dpcm_decoder; extern AVCodec ff_sol_dpcm_decoder; extern AVCodec ff_xan_dpcm_decoder; @@ -578,6 +588,7 @@ extern AVCodec ff_adpcm_adx_decoder; extern AVCodec ff_adpcm_afc_decoder; extern AVCodec ff_adpcm_agm_decoder; extern AVCodec ff_adpcm_aica_decoder; +extern AVCodec ff_adpcm_argo_decoder; extern AVCodec ff_adpcm_ct_decoder; extern AVCodec ff_adpcm_dtk_decoder; extern AVCodec ff_adpcm_ea_decoder; @@ -593,17 +604,23 @@ extern AVCodec ff_adpcm_g726_decoder; extern AVCodec ff_adpcm_g726le_encoder; extern AVCodec ff_adpcm_g726le_decoder; extern AVCodec ff_adpcm_ima_amv_decoder; +extern AVCodec ff_adpcm_ima_alp_decoder; extern AVCodec ff_adpcm_ima_apc_decoder; +extern AVCodec ff_adpcm_ima_apm_decoder; +extern AVCodec ff_adpcm_ima_cunning_decoder; extern AVCodec ff_adpcm_ima_dat4_decoder; extern AVCodec ff_adpcm_ima_dk3_decoder; extern AVCodec ff_adpcm_ima_dk4_decoder; extern AVCodec ff_adpcm_ima_ea_eacs_decoder; extern AVCodec ff_adpcm_ima_ea_sead_decoder; extern AVCodec ff_adpcm_ima_iss_decoder; +extern AVCodec ff_adpcm_ima_mtf_decoder; extern AVCodec ff_adpcm_ima_oki_decoder; extern AVCodec ff_adpcm_ima_qt_encoder; extern AVCodec ff_adpcm_ima_qt_decoder; extern AVCodec ff_adpcm_ima_rad_decoder; +extern AVCodec ff_adpcm_ima_ssi_decoder; +extern AVCodec ff_adpcm_ima_ssi_encoder; extern AVCodec ff_adpcm_ima_smjpeg_decoder; extern AVCodec ff_adpcm_ima_wav_encoder; extern AVCodec ff_adpcm_ima_wav_decoder; @@ -623,6 +640,7 @@ extern AVCodec ff_adpcm_vima_decoder; extern AVCodec ff_adpcm_xa_decoder; extern AVCodec ff_adpcm_yamaha_encoder; extern AVCodec ff_adpcm_yamaha_decoder; +extern AVCodec ff_adpcm_zork_decoder; /* subtitles */ extern AVCodec ff_ssa_encoder; @@ -661,7 +679,9 @@ extern AVCodec ff_xsub_decoder; /* external libraries */ extern AVCodec ff_aac_at_encoder; extern AVCodec ff_aac_at_decoder; +extern AVCodec ff_aac_mf_encoder; extern AVCodec ff_ac3_at_decoder; +extern AVCodec ff_ac3_mf_encoder; extern AVCodec ff_adpcm_ima_qt_at_decoder; extern AVCodec ff_alac_at_encoder; extern AVCodec ff_alac_at_decoder; @@ -673,13 +693,13 @@ extern AVCodec ff_ilbc_at_decoder; extern AVCodec ff_mp1_at_decoder; extern AVCodec ff_mp2_at_decoder; extern AVCodec ff_mp3_at_decoder; +extern AVCodec ff_mp3_mf_encoder; extern AVCodec ff_pcm_alaw_at_encoder; extern AVCodec ff_pcm_alaw_at_decoder; extern AVCodec ff_pcm_mulaw_at_encoder; extern AVCodec ff_pcm_mulaw_at_decoder; extern AVCodec ff_qdmc_at_decoder; extern AVCodec ff_qdm2_at_decoder; -extern AVCodec ff_libaom_av1_decoder; extern AVCodec ff_libaom_av1_encoder; extern AVCodec ff_libaribb24_decoder; extern AVCodec ff_libcelt_decoder; @@ -703,6 +723,7 @@ extern AVCodec ff_libopenjpeg_encoder; extern AVCodec ff_libopenjpeg_decoder; extern AVCodec ff_libopus_encoder; extern AVCodec ff_libopus_decoder; +extern AVCodec ff_librav1e_encoder; extern AVCodec ff_librsvg_decoder; extern AVCodec ff_libshine_encoder; extern AVCodec ff_libspeex_encoder; @@ -737,10 +758,12 @@ extern AVCodec ff_idf_decoder; /* external libraries, that shouldn't be used by default if one of the * above is available */ extern AVCodec ff_h263_v4l2m2m_encoder; +extern AVCodec ff_libaom_av1_decoder; extern AVCodec ff_libopenh264_encoder; extern AVCodec ff_libopenh264_decoder; extern AVCodec ff_h264_amf_encoder; extern AVCodec ff_h264_cuvid_decoder; +extern AVCodec ff_h264_mf_encoder; extern AVCodec ff_h264_nvenc_encoder; extern AVCodec ff_h264_omx_encoder; extern AVCodec ff_h264_qsv_encoder; @@ -755,6 +778,7 @@ extern AVCodec ff_nvenc_hevc_encoder; extern AVCodec ff_hevc_amf_encoder; extern AVCodec ff_hevc_cuvid_decoder; extern AVCodec ff_hevc_mediacodec_decoder; +extern AVCodec ff_hevc_mf_encoder; extern AVCodec ff_hevc_nvenc_encoder; extern AVCodec ff_hevc_qsv_encoder; extern AVCodec ff_hevc_v4l2m2m_encoder; @@ -763,6 +787,7 @@ extern AVCodec ff_hevc_videotoolbox_encoder; extern AVCodec ff_libkvazaar_encoder; extern AVCodec ff_mjpeg_cuvid_decoder; extern AVCodec ff_mjpeg_qsv_encoder; +extern AVCodec ff_mjpeg_qsv_decoder; extern AVCodec ff_mjpeg_vaapi_encoder; extern AVCodec ff_mpeg1_cuvid_decoder; extern AVCodec ff_mpeg2_cuvid_decoder; @@ -770,6 +795,7 @@ extern AVCodec ff_mpeg2_qsv_encoder; extern AVCodec ff_mpeg2_vaapi_encoder; extern AVCodec ff_mpeg4_cuvid_decoder; extern AVCodec ff_mpeg4_mediacodec_decoder; +extern AVCodec ff_mpeg4_omx_encoder; extern AVCodec ff_mpeg4_v4l2m2m_encoder; extern AVCodec ff_vc1_cuvid_decoder; extern AVCodec ff_vp8_cuvid_decoder; @@ -779,11 +805,14 @@ extern AVCodec ff_vp8_v4l2m2m_encoder; extern AVCodec ff_vp8_vaapi_encoder; extern AVCodec ff_vp9_cuvid_decoder; extern AVCodec ff_vp9_mediacodec_decoder; +extern AVCodec ff_vp9_qsv_decoder; extern AVCodec ff_vp9_vaapi_encoder; +extern AVCodec ff_vp9_qsv_encoder; // The iterate API is not usable with ossfuzz due to the excessive size of binaries created #if CONFIG_OSSFUZZ AVCodec * codec_list[] = { + NULL, NULL, NULL }; diff --git a/libavcodec/alsdec.c b/libavcodec/alsdec.c index f8d10df8c6c..62c60360378 100644 --- a/libavcodec/alsdec.c +++ b/libavcodec/alsdec.c @@ -236,6 +236,7 @@ typedef struct ALSDecContext { int **raw_mantissa; ///< decoded mantissa bits of the difference signal unsigned char *larray; ///< buffer to store the output of masked lz decompression int *nbits; ///< contains the number of bits to read for masked lz decompression for all samples + int highest_decoded_channel; } ALSDecContext; @@ -302,8 +303,8 @@ static av_cold int read_specific_config(ALSDecContext *ctx) if ((ret = init_get_bits8(&gb, avctx->extradata, avctx->extradata_size)) < 0) return ret; - config_offset = avpriv_mpeg4audio_get_config(&m4ac, avctx->extradata, - avctx->extradata_size * 8, 1); + config_offset = avpriv_mpeg4audio_get_config2(&m4ac, avctx->extradata, + avctx->extradata_size, 1, avctx); if (config_offset < 0) return AVERROR_INVALIDDATA; @@ -833,6 +834,9 @@ static int read_var_block_data(ALSDecContext *ctx, ALSBlockData *bd) k [sb] = s[sb] > b ? s[sb] - b : 0; delta[sb] = 5 - s[sb] + k[sb]; + if (k[sb] >= 32) + return AVERROR_INVALIDDATA; + ff_bgmc_decode(gb, sb_len, current_res, delta[sb], sx[sb], &high, &low, &value, ctx->bgmc_lut, ctx->bgmc_lut_status); @@ -1472,6 +1476,9 @@ static int read_diff_float_data(ALSDecContext *ctx, unsigned int ra_frame) { ff_mlz_flush_dict(ctx->mlz); } + if (avctx->channels * 8 > get_bits_left(gb)) + return AVERROR_INVALIDDATA; + for (c = 0; c < avctx->channels; ++c) { if (use_acf) { //acf_flag @@ -1672,6 +1679,7 @@ static int read_frame_data(ALSDecContext *ctx, unsigned int ra_frame) memmove(ctx->raw_samples[c] - sconf->max_order, ctx->raw_samples[c] - sconf->max_order + sconf->frame_length, sizeof(*ctx->raw_samples[c]) * sconf->max_order); + ctx->highest_decoded_channel = c; } } else { // multi-channel coding ALSBlockData bd = { 0 }; @@ -1740,6 +1748,8 @@ static int read_frame_data(ALSDecContext *ctx, unsigned int ra_frame) if ((ret = decode_block(ctx, &bd)) < 0) return ret; + + ctx->highest_decoded_channel = FFMAX(ctx->highest_decoded_channel, c); } memset(reverted_channels, 0, avctx->channels * sizeof(*reverted_channels)); @@ -1796,11 +1806,15 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame_ptr, else ctx->cur_frame_length = sconf->frame_length; + ctx->highest_decoded_channel = 0; // decode the frame data if ((invalid_frame = read_frame_data(ctx, ra_frame)) < 0) av_log(ctx->avctx, AV_LOG_WARNING, "Reading frame data failed. Skipping RA unit.\n"); + if (ctx->highest_decoded_channel == 0) + return AVERROR_INVALIDDATA; + ctx->frame_id++; /* get output buffer */ @@ -1812,15 +1826,18 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame_ptr, #define INTERLEAVE_OUTPUT(bps) \ { \ int##bps##_t *dest = (int##bps##_t*)frame->data[0]; \ + int channels = avctx->channels; \ + int32_t *raw_samples = ctx->raw_samples[0]; \ + int raw_step = channels > 1 ? ctx->raw_samples[1] - raw_samples : 1; \ shift = bps - ctx->avctx->bits_per_raw_sample; \ if (!ctx->cs_switch) { \ for (sample = 0; sample < ctx->cur_frame_length; sample++) \ - for (c = 0; c < avctx->channels; c++) \ - *dest++ = ctx->raw_samples[c][sample] * (1U << shift); \ + for (c = 0; c < channels; c++) \ + *dest++ = raw_samples[c*raw_step + sample] * (1U << shift); \ } else { \ for (sample = 0; sample < ctx->cur_frame_length; sample++) \ - for (c = 0; c < avctx->channels; c++) \ - *dest++ = ctx->raw_samples[sconf->chan_pos[c]][sample] * (1U << shift); \ + for (c = 0; c < channels; c++) \ + *dest++ = raw_samples[sconf->chan_pos[c]*raw_step + sample] * (1U << shift);\ } \ } diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c index 384d8efc92c..876fddd2b6e 100644 --- a/libavcodec/amfenc.c +++ b/libavcodec/amfenc.c @@ -213,6 +213,7 @@ static int amf_init_from_dxva2_device(AVCodecContext *avctx, AVDXVA2DeviceContex static int amf_init_context(AVCodecContext *avctx) { AmfContext *ctx = avctx->priv_data; + AMFContext1 *context1 = NULL; AMF_RESULT res; av_unused int ret; @@ -311,8 +312,20 @@ static int amf_init_context(AVCodecContext *avctx) if (res == AMF_OK) { av_log(avctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D9.\n"); } else { - av_log(avctx, AV_LOG_ERROR, "AMF initialisation failed via D3D9: error %d.\n", res); - return AVERROR(ENOSYS); + AMFGuid guid = IID_AMFContext1(); + res = ctx->context->pVtbl->QueryInterface(ctx->context, &guid, (void**)&context1); + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext1() failed with error %d\n", res); + + res = context1->pVtbl->InitVulkan(context1, NULL); + context1->pVtbl->Release(context1); + if (res != AMF_OK) { + if (res == AMF_NOT_SUPPORTED) + av_log(avctx, AV_LOG_ERROR, "AMF via Vulkan is not supported on the given device.\n"); + else + av_log(avctx, AV_LOG_ERROR, "AMF failed to initialise on the given Vulkan device: %d.\n", res); + return AVERROR(ENOSYS); + } + av_log(avctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via Vulkan.\n"); } } } @@ -438,7 +451,7 @@ static int amf_copy_buffer(AVCodecContext *avctx, AVPacket *pkt, AMFBuffer *buff int64_t timestamp = AV_NOPTS_VALUE; int64_t size = buffer->pVtbl->GetSize(buffer); - if ((ret = ff_alloc_packet2(avctx, pkt, size, 0)) < 0) { + if ((ret = av_new_packet(pkt, size)) < 0) { return ret; } memcpy(pkt->data, buffer->pVtbl->GetNative(buffer), size); diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c index 2c082e93bd9..7f2817f1157 100644 --- a/libavcodec/amfenc_h264.c +++ b/libavcodec/amfenc_h264.c @@ -366,6 +366,7 @@ static const AVCodecDefault defaults[] = { { "b", "2M" }, { "g", "250" }, { "slices", "1" }, + { "flags", "+loop"}, { NULL }, }; diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c index 7c9a33ab335..77e57d24617 100644 --- a/libavcodec/amfenc_hevc.c +++ b/libavcodec/amfenc_hevc.c @@ -136,7 +136,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 == 0) { + if (profile_level == FF_LEVEL_UNKNOWN) { profile_level = ctx->level; } if (profile_level != 0) { @@ -144,7 +144,7 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) } AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET, ctx->quality); // Maximum Reference Frames - if (avctx->refs != 0) { + if (avctx->refs != -1) { AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_MAX_NUM_REFRAMES, avctx->refs); } // Aspect Ratio @@ -254,10 +254,10 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) } if (ctx->qp_p != -1) { - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QP_I, ctx->qp_p); + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QP_P, ctx->qp_p); } if (ctx->qp_i != -1) { - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QP_P, ctx->qp_i); + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QP_I, ctx->qp_i); } AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_SKIP_FRAME_ENABLE, ctx->skip_frame); diff --git a/libavcodec/amrwbdata.h b/libavcodec/amrwbdata.h index 8a8cbfdddf2..95c0aaa37b1 100644 --- a/libavcodec/amrwbdata.h +++ b/libavcodec/amrwbdata.h @@ -1884,7 +1884,7 @@ static const float lpf_7_coef[31] = { // low pass, 7kHz cutoff /** Core frame sizes in each mode */ static const uint16_t cf_sizes_wb[] = { 132, 177, 253, 285, 317, 365, 397, 461, 477, - 40 /// SID/comfort noise frame + 40, 0, 0, 0, 0, 0, 0 }; #endif /* AVCODEC_AMRWBDATA_H */ diff --git a/libavcodec/amrwbdec.c b/libavcodec/amrwbdec.c index 47fe7eb55e1..555c4bc45dd 100644 --- a/libavcodec/amrwbdec.c +++ b/libavcodec/amrwbdec.c @@ -1119,12 +1119,23 @@ static int amrwb_decode_frame(AVCodecContext *avctx, void *data, buf_out = (float *)frame->data[0]; header_size = decode_mime_header(ctx, buf); + expected_fr_size = ((cf_sizes_wb[ctx->fr_cur_mode] + 7) >> 3) + 1; + + if (!ctx->fr_quality) + av_log(avctx, AV_LOG_ERROR, "Encountered a bad or corrupted frame\n"); + + if (ctx->fr_cur_mode == NO_DATA || !ctx->fr_quality) { + /* The specification suggests a "random signal" and + "a muting technique" to "gradually decrease the output level". */ + av_samples_set_silence(&frame->data[0], 0, frame->nb_samples, 1, AV_SAMPLE_FMT_FLT); + *got_frame_ptr = 1; + return expected_fr_size; + } if (ctx->fr_cur_mode > MODE_SID) { av_log(avctx, AV_LOG_ERROR, "Invalid mode %d\n", ctx->fr_cur_mode); return AVERROR_INVALIDDATA; } - expected_fr_size = ((cf_sizes_wb[ctx->fr_cur_mode] + 7) >> 3) + 1; if (buf_size < expected_fr_size) { av_log(avctx, AV_LOG_ERROR, @@ -1133,9 +1144,6 @@ static int amrwb_decode_frame(AVCodecContext *avctx, void *data, return AVERROR_INVALIDDATA; } - if (!ctx->fr_quality || ctx->fr_cur_mode > MODE_SID) - av_log(avctx, AV_LOG_ERROR, "Encountered a bad or corrupted frame\n"); - if (ctx->fr_cur_mode == MODE_SID) { /* Comfort noise frame */ avpriv_request_sample(avctx, "SID mode"); return AVERROR_PATCHWELCOME; diff --git a/libavcodec/anm.c b/libavcodec/anm.c index 778f38413e0..134640ee368 100644 --- a/libavcodec/anm.c +++ b/libavcodec/anm.c @@ -31,30 +31,27 @@ typedef struct AnmContext { AVFrame *frame; int palette[AVPALETTE_COUNT]; - GetByteContext gb; - int x; ///< x coordinate position } AnmContext; static av_cold int decode_init(AVCodecContext *avctx) { AnmContext *s = avctx->priv_data; + GetByteContext gb; int i; + if (avctx->extradata_size < 16 * 8 + 4 * 256) + return AVERROR_INVALIDDATA; + avctx->pix_fmt = AV_PIX_FMT_PAL8; s->frame = av_frame_alloc(); if (!s->frame) return AVERROR(ENOMEM); - bytestream2_init(&s->gb, avctx->extradata, avctx->extradata_size); - if (bytestream2_get_bytes_left(&s->gb) < 16 * 8 + 4 * 256) { - av_frame_free(&s->frame); - return AVERROR_INVALIDDATA; - } - - bytestream2_skipu(&s->gb, 16 * 8); + bytestream2_init(&gb, avctx->extradata, avctx->extradata_size); + bytestream2_skipu(&gb, 16 * 8); for (i = 0; i < 256; i++) - s->palette[i] = (0xFFU << 24) | bytestream2_get_le32u(&s->gb); + s->palette[i] = (0xFFU << 24) | bytestream2_get_le32u(&gb); return 0; } @@ -117,47 +114,47 @@ static int decode_frame(AVCodecContext *avctx, AnmContext *s = avctx->priv_data; const int buf_size = avpkt->size; uint8_t *dst, *dst_end; - int count, ret; + GetByteContext gb; + int count, ret, x = 0; if (buf_size < 7) return AVERROR_INVALIDDATA; - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; dst = s->frame->data[0]; dst_end = s->frame->data[0] + s->frame->linesize[0]*avctx->height; - bytestream2_init(&s->gb, avpkt->data, buf_size); + bytestream2_init(&gb, avpkt->data, buf_size); - if (bytestream2_get_byte(&s->gb) != 0x42) { + if (bytestream2_get_byte(&gb) != 0x42) { avpriv_request_sample(avctx, "Unknown record type"); return AVERROR_INVALIDDATA; } - if (bytestream2_get_byte(&s->gb)) { + if (bytestream2_get_byte(&gb)) { avpriv_request_sample(avctx, "Padding bytes"); return AVERROR_PATCHWELCOME; } - bytestream2_skip(&s->gb, 2); + bytestream2_skip(&gb, 2); - s->x = 0; do { /* if statements are ordered by probability */ #define OP(gb, pixel, count) \ - op(&dst, dst_end, (gb), (pixel), (count), &s->x, avctx->width, s->frame->linesize[0]) + op(&dst, dst_end, (gb), (pixel), (count), &x, avctx->width, s->frame->linesize[0]) - int type = bytestream2_get_byte(&s->gb); + int type = bytestream2_get_byte(&gb); count = type & 0x7F; type >>= 7; if (count) { - if (OP(type ? NULL : &s->gb, -1, count)) break; + if (OP(type ? NULL : &gb, -1, count)) break; } else if (!type) { int pixel; - count = bytestream2_get_byte(&s->gb); /* count==0 gives nop */ - pixel = bytestream2_get_byte(&s->gb); + count = bytestream2_get_byte(&gb); /* count==0 gives nop */ + pixel = bytestream2_get_byte(&gb); if (OP(NULL, pixel, count)) break; } else { int pixel; - type = bytestream2_get_le16(&s->gb); + type = bytestream2_get_le16(&gb); count = type & 0x3FFF; type >>= 14; if (!count) { @@ -169,11 +166,11 @@ static int decode_frame(AVCodecContext *avctx, } continue; } - pixel = type == 3 ? bytestream2_get_byte(&s->gb) : -1; + pixel = type == 3 ? bytestream2_get_byte(&gb) : -1; if (type == 1) count += 0x4000; - if (OP(type == 2 ? &s->gb : NULL, pixel, count)) break; + if (OP(type == 2 ? &gb : NULL, pixel, count)) break; } - } while (bytestream2_get_bytes_left(&s->gb) > 0); + } while (bytestream2_get_bytes_left(&gb) > 0); memcpy(s->frame->data[1], s->palette, AVPALETTE_SIZE); diff --git a/libavcodec/ansi.c b/libavcodec/ansi.c index f1fafab771b..516d07db69f 100644 --- a/libavcodec/ansi.c +++ b/libavcodec/ansi.c @@ -34,6 +34,7 @@ #define ATTR_BOLD 0x01 /**< Bold/Bright-foreground (mode 1) */ #define ATTR_FAINT 0x02 /**< Faint (mode 2) */ +#define ATTR_ITALICS 0x04 /**< Italics (mode 3) */ #define ATTR_UNDERLINE 0x08 /**< Underline (mode 4) */ #define ATTR_BLINK 0x10 /**< Blink/Bright-background (mode 5) */ #define ATTR_REVERSE 0x40 /**< Reverse (mode 7) */ @@ -308,7 +309,7 @@ static int execute_code(AVCodecContext * avctx, int c) s->attributes = 0; s->fg = DEFAULT_FG_COLOR; s->bg = DEFAULT_BG_COLOR; - } else if (m == 1 || m == 2 || m == 4 || m == 5 || m == 7 || m == 8) { + } else if (m == 1 || m == 2 || m == 3 || m == 4 || m == 5 || m == 7 || m == 8) { s->attributes |= 1 << (m - 1); } else if (m >= 30 && m <= 37) { s->fg = ansi_to_cga[m - 30]; @@ -362,7 +363,7 @@ static int decode_frame(AVCodecContext *avctx, const uint8_t *buf_end = buf+buf_size; int ret, i, count; - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; if (!avctx->frame_number) { for (i=0; iheight; i++) diff --git a/libavcodec/apedec.c b/libavcodec/apedec.c index 59e829ee5b1..4cbbfa40ad2 100644 --- a/libavcodec/apedec.c +++ b/libavcodec/apedec.c @@ -24,6 +24,7 @@ #include "libavutil/avassert.h" #include "libavutil/channel_layout.h" +#include "libavutil/crc.h" #include "libavutil/opt.h" #include "lossless_audiodsp.h" #include "avcodec.h" @@ -125,8 +126,8 @@ typedef struct APEPredictor { int32_t filterA[2]; int32_t filterB[2]; - int32_t coeffsA[2][4]; ///< adaption coefficients - int32_t coeffsB[2][5]; ///< adaption coefficients + uint32_t coeffsA[2][4]; ///< adaption coefficients + uint32_t coeffsB[2][5]; ///< adaption coefficients int32_t historybuffer[HISTORY_SIZE + PREDICTOR_SIZE]; unsigned int sample_pos; @@ -147,7 +148,8 @@ typedef struct APEContext { int fset; ///< which filter set to use (calculated from compression level) int flags; ///< global decoder flags - uint32_t CRC; ///< frame CRC + uint32_t CRC; ///< signalled frame CRC + uint32_t CRC_state; ///< accumulated CRC int frameflags; ///< frame flags APEPredictor predictor; ///< predictor used for final reconstruction @@ -496,6 +498,7 @@ static inline int ape_decode_value_3860(APEContext *ctx, GetBitContext *gb, x = (overflow << rice->k) + get_bits(gb, rice->k); } else { av_log(ctx->avctx, AV_LOG_ERROR, "Too many bits: %"PRIu32"\n", rice->k); + ctx->error = 1; return AVERROR_INVALIDDATA; } rice->ksum += x - (rice->ksum + 8 >> 4); @@ -585,6 +588,11 @@ static inline int ape_decode_value_3990(APEContext *ctx, APERice *rice) return ((x >> 1) ^ ((x & 1) - 1)) + 1; } +static int get_k(int ksum) +{ + return av_log2(ksum) + !!ksum; +} + static void decode_array_0000(APEContext *ctx, GetBitContext *gb, int32_t *out, APERice *rice, int blockstodecode) { @@ -596,21 +604,34 @@ static void decode_array_0000(APEContext *ctx, GetBitContext *gb, out[i] = get_rice_ook(&ctx->gb, 10); rice->ksum += out[i]; } - rice->k = av_log2(rice->ksum / 10) + 1; + + if (blockstodecode <= 5) + goto end; + + rice->k = get_k(rice->ksum / 10); if (rice->k >= 24) return; for (; i < FFMIN(blockstodecode, 64); i++) { out[i] = get_rice_ook(&ctx->gb, rice->k); rice->ksum += out[i]; - rice->k = av_log2(rice->ksum / ((i + 1) * 2)) + 1; + rice->k = get_k(rice->ksum / ((i + 1) * 2)); if (rice->k >= 24) return; } + + if (blockstodecode <= 64) + goto end; + + rice->k = get_k(rice->ksum >> 7); ksummax = 1 << rice->k + 7; ksummin = rice->k ? (1 << rice->k + 6) : 0; for (; i < blockstodecode; i++) { + if (get_bits_left(&ctx->gb) < 1) { + ctx->error = 1; + return; + } out[i] = get_rice_ook(&ctx->gb, rice->k); - rice->ksum += out[i] - out[i - 64]; + rice->ksum += out[i] - (unsigned)out[i - 64]; while (rice->ksum < ksummin) { rice->k--; ksummin = rice->k ? ksummin >> 1 : 0; @@ -625,6 +646,7 @@ static void decode_array_0000(APEContext *ctx, GetBitContext *gb, } } +end: for (i = 0; i < blockstodecode; i++) out[i] = ((out[i] >> 1) ^ ((out[i] & 1) - 1)) + 1; } @@ -730,6 +752,7 @@ static int init_entropy_decoder(APEContext *ctx) /* Read the frame flags if they exist */ ctx->frameflags = 0; + ctx->CRC_state = UINT32_MAX; if ((ctx->fileversion > 3820) && (ctx->CRC & 0x80000000)) { ctx->CRC &= ~0x80000000; @@ -828,8 +851,8 @@ static av_always_inline int filter_fast_3320(APEPredictor *p, return decoded; } - predictionA = p->buf[delayA] * 2 - p->buf[delayA - 1]; - p->lastA[filter] = decoded + (predictionA * p->coeffsA[filter][0] >> 9); + predictionA = p->buf[delayA] * 2U - p->buf[delayA - 1]; + p->lastA[filter] = decoded + ((int32_t)(predictionA * p->coeffsA[filter][0]) >> 9); if ((decoded ^ predictionA) > 0) p->coeffsA[filter][0]++; @@ -842,7 +865,7 @@ static av_always_inline int filter_fast_3320(APEPredictor *p, } static av_always_inline int filter_3800(APEPredictor *p, - const int decoded, const int filter, + const unsigned decoded, const int filter, const int delayA, const int delayB, const int start, const int shift) { @@ -881,7 +904,7 @@ static av_always_inline int filter_3800(APEPredictor *p, p->coeffsB[filter][1] -= (((d4 >> 30) & 2) - 1) * sign; p->filterB[filter] = p->lastA[filter] + (predictionB >> shift); - p->filterA[filter] = p->filterB[filter] + ((int)(p->filterA[filter] * 31U) >> 5); + p->filterA[filter] = p->filterB[filter] + (unsigned)((int)(p->filterA[filter] * 31U) >> 5); return p->filterA[filter]; } @@ -916,7 +939,8 @@ static void long_filter_ehigh_3830(int32_t *buffer, int length) { int i, j; int32_t dotprod, sign; - int32_t coeffs[8] = { 0 }, delay[8] = { 0 }; + int32_t delay[8] = { 0 }; + uint32_t coeffs[8] = { 0 }; for (i = 0; i < length; i++) { dotprod = 0; @@ -1051,7 +1075,7 @@ static av_always_inline int predictor_update_3930(APEPredictor *p, d3 * p->coeffsA[filter][3]; p->lastA[filter] = decoded + (predictionA >> 9); - p->filterA[filter] = p->lastA[filter] + ((p->filterA[filter] * 31) >> 5); + p->filterA[filter] = p->lastA[filter] + ((int)(p->filterA[filter] * 31U) >> 5); sign = APESIGN(decoded); p->coeffsA[filter][0] += ((d0 < 0) * 2 - 1) * sign; @@ -1121,7 +1145,7 @@ static av_always_inline int predictor_update_filter(APEPredictor *p, p->buf[delayA] = p->lastA[filter]; p->buf[adaptA] = APESIGN(p->buf[delayA]); - p->buf[delayA - 1] = p->buf[delayA] - p->buf[delayA - 1]; + p->buf[delayA - 1] = p->buf[delayA] - (unsigned)p->buf[delayA - 1]; p->buf[adaptA - 1] = APESIGN(p->buf[delayA - 1]); predictionA = p->buf[delayA ] * p->coeffsA[filter][0] + @@ -1132,7 +1156,7 @@ static av_always_inline int predictor_update_filter(APEPredictor *p, /* Apply a scaled first-order filter compression */ p->buf[delayB] = p->filterA[filter ^ 1] - ((int)(p->filterB[filter] * 31U) >> 5); p->buf[adaptB] = APESIGN(p->buf[delayB]); - p->buf[delayB - 1] = p->buf[delayB] - p->buf[delayB - 1]; + p->buf[delayB - 1] = p->buf[delayB] - (unsigned)p->buf[delayB - 1]; p->buf[adaptB - 1] = APESIGN(p->buf[delayB - 1]); p->filterB[filter] = p->filterA[filter ^ 1]; @@ -1202,14 +1226,14 @@ static void predictor_decode_mono_3950(APEContext *ctx, int count) A = *decoded0; p->buf[YDELAYA] = currentA; - p->buf[YDELAYA - 1] = p->buf[YDELAYA] - p->buf[YDELAYA - 1]; + p->buf[YDELAYA - 1] = p->buf[YDELAYA] - (unsigned)p->buf[YDELAYA - 1]; predictionA = p->buf[YDELAYA ] * p->coeffsA[0][0] + p->buf[YDELAYA - 1] * p->coeffsA[0][1] + p->buf[YDELAYA - 2] * p->coeffsA[0][2] + p->buf[YDELAYA - 3] * p->coeffsA[0][3]; - currentA = A + (predictionA >> 10); + currentA = A + (unsigned)(predictionA >> 10); p->buf[YADAPTCOEFFSA] = APESIGN(p->buf[YDELAYA ]); p->buf[YADAPTCOEFFSA - 1] = APESIGN(p->buf[YDELAYA - 1]); @@ -1229,7 +1253,7 @@ static void predictor_decode_mono_3950(APEContext *ctx, int count) p->buf = p->historybuffer; } - p->filterA[0] = currentA + ((int)(p->filterA[0] * 31U) >> 5); + p->filterA[0] = currentA + (unsigned)((int)(p->filterA[0] * 31U) >> 5); *(decoded0++) = p->filterA[0]; } @@ -1267,7 +1291,7 @@ static void do_apply_filter(APEContext *ctx, int version, APEFilter *f, f->adaptcoeffs - order, order, APESIGN(*data)); res = (int)(res + (1U << (fracbits - 1))) >> fracbits; - res += *data; + res += (unsigned)*data; *data++ = res; /* Update the output history */ @@ -1282,7 +1306,7 @@ static void do_apply_filter(APEContext *ctx, int version, APEFilter *f, /* Version 3.98 and later files */ /* Update the adaption coefficients */ - absres = FFABS(res); + absres = res < 0 ? -(unsigned)res : res; if (absres) *f->adaptcoeffs = APESIGN(res) * (8 << ((absres > f->avg * 3) + (absres > f->avg * 4 / 3))); @@ -1297,7 +1321,7 @@ static void do_apply_filter(APEContext *ctx, int version, APEFilter *f, else *f->adaptcoeffs = 0; - f->avg += (absres - f->avg) / 16; + f->avg += (int)(absres - (unsigned)f->avg) / 16; f->adaptcoeffs[-1] >>= 1; f->adaptcoeffs[-2] >>= 1; @@ -1364,6 +1388,8 @@ static void ape_unpack_mono(APEContext *ctx, int count) } ctx->entropy_decode_mono(ctx, count); + if (ctx->error) + return; /* Now apply the predictor decoding */ ctx->predictor_decode_mono(ctx, count); @@ -1387,6 +1413,8 @@ static void ape_unpack_stereo(APEContext *ctx, int count) } ctx->entropy_decode_stereo(ctx, count); + if (ctx->error) + return; /* Now apply the predictor decoding */ ctx->predictor_decode_stereo(ctx, count); @@ -1497,18 +1525,21 @@ static int ape_decode_frame(AVCodecContext *avctx, void *data, /* reallocate decoded sample buffer if needed */ decoded_buffer_size = 2LL * FFALIGN(blockstodecode, 8) * sizeof(*s->decoded_buffer); av_assert0(decoded_buffer_size <= INT_MAX); + + /* get output buffer */ + frame->nb_samples = blockstodecode; + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) { + s->samples=0; + return ret; + } + av_fast_malloc(&s->decoded_buffer, &s->decoded_size, decoded_buffer_size); if (!s->decoded_buffer) return AVERROR(ENOMEM); - memset(s->decoded_buffer, 0, s->decoded_size); + memset(s->decoded_buffer, 0, decoded_buffer_size); s->decoded[0] = s->decoded_buffer; s->decoded[1] = s->decoded_buffer + FFALIGN(blockstodecode, 8); - /* get output buffer */ - frame->nb_samples = blockstodecode; - if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) - return ret; - s->error=0; if ((s->channels == 1) || (s->frameflags & APE_FRAMECODE_PSEUDO_STEREO)) @@ -1542,13 +1573,34 @@ static int ape_decode_frame(AVCodecContext *avctx, void *data, for (ch = 0; ch < s->channels; ch++) { sample24 = (int32_t *)frame->data[ch]; for (i = 0; i < blockstodecode; i++) - *sample24++ = s->decoded[ch][i] << 8; + *sample24++ = s->decoded[ch][i] * 256U; } break; } s->samples -= blockstodecode; + if (avctx->err_recognition & AV_EF_CRCCHECK && + s->fileversion >= 3900 && s->bps < 24) { + uint32_t crc = s->CRC_state; + const AVCRC *crc_tab = av_crc_get_table(AV_CRC_32_IEEE_LE); + for (i = 0; i < blockstodecode; i++) { + for (ch = 0; ch < s->channels; ch++) { + uint8_t *smp = frame->data[ch] + (i*(s->bps >> 3)); + crc = av_crc(crc_tab, crc, smp, s->bps >> 3); + } + } + + if (!s->samples && (~crc >> 1) ^ s->CRC) { + av_log(avctx, AV_LOG_ERROR, "CRC mismatch! Previously decoded " + "frames may have been affected as well.\n"); + if (avctx->err_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + } + + s->CRC_state = crc; + } + *got_frame_ptr = 1; return !s->samples ? avpkt->size : 0; diff --git a/libavcodec/aptx.c b/libavcodec/aptx.c index 8750d8421fb..3aeee1907c9 100644 --- a/libavcodec/aptx.c +++ b/libavcodec/aptx.c @@ -20,81 +20,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/intreadwrite.h" -#include "avcodec.h" -#include "internal.h" -#include "mathops.h" -#include "audio_frame_queue.h" - - -enum channels { - LEFT, - RIGHT, - NB_CHANNELS -}; - -enum subbands { - LF, // Low Frequency (0-5.5 kHz) - MLF, // Medium-Low Frequency (5.5-11kHz) - MHF, // Medium-High Frequency (11-16.5kHz) - HF, // High Frequency (16.5-22kHz) - NB_SUBBANDS -}; - -#define NB_FILTERS 2 -#define FILTER_TAPS 16 - -typedef struct { - int pos; - int32_t buffer[2*FILTER_TAPS]; -} FilterSignal; - -typedef struct { - FilterSignal outer_filter_signal[NB_FILTERS]; - FilterSignal inner_filter_signal[NB_FILTERS][NB_FILTERS]; -} QMFAnalysis; - -typedef struct { - int32_t quantized_sample; - int32_t quantized_sample_parity_change; - int32_t error; -} Quantize; - -typedef struct { - int32_t quantization_factor; - int32_t factor_select; - int32_t reconstructed_difference; -} InvertQuantize; - -typedef struct { - int32_t prev_sign[2]; - int32_t s_weight[2]; - int32_t d_weight[24]; - int32_t pos; - int32_t reconstructed_differences[48]; - int32_t previous_reconstructed_sample; - int32_t predicted_difference; - int32_t predicted_sample; -} Prediction; - -typedef struct { - int32_t codeword_history; - int32_t dither_parity; - int32_t dither[NB_SUBBANDS]; - - QMFAnalysis qmf; - Quantize quantize[NB_SUBBANDS]; - InvertQuantize invert_quantize[NB_SUBBANDS]; - Prediction prediction[NB_SUBBANDS]; -} Channel; - -typedef struct { - int hd; - int block_size; - int32_t sync_idx; - Channel channels[NB_CHANNELS]; - AudioFrameQueue afq; -} AptXContext; +#include "aptx.h" static const int32_t quantize_intervals_LF[65] = { @@ -383,17 +309,7 @@ static const int16_t hd_quantize_factor_select_offset_HF[17] = { 70, 90, 115, 147, 192, 264, 398, 521, 521, }; -typedef const struct { - const int32_t *quantize_intervals; - const int32_t *invert_quantize_dither_factors; - const int32_t *quantize_dither_factors; - const int16_t *quantize_factor_select_offset; - int tables_size; - int32_t factor_max; - int32_t prediction_order; -} ConstTables; - -static ConstTables tables[2][NB_SUBBANDS] = { +ConstTables ff_aptx_quant_tables[2][NB_SUBBANDS] = { { [LF] = { quantize_intervals_LF, invert_quantize_dither_factors_LF, @@ -456,34 +372,16 @@ static const int16_t quantization_factors[32] = { }; -/* Rounded right shift with optionnal clipping */ -#define RSHIFT_SIZE(size) \ -av_always_inline \ -static int##size##_t rshift##size(int##size##_t value, int shift) \ -{ \ - int##size##_t rounding = (int##size##_t)1 << (shift - 1); \ - int##size##_t mask = ((int##size##_t)1 << (shift + 1)) - 1; \ - return ((value + rounding) >> shift) - ((value & mask) == rounding); \ -} \ -av_always_inline \ -static int##size##_t rshift##size##_clip24(int##size##_t value, int shift) \ -{ \ - return av_clip_intp2(rshift##size(value, shift), 23); \ -} -RSHIFT_SIZE(32) -RSHIFT_SIZE(64) - - av_always_inline static void aptx_update_codeword_history(Channel *channel) { int32_t cw = ((channel->quantize[0].quantized_sample & 3) << 0) + ((channel->quantize[1].quantized_sample & 2) << 1) + ((channel->quantize[2].quantized_sample & 1) << 3); - channel->codeword_history = (cw << 8) + (channel->codeword_history << 4); + channel->codeword_history = (cw << 8) + ((unsigned)channel->codeword_history << 4); } -static void aptx_generate_dither(Channel *channel) +void ff_aptx_generate_dither(Channel *channel) { int subband; int64_t m; @@ -492,262 +390,12 @@ static void aptx_generate_dither(Channel *channel) aptx_update_codeword_history(channel); m = (int64_t)5184443 * (channel->codeword_history >> 7); - d = (m << 2) + (m >> 22); + d = (m * 4) + (m >> 22); for (subband = 0; subband < NB_SUBBANDS; subband++) - channel->dither[subband] = d << (23 - 5*subband); + channel->dither[subband] = (unsigned)d << (23 - 5*subband); channel->dither_parity = (d >> 25) & 1; } -/* - * Convolution filter coefficients for the outer QMF of the QMF tree. - * The 2 sets are a mirror of each other. - */ -static const int32_t aptx_qmf_outer_coeffs[NB_FILTERS][FILTER_TAPS] = { - { - 730, -413, -9611, 43626, -121026, 269973, -585547, 2801966, - 697128, -160481, 27611, 8478, -10043, 3511, 688, -897, - }, - { - -897, 688, 3511, -10043, 8478, 27611, -160481, 697128, - 2801966, -585547, 269973, -121026, 43626, -9611, -413, 730, - }, -}; - -/* - * Convolution filter coefficients for the inner QMF of the QMF tree. - * The 2 sets are a mirror of each other. - */ -static const int32_t aptx_qmf_inner_coeffs[NB_FILTERS][FILTER_TAPS] = { - { - 1033, -584, -13592, 61697, -171156, 381799, -828088, 3962579, - 985888, -226954, 39048, 11990, -14203, 4966, 973, -1268, - }, - { - -1268, 973, 4966, -14203, 11990, 39048, -226954, 985888, - 3962579, -828088, 381799, -171156, 61697, -13592, -584, 1033, - }, -}; - -/* - * Push one sample into a circular signal buffer. - */ -av_always_inline -static void aptx_qmf_filter_signal_push(FilterSignal *signal, int32_t sample) -{ - signal->buffer[signal->pos ] = sample; - signal->buffer[signal->pos+FILTER_TAPS] = sample; - signal->pos = (signal->pos + 1) & (FILTER_TAPS - 1); -} - -/* - * Compute the convolution of the signal with the coefficients, and reduce - * to 24 bits by applying the specified right shifting. - */ -av_always_inline -static int32_t aptx_qmf_convolution(FilterSignal *signal, - const int32_t coeffs[FILTER_TAPS], - int shift) -{ - int32_t *sig = &signal->buffer[signal->pos]; - int64_t e = 0; - int i; - - for (i = 0; i < FILTER_TAPS; i++) - e += MUL64(sig[i], coeffs[i]); - - return rshift64_clip24(e, shift); -} - -/* - * Half-band QMF analysis filter realized with a polyphase FIR filter. - * Split into 2 subbands and downsample by 2. - * So for each pair of samples that goes in, one sample goes out, - * split into 2 separate subbands. - */ -av_always_inline -static void aptx_qmf_polyphase_analysis(FilterSignal signal[NB_FILTERS], - const int32_t coeffs[NB_FILTERS][FILTER_TAPS], - int shift, - int32_t samples[NB_FILTERS], - int32_t *low_subband_output, - int32_t *high_subband_output) -{ - int32_t subbands[NB_FILTERS]; - int i; - - for (i = 0; i < NB_FILTERS; i++) { - aptx_qmf_filter_signal_push(&signal[i], samples[NB_FILTERS-1-i]); - subbands[i] = aptx_qmf_convolution(&signal[i], coeffs[i], shift); - } - - *low_subband_output = av_clip_intp2(subbands[0] + subbands[1], 23); - *high_subband_output = av_clip_intp2(subbands[0] - subbands[1], 23); -} - -/* - * Two stage QMF analysis tree. - * Split 4 input samples into 4 subbands and downsample by 4. - * So for each group of 4 samples that goes in, one sample goes out, - * split into 4 separate subbands. - */ -static void aptx_qmf_tree_analysis(QMFAnalysis *qmf, - int32_t samples[4], - int32_t subband_samples[4]) -{ - int32_t intermediate_samples[4]; - int i; - - /* Split 4 input samples into 2 intermediate subbands downsampled to 2 samples */ - for (i = 0; i < 2; i++) - aptx_qmf_polyphase_analysis(qmf->outer_filter_signal, - aptx_qmf_outer_coeffs, 23, - &samples[2*i], - &intermediate_samples[0+i], - &intermediate_samples[2+i]); - - /* Split 2 intermediate subband samples into 4 final subbands downsampled to 1 sample */ - for (i = 0; i < 2; i++) - aptx_qmf_polyphase_analysis(qmf->inner_filter_signal[i], - aptx_qmf_inner_coeffs, 23, - &intermediate_samples[2*i], - &subband_samples[2*i+0], - &subband_samples[2*i+1]); -} - -/* - * Half-band QMF synthesis filter realized with a polyphase FIR filter. - * Join 2 subbands and upsample by 2. - * So for each 2 subbands sample that goes in, a pair of samples goes out. - */ -av_always_inline -static void aptx_qmf_polyphase_synthesis(FilterSignal signal[NB_FILTERS], - const int32_t coeffs[NB_FILTERS][FILTER_TAPS], - int shift, - int32_t low_subband_input, - int32_t high_subband_input, - int32_t samples[NB_FILTERS]) -{ - int32_t subbands[NB_FILTERS]; - int i; - - subbands[0] = low_subband_input + high_subband_input; - subbands[1] = low_subband_input - high_subband_input; - - for (i = 0; i < NB_FILTERS; i++) { - aptx_qmf_filter_signal_push(&signal[i], subbands[1-i]); - samples[i] = aptx_qmf_convolution(&signal[i], coeffs[i], shift); - } -} - -/* - * Two stage QMF synthesis tree. - * Join 4 subbands and upsample by 4. - * So for each 4 subbands sample that goes in, a group of 4 samples goes out. - */ -static void aptx_qmf_tree_synthesis(QMFAnalysis *qmf, - int32_t subband_samples[4], - int32_t samples[4]) -{ - int32_t intermediate_samples[4]; - int i; - - /* Join 4 subbands into 2 intermediate subbands upsampled to 2 samples. */ - for (i = 0; i < 2; i++) - aptx_qmf_polyphase_synthesis(qmf->inner_filter_signal[i], - aptx_qmf_inner_coeffs, 22, - subband_samples[2*i+0], - subband_samples[2*i+1], - &intermediate_samples[2*i]); - - /* Join 2 samples from intermediate subbands upsampled to 4 samples. */ - for (i = 0; i < 2; i++) - aptx_qmf_polyphase_synthesis(qmf->outer_filter_signal, - aptx_qmf_outer_coeffs, 21, - intermediate_samples[0+i], - intermediate_samples[2+i], - &samples[2*i]); -} - - -av_always_inline -static int32_t aptx_bin_search(int32_t value, int32_t factor, - const int32_t *intervals, int32_t nb_intervals) -{ - int32_t idx = 0; - int i; - - for (i = nb_intervals >> 1; i > 0; i >>= 1) - if (MUL64(factor, intervals[idx + i]) <= ((int64_t)value << 24)) - idx += i; - - return idx; -} - -static void aptx_quantize_difference(Quantize *quantize, - int32_t sample_difference, - int32_t dither, - int32_t quantization_factor, - ConstTables *tables) -{ - const int32_t *intervals = tables->quantize_intervals; - int32_t quantized_sample, dithered_sample, parity_change; - int32_t d, mean, interval, inv, sample_difference_abs; - int64_t error; - - sample_difference_abs = FFABS(sample_difference); - sample_difference_abs = FFMIN(sample_difference_abs, (1 << 23) - 1); - - quantized_sample = aptx_bin_search(sample_difference_abs >> 4, - quantization_factor, - intervals, tables->tables_size); - - d = rshift32_clip24(MULH(dither, dither), 7) - (1 << 23); - d = rshift64(MUL64(d, tables->quantize_dither_factors[quantized_sample]), 23); - - intervals += quantized_sample; - mean = (intervals[1] + intervals[0]) / 2; - interval = (intervals[1] - intervals[0]) * (-(sample_difference < 0) | 1); - - dithered_sample = rshift64_clip24(MUL64(dither, interval) + ((int64_t)av_clip_intp2(mean + d, 23) << 32), 32); - error = ((int64_t)sample_difference_abs << 20) - MUL64(dithered_sample, quantization_factor); - quantize->error = FFABS(rshift64(error, 23)); - - parity_change = quantized_sample; - if (error < 0) - quantized_sample--; - else - parity_change--; - - inv = -(sample_difference < 0); - quantize->quantized_sample = quantized_sample ^ inv; - quantize->quantized_sample_parity_change = parity_change ^ inv; -} - -static void aptx_encode_channel(Channel *channel, int32_t samples[4], int hd) -{ - int32_t subband_samples[4]; - int subband; - aptx_qmf_tree_analysis(&channel->qmf, samples, subband_samples); - aptx_generate_dither(channel); - for (subband = 0; subband < NB_SUBBANDS; subband++) { - int32_t diff = av_clip_intp2(subband_samples[subband] - channel->prediction[subband].predicted_sample, 23); - aptx_quantize_difference(&channel->quantize[subband], diff, - channel->dither[subband], - channel->invert_quantize[subband].quantization_factor, - &tables[hd][subband]); - } -} - -static void aptx_decode_channel(Channel *channel, int32_t samples[4]) -{ - int32_t subband_samples[4]; - int subband; - for (subband = 0; subband < NB_SUBBANDS; subband++) - subband_samples[subband] = channel->prediction[subband].previous_reconstructed_sample; - aptx_qmf_tree_synthesis(&channel->qmf, subband_samples, samples); -} - - static void aptx_invert_quantization(InvertQuantize *invert_quantize, int32_t quantized_sample, int32_t dither, ConstTables *tables) @@ -759,12 +407,12 @@ static void aptx_invert_quantization(InvertQuantize *invert_quantize, if (quantized_sample < 0) qr = -qr; - qr = rshift64_clip24(((int64_t)qr<<32) + MUL64(dither, tables->invert_quantize_dither_factors[idx]), 32); + qr = rshift64_clip24((qr * (1LL<<32)) + MUL64(dither, tables->invert_quantize_dither_factors[idx]), 32); invert_quantize->reconstructed_difference = MUL64(invert_quantize->quantization_factor, qr) >> 19; /* update factor_select */ factor_select = 32620 * invert_quantize->factor_select; - factor_select = rshift32(factor_select + (tables->quantize_factor_select_offset[idx] << 15), 15); + factor_select = rshift32(factor_select + (tables->quantize_factor_select_offset[idx] * (1 << 15)), 15); invert_quantize->factor_select = av_clip(factor_select, 0, tables->factor_max); /* update quantization factor */ @@ -801,7 +449,7 @@ static void aptx_prediction_filtering(Prediction *prediction, prediction->previous_reconstructed_sample = reconstructed_sample; reconstructed_differences = aptx_reconstructed_differences_update(prediction, reconstructed_difference, order); - srd0 = FFDIFFSIGN(reconstructed_difference, 0) << 23; + srd0 = FFDIFFSIGN(reconstructed_difference, 0) * (1 << 23); for (i = 0; i < order; i++) { int32_t srd = FF_SIGNBIT(reconstructed_differences[-i-1]) | 1; prediction->d_weight[i] -= rshift32(prediction->d_weight[i] - srd*srd0, 8); @@ -830,7 +478,7 @@ static void aptx_process_subband(InvertQuantize *invert_quantize, range = 0x100000; sw1 = rshift32(-same_sign[1] * prediction->s_weight[1], 1); - sw1 = (av_clip(sw1, -range, range) & ~0xF) << 4; + sw1 = (av_clip(sw1, -range, range) & ~0xF) * 16; range = 0x300000; weight[0] = 254 * prediction->s_weight[0] + 0x800000*same_sign[0] + sw1; @@ -845,7 +493,7 @@ static void aptx_process_subband(InvertQuantize *invert_quantize, tables->prediction_order); } -static void aptx_invert_quantize_and_prediction(Channel *channel, int hd) +void ff_aptx_invert_quantize_and_prediction(Channel *channel, int hd) { int subband; for (subband = 0; subband < NB_SUBBANDS; subband++) @@ -853,142 +501,17 @@ static void aptx_invert_quantize_and_prediction(Channel *channel, int hd) &channel->prediction[subband], channel->quantize[subband].quantized_sample, channel->dither[subband], - &tables[hd][subband]); -} - -static int32_t aptx_quantized_parity(Channel *channel) -{ - int32_t parity = channel->dither_parity; - int subband; - - for (subband = 0; subband < NB_SUBBANDS; subband++) - parity ^= channel->quantize[subband].quantized_sample; - - return parity & 1; -} - -/* For each sample, ensure that the parity of all subbands of all channels - * is 0 except once every 8 samples where the parity is forced to 1. */ -static int aptx_check_parity(Channel channels[NB_CHANNELS], int32_t *idx) -{ - int32_t parity = aptx_quantized_parity(&channels[LEFT]) - ^ aptx_quantized_parity(&channels[RIGHT]); - - int eighth = *idx == 7; - *idx = (*idx + 1) & 7; - - return parity ^ eighth; + &ff_aptx_quant_tables[hd][subband]); } -static void aptx_insert_sync(Channel channels[NB_CHANNELS], int32_t *idx) -{ - if (aptx_check_parity(channels, idx)) { - int i; - Channel *c; - static const int map[] = { 1, 2, 0, 3 }; - Quantize *min = &channels[NB_CHANNELS-1].quantize[map[0]]; - for (c = &channels[NB_CHANNELS-1]; c >= channels; c--) - for (i = 0; i < NB_SUBBANDS; i++) - if (c->quantize[map[i]].error < min->error) - min = &c->quantize[map[i]]; - - /* Forcing the desired parity is done by offsetting by 1 the quantized - * sample from the subband featuring the smallest quantization error. */ - min->quantized_sample = min->quantized_sample_parity_change; - } -} - -static uint16_t aptx_pack_codeword(Channel *channel) -{ - int32_t parity = aptx_quantized_parity(channel); - return (((channel->quantize[3].quantized_sample & 0x06) | parity) << 13) - | (((channel->quantize[2].quantized_sample & 0x03) ) << 11) - | (((channel->quantize[1].quantized_sample & 0x0F) ) << 7) - | (((channel->quantize[0].quantized_sample & 0x7F) ) << 0); -} - -static uint32_t aptxhd_pack_codeword(Channel *channel) -{ - int32_t parity = aptx_quantized_parity(channel); - return (((channel->quantize[3].quantized_sample & 0x01E) | parity) << 19) - | (((channel->quantize[2].quantized_sample & 0x00F) ) << 15) - | (((channel->quantize[1].quantized_sample & 0x03F) ) << 9) - | (((channel->quantize[0].quantized_sample & 0x1FF) ) << 0); -} - -static void aptx_unpack_codeword(Channel *channel, uint16_t codeword) -{ - channel->quantize[0].quantized_sample = sign_extend(codeword >> 0, 7); - channel->quantize[1].quantized_sample = sign_extend(codeword >> 7, 4); - channel->quantize[2].quantized_sample = sign_extend(codeword >> 11, 2); - channel->quantize[3].quantized_sample = sign_extend(codeword >> 13, 3); - channel->quantize[3].quantized_sample = (channel->quantize[3].quantized_sample & ~1) - | aptx_quantized_parity(channel); -} - -static void aptxhd_unpack_codeword(Channel *channel, uint32_t codeword) -{ - channel->quantize[0].quantized_sample = sign_extend(codeword >> 0, 9); - channel->quantize[1].quantized_sample = sign_extend(codeword >> 9, 6); - channel->quantize[2].quantized_sample = sign_extend(codeword >> 15, 4); - channel->quantize[3].quantized_sample = sign_extend(codeword >> 19, 5); - channel->quantize[3].quantized_sample = (channel->quantize[3].quantized_sample & ~1) - | aptx_quantized_parity(channel); -} - -static void aptx_encode_samples(AptXContext *ctx, - int32_t samples[NB_CHANNELS][4], - uint8_t *output) -{ - int channel; - for (channel = 0; channel < NB_CHANNELS; channel++) - aptx_encode_channel(&ctx->channels[channel], samples[channel], ctx->hd); - - aptx_insert_sync(ctx->channels, &ctx->sync_idx); - - for (channel = 0; channel < NB_CHANNELS; channel++) { - aptx_invert_quantize_and_prediction(&ctx->channels[channel], ctx->hd); - if (ctx->hd) - AV_WB24(output + 3*channel, - aptxhd_pack_codeword(&ctx->channels[channel])); - else - AV_WB16(output + 2*channel, - aptx_pack_codeword(&ctx->channels[channel])); - } -} - -static int aptx_decode_samples(AptXContext *ctx, - const uint8_t *input, - int32_t samples[NB_CHANNELS][4]) -{ - int channel, ret; - - for (channel = 0; channel < NB_CHANNELS; channel++) { - aptx_generate_dither(&ctx->channels[channel]); - - if (ctx->hd) - aptxhd_unpack_codeword(&ctx->channels[channel], - AV_RB24(input + 3*channel)); - else - aptx_unpack_codeword(&ctx->channels[channel], - AV_RB16(input + 2*channel)); - aptx_invert_quantize_and_prediction(&ctx->channels[channel], ctx->hd); - } - - ret = aptx_check_parity(ctx->channels, &ctx->sync_idx); - - for (channel = 0; channel < NB_CHANNELS; channel++) - aptx_decode_channel(&ctx->channels[channel], samples[channel]); - - return ret; -} - - -static av_cold int aptx_init(AVCodecContext *avctx) +av_cold int ff_aptx_init(AVCodecContext *avctx) { AptXContext *s = avctx->priv_data; int chan, subband; + if (avctx->channels != 2) + return AVERROR_INVALIDDATA; + s->hd = avctx->codec->id == AV_CODEC_ID_APTX_HD; s->block_size = s->hd ? 6 : 4; @@ -1013,150 +536,3 @@ static av_cold int aptx_init(AVCodecContext *avctx) ff_af_queue_init(avctx, &s->afq); return 0; } - -static int aptx_decode_frame(AVCodecContext *avctx, void *data, - int *got_frame_ptr, AVPacket *avpkt) -{ - AptXContext *s = avctx->priv_data; - AVFrame *frame = data; - int pos, opos, channel, sample, ret; - - if (avpkt->size < s->block_size) { - av_log(avctx, AV_LOG_ERROR, "Packet is too small\n"); - return AVERROR_INVALIDDATA; - } - - /* get output buffer */ - frame->channels = NB_CHANNELS; - frame->format = AV_SAMPLE_FMT_S32P; - frame->nb_samples = 4 * avpkt->size / s->block_size; - if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) - return ret; - - for (pos = 0, opos = 0; opos < frame->nb_samples; pos += s->block_size, opos += 4) { - int32_t samples[NB_CHANNELS][4]; - - if (aptx_decode_samples(s, &avpkt->data[pos], samples)) { - av_log(avctx, AV_LOG_ERROR, "Synchronization error\n"); - return AVERROR_INVALIDDATA; - } - - for (channel = 0; channel < NB_CHANNELS; channel++) - for (sample = 0; sample < 4; sample++) - AV_WN32A(&frame->data[channel][4*(opos+sample)], - samples[channel][sample] << 8); - } - - *got_frame_ptr = 1; - return s->block_size * frame->nb_samples / 4; -} - -static int aptx_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, - const AVFrame *frame, int *got_packet_ptr) -{ - AptXContext *s = avctx->priv_data; - int pos, ipos, channel, sample, output_size, ret; - - if ((ret = ff_af_queue_add(&s->afq, frame)) < 0) - return ret; - - output_size = s->block_size * frame->nb_samples/4; - if ((ret = ff_alloc_packet2(avctx, avpkt, output_size, 0)) < 0) - return ret; - - for (pos = 0, ipos = 0; pos < output_size; pos += s->block_size, ipos += 4) { - int32_t samples[NB_CHANNELS][4]; - - for (channel = 0; channel < NB_CHANNELS; channel++) - for (sample = 0; sample < 4; sample++) - samples[channel][sample] = (int32_t)AV_RN32A(&frame->data[channel][4*(ipos+sample)]) >> 8; - - aptx_encode_samples(s, samples, avpkt->data + pos); - } - - ff_af_queue_remove(&s->afq, frame->nb_samples, &avpkt->pts, &avpkt->duration); - *got_packet_ptr = 1; - return 0; -} - -static av_cold int aptx_close(AVCodecContext *avctx) -{ - AptXContext *s = avctx->priv_data; - ff_af_queue_close(&s->afq); - return 0; -} - - -#if CONFIG_APTX_DECODER -AVCodec ff_aptx_decoder = { - .name = "aptx", - .long_name = NULL_IF_CONFIG_SMALL("aptX (Audio Processing Technology for Bluetooth)"), - .type = AVMEDIA_TYPE_AUDIO, - .id = AV_CODEC_ID_APTX, - .priv_data_size = sizeof(AptXContext), - .init = aptx_init, - .decode = aptx_decode_frame, - .close = aptx_close, - .capabilities = AV_CODEC_CAP_DR1, - .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, - .channel_layouts = (const uint64_t[]) { AV_CH_LAYOUT_STEREO, 0}, - .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S32P, - AV_SAMPLE_FMT_NONE }, -}; -#endif - -#if CONFIG_APTX_HD_DECODER -AVCodec ff_aptx_hd_decoder = { - .name = "aptx_hd", - .long_name = NULL_IF_CONFIG_SMALL("aptX HD (Audio Processing Technology for Bluetooth)"), - .type = AVMEDIA_TYPE_AUDIO, - .id = AV_CODEC_ID_APTX_HD, - .priv_data_size = sizeof(AptXContext), - .init = aptx_init, - .decode = aptx_decode_frame, - .close = aptx_close, - .capabilities = AV_CODEC_CAP_DR1, - .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, - .channel_layouts = (const uint64_t[]) { AV_CH_LAYOUT_STEREO, 0}, - .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S32P, - AV_SAMPLE_FMT_NONE }, -}; -#endif - -#if CONFIG_APTX_ENCODER -AVCodec ff_aptx_encoder = { - .name = "aptx", - .long_name = NULL_IF_CONFIG_SMALL("aptX (Audio Processing Technology for Bluetooth)"), - .type = AVMEDIA_TYPE_AUDIO, - .id = AV_CODEC_ID_APTX, - .priv_data_size = sizeof(AptXContext), - .init = aptx_init, - .encode2 = aptx_encode_frame, - .close = aptx_close, - .capabilities = AV_CODEC_CAP_SMALL_LAST_FRAME, - .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, - .channel_layouts = (const uint64_t[]) { AV_CH_LAYOUT_STEREO, 0}, - .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S32P, - AV_SAMPLE_FMT_NONE }, - .supported_samplerates = (const int[]) {8000, 16000, 24000, 32000, 44100, 48000, 0}, -}; -#endif - -#if CONFIG_APTX_HD_ENCODER -AVCodec ff_aptx_hd_encoder = { - .name = "aptx_hd", - .long_name = NULL_IF_CONFIG_SMALL("aptX HD (Audio Processing Technology for Bluetooth)"), - .type = AVMEDIA_TYPE_AUDIO, - .id = AV_CODEC_ID_APTX_HD, - .priv_data_size = sizeof(AptXContext), - .init = aptx_init, - .encode2 = aptx_encode_frame, - .close = aptx_close, - .capabilities = AV_CODEC_CAP_SMALL_LAST_FRAME, - .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, - .channel_layouts = (const uint64_t[]) { AV_CH_LAYOUT_STEREO, 0}, - .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S32P, - AV_SAMPLE_FMT_NONE }, - .supported_samplerates = (const int[]) {8000, 16000, 24000, 32000, 44100, 48000, 0}, -}; -#endif diff --git a/libavcodec/aptx.h b/libavcodec/aptx.h new file mode 100644 index 00000000000..ce3d7dc6c19 --- /dev/null +++ b/libavcodec/aptx.h @@ -0,0 +1,220 @@ +/* + * Audio Processing Technology codec for Bluetooth (aptX) + * + * Copyright (C) 2017 Aurelien Jacobs + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_APTX_H +#define AVCODEC_APTX_H + +#include "libavutil/intreadwrite.h" +#include "avcodec.h" +#include "internal.h" +#include "mathops.h" +#include "audio_frame_queue.h" + + +enum channels { + LEFT, + RIGHT, + NB_CHANNELS +}; + +enum subbands { + LF, // Low Frequency (0-5.5 kHz) + MLF, // Medium-Low Frequency (5.5-11kHz) + MHF, // Medium-High Frequency (11-16.5kHz) + HF, // High Frequency (16.5-22kHz) + NB_SUBBANDS +}; + +#define NB_FILTERS 2 +#define FILTER_TAPS 16 + +typedef struct { + int pos; + int32_t buffer[2*FILTER_TAPS]; +} FilterSignal; + +typedef struct { + FilterSignal outer_filter_signal[NB_FILTERS]; + FilterSignal inner_filter_signal[NB_FILTERS][NB_FILTERS]; +} QMFAnalysis; + +typedef struct { + int32_t quantized_sample; + int32_t quantized_sample_parity_change; + int32_t error; +} Quantize; + +typedef struct { + int32_t quantization_factor; + int32_t factor_select; + int32_t reconstructed_difference; +} InvertQuantize; + +typedef struct { + int32_t prev_sign[2]; + int32_t s_weight[2]; + int32_t d_weight[24]; + int32_t pos; + int32_t reconstructed_differences[48]; + int32_t previous_reconstructed_sample; + int32_t predicted_difference; + int32_t predicted_sample; +} Prediction; + +typedef struct { + int32_t codeword_history; + int32_t dither_parity; + int32_t dither[NB_SUBBANDS]; + + QMFAnalysis qmf; + Quantize quantize[NB_SUBBANDS]; + InvertQuantize invert_quantize[NB_SUBBANDS]; + Prediction prediction[NB_SUBBANDS]; +} Channel; + +typedef struct { + int hd; + int block_size; + int32_t sync_idx; + Channel channels[NB_CHANNELS]; + AudioFrameQueue afq; +} AptXContext; + +typedef const struct { + const int32_t *quantize_intervals; + const int32_t *invert_quantize_dither_factors; + const int32_t *quantize_dither_factors; + const int16_t *quantize_factor_select_offset; + int tables_size; + int32_t factor_max; + int32_t prediction_order; +} ConstTables; + +extern ConstTables ff_aptx_quant_tables[2][NB_SUBBANDS]; + +/* Rounded right shift with optionnal clipping */ +#define RSHIFT_SIZE(size) \ +av_always_inline \ +static int##size##_t rshift##size(int##size##_t value, int shift) \ +{ \ + int##size##_t rounding = (int##size##_t)1 << (shift - 1); \ + int##size##_t mask = ((int##size##_t)1 << (shift + 1)) - 1; \ + return ((value + rounding) >> shift) - ((value & mask) == rounding); \ +} \ +av_always_inline \ +static int##size##_t rshift##size##_clip24(int##size##_t value, int shift) \ +{ \ + return av_clip_intp2(rshift##size(value, shift), 23); \ +} +RSHIFT_SIZE(32) +RSHIFT_SIZE(64) + +/* + * Convolution filter coefficients for the outer QMF of the QMF tree. + * The 2 sets are a mirror of each other. + */ +static const int32_t aptx_qmf_outer_coeffs[NB_FILTERS][FILTER_TAPS] = { + { + 730, -413, -9611, 43626, -121026, 269973, -585547, 2801966, + 697128, -160481, 27611, 8478, -10043, 3511, 688, -897, + }, + { + -897, 688, 3511, -10043, 8478, 27611, -160481, 697128, + 2801966, -585547, 269973, -121026, 43626, -9611, -413, 730, + }, +}; + +/* + * Convolution filter coefficients for the inner QMF of the QMF tree. + * The 2 sets are a mirror of each other. + */ +static const int32_t aptx_qmf_inner_coeffs[NB_FILTERS][FILTER_TAPS] = { + { + 1033, -584, -13592, 61697, -171156, 381799, -828088, 3962579, + 985888, -226954, 39048, 11990, -14203, 4966, 973, -1268, + }, + { + -1268, 973, 4966, -14203, 11990, 39048, -226954, 985888, + 3962579, -828088, 381799, -171156, 61697, -13592, -584, 1033, + }, +}; + +/* + * Push one sample into a circular signal buffer. + */ +av_always_inline +static void aptx_qmf_filter_signal_push(FilterSignal *signal, int32_t sample) +{ + signal->buffer[signal->pos ] = sample; + signal->buffer[signal->pos+FILTER_TAPS] = sample; + signal->pos = (signal->pos + 1) & (FILTER_TAPS - 1); +} + +/* + * Compute the convolution of the signal with the coefficients, and reduce + * to 24 bits by applying the specified right shifting. + */ +av_always_inline +static int32_t aptx_qmf_convolution(FilterSignal *signal, + const int32_t coeffs[FILTER_TAPS], + int shift) +{ + int32_t *sig = &signal->buffer[signal->pos]; + int64_t e = 0; + int i; + + for (i = 0; i < FILTER_TAPS; i++) + e += MUL64(sig[i], coeffs[i]); + + return rshift64_clip24(e, shift); +} + +static inline int32_t aptx_quantized_parity(Channel *channel) +{ + int32_t parity = channel->dither_parity; + int subband; + + for (subband = 0; subband < NB_SUBBANDS; subband++) + parity ^= channel->quantize[subband].quantized_sample; + + return parity & 1; +} + +/* For each sample, ensure that the parity of all subbands of all channels + * is 0 except once every 8 samples where the parity is forced to 1. */ +static inline int aptx_check_parity(Channel channels[NB_CHANNELS], int32_t *idx) +{ + int32_t parity = aptx_quantized_parity(&channels[LEFT]) + ^ aptx_quantized_parity(&channels[RIGHT]); + + int eighth = *idx == 7; + *idx = (*idx + 1) & 7; + + return parity ^ eighth; +} + +void ff_aptx_invert_quantize_and_prediction(Channel *channel, int hd); +void ff_aptx_generate_dither(Channel *channel); + +int ff_aptx_init(AVCodecContext *avctx); + +#endif /* AVCODEC_APTX_H */ diff --git a/libavcodec/aptxdec.c b/libavcodec/aptxdec.c new file mode 100644 index 00000000000..3bbf0104dff --- /dev/null +++ b/libavcodec/aptxdec.c @@ -0,0 +1,204 @@ +/* + * Audio Processing Technology codec for Bluetooth (aptX) + * + * Copyright (C) 2017 Aurelien Jacobs + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 "aptx.h" + +/* + * Half-band QMF synthesis filter realized with a polyphase FIR filter. + * Join 2 subbands and upsample by 2. + * So for each 2 subbands sample that goes in, a pair of samples goes out. + */ +av_always_inline +static void aptx_qmf_polyphase_synthesis(FilterSignal signal[NB_FILTERS], + const int32_t coeffs[NB_FILTERS][FILTER_TAPS], + int shift, + int32_t low_subband_input, + int32_t high_subband_input, + int32_t samples[NB_FILTERS]) +{ + int32_t subbands[NB_FILTERS]; + int i; + + subbands[0] = low_subband_input + high_subband_input; + subbands[1] = low_subband_input - high_subband_input; + + for (i = 0; i < NB_FILTERS; i++) { + aptx_qmf_filter_signal_push(&signal[i], subbands[1-i]); + samples[i] = aptx_qmf_convolution(&signal[i], coeffs[i], shift); + } +} + +/* + * Two stage QMF synthesis tree. + * Join 4 subbands and upsample by 4. + * So for each 4 subbands sample that goes in, a group of 4 samples goes out. + */ +static void aptx_qmf_tree_synthesis(QMFAnalysis *qmf, + int32_t subband_samples[4], + int32_t samples[4]) +{ + int32_t intermediate_samples[4]; + int i; + + /* Join 4 subbands into 2 intermediate subbands upsampled to 2 samples. */ + for (i = 0; i < 2; i++) + aptx_qmf_polyphase_synthesis(qmf->inner_filter_signal[i], + aptx_qmf_inner_coeffs, 22, + subband_samples[2*i+0], + subband_samples[2*i+1], + &intermediate_samples[2*i]); + + /* Join 2 samples from intermediate subbands upsampled to 4 samples. */ + for (i = 0; i < 2; i++) + aptx_qmf_polyphase_synthesis(qmf->outer_filter_signal, + aptx_qmf_outer_coeffs, 21, + intermediate_samples[0+i], + intermediate_samples[2+i], + &samples[2*i]); +} + + +static void aptx_decode_channel(Channel *channel, int32_t samples[4]) +{ + int32_t subband_samples[4]; + int subband; + for (subband = 0; subband < NB_SUBBANDS; subband++) + subband_samples[subband] = channel->prediction[subband].previous_reconstructed_sample; + aptx_qmf_tree_synthesis(&channel->qmf, subband_samples, samples); +} + +static void aptx_unpack_codeword(Channel *channel, uint16_t codeword) +{ + channel->quantize[0].quantized_sample = sign_extend(codeword >> 0, 7); + channel->quantize[1].quantized_sample = sign_extend(codeword >> 7, 4); + channel->quantize[2].quantized_sample = sign_extend(codeword >> 11, 2); + channel->quantize[3].quantized_sample = sign_extend(codeword >> 13, 3); + channel->quantize[3].quantized_sample = (channel->quantize[3].quantized_sample & ~1) + | aptx_quantized_parity(channel); +} + +static void aptxhd_unpack_codeword(Channel *channel, uint32_t codeword) +{ + channel->quantize[0].quantized_sample = sign_extend(codeword >> 0, 9); + channel->quantize[1].quantized_sample = sign_extend(codeword >> 9, 6); + channel->quantize[2].quantized_sample = sign_extend(codeword >> 15, 4); + channel->quantize[3].quantized_sample = sign_extend(codeword >> 19, 5); + channel->quantize[3].quantized_sample = (channel->quantize[3].quantized_sample & ~1) + | aptx_quantized_parity(channel); +} + +static int aptx_decode_samples(AptXContext *ctx, + const uint8_t *input, + int32_t samples[NB_CHANNELS][4]) +{ + int channel, ret; + + for (channel = 0; channel < NB_CHANNELS; channel++) { + ff_aptx_generate_dither(&ctx->channels[channel]); + + if (ctx->hd) + aptxhd_unpack_codeword(&ctx->channels[channel], + AV_RB24(input + 3*channel)); + else + aptx_unpack_codeword(&ctx->channels[channel], + AV_RB16(input + 2*channel)); + ff_aptx_invert_quantize_and_prediction(&ctx->channels[channel], ctx->hd); + } + + ret = aptx_check_parity(ctx->channels, &ctx->sync_idx); + + for (channel = 0; channel < NB_CHANNELS; channel++) + aptx_decode_channel(&ctx->channels[channel], samples[channel]); + + return ret; +} + +static int aptx_decode_frame(AVCodecContext *avctx, void *data, + int *got_frame_ptr, AVPacket *avpkt) +{ + AptXContext *s = avctx->priv_data; + AVFrame *frame = data; + int pos, opos, channel, sample, ret; + + if (avpkt->size < s->block_size) { + av_log(avctx, AV_LOG_ERROR, "Packet is too small\n"); + return AVERROR_INVALIDDATA; + } + + /* get output buffer */ + frame->channels = NB_CHANNELS; + frame->format = AV_SAMPLE_FMT_S32P; + frame->nb_samples = 4 * avpkt->size / s->block_size; + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + return ret; + + for (pos = 0, opos = 0; opos < frame->nb_samples; pos += s->block_size, opos += 4) { + int32_t samples[NB_CHANNELS][4]; + + if (aptx_decode_samples(s, &avpkt->data[pos], samples)) { + av_log(avctx, AV_LOG_ERROR, "Synchronization error\n"); + return AVERROR_INVALIDDATA; + } + + for (channel = 0; channel < NB_CHANNELS; channel++) + for (sample = 0; sample < 4; sample++) + AV_WN32A(&frame->data[channel][4*(opos+sample)], + samples[channel][sample] * 256); + } + + *got_frame_ptr = 1; + return s->block_size * frame->nb_samples / 4; +} + +#if CONFIG_APTX_DECODER +AVCodec ff_aptx_decoder = { + .name = "aptx", + .long_name = NULL_IF_CONFIG_SMALL("aptX (Audio Processing Technology for Bluetooth)"), + .type = AVMEDIA_TYPE_AUDIO, + .id = AV_CODEC_ID_APTX, + .priv_data_size = sizeof(AptXContext), + .init = ff_aptx_init, + .decode = aptx_decode_frame, + .capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, + .channel_layouts = (const uint64_t[]) { AV_CH_LAYOUT_STEREO, 0}, + .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S32P, + AV_SAMPLE_FMT_NONE }, +}; +#endif + +#if CONFIG_APTX_HD_DECODER +AVCodec ff_aptx_hd_decoder = { + .name = "aptx_hd", + .long_name = NULL_IF_CONFIG_SMALL("aptX HD (Audio Processing Technology for Bluetooth)"), + .type = AVMEDIA_TYPE_AUDIO, + .id = AV_CODEC_ID_APTX_HD, + .priv_data_size = sizeof(AptXContext), + .init = ff_aptx_init, + .decode = aptx_decode_frame, + .capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, + .channel_layouts = (const uint64_t[]) { AV_CH_LAYOUT_STEREO, 0}, + .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S32P, + AV_SAMPLE_FMT_NONE }, +}; +#endif diff --git a/libavcodec/aptxenc.c b/libavcodec/aptxenc.c new file mode 100644 index 00000000000..60de73ec28b --- /dev/null +++ b/libavcodec/aptxenc.c @@ -0,0 +1,278 @@ +/* + * Audio Processing Technology codec for Bluetooth (aptX) + * + * Copyright (C) 2017 Aurelien Jacobs + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 "aptx.h" + +/* + * Half-band QMF analysis filter realized with a polyphase FIR filter. + * Split into 2 subbands and downsample by 2. + * So for each pair of samples that goes in, one sample goes out, + * split into 2 separate subbands. + */ +av_always_inline +static void aptx_qmf_polyphase_analysis(FilterSignal signal[NB_FILTERS], + const int32_t coeffs[NB_FILTERS][FILTER_TAPS], + int shift, + int32_t samples[NB_FILTERS], + int32_t *low_subband_output, + int32_t *high_subband_output) +{ + int32_t subbands[NB_FILTERS]; + int i; + + for (i = 0; i < NB_FILTERS; i++) { + aptx_qmf_filter_signal_push(&signal[i], samples[NB_FILTERS-1-i]); + subbands[i] = aptx_qmf_convolution(&signal[i], coeffs[i], shift); + } + + *low_subband_output = av_clip_intp2(subbands[0] + subbands[1], 23); + *high_subband_output = av_clip_intp2(subbands[0] - subbands[1], 23); +} + +/* + * Two stage QMF analysis tree. + * Split 4 input samples into 4 subbands and downsample by 4. + * So for each group of 4 samples that goes in, one sample goes out, + * split into 4 separate subbands. + */ +static void aptx_qmf_tree_analysis(QMFAnalysis *qmf, + int32_t samples[4], + int32_t subband_samples[4]) +{ + int32_t intermediate_samples[4]; + int i; + + /* Split 4 input samples into 2 intermediate subbands downsampled to 2 samples */ + for (i = 0; i < 2; i++) + aptx_qmf_polyphase_analysis(qmf->outer_filter_signal, + aptx_qmf_outer_coeffs, 23, + &samples[2*i], + &intermediate_samples[0+i], + &intermediate_samples[2+i]); + + /* Split 2 intermediate subband samples into 4 final subbands downsampled to 1 sample */ + for (i = 0; i < 2; i++) + aptx_qmf_polyphase_analysis(qmf->inner_filter_signal[i], + aptx_qmf_inner_coeffs, 23, + &intermediate_samples[2*i], + &subband_samples[2*i+0], + &subband_samples[2*i+1]); +} + +av_always_inline +static int32_t aptx_bin_search(int32_t value, int32_t factor, + const int32_t *intervals, int32_t nb_intervals) +{ + int32_t idx = 0; + int i; + + for (i = nb_intervals >> 1; i > 0; i >>= 1) + if (MUL64(factor, intervals[idx + i]) <= ((int64_t)value << 24)) + idx += i; + + return idx; +} + +static void aptx_quantize_difference(Quantize *quantize, + int32_t sample_difference, + int32_t dither, + int32_t quantization_factor, + ConstTables *tables) +{ + const int32_t *intervals = tables->quantize_intervals; + int32_t quantized_sample, dithered_sample, parity_change; + int32_t d, mean, interval, inv, sample_difference_abs; + int64_t error; + + sample_difference_abs = FFABS(sample_difference); + sample_difference_abs = FFMIN(sample_difference_abs, (1 << 23) - 1); + + quantized_sample = aptx_bin_search(sample_difference_abs >> 4, + quantization_factor, + intervals, tables->tables_size); + + d = rshift32_clip24(MULH(dither, dither), 7) - (1 << 23); + d = rshift64(MUL64(d, tables->quantize_dither_factors[quantized_sample]), 23); + + intervals += quantized_sample; + mean = (intervals[1] + intervals[0]) / 2; + interval = (intervals[1] - intervals[0]) * (-(sample_difference < 0) | 1); + + dithered_sample = rshift64_clip24(MUL64(dither, interval) + ((int64_t)av_clip_intp2(mean + d, 23) << 32), 32); + error = ((int64_t)sample_difference_abs << 20) - MUL64(dithered_sample, quantization_factor); + quantize->error = FFABS(rshift64(error, 23)); + + parity_change = quantized_sample; + if (error < 0) + quantized_sample--; + else + parity_change--; + + inv = -(sample_difference < 0); + quantize->quantized_sample = quantized_sample ^ inv; + quantize->quantized_sample_parity_change = parity_change ^ inv; +} + +static void aptx_encode_channel(Channel *channel, int32_t samples[4], int hd) +{ + int32_t subband_samples[4]; + int subband; + aptx_qmf_tree_analysis(&channel->qmf, samples, subband_samples); + ff_aptx_generate_dither(channel); + for (subband = 0; subband < NB_SUBBANDS; subband++) { + int32_t diff = av_clip_intp2(subband_samples[subband] - channel->prediction[subband].predicted_sample, 23); + aptx_quantize_difference(&channel->quantize[subband], diff, + channel->dither[subband], + channel->invert_quantize[subband].quantization_factor, + &ff_aptx_quant_tables[hd][subband]); + } +} + +static void aptx_insert_sync(Channel channels[NB_CHANNELS], int32_t *idx) +{ + if (aptx_check_parity(channels, idx)) { + int i; + Channel *c; + static const int map[] = { 1, 2, 0, 3 }; + Quantize *min = &channels[NB_CHANNELS-1].quantize[map[0]]; + for (c = &channels[NB_CHANNELS-1]; c >= channels; c--) + for (i = 0; i < NB_SUBBANDS; i++) + if (c->quantize[map[i]].error < min->error) + min = &c->quantize[map[i]]; + + /* Forcing the desired parity is done by offsetting by 1 the quantized + * sample from the subband featuring the smallest quantization error. */ + min->quantized_sample = min->quantized_sample_parity_change; + } +} + +static uint16_t aptx_pack_codeword(Channel *channel) +{ + int32_t parity = aptx_quantized_parity(channel); + return (((channel->quantize[3].quantized_sample & 0x06) | parity) << 13) + | (((channel->quantize[2].quantized_sample & 0x03) ) << 11) + | (((channel->quantize[1].quantized_sample & 0x0F) ) << 7) + | (((channel->quantize[0].quantized_sample & 0x7F) ) << 0); +} + +static uint32_t aptxhd_pack_codeword(Channel *channel) +{ + int32_t parity = aptx_quantized_parity(channel); + return (((channel->quantize[3].quantized_sample & 0x01E) | parity) << 19) + | (((channel->quantize[2].quantized_sample & 0x00F) ) << 15) + | (((channel->quantize[1].quantized_sample & 0x03F) ) << 9) + | (((channel->quantize[0].quantized_sample & 0x1FF) ) << 0); +} + +static void aptx_encode_samples(AptXContext *ctx, + int32_t samples[NB_CHANNELS][4], + uint8_t *output) +{ + int channel; + for (channel = 0; channel < NB_CHANNELS; channel++) + aptx_encode_channel(&ctx->channels[channel], samples[channel], ctx->hd); + + aptx_insert_sync(ctx->channels, &ctx->sync_idx); + + for (channel = 0; channel < NB_CHANNELS; channel++) { + ff_aptx_invert_quantize_and_prediction(&ctx->channels[channel], ctx->hd); + if (ctx->hd) + AV_WB24(output + 3*channel, + aptxhd_pack_codeword(&ctx->channels[channel])); + else + AV_WB16(output + 2*channel, + aptx_pack_codeword(&ctx->channels[channel])); + } +} + +static int aptx_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, + const AVFrame *frame, int *got_packet_ptr) +{ + AptXContext *s = avctx->priv_data; + int pos, ipos, channel, sample, output_size, ret; + + if ((ret = ff_af_queue_add(&s->afq, frame)) < 0) + return ret; + + output_size = s->block_size * frame->nb_samples/4; + if ((ret = ff_alloc_packet2(avctx, avpkt, output_size, 0)) < 0) + return ret; + + for (pos = 0, ipos = 0; pos < output_size; pos += s->block_size, ipos += 4) { + int32_t samples[NB_CHANNELS][4]; + + for (channel = 0; channel < NB_CHANNELS; channel++) + for (sample = 0; sample < 4; sample++) + samples[channel][sample] = (int32_t)AV_RN32A(&frame->data[channel][4*(ipos+sample)]) >> 8; + + aptx_encode_samples(s, samples, avpkt->data + pos); + } + + ff_af_queue_remove(&s->afq, frame->nb_samples, &avpkt->pts, &avpkt->duration); + *got_packet_ptr = 1; + return 0; +} + +static av_cold int aptx_close(AVCodecContext *avctx) +{ + AptXContext *s = avctx->priv_data; + ff_af_queue_close(&s->afq); + return 0; +} + +#if CONFIG_APTX_ENCODER +AVCodec ff_aptx_encoder = { + .name = "aptx", + .long_name = NULL_IF_CONFIG_SMALL("aptX (Audio Processing Technology for Bluetooth)"), + .type = AVMEDIA_TYPE_AUDIO, + .id = AV_CODEC_ID_APTX, + .priv_data_size = sizeof(AptXContext), + .init = ff_aptx_init, + .encode2 = aptx_encode_frame, + .close = aptx_close, + .capabilities = AV_CODEC_CAP_SMALL_LAST_FRAME, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, + .channel_layouts = (const uint64_t[]) { AV_CH_LAYOUT_STEREO, 0}, + .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S32P, + AV_SAMPLE_FMT_NONE }, + .supported_samplerates = (const int[]) {8000, 16000, 24000, 32000, 44100, 48000, 0}, +}; +#endif + +#if CONFIG_APTX_HD_ENCODER +AVCodec ff_aptx_hd_encoder = { + .name = "aptx_hd", + .long_name = NULL_IF_CONFIG_SMALL("aptX HD (Audio Processing Technology for Bluetooth)"), + .type = AVMEDIA_TYPE_AUDIO, + .id = AV_CODEC_ID_APTX_HD, + .priv_data_size = sizeof(AptXContext), + .init = ff_aptx_init, + .encode2 = aptx_encode_frame, + .close = aptx_close, + .capabilities = AV_CODEC_CAP_SMALL_LAST_FRAME, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, + .channel_layouts = (const uint64_t[]) { AV_CH_LAYOUT_STEREO, 0}, + .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S32P, + AV_SAMPLE_FMT_NONE }, + .supported_samplerates = (const int[]) {8000, 16000, 24000, 32000, 44100, 48000, 0}, +}; +#endif diff --git a/libavcodec/arm/Makefile b/libavcodec/arm/Makefile index e656011c3c5..c6be8141530 100644 --- a/libavcodec/arm/Makefile +++ b/libavcodec/arm/Makefile @@ -43,6 +43,7 @@ OBJS-$(CONFIG_HEVC_DECODER) += arm/hevcdsp_init_arm.o OBJS-$(CONFIG_MLP_DECODER) += arm/mlpdsp_init_arm.o OBJS-$(CONFIG_RV40_DECODER) += arm/rv40dsp_init_arm.o OBJS-$(CONFIG_SBC_ENCODER) += arm/sbcdsp_init_arm.o +OBJS-$(CONFIG_TRUEHD_DECODER) += arm/mlpdsp_init_arm.o OBJS-$(CONFIG_VORBIS_DECODER) += arm/vorbisdsp_init_arm.o OBJS-$(CONFIG_VP6_DECODER) += arm/vp6dsp_init_arm.o OBJS-$(CONFIG_VP9_DECODER) += arm/vp9dsp_init_10bpp_arm.o \ @@ -61,6 +62,7 @@ ARMV5TE-OBJS-$(CONFIG_VIDEODSP) += arm/videodsp_init_armv5te.o \ # decoders/encoders ARMV5TE-OBJS-$(CONFIG_MLP_DECODER) += arm/mlpdsp_armv5te.o +ARMV5TE-OBJS-$(CONFIG_TRUEHD_DECODER) += arm/mlpdsp_armv5te.o # ARMv6 optimizations @@ -83,6 +85,7 @@ ARMV6-OBJS-$(CONFIG_VP8DSP) += arm/vp8_armv6.o \ # decoders/encoders ARMV6-OBJS-$(CONFIG_MLP_DECODER) += arm/mlpdsp_armv6.o ARMV6-OBJS-$(CONFIG_SBC_ENCODER) += arm/sbcdsp_armv6.o +ARMV6-OBJS-$(CONFIG_TRUEHD_DECODER) += arm/mlpdsp_armv6.o # VFP optimizations @@ -123,6 +126,7 @@ NEON-OBJS-$(CONFIG_IDCTDSP) += arm/idctdsp_init_neon.o \ NEON-OBJS-$(CONFIG_MDCT) += arm/mdct_neon.o \ arm/mdct_fixed_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 diff --git a/libavcodec/arm/hevcdsp_sao_neon.S b/libavcodec/arm/hevcdsp_sao_neon.S index 347167951b7..8fd9d1e0df0 100644 --- a/libavcodec/arm/hevcdsp_sao_neon.S +++ b/libavcodec/arm/hevcdsp_sao_neon.S @@ -35,10 +35,10 @@ function ff_hevc_sao_band_filter_neon_8, export=1 vmov.u16 q15, #1 vmov.u8 q14, #32 0: pld [r1] - vld1.8 {d16}, [r1], r3 cmp r5, #4 beq 4f 8: subs r4, #1 + vld1.8 {d16}, [r1], r3 vshr.u8 d17, d16, #3 // index = [src>>3] vshll.u8 q9, d17, #1 // lowIndex = 2*index vadd.u16 q11, q9, q15 // highIndex = (2*index+1) << 8 @@ -54,7 +54,6 @@ function ff_hevc_sao_band_filter_neon_8, export=1 vaddw.u8 q13, q12, d16 vqmovun.s16 d8, q13 vst1.8 d8, [r0], r2 - vld1.8 {d16}, [r1], r3 bne 8b subs r5, #8 beq 99f @@ -65,6 +64,7 @@ function ff_hevc_sao_band_filter_neon_8, export=1 mov r1, r7 b 0b 4: subs r4, #1 + vld1.32 {d16[0]}, [r1], r3 vshr.u8 d17, d16, #3 // src>>3 vshll.u8 q9, d17, #1 // lowIndex = 2*index vadd.u16 q11, q9, q15 // highIndex = (2*index+1) << 8 @@ -80,7 +80,6 @@ function ff_hevc_sao_band_filter_neon_8, export=1 vaddw.u8 q13, q12, d16 vqmovun.s16 d14, q13 vst1.32 d14[0], [r0], r2 - vld1.32 {d16[0]}, [r1], r3 bne 4b b 99f 99: @@ -110,12 +109,12 @@ function ff_hevc_sao_edge_filter_neon_8, export=1 mov r11, r1 add r11, r9 // src[x + b_stride] pld [r1] - vld1.8 {d16}, [r1], r3 // src[x] 8x8bit - vld1.8 {d17}, [r10], r3 // src[x + a_stride] - vld1.8 {d18}, [r11], r3 // src[x + b_stride] cmp r5, #4 beq 4f 8: subs r4, #1 + vld1.8 {d16}, [r1], r3 // src[x] 8x8bit + vld1.8 {d17}, [r10], r3 // src[x + a_stride] + vld1.8 {d18}, [r11], r3 // src[x + b_stride] vcgt.u8 d8, d16, d17 vshr.u8 d9, d8, #7 vclt.u8 d8, d16, d17 @@ -136,9 +135,6 @@ function ff_hevc_sao_edge_filter_neon_8, export=1 vaddw.u8 q12, q11, d16 vqmovun.s16 d26, q12 vst1.8 d26, [r0], r2 - vld1.8 {d16}, [r1], r3 // src[x] 8x8bit - vld1.8 {d17}, [r10], r3 // src[x + a_stride] - vld1.8 {d18}, [r11], r3 // src[x + b_stride] bne 8b subs r5, #8 beq 99f @@ -149,6 +145,9 @@ function ff_hevc_sao_edge_filter_neon_8, export=1 mov r1, r7 b 0b 4: subs r4, #1 + vld1.32 {d16[0]}, [r1], r3 + vld1.32 {d17[0]}, [r10], r3 // src[x + a_stride] + vld1.32 {d18[0]}, [r11], r3 // src[x + b_stride] vcgt.u8 d8, d16, d17 vshr.u8 d9, d8, #7 vclt.u8 d8, d16, d17 @@ -169,9 +168,6 @@ function ff_hevc_sao_edge_filter_neon_8, export=1 vaddw.u8 q12, q11, d16 vqmovun.s16 d26, q12 vst1.32 d26[0], [r0], r2 - vld1.32 {d16[0]}, [r1], r3 - vld1.32 {d17[0]}, [r10], r3 // src[x + a_stride] - vld1.32 {d18[0]}, [r11], r3 // src[x + b_stride] bne 4b b 99f 99: diff --git a/libavcodec/arm/pixblockdsp_init_arm.c b/libavcodec/arm/pixblockdsp_init_arm.c index 59d2b493816..5481c0178c0 100644 --- a/libavcodec/arm/pixblockdsp_init_arm.c +++ b/libavcodec/arm/pixblockdsp_init_arm.c @@ -29,6 +29,15 @@ void ff_get_pixels_armv6(int16_t *block, const uint8_t *pixels, void ff_diff_pixels_armv6(int16_t *block, const uint8_t *s1, const uint8_t *s2, ptrdiff_t stride); +void ff_get_pixels_neon(int16_t *block, const uint8_t *pixels, + ptrdiff_t stride); +void ff_get_pixels_unaligned_neon(int16_t *block, const uint8_t *pixels, + ptrdiff_t stride); +void ff_diff_pixels_neon(int16_t *block, const uint8_t *s1, + const uint8_t *s2, ptrdiff_t stride); +void ff_diff_pixels_unaligned_neon(int16_t *block, const uint8_t *s1, + const uint8_t *s2, ptrdiff_t stride); + av_cold void ff_pixblockdsp_init_arm(PixblockDSPContext *c, AVCodecContext *avctx, unsigned high_bit_depth) @@ -40,4 +49,13 @@ av_cold void ff_pixblockdsp_init_arm(PixblockDSPContext *c, c->get_pixels = ff_get_pixels_armv6; c->diff_pixels = ff_diff_pixels_armv6; } + + if (have_neon(cpu_flags)) { + if (!high_bit_depth) { + c->get_pixels_unaligned = ff_get_pixels_unaligned_neon; + c->get_pixels = ff_get_pixels_neon; + } + c->diff_pixels_unaligned = ff_diff_pixels_unaligned_neon; + c->diff_pixels = ff_diff_pixels_neon; + } } diff --git a/libavcodec/arm/pixblockdsp_neon.S b/libavcodec/arm/pixblockdsp_neon.S new file mode 100644 index 00000000000..25674586ea9 --- /dev/null +++ b/libavcodec/arm/pixblockdsp_neon.S @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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" + +.macro vld1_8 dst, src, incr, aligned +.if \aligned + vld1.8 {\dst}, [\src, :64], \incr +.else + vld1.8 {\dst}, [\src], \incr +.endif +.endm + +.macro get_pixels suffix, aligned +function ff_get_pixels\suffix\()_neon, export=1 + mov r3, #8 +1: + vld1_8 d0, r1, r2, \aligned + subs r3, r3, #2 + vld1_8 d2, r1, r2, \aligned + vmovl.u8 q0, d0 + vmovl.u8 q1, d2 + vst1.16 {q0, q1}, [r0, :128]! + bgt 1b + + bx lr +endfunc +.endm + +get_pixels , aligned=1 +get_pixels _unaligned, aligned=0 + +.macro diff_pixels suffix, aligned=0 +function ff_diff_pixels\suffix\()_neon, export=1 + mov r12, #8 +1: + vld1_8 d0, r1, r3, \aligned + vld1_8 d1, r2, r3, \aligned + subs r12, r12, #2 + vld1_8 d2, r1, r3, \aligned + vsubl.u8 q0, d0, d1 + vld1_8 d3, r2, r3, \aligned + vsubl.u8 q1, d2, d3 + vst1.16 {q0, q1}, [r0]! + bgt 1b + + bx lr +endfunc +.endm + +diff_pixels , aligned=1 +diff_pixels _unaligned, aligned=0 diff --git a/libavcodec/ass.c b/libavcodec/ass.c index b4f081c819e..627741936ae 100644 --- a/libavcodec/ass.c +++ b/libavcodec/ass.c @@ -26,11 +26,13 @@ #include "libavutil/bprint.h" #include "libavutil/common.h" -int ff_ass_subtitle_header(AVCodecContext *avctx, - const char *font, int font_size, - int color, int back_color, - int bold, int italic, int underline, - int border_style, int alignment) +int ff_ass_subtitle_header_full(AVCodecContext *avctx, + int play_res_x, int play_res_y, + const char *font, int font_size, + int primary_color, int secondary_color, + int outline_color, int back_color, + int bold, int italic, int underline, + int border_style, int alignment) { avctx->subtitle_header = av_asprintf( "[Script Info]\r\n" @@ -38,6 +40,7 @@ int ff_ass_subtitle_header(AVCodecContext *avctx, "ScriptType: v4.00+\r\n" "PlayResX: %d\r\n" "PlayResY: %d\r\n" + "ScaledBorderAndShadow: yes\r\n" "\r\n" "[V4+ Styles]\r\n" @@ -67,8 +70,8 @@ int ff_ass_subtitle_header(AVCodecContext *avctx, "[Events]\r\n" "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n", !(avctx->flags & AV_CODEC_FLAG_BITEXACT) ? AV_STRINGIFY(LIBAVCODEC_VERSION) : "", - ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, - font, font_size, color, color, back_color, back_color, + play_res_x, play_res_y, font, font_size, + primary_color, secondary_color, outline_color, back_color, -bold, -italic, -underline, border_style, alignment); if (!avctx->subtitle_header) @@ -77,6 +80,20 @@ int ff_ass_subtitle_header(AVCodecContext *avctx, return 0; } +int ff_ass_subtitle_header(AVCodecContext *avctx, + const char *font, int font_size, + int color, int back_color, + int bold, int italic, int underline, + int border_style, int alignment) +{ + return ff_ass_subtitle_header_full(avctx, + ASS_DEFAULT_PLAYRESX, ASS_DEFAULT_PLAYRESY, + font, font_size, color, color, + back_color, back_color, + bold, italic, underline, + border_style, alignment); +} + int ff_ass_subtitle_header_default(AVCodecContext *avctx) { return ff_ass_subtitle_header(avctx, ASS_DEFAULT_FONT, @@ -105,7 +122,7 @@ int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, char *ass_str; AVSubtitleRect **rects; - rects = av_realloc_array(sub->rects, (sub->num_rects+1), sizeof(*sub->rects)); + rects = av_realloc_array(sub->rects, sub->num_rects+1, sizeof(*sub->rects)); if (!rects) return AVERROR(ENOMEM); sub->rects = rects; diff --git a/libavcodec/ass.h b/libavcodec/ass.h index 314b43b686b..2c260e4e785 100644 --- a/libavcodec/ass.h +++ b/libavcodec/ass.h @@ -47,6 +47,34 @@ typedef struct FFASSDecoderContext { int readorder; } FFASSDecoderContext; +/** + * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS. + * Can specify all fields explicitly + * + * @param avctx pointer to the AVCodecContext + * @param play_res_x subtitle frame width + * @param play_res_y subtitle frame height + * @param font name of the default font face to use + * @param font_size default font size to use + * @param primary_color default text color to use (ABGR) + * @param secondary_color default secondary text color to use (ABGR) + * @param outline_color default outline color to use (ABGR) + * @param back_color default background color to use (ABGR) + * @param bold 1 for bold text, 0 for normal text + * @param italic 1 for italic text, 0 for normal text + * @param underline 1 for underline text, 0 for normal text + * @param border_style 1 for outline, 3 for opaque box + * @param alignment position of the text (left, center, top...), defined after + * the layout of the numpad (1-3 sub, 4-6 mid, 7-9 top) + * @return >= 0 on success otherwise an error code <0 + */ +int ff_ass_subtitle_header_full(AVCodecContext *avctx, + int play_res_x, int play_res_y, + const char *font, int font_size, + int primary_color, int secondary_color, + int outline_color, int back_color, + int bold, int italic, int underline, + int border_style, int alignment); /** * Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS. * diff --git a/libavcodec/ass_split.c b/libavcodec/ass_split.c index 67da7c6d84c..c2c388d9f05 100644 --- a/libavcodec/ass_split.c +++ b/libavcodec/ass_split.c @@ -376,6 +376,8 @@ ASSSplitContext *ff_ass_split(const char *buf) ASSSplitContext *ctx = av_mallocz(sizeof(*ctx)); if (!ctx) return NULL; + if (buf && !memcmp(buf, "\xef\xbb\xbf", 3)) // Skip UTF-8 BOM header + buf += 3; ctx->current_section = -1; if (ass_split(ctx, buf) < 0) { ff_ass_split_free(ctx); diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c index e54c1d8ec39..a6e1d5d8b90 100644 --- a/libavcodec/assenc.c +++ b/libavcodec/assenc.c @@ -93,7 +93,7 @@ static int ass_encode_frame(AVCodecContext *avctx, if (len > bufsize-total_len-1) { av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); - return AVERROR(EINVAL); + return AVERROR_BUFFER_TOO_SMALL; } total_len += len; diff --git a/libavcodec/asvenc.c b/libavcodec/asvenc.c index 3cc94bf91a2..c2c940f3650 100644 --- a/libavcodec/asvenc.c +++ b/libavcodec/asvenc.c @@ -228,7 +228,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, clone->format = pict->format; clone->width = FFALIGN(pict->width, 16); clone->height = FFALIGN(pict->height, 16); - ret = av_frame_get_buffer(clone, 32); + ret = av_frame_get_buffer(clone, 0); if (ret < 0) { av_frame_free(&clone); return ret; diff --git a/libavcodec/atrac3.c b/libavcodec/atrac3.c index 6cdcdf19644..067aa23f1fa 100644 --- a/libavcodec/atrac3.c +++ b/libavcodec/atrac3.c @@ -964,7 +964,7 @@ static av_cold int atrac3_decode_init(AVCodecContext *avctx) return AVERROR_INVALIDDATA; } - if (avctx->block_align >= UINT_MAX / 2) + if (avctx->block_align > 1024 || avctx->block_align <= 0) return AVERROR(EINVAL); q->decoded_bytes_buffer = av_mallocz(FFALIGN(avctx->block_align, 4) + diff --git a/libavcodec/atrac3plus.c b/libavcodec/atrac3plus.c index 9f964efb4dd..8d178895822 100644 --- a/libavcodec/atrac3plus.c +++ b/libavcodec/atrac3plus.c @@ -456,6 +456,10 @@ static int decode_channel_wordlen(GetBitContext *gb, Atrac3pChanUnitCtx *ctx, } else if (chan->fill_mode == 3) { pos = ch_num ? chan->num_coded_vals + chan->split_point : ctx->num_quant_units - chan->split_point; + if (pos > FF_ARRAY_ELEMS(chan->qu_wordlen)) { + av_log(avctx, AV_LOG_ERROR, "Split point beyond array\n"); + pos = FF_ARRAY_ELEMS(chan->qu_wordlen); + } for (i = chan->num_coded_vals; i < pos; i++) chan->qu_wordlen[i] = 1; } diff --git a/libavcodec/atrac3plusdec.c b/libavcodec/atrac3plusdec.c index 666d1a5c018..4b008ba0ee2 100644 --- a/libavcodec/atrac3plusdec.c +++ b/libavcodec/atrac3plusdec.c @@ -174,7 +174,6 @@ static av_cold int atrac3p_decode_init(AVCodecContext *avctx) ctx->fdsp = avpriv_float_dsp_alloc(avctx->flags & AV_CODEC_FLAG_BITEXACT); if (!ctx->ch_units || !ctx->fdsp) { - atrac3p_decode_close(avctx); return AVERROR(ENOMEM); } @@ -393,6 +392,7 @@ AVCodec ff_atrac3p_decoder = { .type = AVMEDIA_TYPE_AUDIO, .id = AV_CODEC_ID_ATRAC3P, .capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .priv_data_size = sizeof(ATRAC3PContext), .init = atrac3p_decode_init, .close = atrac3p_decode_close, @@ -405,6 +405,7 @@ AVCodec ff_atrac3pal_decoder = { .type = AVMEDIA_TYPE_AUDIO, .id = AV_CODEC_ID_ATRAC3PAL, .capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .priv_data_size = sizeof(ATRAC3PContext), .init = atrac3p_decode_init, .close = atrac3p_decode_close, diff --git a/libavcodec/atrac9dec.c b/libavcodec/atrac9dec.c index 0820418902a..075d610e751 100644 --- a/libavcodec/atrac9dec.c +++ b/libavcodec/atrac9dec.c @@ -124,9 +124,6 @@ static inline int parse_gradient(ATRAC9Context *s, ATRAC9BlockData *b, if (grad_range[0] >= grad_range[1] || grad_range[1] > 31) return AVERROR_INVALIDDATA; - if (grad_value[0] > 31 || grad_value[1] > 31) - return AVERROR_INVALIDDATA; - if (b->grad_boundary > b->q_unit_cnt) return AVERROR_INVALIDDATA; @@ -190,7 +187,7 @@ static inline void calc_precision(ATRAC9Context *s, ATRAC9BlockData *b, for (int i = 0; i < b->q_unit_cnt; i++) { c->precision_fine[i] = 0; if (c->precision_coarse[i] > 15) { - c->precision_fine[i] = c->precision_coarse[i] - 15; + c->precision_fine[i] = FFMIN(c->precision_coarse[i], 30) - 15; c->precision_coarse[i] = 15; } } @@ -202,7 +199,7 @@ static inline int parse_band_ext(ATRAC9Context *s, ATRAC9BlockData *b, int ext_band = 0; if (b->has_band_ext) { - if (b->q_unit_cnt < 13) + if (b->q_unit_cnt < 13 || b->q_unit_cnt > 20) return AVERROR_INVALIDDATA; ext_band = at9_tab_band_ext_group[b->q_unit_cnt - 13][2]; if (stereo) { @@ -226,8 +223,18 @@ static inline int parse_band_ext(ATRAC9Context *s, ATRAC9BlockData *b, b->channel[0].band_ext = get_bits(gb, 2); b->channel[0].band_ext = ext_band > 2 ? b->channel[0].band_ext : 4; - if (!get_bits(gb, 5)) + if (!get_bits(gb, 5)) { + for (int i = 0; i <= stereo; i++) { + ATRAC9ChannelData *c = &b->channel[i]; + const int count = at9_tab_band_ext_cnt[c->band_ext][ext_band]; + for (int j = 0; j < count; j++) { + int len = at9_tab_band_ext_lengths[c->band_ext][ext_band][j]; + c->band_ext_data[j] = av_clip_uintp2_c(c->band_ext_data[j], len); + } + } + return 0; + } for (int i = 0; i <= stereo; i++) { ATRAC9ChannelData *c = &b->channel[i]; @@ -876,6 +883,7 @@ static av_cold int atrac9_decode_init(AVCodecContext *avctx) s->block_config = &at9_block_layout[block_config_idx]; avctx->channel_layout = s->block_config->channel_layout; + avctx->channels = av_get_channel_layout_nb_channels(avctx->channel_layout); avctx->sample_fmt = AV_SAMPLE_FMT_FLTP; if (get_bits1(&gb)) { diff --git a/libavcodec/atrac9tab.h b/libavcodec/atrac9tab.h index d25c6f1b7fa..8f290f158c1 100644 --- a/libavcodec/atrac9tab.h +++ b/libavcodec/atrac9tab.h @@ -41,61 +41,48 @@ typedef struct ATRAC9BlockConfig { static const ATRAC9BlockConfig at9_block_layout[] = { { /* Mono */ - AV_CH_LAYOUT_MONO, - { - ATRAC9_BLOCK_TYPE_SCE, - }, - { { 0 }, }, - 1, + .channel_layout = AV_CH_LAYOUT_MONO, + .type = { ATRAC9_BLOCK_TYPE_SCE, }, + .count = 1, }, { /* Dual Mono */ - AV_CH_LAYOUT_STEREO, - { - ATRAC9_BLOCK_TYPE_SCE, - ATRAC9_BLOCK_TYPE_SCE, - }, - { { 0 }, { 1 }, }, - 2, + .channel_layout = AV_CH_LAYOUT_STEREO, + .type = { ATRAC9_BLOCK_TYPE_SCE, + ATRAC9_BLOCK_TYPE_SCE, }, + .plane_map = { { 0 }, { 1 }, }, + .count = 2, }, { /* Stereo */ - AV_CH_LAYOUT_STEREO, - { - ATRAC9_BLOCK_TYPE_CPE, - }, - { { 0, 1 }, }, - 1, + .channel_layout = AV_CH_LAYOUT_STEREO, + .type = { ATRAC9_BLOCK_TYPE_CPE, }, + .plane_map = { { 0, 1 }, }, + .count = 1, }, { /* 5.1 */ - AV_CH_LAYOUT_5POINT1, - { - ATRAC9_BLOCK_TYPE_CPE, - ATRAC9_BLOCK_TYPE_SCE, - ATRAC9_BLOCK_TYPE_LFE, - ATRAC9_BLOCK_TYPE_CPE, - }, - { { 0, 1 }, { 2 }, { 3 }, { 4, 5 }, }, - 4, + .channel_layout = AV_CH_LAYOUT_5POINT1, + .type = { ATRAC9_BLOCK_TYPE_CPE, + ATRAC9_BLOCK_TYPE_SCE, + ATRAC9_BLOCK_TYPE_LFE, + ATRAC9_BLOCK_TYPE_CPE, }, + .plane_map = { { 0, 1 }, { 2 }, { 3 }, { 4, 5 }, }, + .count = 4, }, - { /* 5.1 */ - AV_CH_LAYOUT_7POINT1, - { - ATRAC9_BLOCK_TYPE_CPE, - ATRAC9_BLOCK_TYPE_SCE, - ATRAC9_BLOCK_TYPE_LFE, - ATRAC9_BLOCK_TYPE_CPE, - ATRAC9_BLOCK_TYPE_CPE, - }, - { { 0, 1 }, { 2 }, { 3 }, { 4, 5 }, { 6, 7 }, }, - 5, + { /* 7.1 */ + .channel_layout = AV_CH_LAYOUT_7POINT1, + .type = { ATRAC9_BLOCK_TYPE_CPE, + ATRAC9_BLOCK_TYPE_SCE, + ATRAC9_BLOCK_TYPE_LFE, + ATRAC9_BLOCK_TYPE_CPE, + ATRAC9_BLOCK_TYPE_CPE, }, + .plane_map = { { 0, 1 }, { 2 }, { 3 }, { 4, 5 }, { 6, 7 }, }, + .count = 5, }, { /* Quad */ - AV_CH_LAYOUT_QUAD, - { - ATRAC9_BLOCK_TYPE_CPE, - ATRAC9_BLOCK_TYPE_CPE, - }, - { { 0, 1 }, { 2, 3 }, }, - 2, + .channel_layout = AV_CH_LAYOUT_QUAD, + .type = { ATRAC9_BLOCK_TYPE_CPE, + ATRAC9_BLOCK_TYPE_CPE, }, + .plane_map = { { 0, 1 }, { 2, 3 }, }, + .count = 2, }, }; diff --git a/libavcodec/audiodsp.c b/libavcodec/audiodsp.c index 3c7a3a7583a..efcb0a8e8a1 100644 --- a/libavcodec/audiodsp.c +++ b/libavcodec/audiodsp.c @@ -79,7 +79,7 @@ static void vector_clipf_c(float *dst, const float *src, int len, static int32_t scalarproduct_int16_c(const int16_t *v1, const int16_t *v2, int order) { - int res = 0; + unsigned res = 0; while (order--) res += *v1++ **v2++; diff --git a/libavcodec/audiotoolboxenc.c b/libavcodec/audiotoolboxenc.c index 2c1891693e2..e8748b3c683 100644 --- a/libavcodec/audiotoolboxenc.c +++ b/libavcodec/audiotoolboxenc.c @@ -627,7 +627,8 @@ static const AVOption options[] = { .encode2 = ffat_encode, \ .flush = ffat_encode_flush, \ .priv_class = &ffat_##NAME##_enc_class, \ - .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY __VA_ARGS__, \ + .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | \ + AV_CODEC_CAP_ENCODER_FLUSH __VA_ARGS__, \ .sample_fmts = (const enum AVSampleFormat[]) { \ AV_SAMPLE_FMT_S16, \ AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_NONE \ @@ -655,7 +656,7 @@ static const uint64_t aac_at_channel_layouts[] = { FFAT_ENC(aac, AV_CODEC_ID_AAC, aac_profiles, , .channel_layouts = aac_at_channel_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 | AV_CODEC_CAP_LOSSLESS) +FFAT_ENC(alac, AV_CODEC_ID_ALAC, NULL, | AV_CODEC_CAP_VARIABLE_FRAME_SIZE) FFAT_ENC(ilbc, AV_CODEC_ID_ILBC, NULL) FFAT_ENC(pcm_alaw, AV_CODEC_ID_PCM_ALAW, NULL) FFAT_ENC(pcm_mulaw, AV_CODEC_ID_PCM_MULAW, NULL) diff --git a/libavcodec/av1_frame_merge_bsf.c b/libavcodec/av1_frame_merge_bsf.c new file mode 100644 index 00000000000..b5aa57e0fff --- /dev/null +++ b/libavcodec/av1_frame_merge_bsf.c @@ -0,0 +1,155 @@ +/* + * 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 "bsf.h" +#include "bsf_internal.h" +#include "cbs.h" +#include "cbs_av1.h" + +typedef struct AV1FMergeContext { + CodedBitstreamContext *cbc; + CodedBitstreamFragment frag[2]; + AVPacket *pkt, *in; + int idx; +} AV1FMergeContext; + +static void av1_frame_merge_flush(AVBSFContext *bsf) +{ + AV1FMergeContext *ctx = bsf->priv_data; + + ff_cbs_fragment_reset(ctx->cbc, &ctx->frag[0]); + ff_cbs_fragment_reset(ctx->cbc, &ctx->frag[1]); + av_packet_unref(ctx->in); + av_packet_unref(ctx->pkt); +} + +static int av1_frame_merge_filter(AVBSFContext *bsf, AVPacket *out) +{ + AV1FMergeContext *ctx = bsf->priv_data; + CodedBitstreamFragment *frag = &ctx->frag[ctx->idx], *tu = &ctx->frag[!ctx->idx]; + AVPacket *in = ctx->in, *buffer_pkt = ctx->pkt; + int err, i; + + err = ff_bsf_get_packet_ref(bsf, in); + if (err < 0) { + if (err == AVERROR_EOF && tu->nb_units > 0) + goto eof; + return err; + } + + err = ff_cbs_read_packet(ctx->cbc, frag, in); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n"); + goto fail; + } + + if (frag->nb_units == 0) { + av_log(bsf, AV_LOG_ERROR, "No OBU in packet.\n"); + err = AVERROR_INVALIDDATA; + goto fail; + } + + if (tu->nb_units == 0 && frag->units[0].type != AV1_OBU_TEMPORAL_DELIMITER) { + av_log(bsf, AV_LOG_ERROR, "Missing Temporal Delimiter.\n"); + err = AVERROR_INVALIDDATA; + goto fail; + } + + for (i = 1; i < frag->nb_units; i++) { + if (frag->units[i].type == AV1_OBU_TEMPORAL_DELIMITER) { + av_log(bsf, AV_LOG_ERROR, "Temporal Delimiter in the middle of a packet.\n"); + err = AVERROR_INVALIDDATA; + goto fail; + } + } + + if (tu->nb_units > 0 && frag->units[0].type == AV1_OBU_TEMPORAL_DELIMITER) { +eof: + err = ff_cbs_write_packet(ctx->cbc, buffer_pkt, tu); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to write packet.\n"); + goto fail; + } + av_packet_move_ref(out, buffer_pkt); + + // Swap fragment index, to avoid copying fragment references. + ctx->idx = !ctx->idx; + } else { + for (i = 0; i < frag->nb_units; i++) { + err = ff_cbs_insert_unit_content(ctx->cbc, tu, -1, frag->units[i].type, + frag->units[i].content, frag->units[i].content_ref); + if (err < 0) + goto fail; + } + + err = AVERROR(EAGAIN); + } + + // Buffer packets with timestamps. There should be at most one per TU, be it split or not. + if (!buffer_pkt->data && in->pts != AV_NOPTS_VALUE) + av_packet_move_ref(buffer_pkt, in); + else + av_packet_unref(in); + + ff_cbs_fragment_reset(ctx->cbc, &ctx->frag[ctx->idx]); + +fail: + if (err < 0 && err != AVERROR(EAGAIN)) + av1_frame_merge_flush(bsf); + + return err; +} + +static int av1_frame_merge_init(AVBSFContext *bsf) +{ + AV1FMergeContext *ctx = bsf->priv_data; + + ctx->in = av_packet_alloc(); + ctx->pkt = av_packet_alloc(); + if (!ctx->in || !ctx->pkt) + return AVERROR(ENOMEM); + + return ff_cbs_init(&ctx->cbc, AV_CODEC_ID_AV1, bsf); +} + +static void av1_frame_merge_close(AVBSFContext *bsf) +{ + AV1FMergeContext *ctx = bsf->priv_data; + + ff_cbs_fragment_free(ctx->cbc, &ctx->frag[0]); + ff_cbs_fragment_free(ctx->cbc, &ctx->frag[1]); + av_packet_free(&ctx->in); + av_packet_free(&ctx->pkt); + ff_cbs_close(&ctx->cbc); +} + +static const enum AVCodecID av1_frame_merge_codec_ids[] = { + AV_CODEC_ID_AV1, AV_CODEC_ID_NONE, +}; + +const AVBitStreamFilter ff_av1_frame_merge_bsf = { + .name = "av1_frame_merge", + .priv_data_size = sizeof(AV1FMergeContext), + .init = av1_frame_merge_init, + .flush = av1_frame_merge_flush, + .close = av1_frame_merge_close, + .filter = av1_frame_merge_filter, + .codec_ids = av1_frame_merge_codec_ids, +}; diff --git a/libavcodec/av1_frame_split_bsf.c b/libavcodec/av1_frame_split_bsf.c index a386c6edc1a..87dfc831032 100644 --- a/libavcodec/av1_frame_split_bsf.c +++ b/libavcodec/av1_frame_split_bsf.c @@ -32,8 +32,8 @@ #include "libavutil/avassert.h" -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "cbs.h" #include "cbs_av1.h" diff --git a/libavcodec/av1_metadata_bsf.c b/libavcodec/av1_metadata_bsf.c index 226f7dffa49..ee1a63c1ecf 100644 --- a/libavcodec/av1_metadata_bsf.c +++ b/libavcodec/av1_metadata_bsf.c @@ -20,6 +20,7 @@ #include "libavutil/opt.h" #include "bsf.h" +#include "bsf_internal.h" #include "cbs.h" #include "cbs_av1.h" @@ -61,12 +62,7 @@ static int av1_metadata_update_sequence_header(AVBSFContext *bsf, if (ctx->color_primaries >= 0 || ctx->transfer_characteristics >= 0 || ctx->matrix_coefficients >= 0) { - if (!clc->color_description_present_flag) { - clc->color_description_present_flag = 1; - clc->color_primaries = AVCOL_PRI_UNSPECIFIED; - clc->transfer_characteristics = AVCOL_TRC_UNSPECIFIED; - clc->matrix_coefficients = AVCOL_SPC_UNSPECIFIED; - } + clc->color_description_present_flag = 1; if (ctx->color_primaries >= 0) clc->color_primaries = ctx->color_primaries; @@ -116,6 +112,50 @@ static int av1_metadata_update_sequence_header(AVBSFContext *bsf, return 0; } +static int av1_metadata_update_side_data(AVBSFContext *bsf, AVPacket *pkt) +{ + AV1MetadataContext *ctx = bsf->priv_data; + CodedBitstreamFragment *frag = &ctx->access_unit; + uint8_t *side_data; + int side_data_size; + int err, i; + + side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, + &side_data_size); + if (!side_data_size) + return 0; + + err = ff_cbs_read(ctx->cbc, frag, side_data, side_data_size); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read extradata from packet side data.\n"); + return err; + } + + for (i = 0; i < frag->nb_units; i++) { + if (frag->units[i].type == AV1_OBU_SEQUENCE_HEADER) { + AV1RawOBU *obu = frag->units[i].content; + err = av1_metadata_update_sequence_header(bsf, &obu->obu.sequence_header); + if (err < 0) + return err; + } + } + + err = ff_cbs_write_fragment_data(ctx->cbc, frag); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to write extradata into packet side data.\n"); + return err; + } + + side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, frag->data_size); + if (!side_data) + return AVERROR(ENOMEM); + memcpy(side_data, frag->data, frag->data_size); + + ff_cbs_fragment_reset(ctx->cbc, frag); + + return 0; +} + static int av1_metadata_filter(AVBSFContext *bsf, AVPacket *pkt) { AV1MetadataContext *ctx = bsf->priv_data; @@ -127,6 +167,10 @@ static int av1_metadata_filter(AVBSFContext *bsf, AVPacket *pkt) if (err < 0) return err; + err = av1_metadata_update_side_data(bsf, pkt); + if (err < 0) + goto fail; + err = ff_cbs_read_packet(ctx->cbc, frag, pkt); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n"); diff --git a/libavcodec/av1_parse.c b/libavcodec/av1_parse.c index cdd524baa87..59ea0bc6e75 100644 --- a/libavcodec/av1_parse.c +++ b/libavcodec/av1_parse.c @@ -66,13 +66,16 @@ int ff_av1_packet_split(AV1Packet *pkt, const uint8_t *buf, int length, void *lo if (pkt->obus_allocated < pkt->nb_obus + 1) { int new_size = pkt->obus_allocated + 1; - AV1OBU *tmp = av_realloc_array(pkt->obus, new_size, sizeof(*tmp)); + AV1OBU *tmp; + + if (new_size >= INT_MAX / sizeof(*tmp)) + return AVERROR(ENOMEM); + tmp = av_fast_realloc(pkt->obus, &pkt->obus_allocated_size, new_size * sizeof(*tmp)); if (!tmp) return AVERROR(ENOMEM); pkt->obus = tmp; - memset(pkt->obus + pkt->obus_allocated, 0, - (new_size - pkt->obus_allocated) * sizeof(*tmp)); + memset(pkt->obus + pkt->obus_allocated, 0, sizeof(*pkt->obus)); pkt->obus_allocated = new_size; } obu = &pkt->obus[pkt->nb_obus]; @@ -103,5 +106,5 @@ int ff_av1_packet_split(AV1Packet *pkt, const uint8_t *buf, int length, void *lo void ff_av1_packet_uninit(AV1Packet *pkt) { av_freep(&pkt->obus); - pkt->obus_allocated = 0; + pkt->obus_allocated = pkt->obus_allocated_size = 0; } diff --git a/libavcodec/av1_parse.h b/libavcodec/av1_parse.h index 864308f81da..01bcd646c2e 100644 --- a/libavcodec/av1_parse.h +++ b/libavcodec/av1_parse.h @@ -56,6 +56,7 @@ typedef struct AV1Packet { AV1OBU *obus; int nb_obus; int obus_allocated; + unsigned obus_allocated_size; } AV1Packet; /** @@ -145,7 +146,9 @@ static inline int get_obu_bit_length(const uint8_t *buf, int size, int type) int v; /* There are no trailing bits on these */ - if (type == AV1_OBU_TILE_GROUP || type == AV1_OBU_FRAME) { + if (type == AV1_OBU_TILE_GROUP || + type == AV1_OBU_TILE_LIST || + type == AV1_OBU_FRAME) { if (size > INT_MAX / 8) return AVERROR(ERANGE); else diff --git a/libavcodec/av1_parser.c b/libavcodec/av1_parser.c index b916608d65c..036ab5e14e8 100644 --- a/libavcodec/av1_parser.c +++ b/libavcodec/av1_parser.c @@ -23,6 +23,7 @@ #include "av1_parse.h" #include "cbs.h" #include "cbs_av1.h" +#include "internal.h" #include "parser.h" typedef struct AV1ParseContext { @@ -44,6 +45,10 @@ static const enum AVPixelFormat pix_fmts_12bit[2][2] = { { AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12 }, }; +static const enum AVPixelFormat pix_fmts_rgb[3] = { + AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12, +}; + static int av1_parser_parse(AVCodecParserContext *ctx, AVCodecContext *avctx, const uint8_t **out_data, int *out_size, @@ -52,6 +57,8 @@ static int av1_parser_parse(AVCodecParserContext *ctx, AV1ParseContext *s = ctx->priv_data; CodedBitstreamFragment *td = &s->temporal_unit; CodedBitstreamAV1Context *av1 = s->cbc->priv_data; + AV1RawSequenceHeader *seq; + AV1RawColorConfig *color; int ret; *out_data = data; @@ -85,11 +92,12 @@ static int av1_parser_parse(AVCodecParserContext *ctx, goto end; } + seq = av1->sequence_header; + color = &seq->color_config; + for (int i = 0; i < td->nb_units; i++) { CodedBitstreamUnit *unit = &td->units[i]; AV1RawOBU *obu = unit->content; - AV1RawSequenceHeader *seq = av1->sequence_header; - AV1RawColorConfig *color = &seq->color_config; AV1RawFrameHeader *frame; int frame_type; @@ -100,6 +108,9 @@ static int av1_parser_parse(AVCodecParserContext *ctx, else continue; + if (obu->header.spatial_id > 0) + continue; + if (frame->show_existing_frame) { AV1ReferenceFrameState *ref = &av1->ref[frame->frame_to_show_map_idx]; @@ -123,9 +134,6 @@ static int av1_parser_parse(AVCodecParserContext *ctx, ctx->key_frame = frame_type == AV1_FRAME_KEY; } - avctx->profile = seq->seq_profile; - avctx->level = seq->seq_level_idx[0]; - switch (frame_type) { case AV1_FRAME_KEY: case AV1_FRAME_INTRA_ONLY: @@ -139,23 +147,48 @@ static int av1_parser_parse(AVCodecParserContext *ctx, break; } ctx->picture_structure = AV_PICTURE_STRUCTURE_FRAME; + } - switch (av1->bit_depth) { - case 8: - ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY8 - : pix_fmts_8bit [color->subsampling_x][color->subsampling_y]; - break; - case 10: - ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY10 - : pix_fmts_10bit[color->subsampling_x][color->subsampling_y]; - break; - case 12: - ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY12 - : pix_fmts_12bit[color->subsampling_x][color->subsampling_y]; - break; - } - av_assert2(ctx->format != AV_PIX_FMT_NONE); + switch (av1->bit_depth) { + case 8: + ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY8 + : pix_fmts_8bit [color->subsampling_x][color->subsampling_y]; + break; + case 10: + ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY10 + : pix_fmts_10bit[color->subsampling_x][color->subsampling_y]; + break; + case 12: + ctx->format = color->mono_chrome ? AV_PIX_FMT_GRAY12 + : pix_fmts_12bit[color->subsampling_x][color->subsampling_y]; + break; } + av_assert2(ctx->format != AV_PIX_FMT_NONE); + + if (!color->subsampling_x && !color->subsampling_y && + color->matrix_coefficients == AVCOL_SPC_RGB && + color->color_primaries == AVCOL_PRI_BT709 && + color->transfer_characteristics == AVCOL_TRC_IEC61966_2_1) + ctx->format = pix_fmts_rgb[color->high_bitdepth + color->twelve_bit]; + + avctx->pix_fmt = ctx->format; + + avctx->profile = seq->seq_profile; + avctx->level = seq->seq_level_idx[0]; + + avctx->colorspace = (enum AVColorSpace) color->matrix_coefficients; + avctx->color_primaries = (enum AVColorPrimaries) color->color_primaries; + avctx->color_trc = (enum AVColorTransferCharacteristic) color->transfer_characteristics; + avctx->color_range = color->color_range ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; + + if (ctx->width != avctx->width || ctx->height != avctx->height) { + ret = ff_set_dimensions(avctx, ctx->width, ctx->height); + if (ret < 0) + goto end; + } + + if (avctx->framerate.num) + avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1})); end: ff_cbs_fragment_reset(s->cbc, td); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index d234271c5b8..c91b2fd169a 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -41,6 +41,12 @@ #include "libavutil/pixfmt.h" #include "libavutil/rational.h" +#include "bsf.h" +#include "codec.h" +#include "codec_desc.h" +#include "codec_par.h" +#include "codec_id.h" +#include "packet.h" #include "version.h" /** @@ -92,6 +98,7 @@ * compressed data in an AVPacket. * - For encoding, call avcodec_send_frame() to give the encoder an AVFrame * containing uncompressed audio or video. + * * In both cases, it is recommended that AVPackets and AVFrames are * refcounted, or libavcodec might have to copy the input data. (libavformat * always returns refcounted AVPackets, and av_frame_get_buffer() allocates @@ -102,6 +109,7 @@ * an AVFrame containing uncompressed audio or video data. * - For encoding, call avcodec_receive_packet(). On success, it will return * an AVPacket with a compressed frame. + * * Repeat this call until it returns AVERROR(EAGAIN) or an error. The * AVERROR(EAGAIN) return value means that new input data is required to * return new output. In this case, continue with sending input. For each @@ -196,589 +204,6 @@ * @{ */ - -/** - * Identify the syntax and semantics of the bitstream. - * The principle is roughly: - * Two decoders with the same ID can decode the same streams. - * Two encoders with the same ID can encode compatible streams. - * There may be slight deviations from the principle due to implementation - * details. - * - * If you add a codec ID to this list, add it so that - * 1. no value of an existing codec ID changes (that would break ABI), - * 2. it is as close as possible to similar codecs - * - * After adding new codec IDs, do not forget to add an entry to the codec - * descriptor list and bump libavcodec minor version. - */ -enum AVCodecID { - AV_CODEC_ID_NONE, - - /* video codecs */ - AV_CODEC_ID_MPEG1VIDEO, - AV_CODEC_ID_MPEG2VIDEO, ///< preferred ID for MPEG-1/2 video decoding - AV_CODEC_ID_H261, - AV_CODEC_ID_H263, - AV_CODEC_ID_RV10, - AV_CODEC_ID_RV20, - AV_CODEC_ID_MJPEG, - AV_CODEC_ID_MJPEGB, - AV_CODEC_ID_LJPEG, - AV_CODEC_ID_SP5X, - AV_CODEC_ID_JPEGLS, - AV_CODEC_ID_MPEG4, - AV_CODEC_ID_RAWVIDEO, - AV_CODEC_ID_MSMPEG4V1, - AV_CODEC_ID_MSMPEG4V2, - AV_CODEC_ID_MSMPEG4V3, - AV_CODEC_ID_WMV1, - AV_CODEC_ID_WMV2, - AV_CODEC_ID_H263P, - AV_CODEC_ID_H263I, - AV_CODEC_ID_FLV1, - AV_CODEC_ID_SVQ1, - AV_CODEC_ID_SVQ3, - AV_CODEC_ID_DVVIDEO, - AV_CODEC_ID_HUFFYUV, - AV_CODEC_ID_CYUV, - AV_CODEC_ID_H264, - AV_CODEC_ID_INDEO3, - AV_CODEC_ID_VP3, - AV_CODEC_ID_THEORA, - AV_CODEC_ID_ASV1, - AV_CODEC_ID_ASV2, - AV_CODEC_ID_FFV1, - AV_CODEC_ID_4XM, - AV_CODEC_ID_VCR1, - AV_CODEC_ID_CLJR, - AV_CODEC_ID_MDEC, - AV_CODEC_ID_ROQ, - AV_CODEC_ID_INTERPLAY_VIDEO, - AV_CODEC_ID_XAN_WC3, - AV_CODEC_ID_XAN_WC4, - AV_CODEC_ID_RPZA, - AV_CODEC_ID_CINEPAK, - AV_CODEC_ID_WS_VQA, - AV_CODEC_ID_MSRLE, - AV_CODEC_ID_MSVIDEO1, - AV_CODEC_ID_IDCIN, - AV_CODEC_ID_8BPS, - AV_CODEC_ID_SMC, - AV_CODEC_ID_FLIC, - AV_CODEC_ID_TRUEMOTION1, - AV_CODEC_ID_VMDVIDEO, - AV_CODEC_ID_MSZH, - AV_CODEC_ID_ZLIB, - AV_CODEC_ID_QTRLE, - AV_CODEC_ID_TSCC, - AV_CODEC_ID_ULTI, - AV_CODEC_ID_QDRAW, - AV_CODEC_ID_VIXL, - AV_CODEC_ID_QPEG, - AV_CODEC_ID_PNG, - AV_CODEC_ID_PPM, - AV_CODEC_ID_PBM, - AV_CODEC_ID_PGM, - AV_CODEC_ID_PGMYUV, - AV_CODEC_ID_PAM, - AV_CODEC_ID_FFVHUFF, - AV_CODEC_ID_RV30, - AV_CODEC_ID_RV40, - AV_CODEC_ID_VC1, - AV_CODEC_ID_WMV3, - AV_CODEC_ID_LOCO, - AV_CODEC_ID_WNV1, - AV_CODEC_ID_AASC, - AV_CODEC_ID_INDEO2, - AV_CODEC_ID_FRAPS, - AV_CODEC_ID_TRUEMOTION2, - AV_CODEC_ID_BMP, - AV_CODEC_ID_CSCD, - AV_CODEC_ID_MMVIDEO, - AV_CODEC_ID_ZMBV, - AV_CODEC_ID_AVS, - AV_CODEC_ID_SMACKVIDEO, - AV_CODEC_ID_NUV, - AV_CODEC_ID_KMVC, - AV_CODEC_ID_FLASHSV, - AV_CODEC_ID_CAVS, - AV_CODEC_ID_JPEG2000, - AV_CODEC_ID_VMNC, - AV_CODEC_ID_VP5, - AV_CODEC_ID_VP6, - AV_CODEC_ID_VP6F, - AV_CODEC_ID_TARGA, - AV_CODEC_ID_DSICINVIDEO, - AV_CODEC_ID_TIERTEXSEQVIDEO, - AV_CODEC_ID_TIFF, - AV_CODEC_ID_GIF, - AV_CODEC_ID_DXA, - AV_CODEC_ID_DNXHD, - AV_CODEC_ID_THP, - AV_CODEC_ID_SGI, - AV_CODEC_ID_C93, - AV_CODEC_ID_BETHSOFTVID, - AV_CODEC_ID_PTX, - AV_CODEC_ID_TXD, - AV_CODEC_ID_VP6A, - AV_CODEC_ID_AMV, - AV_CODEC_ID_VB, - AV_CODEC_ID_PCX, - AV_CODEC_ID_SUNRAST, - AV_CODEC_ID_INDEO4, - AV_CODEC_ID_INDEO5, - AV_CODEC_ID_MIMIC, - AV_CODEC_ID_RL2, - AV_CODEC_ID_ESCAPE124, - AV_CODEC_ID_DIRAC, - AV_CODEC_ID_BFI, - AV_CODEC_ID_CMV, - AV_CODEC_ID_MOTIONPIXELS, - AV_CODEC_ID_TGV, - AV_CODEC_ID_TGQ, - AV_CODEC_ID_TQI, - AV_CODEC_ID_AURA, - AV_CODEC_ID_AURA2, - AV_CODEC_ID_V210X, - AV_CODEC_ID_TMV, - AV_CODEC_ID_V210, - AV_CODEC_ID_DPX, - AV_CODEC_ID_MAD, - AV_CODEC_ID_FRWU, - AV_CODEC_ID_FLASHSV2, - AV_CODEC_ID_CDGRAPHICS, - AV_CODEC_ID_R210, - AV_CODEC_ID_ANM, - AV_CODEC_ID_BINKVIDEO, - AV_CODEC_ID_IFF_ILBM, -#define AV_CODEC_ID_IFF_BYTERUN1 AV_CODEC_ID_IFF_ILBM - AV_CODEC_ID_KGV1, - AV_CODEC_ID_YOP, - AV_CODEC_ID_VP8, - AV_CODEC_ID_PICTOR, - AV_CODEC_ID_ANSI, - AV_CODEC_ID_A64_MULTI, - AV_CODEC_ID_A64_MULTI5, - AV_CODEC_ID_R10K, - AV_CODEC_ID_MXPEG, - AV_CODEC_ID_LAGARITH, - AV_CODEC_ID_PRORES, - AV_CODEC_ID_JV, - AV_CODEC_ID_DFA, - AV_CODEC_ID_WMV3IMAGE, - AV_CODEC_ID_VC1IMAGE, - AV_CODEC_ID_UTVIDEO, - AV_CODEC_ID_BMV_VIDEO, - AV_CODEC_ID_VBLE, - AV_CODEC_ID_DXTORY, - AV_CODEC_ID_V410, - AV_CODEC_ID_XWD, - AV_CODEC_ID_CDXL, - AV_CODEC_ID_XBM, - AV_CODEC_ID_ZEROCODEC, - AV_CODEC_ID_MSS1, - AV_CODEC_ID_MSA1, - AV_CODEC_ID_TSCC2, - AV_CODEC_ID_MTS2, - AV_CODEC_ID_CLLC, - AV_CODEC_ID_MSS2, - AV_CODEC_ID_VP9, - AV_CODEC_ID_AIC, - AV_CODEC_ID_ESCAPE130, - AV_CODEC_ID_G2M, - AV_CODEC_ID_WEBP, - AV_CODEC_ID_HNM4_VIDEO, - AV_CODEC_ID_HEVC, -#define AV_CODEC_ID_H265 AV_CODEC_ID_HEVC - AV_CODEC_ID_FIC, - AV_CODEC_ID_ALIAS_PIX, - AV_CODEC_ID_BRENDER_PIX, - AV_CODEC_ID_PAF_VIDEO, - AV_CODEC_ID_EXR, - AV_CODEC_ID_VP7, - AV_CODEC_ID_SANM, - AV_CODEC_ID_SGIRLE, - AV_CODEC_ID_MVC1, - AV_CODEC_ID_MVC2, - AV_CODEC_ID_HQX, - AV_CODEC_ID_TDSC, - AV_CODEC_ID_HQ_HQA, - AV_CODEC_ID_HAP, - AV_CODEC_ID_DDS, - AV_CODEC_ID_DXV, - AV_CODEC_ID_SCREENPRESSO, - AV_CODEC_ID_RSCC, - AV_CODEC_ID_AVS2, - - AV_CODEC_ID_Y41P = 0x8000, - AV_CODEC_ID_AVRP, - AV_CODEC_ID_012V, - AV_CODEC_ID_AVUI, - AV_CODEC_ID_AYUV, - AV_CODEC_ID_TARGA_Y216, - AV_CODEC_ID_V308, - AV_CODEC_ID_V408, - AV_CODEC_ID_YUV4, - AV_CODEC_ID_AVRN, - AV_CODEC_ID_CPIA, - AV_CODEC_ID_XFACE, - AV_CODEC_ID_SNOW, - AV_CODEC_ID_SMVJPEG, - AV_CODEC_ID_APNG, - AV_CODEC_ID_DAALA, - AV_CODEC_ID_CFHD, - AV_CODEC_ID_TRUEMOTION2RT, - AV_CODEC_ID_M101, - AV_CODEC_ID_MAGICYUV, - AV_CODEC_ID_SHEERVIDEO, - AV_CODEC_ID_YLC, - AV_CODEC_ID_PSD, - AV_CODEC_ID_PIXLET, - AV_CODEC_ID_SPEEDHQ, - AV_CODEC_ID_FMVC, - AV_CODEC_ID_SCPR, - AV_CODEC_ID_CLEARVIDEO, - AV_CODEC_ID_XPM, - AV_CODEC_ID_AV1, - AV_CODEC_ID_BITPACKED, - AV_CODEC_ID_MSCC, - AV_CODEC_ID_SRGC, - AV_CODEC_ID_SVG, - AV_CODEC_ID_GDV, - AV_CODEC_ID_FITS, - AV_CODEC_ID_IMM4, - AV_CODEC_ID_PROSUMER, - AV_CODEC_ID_MWSC, - AV_CODEC_ID_WCMV, - AV_CODEC_ID_RASC, - AV_CODEC_ID_HYMT, - AV_CODEC_ID_ARBC, - AV_CODEC_ID_AGM, - AV_CODEC_ID_LSCR, - AV_CODEC_ID_VP4, - - /* various PCM "codecs" */ - AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs - AV_CODEC_ID_PCM_S16LE = 0x10000, - AV_CODEC_ID_PCM_S16BE, - AV_CODEC_ID_PCM_U16LE, - AV_CODEC_ID_PCM_U16BE, - AV_CODEC_ID_PCM_S8, - AV_CODEC_ID_PCM_U8, - AV_CODEC_ID_PCM_MULAW, - AV_CODEC_ID_PCM_ALAW, - AV_CODEC_ID_PCM_S32LE, - AV_CODEC_ID_PCM_S32BE, - AV_CODEC_ID_PCM_U32LE, - AV_CODEC_ID_PCM_U32BE, - AV_CODEC_ID_PCM_S24LE, - AV_CODEC_ID_PCM_S24BE, - AV_CODEC_ID_PCM_U24LE, - AV_CODEC_ID_PCM_U24BE, - AV_CODEC_ID_PCM_S24DAUD, - AV_CODEC_ID_PCM_ZORK, - AV_CODEC_ID_PCM_S16LE_PLANAR, - AV_CODEC_ID_PCM_DVD, - AV_CODEC_ID_PCM_F32BE, - AV_CODEC_ID_PCM_F32LE, - AV_CODEC_ID_PCM_F64BE, - AV_CODEC_ID_PCM_F64LE, - AV_CODEC_ID_PCM_BLURAY, - AV_CODEC_ID_PCM_LXF, - AV_CODEC_ID_S302M, - AV_CODEC_ID_PCM_S8_PLANAR, - AV_CODEC_ID_PCM_S24LE_PLANAR, - AV_CODEC_ID_PCM_S32LE_PLANAR, - AV_CODEC_ID_PCM_S16BE_PLANAR, - - AV_CODEC_ID_PCM_S64LE = 0x10800, - AV_CODEC_ID_PCM_S64BE, - AV_CODEC_ID_PCM_F16LE, - AV_CODEC_ID_PCM_F24LE, - AV_CODEC_ID_PCM_VIDC, - - /* various ADPCM codecs */ - AV_CODEC_ID_ADPCM_IMA_QT = 0x11000, - AV_CODEC_ID_ADPCM_IMA_WAV, - AV_CODEC_ID_ADPCM_IMA_DK3, - AV_CODEC_ID_ADPCM_IMA_DK4, - AV_CODEC_ID_ADPCM_IMA_WS, - AV_CODEC_ID_ADPCM_IMA_SMJPEG, - AV_CODEC_ID_ADPCM_MS, - AV_CODEC_ID_ADPCM_4XM, - AV_CODEC_ID_ADPCM_XA, - AV_CODEC_ID_ADPCM_ADX, - AV_CODEC_ID_ADPCM_EA, - AV_CODEC_ID_ADPCM_G726, - AV_CODEC_ID_ADPCM_CT, - AV_CODEC_ID_ADPCM_SWF, - AV_CODEC_ID_ADPCM_YAMAHA, - AV_CODEC_ID_ADPCM_SBPRO_4, - AV_CODEC_ID_ADPCM_SBPRO_3, - AV_CODEC_ID_ADPCM_SBPRO_2, - AV_CODEC_ID_ADPCM_THP, - AV_CODEC_ID_ADPCM_IMA_AMV, - AV_CODEC_ID_ADPCM_EA_R1, - AV_CODEC_ID_ADPCM_EA_R3, - AV_CODEC_ID_ADPCM_EA_R2, - AV_CODEC_ID_ADPCM_IMA_EA_SEAD, - AV_CODEC_ID_ADPCM_IMA_EA_EACS, - AV_CODEC_ID_ADPCM_EA_XAS, - AV_CODEC_ID_ADPCM_EA_MAXIS_XA, - AV_CODEC_ID_ADPCM_IMA_ISS, - AV_CODEC_ID_ADPCM_G722, - AV_CODEC_ID_ADPCM_IMA_APC, - AV_CODEC_ID_ADPCM_VIMA, - - AV_CODEC_ID_ADPCM_AFC = 0x11800, - AV_CODEC_ID_ADPCM_IMA_OKI, - AV_CODEC_ID_ADPCM_DTK, - AV_CODEC_ID_ADPCM_IMA_RAD, - AV_CODEC_ID_ADPCM_G726LE, - AV_CODEC_ID_ADPCM_THP_LE, - AV_CODEC_ID_ADPCM_PSX, - AV_CODEC_ID_ADPCM_AICA, - AV_CODEC_ID_ADPCM_IMA_DAT4, - AV_CODEC_ID_ADPCM_MTAF, - AV_CODEC_ID_ADPCM_AGM, - - /* AMR */ - AV_CODEC_ID_AMR_NB = 0x12000, - AV_CODEC_ID_AMR_WB, - - /* RealAudio codecs*/ - AV_CODEC_ID_RA_144 = 0x13000, - AV_CODEC_ID_RA_288, - - /* various DPCM codecs */ - AV_CODEC_ID_ROQ_DPCM = 0x14000, - AV_CODEC_ID_INTERPLAY_DPCM, - AV_CODEC_ID_XAN_DPCM, - AV_CODEC_ID_SOL_DPCM, - - AV_CODEC_ID_SDX2_DPCM = 0x14800, - AV_CODEC_ID_GREMLIN_DPCM, - - /* audio codecs */ - AV_CODEC_ID_MP2 = 0x15000, - AV_CODEC_ID_MP3, ///< preferred ID for decoding MPEG audio layer 1, 2 or 3 - AV_CODEC_ID_AAC, - AV_CODEC_ID_AC3, - AV_CODEC_ID_DTS, - AV_CODEC_ID_VORBIS, - AV_CODEC_ID_DVAUDIO, - AV_CODEC_ID_WMAV1, - AV_CODEC_ID_WMAV2, - AV_CODEC_ID_MACE3, - AV_CODEC_ID_MACE6, - AV_CODEC_ID_VMDAUDIO, - AV_CODEC_ID_FLAC, - AV_CODEC_ID_MP3ADU, - AV_CODEC_ID_MP3ON4, - AV_CODEC_ID_SHORTEN, - AV_CODEC_ID_ALAC, - AV_CODEC_ID_WESTWOOD_SND1, - AV_CODEC_ID_GSM, ///< as in Berlin toast format - AV_CODEC_ID_QDM2, - AV_CODEC_ID_COOK, - AV_CODEC_ID_TRUESPEECH, - AV_CODEC_ID_TTA, - AV_CODEC_ID_SMACKAUDIO, - AV_CODEC_ID_QCELP, - AV_CODEC_ID_WAVPACK, - AV_CODEC_ID_DSICINAUDIO, - AV_CODEC_ID_IMC, - AV_CODEC_ID_MUSEPACK7, - AV_CODEC_ID_MLP, - AV_CODEC_ID_GSM_MS, /* as found in WAV */ - AV_CODEC_ID_ATRAC3, - AV_CODEC_ID_APE, - AV_CODEC_ID_NELLYMOSER, - AV_CODEC_ID_MUSEPACK8, - AV_CODEC_ID_SPEEX, - AV_CODEC_ID_WMAVOICE, - AV_CODEC_ID_WMAPRO, - AV_CODEC_ID_WMALOSSLESS, - AV_CODEC_ID_ATRAC3P, - AV_CODEC_ID_EAC3, - AV_CODEC_ID_SIPR, - AV_CODEC_ID_MP1, - AV_CODEC_ID_TWINVQ, - AV_CODEC_ID_TRUEHD, - AV_CODEC_ID_MP4ALS, - AV_CODEC_ID_ATRAC1, - AV_CODEC_ID_BINKAUDIO_RDFT, - AV_CODEC_ID_BINKAUDIO_DCT, - AV_CODEC_ID_AAC_LATM, - AV_CODEC_ID_QDMC, - AV_CODEC_ID_CELT, - AV_CODEC_ID_G723_1, - AV_CODEC_ID_G729, - AV_CODEC_ID_8SVX_EXP, - AV_CODEC_ID_8SVX_FIB, - AV_CODEC_ID_BMV_AUDIO, - AV_CODEC_ID_RALF, - AV_CODEC_ID_IAC, - AV_CODEC_ID_ILBC, - AV_CODEC_ID_OPUS, - AV_CODEC_ID_COMFORT_NOISE, - AV_CODEC_ID_TAK, - AV_CODEC_ID_METASOUND, - AV_CODEC_ID_PAF_AUDIO, - AV_CODEC_ID_ON2AVC, - AV_CODEC_ID_DSS_SP, - AV_CODEC_ID_CODEC2, - - AV_CODEC_ID_FFWAVESYNTH = 0x15800, - AV_CODEC_ID_SONIC, - AV_CODEC_ID_SONIC_LS, - AV_CODEC_ID_EVRC, - AV_CODEC_ID_SMV, - AV_CODEC_ID_DSD_LSBF, - AV_CODEC_ID_DSD_MSBF, - AV_CODEC_ID_DSD_LSBF_PLANAR, - AV_CODEC_ID_DSD_MSBF_PLANAR, - AV_CODEC_ID_4GV, - AV_CODEC_ID_INTERPLAY_ACM, - AV_CODEC_ID_XMA1, - AV_CODEC_ID_XMA2, - AV_CODEC_ID_DST, - AV_CODEC_ID_ATRAC3AL, - AV_CODEC_ID_ATRAC3PAL, - AV_CODEC_ID_DOLBY_E, - AV_CODEC_ID_APTX, - AV_CODEC_ID_APTX_HD, - AV_CODEC_ID_SBC, - AV_CODEC_ID_ATRAC9, - AV_CODEC_ID_HCOM, - - /* subtitle codecs */ - AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs. - AV_CODEC_ID_DVD_SUBTITLE = 0x17000, - AV_CODEC_ID_DVB_SUBTITLE, - AV_CODEC_ID_TEXT, ///< raw UTF-8 text - AV_CODEC_ID_XSUB, - AV_CODEC_ID_SSA, - AV_CODEC_ID_MOV_TEXT, - AV_CODEC_ID_HDMV_PGS_SUBTITLE, - AV_CODEC_ID_DVB_TELETEXT, - AV_CODEC_ID_SRT, - - AV_CODEC_ID_MICRODVD = 0x17800, - AV_CODEC_ID_EIA_608, - AV_CODEC_ID_JACOSUB, - AV_CODEC_ID_SAMI, - AV_CODEC_ID_REALTEXT, - AV_CODEC_ID_STL, - AV_CODEC_ID_SUBVIEWER1, - AV_CODEC_ID_SUBVIEWER, - AV_CODEC_ID_SUBRIP, - AV_CODEC_ID_WEBVTT, - AV_CODEC_ID_MPL2, - AV_CODEC_ID_VPLAYER, - AV_CODEC_ID_PJS, - AV_CODEC_ID_ASS, - AV_CODEC_ID_HDMV_TEXT_SUBTITLE, - AV_CODEC_ID_TTML, - AV_CODEC_ID_ARIB_CAPTION, - - /* other specific kind of codecs (generally used for attachments) */ - AV_CODEC_ID_FIRST_UNKNOWN = 0x18000, ///< A dummy ID pointing at the start of various fake codecs. - AV_CODEC_ID_TTF = 0x18000, - - AV_CODEC_ID_SCTE_35, ///< Contain timestamp estimated through PCR of program stream. - AV_CODEC_ID_BINTEXT = 0x18800, - AV_CODEC_ID_XBIN, - AV_CODEC_ID_IDF, - AV_CODEC_ID_OTF, - AV_CODEC_ID_SMPTE_KLV, - AV_CODEC_ID_DVD_NAV, - AV_CODEC_ID_TIMED_ID3, - AV_CODEC_ID_BIN_DATA, - - - AV_CODEC_ID_PROBE = 0x19000, ///< codec_id is not known (like AV_CODEC_ID_NONE) but lavf should attempt to identify it - - AV_CODEC_ID_MPEG2TS = 0x20000, /**< _FAKE_ codec to indicate a raw MPEG-2 TS - * stream (only used by libavformat) */ - AV_CODEC_ID_MPEG4SYSTEMS = 0x20001, /**< _FAKE_ codec to indicate a MPEG-4 Systems - * stream (only used by libavformat) */ - AV_CODEC_ID_FFMETADATA = 0x21000, ///< Dummy codec for streams containing only metadata information. - AV_CODEC_ID_WRAPPED_AVFRAME = 0x21001, ///< Passthrough codec, AVFrames wrapped in AVPacket -}; - -/** - * This struct describes the properties of a single codec described by an - * AVCodecID. - * @see avcodec_descriptor_get() - */ -typedef struct AVCodecDescriptor { - enum AVCodecID id; - enum AVMediaType type; - /** - * Name of the codec described by this descriptor. It is non-empty and - * unique for each codec descriptor. It should contain alphanumeric - * characters and '_' only. - */ - const char *name; - /** - * A more descriptive name for this codec. May be NULL. - */ - const char *long_name; - /** - * Codec properties, a combination of AV_CODEC_PROP_* flags. - */ - int props; - /** - * MIME type(s) associated with the codec. - * May be NULL; if not, a NULL-terminated array of MIME types. - * The first item is always non-NULL and is the preferred MIME type. - */ - const char *const *mime_types; - /** - * If non-NULL, an array of profiles recognized for this codec. - * Terminated with FF_PROFILE_UNKNOWN. - */ - const struct AVProfile *profiles; -} AVCodecDescriptor; - -/** - * Codec uses only intra compression. - * Video and audio codecs only. - */ -#define AV_CODEC_PROP_INTRA_ONLY (1 << 0) -/** - * Codec supports lossy compression. Audio and video codecs only. - * @note a codec may support both lossy and lossless - * compression modes - */ -#define AV_CODEC_PROP_LOSSY (1 << 1) -/** - * Codec supports lossless compression. Audio and video codecs only. - */ -#define AV_CODEC_PROP_LOSSLESS (1 << 2) -/** - * Codec supports frame reordering. That is, the coded order (the order in which - * the encoded packets are output by the encoders / stored / input to the - * decoders) may be different from the presentation order of the corresponding - * frames. - * - * For codecs that do not have this property set, PTS and DTS should always be - * equal. - */ -#define AV_CODEC_PROP_REORDER (1 << 3) -/** - * Subtitle codec is bitmap based - * Decoded AVSubtitle data can be read from the AVSubtitleRect->pict field. - */ -#define AV_CODEC_PROP_BITMAP_SUB (1 << 16) -/** - * Subtitle codec is text based. - * Decoded AVSubtitle data can be read from the AVSubtitleRect->ass field. - */ -#define AV_CODEC_PROP_TEXT_SUB (1 << 17) - /** * @ingroup lavc_decoding * Required number of additionally allocated bytes at the end of the input bitstream for decoding. @@ -969,127 +394,22 @@ typedef struct RcOverride{ /* /Fx */ /* codec capabilities */ +/* Exported side data. + These flags can be passed in AVCodecContext.export_side_data before initialization. +*/ /** - * Decoder can use draw_horiz_band callback. - */ -#define AV_CODEC_CAP_DRAW_HORIZ_BAND (1 << 0) -/** - * Codec uses get_buffer() for allocating buffers and supports custom allocators. - * If not set, it might not use get_buffer() at all or use operations that - * assume the buffer was allocated by avcodec_default_get_buffer. - */ -#define AV_CODEC_CAP_DR1 (1 << 1) -#define AV_CODEC_CAP_TRUNCATED (1 << 3) -/** - * Encoder or decoder requires flushing with NULL input at the end in order to - * give the complete and correct output. - * - * NOTE: If this flag is not set, the codec is guaranteed to never be fed with - * with NULL data. The user can still send NULL data to the public encode - * or decode function, but libavcodec will not pass it along to the codec - * unless this flag is set. - * - * Decoders: - * The decoder has a non-zero delay and needs to be fed with avpkt->data=NULL, - * avpkt->size=0 at the end to get the delayed data until the decoder no longer - * returns frames. - * - * Encoders: - * The encoder needs to be fed with NULL data at the end of encoding until the - * encoder no longer returns data. - * - * NOTE: For encoders implementing the AVCodec.encode2() function, setting this - * flag also means that the encoder must set the pts and duration for - * each output packet. If this flag is not set, the pts and duration will - * be determined by libavcodec from the input frame. - */ -#define AV_CODEC_CAP_DELAY (1 << 5) -/** - * Codec can be fed a final frame with a smaller size. - * This can be used to prevent truncation of the last audio samples. - */ -#define AV_CODEC_CAP_SMALL_LAST_FRAME (1 << 6) - -/** - * Codec can output multiple frames per AVPacket - * Normally demuxers return one frame at a time, demuxers which do not do - * are connected to a parser to split what they return into proper frames. - * This flag is reserved to the very rare category of codecs which have a - * bitstream that cannot be split into frames without timeconsuming - * operations like full decoding. Demuxers carrying such bitstreams thus - * may return multiple frames in a packet. This has many disadvantages like - * prohibiting stream copy in many cases thus it should only be considered - * as a last resort. - */ -#define AV_CODEC_CAP_SUBFRAMES (1 << 8) -/** - * Codec is experimental and is thus avoided in favor of non experimental - * encoders - */ -#define AV_CODEC_CAP_EXPERIMENTAL (1 << 9) -/** - * Codec should fill in channel configuration and samplerate instead of container - */ -#define AV_CODEC_CAP_CHANNEL_CONF (1 << 10) -/** - * Codec supports frame-level multithreading. - */ -#define AV_CODEC_CAP_FRAME_THREADS (1 << 12) -/** - * Codec supports slice-based (or partition-based) multithreading. - */ -#define AV_CODEC_CAP_SLICE_THREADS (1 << 13) -/** - * Codec supports changed parameters at any point. - */ -#define AV_CODEC_CAP_PARAM_CHANGE (1 << 14) -/** - * Codec supports avctx->thread_count == 0 (auto). - */ -#define AV_CODEC_CAP_AUTO_THREADS (1 << 15) -/** - * Audio encoder supports receiving a different number of samples in each call. - */ -#define AV_CODEC_CAP_VARIABLE_FRAME_SIZE (1 << 16) -/** - * Decoder is not a preferred choice for probing. - * This indicates that the decoder is not a good choice for probing. - * It could for example be an expensive to spin up hardware decoder, - * or it could simply not provide a lot of useful information about - * the stream. - * A decoder marked with this flag should only be used as last resort - * choice for probing. - */ -#define AV_CODEC_CAP_AVOID_PROBING (1 << 17) -/** - * Codec is intra only. - */ -#define AV_CODEC_CAP_INTRA_ONLY 0x40000000 -/** - * Codec is lossless. - */ -#define AV_CODEC_CAP_LOSSLESS 0x80000000 - -/** - * Codec is backed by a hardware implementation. Typically used to - * identify a non-hwaccel hardware decoder. For information about hwaccels, use - * avcodec_get_hw_config() instead. + * Export motion vectors through frame side data */ -#define AV_CODEC_CAP_HARDWARE (1 << 18) - +#define AV_CODEC_EXPORT_DATA_MVS (1 << 0) /** - * Codec is potentially backed by a hardware implementation, but not - * necessarily. This is used instead of AV_CODEC_CAP_HARDWARE, if the - * implementation provides some sort of internal fallback. + * Export encoder Producer Reference Time through packet side data */ -#define AV_CODEC_CAP_HYBRID (1 << 19) - +#define AV_CODEC_EXPORT_DATA_PRFT (1 << 1) /** - * This codec takes the reordered_opaque field from input AVFrames - * and returns it in the corresponding field in AVCodecContext after - * encoding. + * Decoding only. + * Export the AVVideoEncParams structure through frame side data. */ -#define AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE (1 << 20) +#define AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS (1 << 2) /** * Pan Scan area. @@ -1171,384 +491,25 @@ typedef struct AVCPBProperties { } AVCPBProperties; /** - * The decoder will keep a reference to the frame and may reuse it later. - */ -#define AV_GET_BUFFER_FLAG_REF (1 << 0) - -/** - * @defgroup lavc_packet AVPacket - * - * Types and functions for working with AVPacket. - * @{ - */ -enum AVPacketSideDataType { - /** - * An AV_PKT_DATA_PALETTE side data packet contains exactly AVPALETTE_SIZE - * bytes worth of palette. This side data signals that a new palette is - * present. - */ - AV_PKT_DATA_PALETTE, - - /** - * The AV_PKT_DATA_NEW_EXTRADATA is used to notify the codec or the format - * that the extradata buffer was changed and the receiving side should - * act upon it appropriately. The new extradata is embedded in the side - * data buffer and should be immediately used for processing the current - * frame or packet. - */ - AV_PKT_DATA_NEW_EXTRADATA, - - /** - * An AV_PKT_DATA_PARAM_CHANGE side data packet is laid out as follows: - * @code - * u32le param_flags - * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT) - * s32le channel_count - * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT) - * u64le channel_layout - * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE) - * s32le sample_rate - * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS) - * s32le width - * s32le height - * @endcode - */ - AV_PKT_DATA_PARAM_CHANGE, - - /** - * An AV_PKT_DATA_H263_MB_INFO side data packet contains a number of - * structures with info about macroblocks relevant to splitting the - * packet into smaller packets on macroblock edges (e.g. as for RFC 2190). - * That is, it does not necessarily contain info about all macroblocks, - * as long as the distance between macroblocks in the info is smaller - * than the target payload size. - * Each MB info structure is 12 bytes, and is laid out as follows: - * @code - * u32le bit offset from the start of the packet - * u8 current quantizer at the start of the macroblock - * u8 GOB number - * u16le macroblock address within the GOB - * u8 horizontal MV predictor - * u8 vertical MV predictor - * u8 horizontal MV predictor for block number 3 - * u8 vertical MV predictor for block number 3 - * @endcode - */ - AV_PKT_DATA_H263_MB_INFO, - - /** - * This side data should be associated with an audio stream and contains - * ReplayGain information in form of the AVReplayGain struct. - */ - AV_PKT_DATA_REPLAYGAIN, - - /** - * This side data contains a 3x3 transformation matrix describing an affine - * transformation that needs to be applied to the decoded video frames for - * correct presentation. - * - * See libavutil/display.h for a detailed description of the data. - */ - AV_PKT_DATA_DISPLAYMATRIX, - - /** - * This side data should be associated with a video stream and contains - * Stereoscopic 3D information in form of the AVStereo3D struct. - */ - AV_PKT_DATA_STEREO3D, - - /** - * This side data should be associated with an audio stream and corresponds - * to enum AVAudioServiceType. - */ - AV_PKT_DATA_AUDIO_SERVICE_TYPE, - - /** - * This side data contains quality related information from the encoder. - * @code - * u32le quality factor of the compressed frame. Allowed range is between 1 (good) and FF_LAMBDA_MAX (bad). - * u8 picture type - * u8 error count - * u16 reserved - * u64le[error count] sum of squared differences between encoder in and output - * @endcode - */ - AV_PKT_DATA_QUALITY_STATS, - - /** - * This side data contains an integer value representing the stream index - * of a "fallback" track. A fallback track indicates an alternate - * track to use when the current track can not be decoded for some reason. - * e.g. no decoder available for codec. - */ - AV_PKT_DATA_FALLBACK_TRACK, - - /** - * This side data corresponds to the AVCPBProperties struct. - */ - AV_PKT_DATA_CPB_PROPERTIES, - - /** - * Recommmends skipping the specified number of samples - * @code - * u32le number of samples to skip from start of this packet - * u32le number of samples to skip from end of this packet - * u8 reason for start skip - * u8 reason for end skip (0=padding silence, 1=convergence) - * @endcode - */ - AV_PKT_DATA_SKIP_SAMPLES, - - /** - * An AV_PKT_DATA_JP_DUALMONO side data packet indicates that - * the packet may contain "dual mono" audio specific to Japanese DTV - * and if it is true, recommends only the selected channel to be used. - * @code - * u8 selected channels (0=mail/left, 1=sub/right, 2=both) - * @endcode - */ - AV_PKT_DATA_JP_DUALMONO, - - /** - * A list of zero terminated key/value strings. There is no end marker for - * the list, so it is required to rely on the side data size to stop. - */ - AV_PKT_DATA_STRINGS_METADATA, - - /** - * Subtitle event position - * @code - * u32le x1 - * u32le y1 - * u32le x2 - * u32le y2 - * @endcode - */ - AV_PKT_DATA_SUBTITLE_POSITION, - - /** - * Data found in BlockAdditional element of matroska container. There is - * no end marker for the data, so it is required to rely on the side data - * size to recognize the end. 8 byte id (as found in BlockAddId) followed - * by data. - */ - AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, - - /** - * The optional first identifier line of a WebVTT cue. - */ - AV_PKT_DATA_WEBVTT_IDENTIFIER, - - /** - * The optional settings (rendering instructions) that immediately - * follow the timestamp specifier of a WebVTT cue. - */ - AV_PKT_DATA_WEBVTT_SETTINGS, - - /** - * A list of zero terminated key/value strings. There is no end marker for - * the list, so it is required to rely on the side data size to stop. This - * side data includes updated metadata which appeared in the stream. - */ - AV_PKT_DATA_METADATA_UPDATE, - - /** - * MPEGTS stream ID as uint8_t, this is required to pass the stream ID - * information from the demuxer to the corresponding muxer. - */ - AV_PKT_DATA_MPEGTS_STREAM_ID, - - /** - * Mastering display metadata (based on SMPTE-2086:2014). This metadata - * should be associated with a video stream and contains data in the form - * of the AVMasteringDisplayMetadata struct. - */ - AV_PKT_DATA_MASTERING_DISPLAY_METADATA, - - /** - * This side data should be associated with a video stream and corresponds - * to the AVSphericalMapping structure. - */ - AV_PKT_DATA_SPHERICAL, - - /** - * Content light level (based on CTA-861.3). This metadata should be - * associated with a video stream and contains data in the form of the - * AVContentLightMetadata struct. - */ - AV_PKT_DATA_CONTENT_LIGHT_LEVEL, - - /** - * ATSC A53 Part 4 Closed Captions. This metadata should be associated with - * a video stream. A53 CC bitstream is stored as uint8_t in AVPacketSideData.data. - * The number of bytes of CC data is AVPacketSideData.size. - */ - AV_PKT_DATA_A53_CC, - - /** - * This side data is encryption initialization data. - * The format is not part of ABI, use av_encryption_init_info_* methods to - * access. - */ - AV_PKT_DATA_ENCRYPTION_INIT_INFO, - - /** - * This side data contains encryption info for how to decrypt the packet. - * The format is not part of ABI, use av_encryption_info_* methods to access. - */ - AV_PKT_DATA_ENCRYPTION_INFO, - - /** - * Active Format Description data consisting of a single byte as specified - * in ETSI TS 101 154 using AVActiveFormatDescription enum. - */ - AV_PKT_DATA_AFD, - - /** - * The number of side data types. - * This is not part of the public API/ABI in the sense that it may - * change when new side data types are added. - * This must stay the last enum value. - * If its value becomes huge, some code using it - * needs to be updated as it assumes it to be smaller than other limits. - */ - AV_PKT_DATA_NB -}; - -#define AV_PKT_DATA_QUALITY_FACTOR AV_PKT_DATA_QUALITY_STATS //DEPRECATED - -typedef struct AVPacketSideData { - uint8_t *data; - int size; - enum AVPacketSideDataType type; -} AVPacketSideData; - -/** - * 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 - * then passed to muxers. - * - * For video, it should typically contain one compressed frame. For audio it may - * contain several compressed frames. Encoders are allowed to output empty - * packets, with no compressed data, containing only side data - * (e.g. to update some stream parameters at the end of encoding). - * - * AVPacket is one of the few structs in FFmpeg, whose size is a part of public - * ABI. Thus it may be allocated on stack and no new fields can be added to it - * without libavcodec and libavformat major bump. - * - * The semantics of data ownership depends on the buf field. - * If it is set, the packet data is dynamically allocated and is - * valid indefinitely until a call to av_packet_unref() reduces the - * reference count to 0. - * - * If the buf field is not set av_packet_ref() would make a copy instead - * of increasing the reference count. - * - * The side data is always allocated with av_malloc(), copied by - * av_packet_ref() and freed by av_packet_unref(). - * - * @see av_packet_ref - * @see av_packet_unref + * This structure supplies correlation between a packet timestamp and a wall clock + * production time. The definition follows the Producer Reference Time ('prft') + * as defined in ISO/IEC 14496-12 */ -typedef struct AVPacket { - /** - * A reference to the reference-counted buffer where the packet data is - * stored. - * May be NULL, then the packet data is not reference-counted. - */ - AVBufferRef *buf; - /** - * Presentation timestamp in AVStream->time_base units; the time at which - * the decompressed packet will be presented to the user. - * Can be AV_NOPTS_VALUE if it is not stored in the file. - * pts MUST be larger or equal to dts as presentation cannot happen before - * decompression, unless one wants to view hex dumps. Some formats misuse - * the terms dts and pts/cts to mean something different. Such timestamps - * must be converted to true pts/dts before they are stored in AVPacket. - */ - int64_t pts; - /** - * Decompression timestamp in AVStream->time_base units; the time at which - * the packet is decompressed. - * Can be AV_NOPTS_VALUE if it is not stored in the file. - */ - int64_t dts; - uint8_t *data; - int size; - int stream_index; - /** - * A combination of AV_PKT_FLAG values - */ - int flags; - /** - * Additional packet data that can be provided by the container. - * Packet can contain several types of side information. - */ - AVPacketSideData *side_data; - int side_data_elems; - - /** - * Duration of this packet in AVStream->time_base units, 0 if unknown. - * Equals next_pts - this_pts in presentation order. - */ - int64_t duration; - - int64_t pos; ///< byte position in stream, -1 if unknown - -#if FF_API_CONVERGENCE_DURATION +typedef struct AVProducerReferenceTime { /** - * @deprecated Same as the duration field, but as int64_t. This was required - * for Matroska subtitles, whose duration values could overflow when the - * duration field was still an int. + * A UTC timestamp, in microseconds, since Unix epoch (e.g, av_gettime()). */ - attribute_deprecated - int64_t convergence_duration; -#endif -} AVPacket; -#define AV_PKT_FLAG_KEY 0x0001 ///< The packet contains a keyframe -#define AV_PKT_FLAG_CORRUPT 0x0002 ///< The packet content is corrupted -/** - * Flag is used to discard packets which are required to maintain valid - * decoder state but are not required for output and should be dropped - * after decoding. - **/ -#define AV_PKT_FLAG_DISCARD 0x0004 -/** - * The packet comes from a trusted source. - * - * Otherwise-unsafe constructs such as arbitrary pointers to data - * outside the packet may be followed. - */ -#define AV_PKT_FLAG_TRUSTED 0x0008 -/** - * Flag is used to indicate packets that contain frames that can - * be discarded by the decoder. I.e. Non-reference frames. - */ -#define AV_PKT_FLAG_DISPOSABLE 0x0010 - + int64_t wallclock; + int flags; +} AVProducerReferenceTime; -enum AVSideDataParamChangeFlags { - AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT = 0x0001, - AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT = 0x0002, - AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE = 0x0004, - AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS = 0x0008, -}; /** - * @} + * The decoder will keep a reference to the frame and may reuse it later. */ +#define AV_GET_BUFFER_FLAG_REF (1 << 0) struct AVCodecInternal; -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 -}; - /** * main external API structure. * New fields can be added to the end with minor version bumps. @@ -3010,6 +1971,9 @@ typedef struct AVCodecContext { #define FF_PROFILE_ARIB_PROFILE_A 0 #define FF_PROFILE_ARIB_PROFILE_C 1 +#define FF_PROFILE_KLVA_SYNC 0 +#define FF_PROFILE_KLVA_ASYNC 1 + /** * level * - encoding: Set by user. @@ -3370,6 +2334,24 @@ typedef struct AVCodecContext { * - encoding: unused */ int discard_damaged_percentage; + + /** + * The number of samples per frame to maximally accept. + * + * - decoding: set by user + * - encoding: set by user + */ + 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; } AVCodecContext; #if FF_API_CODEC_GET_SET @@ -3408,270 +2390,49 @@ attribute_deprecated void av_codec_set_chroma_intra_matrix(AVCodecContext *avctx, uint16_t *val); #endif +struct AVSubtitle; + +#if FF_API_CODEC_GET_SET +attribute_deprecated +int av_codec_get_max_lowres(const AVCodec *codec); +#endif + +struct MpegEncContext; + /** - * AVProfile. + * @defgroup lavc_hwaccel AVHWAccel + * + * @note Nothing in this structure should be accessed by the user. At some + * point in future it will not be externally visible at all. + * + * @{ */ -typedef struct AVProfile { - int profile; - const char *name; ///< short name for the profile -} AVProfile; - -enum { +typedef struct AVHWAccel { /** - * The codec supports this format via the hw_device_ctx interface. - * - * When selecting this format, AVCodecContext.hw_device_ctx should - * have been set to a device of the specified type before calling - * avcodec_open2(). + * Name of the hardware accelerated codec. + * The name is globally unique among encoders and among decoders (but an + * encoder and a decoder can share the same name). */ - AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX = 0x01, + const char *name; + /** - * The codec supports this format via the hw_frames_ctx interface. + * Type of codec implemented by the hardware accelerator. * - * When selecting this format for a decoder, - * AVCodecContext.hw_frames_ctx should be set to a suitable frames - * context inside the get_format() callback. The frames context - * must have been created on a device of the specified type. + * See AVMEDIA_TYPE_xxx */ - AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX = 0x02, + enum AVMediaType type; + /** - * The codec supports this format by some internal method. + * Codec implemented by the hardware accelerator. * - * This format can be selected without any additional configuration - - * no device or frames context is required. + * See AV_CODEC_ID_xxx */ - AV_CODEC_HW_CONFIG_METHOD_INTERNAL = 0x04, + enum AVCodecID id; + /** - * The codec supports this format by some ad-hoc method. + * Supported pixel format. * - * Additional settings and/or function calls are required. See the - * codec-specific documentation for details. (Methods requiring - * this sort of configuration are deprecated and others should be - * used in preference.) - */ - AV_CODEC_HW_CONFIG_METHOD_AD_HOC = 0x08, -}; - -typedef struct AVCodecHWConfig { - /** - * A hardware pixel format which the codec can use. - */ - enum AVPixelFormat pix_fmt; - /** - * Bit set of AV_CODEC_HW_CONFIG_METHOD_* flags, describing the possible - * setup methods which can be used with this configuration. - */ - int methods; - /** - * The device type associated with the configuration. - * - * Must be set for AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX and - * AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX, otherwise unused. - */ - enum AVHWDeviceType device_type; -} AVCodecHWConfig; - -typedef struct AVCodecDefault AVCodecDefault; - -struct AVSubtitle; - -/** - * AVCodec. - */ -typedef struct AVCodec { - /** - * Name of the codec implementation. - * The name is globally unique among encoders and among decoders (but an - * encoder and a decoder can share the same name). - * This is the primary way to find a codec from the user perspective. - */ - const char *name; - /** - * Descriptive name for the codec, meant to be more human readable than name. - * You should use the NULL_IF_CONFIG_SMALL() macro to define it. - */ - const char *long_name; - enum AVMediaType type; - enum AVCodecID id; - /** - * Codec capabilities. - * see AV_CODEC_CAP_* - */ - int capabilities; - const AVRational *supported_framerates; ///< array of supported framerates, or NULL if any, array is terminated by {0,0} - 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 - const uint64_t *channel_layouts; ///< array of support channel layouts, or NULL if unknown. array is terminated by 0 - uint8_t max_lowres; ///< maximum value for lowres supported by the decoder - 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} - - /** - * Group name of the codec implementation. - * This is a short symbolic name of the wrapper backing this codec. A - * wrapper uses some kind of external implementation for the codec, such - * as an external library, or a codec implementation provided by the OS or - * the hardware. - * If this field is NULL, this is a builtin, libavcodec native codec. - * If non-NULL, this will be the suffix in AVCodec.name in most cases - * (usually AVCodec.name will be of the form "_"). - */ - const char *wrapper_name; - - /***************************************************************** - * 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. - ***************************************************************** - */ - int priv_data_size; - struct AVCodec *next; - /** - * @name Frame-level threading support functions - * @{ - */ - /** - * If defined, called on thread contexts when they are created. - * If the codec allocates writable tables in init(), re-allocate them here. - * priv_data will be set to a copy of the original. - */ - int (*init_thread_copy)(AVCodecContext *); - /** - * Copy necessary context variables from a previous thread context to the current one. - * If not defined, the next thread will start automatically; otherwise, the codec - * must call ff_thread_finish_setup(). - * - * dst and src will (rarely) point to the same context, in which case memcpy should be skipped. - */ - int (*update_thread_context)(AVCodecContext *dst, const AVCodecContext *src); - /** @} */ - - /** - * Private codec-specific defaults. - */ - const AVCodecDefault *defaults; - - /** - * Initialize codec static data, called from avcodec_register(). - * - * This is not intended for time consuming operations as it is - * run for every codec regardless of that codec being used. - */ - void (*init_static_data)(struct AVCodec *codec); - - int (*init)(AVCodecContext *); - int (*encode_sub)(AVCodecContext *, uint8_t *buf, int buf_size, - const struct AVSubtitle *sub); - /** - * Encode data to an AVPacket. - * - * @param avctx codec context - * @param avpkt output AVPacket (may contain a user-provided buffer) - * @param[in] frame AVFrame containing the raw data to be encoded - * @param[out] got_packet_ptr encoder sets to 0 or 1 to indicate that a - * non-empty packet was returned in avpkt. - * @return 0 on success, negative error code on failure - */ - int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, - int *got_packet_ptr); - int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt); - int (*close)(AVCodecContext *); - /** - * Encode API with decoupled packet/frame dataflow. The API is the - * same as the avcodec_ prefixed APIs (avcodec_send_frame() etc.), except - * that: - * - never called if the codec is closed or the wrong type, - * - if AV_CODEC_CAP_DELAY is not set, drain frames are never sent, - * - only one drain frame is ever passed down, - */ - int (*send_frame)(AVCodecContext *avctx, const AVFrame *frame); - int (*receive_packet)(AVCodecContext *avctx, AVPacket *avpkt); - - /** - * Decode API with decoupled packet/frame dataflow. This function is called - * to get one output frame. It should call ff_decode_get_packet() to obtain - * input data. - */ - int (*receive_frame)(AVCodecContext *avctx, AVFrame *frame); - /** - * Flush buffers. - * Will be called when seeking - */ - void (*flush)(AVCodecContext *); - /** - * Internal codec capabilities. - * See FF_CODEC_CAP_* in internal.h - */ - int caps_internal; - - /** - * Decoding only, a comma-separated list of bitstream filters to apply to - * packets before decoding. - */ - const char *bsfs; - - /** - * Array of pointers to hardware configurations supported by the codec, - * or NULL if no hardware supported. The array is terminated by a NULL - * pointer. - * - * The user can only access this field via avcodec_get_hw_config(). - */ - const struct AVCodecHWConfigInternal **hw_configs; -} AVCodec; - -#if FF_API_CODEC_GET_SET -attribute_deprecated -int av_codec_get_max_lowres(const AVCodec *codec); -#endif - -struct MpegEncContext; - -/** - * Retrieve supported hardware configurations for a codec. - * - * Values of index from zero to some maximum return the indexed configuration - * descriptor; all other values return NULL. If the codec does not support - * any hardware configurations then it will always return NULL. - */ -const AVCodecHWConfig *avcodec_get_hw_config(const AVCodec *codec, int index); - -/** - * @defgroup lavc_hwaccel AVHWAccel - * - * @note Nothing in this structure should be accessed by the user. At some - * point in future it will not be externally visible at all. - * - * @{ - */ -typedef struct AVHWAccel { - /** - * Name of the hardware accelerated codec. - * The name is globally unique among encoders and among decoders (but an - * encoder and a decoder can share the same name). - */ - const char *name; - - /** - * Type of codec implemented by the hardware accelerator. - * - * See AVMEDIA_TYPE_xxx - */ - enum AVMediaType type; - - /** - * Codec implemented by the hardware accelerator. - * - * See AV_CODEC_ID_xxx - */ - enum AVCodecID id; - - /** - * Supported pixel format. - * - * Only hardware accelerated formats are supported here. + * Only hardware accelerated formats are supported here. */ enum AVPixelFormat pix_fmt; @@ -3939,175 +2700,6 @@ typedef struct AVSubtitle { int64_t pts; ///< Same as packet pts, in AV_TIME_BASE } AVSubtitle; -/** - * This struct describes the properties of an encoded stream. - * - * sizeof(AVCodecParameters) is not a part of the public ABI, this struct must - * be allocated with avcodec_parameters_alloc() and freed with - * avcodec_parameters_free(). - */ -typedef struct AVCodecParameters { - /** - * General type of the encoded data. - */ - enum AVMediaType codec_type; - /** - * Specific type of the encoded data (the codec used). - */ - enum AVCodecID codec_id; - /** - * Additional information about the codec (corresponds to the AVI FOURCC). - */ - uint32_t codec_tag; - - /** - * Extra binary data needed for initializing the decoder, codec-dependent. - * - * Must be allocated with av_malloc() and will be freed by - * avcodec_parameters_free(). The allocated size of extradata must be at - * least extradata_size + AV_INPUT_BUFFER_PADDING_SIZE, with the padding - * bytes zeroed. - */ - uint8_t *extradata; - /** - * Size of the extradata content in bytes. - */ - int extradata_size; - - /** - * - video: the pixel format, the value corresponds to enum AVPixelFormat. - * - audio: the sample format, the value corresponds to enum AVSampleFormat. - */ - int format; - - /** - * The average bitrate of the encoded data (in bits per second). - */ - int64_t bit_rate; - - /** - * The number of bits per sample in the codedwords. - * - * This is basically the bitrate per sample. It is mandatory for a bunch of - * formats to actually decode them. It's the number of bits for one sample in - * the actual coded bitstream. - * - * This could be for example 4 for ADPCM - * For PCM formats this matches bits_per_raw_sample - * Can be 0 - */ - int bits_per_coded_sample; - - /** - * This is the number of valid bits in each output sample. If the - * sample format has more bits, the least significant bits are additional - * padding bits, which are always 0. Use right shifts to reduce the sample - * to its actual size. For example, audio formats with 24 bit samples will - * have bits_per_raw_sample set to 24, and format set to AV_SAMPLE_FMT_S32. - * To get the original sample use "(int32_t)sample >> 8"." - * - * For ADPCM this might be 12 or 16 or similar - * Can be 0 - */ - int bits_per_raw_sample; - - /** - * Codec-specific bitstream restrictions that the stream conforms to. - */ - int profile; - int level; - - /** - * Video only. The dimensions of the video frame in pixels. - */ - int width; - int height; - - /** - * Video only. The aspect ratio (width / height) which a single pixel - * should have when displayed. - * - * When the aspect ratio is unknown / undefined, the numerator should be - * set to 0 (the denominator may have any value). - */ - AVRational sample_aspect_ratio; - - /** - * Video only. The order of the fields in interlaced video. - */ - enum AVFieldOrder field_order; - - /** - * Video only. Additional colorspace characteristics. - */ - enum AVColorRange color_range; - enum AVColorPrimaries color_primaries; - enum AVColorTransferCharacteristic color_trc; - enum AVColorSpace color_space; - enum AVChromaLocation chroma_location; - - /** - * Video only. Number of delayed frames. - */ - int video_delay; - - /** - * 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. - */ - uint64_t channel_layout; - /** - * Audio only. The number of audio channels. - */ - int channels; - /** - * Audio only. The number of audio samples per second. - */ - int sample_rate; - /** - * Audio only. The number of bytes per coded audio frame, required by some - * formats. - * - * Corresponds to nBlockAlign in WAVEFORMATEX. - */ - int block_align; - /** - * Audio only. Audio frame size, if known. Required by some formats to be static. - */ - int frame_size; - - /** - * Audio only. The amount of padding (in samples) 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. - */ - 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. - */ - int trailing_padding; - /** - * Audio only. Number of samples to skip after a discontinuity. - */ - int seek_preroll; -} AVCodecParameters; - -/** - * Iterate over all registered codecs. - * - * @param opaque a pointer where libavcodec will store the iteration state. Must - * point to NULL to start the iteration. - * - * @return the next registered codec or NULL when the iteration is - * finished - */ -const AVCodec *av_codec_iterate(void **opaque); - #if FF_API_NEXT /** * If c is NULL, returns the first registered codec, @@ -4215,439 +2807,108 @@ const AVClass *avcodec_get_frame_class(void); const AVClass *avcodec_get_subtitle_rect_class(void); /** - * Copy the settings of the source AVCodecContext into the destination - * AVCodecContext. The resulting destination codec context will be - * unopened, i.e. you are required to call avcodec_open2() before you - * can use this AVCodecContext to decode/encode video/audio data. - * - * @param dest target codec context, should be initialized with - * avcodec_alloc_context3(NULL), but otherwise uninitialized - * @param src source codec context - * @return AVERROR() on error (e.g. memory allocation error), 0 on success - * - * @deprecated The semantics of this function are ill-defined and it should not - * be used. If you need to transfer the stream parameters from one codec context - * to another, use an intermediate AVCodecParameters instance and the - * avcodec_parameters_from_context() / avcodec_parameters_to_context() - * functions. - */ -attribute_deprecated -int avcodec_copy_context(AVCodecContext *dest, const AVCodecContext *src); -#endif - -/** - * Allocate a new AVCodecParameters and set its fields to default values - * (unknown/invalid/0). The returned struct must be freed with - * avcodec_parameters_free(). - */ -AVCodecParameters *avcodec_parameters_alloc(void); - -/** - * Free an AVCodecParameters instance and everything associated with it and - * write NULL to the supplied pointer. - */ -void avcodec_parameters_free(AVCodecParameters **par); - -/** - * Copy the contents of src to dst. Any allocated fields in dst are freed and - * replaced with newly allocated duplicates of the corresponding fields in src. - * - * @return >= 0 on success, a negative AVERROR code on failure. - */ -int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src); - -/** - * Fill the parameters struct based on the values from the supplied codec - * context. Any allocated fields in par are freed and replaced with duplicates - * of the corresponding fields in codec. - * - * @return >= 0 on success, a negative AVERROR code on failure - */ -int avcodec_parameters_from_context(AVCodecParameters *par, - const AVCodecContext *codec); - -/** - * Fill the codec context based on the values from the supplied codec - * parameters. Any allocated fields in codec that have a corresponding field in - * par are freed and replaced with duplicates of the corresponding field in par. - * Fields in codec that do not have a counterpart in par are not touched. - * - * @return >= 0 on success, a negative AVERROR code on failure. - */ -int avcodec_parameters_to_context(AVCodecContext *codec, - const AVCodecParameters *par); - -/** - * Initialize the AVCodecContext to use the given AVCodec. Prior to using this - * function the context has to be allocated with avcodec_alloc_context3(). - * - * The functions avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(), - * avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for - * retrieving a codec. - * - * @warning This function is not thread safe! - * - * @note Always call this function before using decoding routines (such as - * @ref avcodec_receive_frame()). - * - * @code - * avcodec_register_all(); - * av_dict_set(&opts, "b", "2.5M", 0); - * codec = avcodec_find_decoder(AV_CODEC_ID_H264); - * if (!codec) - * exit(1); - * - * context = avcodec_alloc_context3(codec); - * - * if (avcodec_open2(context, codec, opts) < 0) - * exit(1); - * @endcode - * - * @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. - * - * @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(). - */ -int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); - -/** - * Close a given AVCodecContext and free all the data associated with it - * (but not the AVCodecContext itself). - * - * Calling this function on an AVCodecContext that hasn't been opened will free - * 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 - * codec context (either open or closed). Opening and closing a codec context - * multiple times is not supported anymore -- use multiple codec contexts - * instead. - */ -int avcodec_close(AVCodecContext *avctx); - -/** - * Free all allocated data in the given subtitle struct. - * - * @param sub AVSubtitle to free. - */ -void avsubtitle_free(AVSubtitle *sub); - -/** - * @} - */ - -/** - * @addtogroup lavc_packet - * @{ - */ - -/** - * Allocate an AVPacket and set its fields to default values. The resulting - * struct must be freed using av_packet_free(). - * - * @return An AVPacket filled with default values or NULL on failure. - * - * @note this only allocates the AVPacket itself, not the data buffers. Those - * must be allocated through other means such as av_new_packet. - * - * @see av_new_packet - */ -AVPacket *av_packet_alloc(void); - -/** - * Create a new packet that references the same data as src. - * - * This is a shortcut for av_packet_alloc()+av_packet_ref(). - * - * @return newly created AVPacket on success, NULL on error. - * - * @see av_packet_alloc - * @see av_packet_ref - */ -AVPacket *av_packet_clone(const AVPacket *src); - -/** - * Free the packet, if the packet is reference counted, it will be - * unreferenced first. - * - * @param pkt packet to be freed. The pointer will be set to NULL. - * @note passing NULL is a no-op. - */ -void av_packet_free(AVPacket **pkt); - -/** - * Initialize optional fields of a packet with default values. - * - * Note, this does not touch the data and size members, which have to be - * initialized separately. - * - * @param pkt packet - */ -void av_init_packet(AVPacket *pkt); - -/** - * Allocate the payload of a packet and initialize its fields with - * default values. - * - * @param pkt packet - * @param size wanted payload size - * @return 0 if OK, AVERROR_xxx otherwise - */ -int av_new_packet(AVPacket *pkt, int size); - -/** - * Reduce packet size, correctly zeroing padding - * - * @param pkt packet - * @param size new size - */ -void av_shrink_packet(AVPacket *pkt, int size); - -/** - * Increase packet size, correctly zeroing padding - * - * @param pkt packet - * @param grow_by number of bytes by which to increase the size of the packet - */ -int av_grow_packet(AVPacket *pkt, int grow_by); - -/** - * Initialize a reference-counted packet from av_malloc()ed data. - * - * @param pkt packet to be initialized. This function will set the data, size, - * and buf fields, all others are left untouched. - * @param data Data allocated by av_malloc() to be used as packet data. If this - * function returns successfully, the data is owned by the underlying AVBuffer. - * The caller may not access the data through other means. - * @param size size of data in bytes, without the padding. I.e. the full buffer - * size is assumed to be size + AV_INPUT_BUFFER_PADDING_SIZE. - * - * @return 0 on success, a negative AVERROR on error - */ -int av_packet_from_data(AVPacket *pkt, uint8_t *data, int size); - -#if FF_API_AVPACKET_OLD_API -/** - * @warning This is a hack - the packet memory allocation stuff is broken. The - * packet is allocated if it was not really allocated. - * - * @deprecated Use av_packet_ref or av_packet_make_refcounted - */ -attribute_deprecated -int av_dup_packet(AVPacket *pkt); -/** - * Copy packet, including contents - * - * @return 0 on success, negative AVERROR on fail - * - * @deprecated Use av_packet_ref - */ -attribute_deprecated -int av_copy_packet(AVPacket *dst, const AVPacket *src); - -/** - * Copy packet side data - * - * @return 0 on success, negative AVERROR on fail - * - * @deprecated Use av_packet_copy_props - */ -attribute_deprecated -int av_copy_packet_side_data(AVPacket *dst, const AVPacket *src); - -/** - * Free a packet. - * - * @deprecated Use av_packet_unref - * - * @param pkt packet to free - */ -attribute_deprecated -void av_free_packet(AVPacket *pkt); -#endif -/** - * Allocate new information of a packet. - * - * @param pkt packet - * @param type side information type - * @param size side information size - * @return pointer to fresh allocated data or NULL otherwise - */ -uint8_t* av_packet_new_side_data(AVPacket *pkt, enum AVPacketSideDataType type, - int size); - -/** - * Wrap an existing array as a packet side data. - * - * @param pkt packet - * @param type side information type - * @param data the side data array. It must be allocated with the av_malloc() - * family of functions. The ownership of the data is transferred to - * pkt. - * @param size side information size - * @return a non-negative number on success, a negative AVERROR code on - * failure. On failure, the packet is unchanged and the data remains - * owned by the caller. - */ -int av_packet_add_side_data(AVPacket *pkt, enum AVPacketSideDataType type, - uint8_t *data, size_t size); - -/** - * Shrink the already allocated side data buffer - * - * @param pkt packet - * @param type side information type - * @param size new side information size - * @return 0 on success, < 0 on failure - */ -int av_packet_shrink_side_data(AVPacket *pkt, enum AVPacketSideDataType type, - int size); - -/** - * Get side information from packet. - * - * @param pkt packet - * @param type desired side information type - * @param size pointer for side information size to store (optional) - * @return pointer to data if present or NULL otherwise - */ -uint8_t* av_packet_get_side_data(const AVPacket *pkt, enum AVPacketSideDataType type, - int *size); - -#if FF_API_MERGE_SD_API -attribute_deprecated -int av_packet_merge_side_data(AVPacket *pkt); - -attribute_deprecated -int av_packet_split_side_data(AVPacket *pkt); -#endif - -const char *av_packet_side_data_name(enum AVPacketSideDataType type); - -/** - * Pack a dictionary for use in side_data. - * - * @param dict The dictionary to pack. - * @param size pointer to store the size of the returned data - * @return pointer to data if successful, NULL otherwise - */ -uint8_t *av_packet_pack_dictionary(AVDictionary *dict, int *size); -/** - * Unpack a dictionary from side_data. - * - * @param data data from side_data - * @param size size of the data - * @param dict the metadata storage dictionary - * @return 0 on success, < 0 on failure - */ -int av_packet_unpack_dictionary(const uint8_t *data, int size, AVDictionary **dict); - - -/** - * Convenience function to free all the side data stored. - * All the other fields stay untouched. - * - * @param pkt packet - */ -void av_packet_free_side_data(AVPacket *pkt); - -/** - * Setup a new reference to the data described by a given packet - * - * If src is reference-counted, setup dst as a new reference to the - * buffer in src. Otherwise allocate a new buffer in dst and copy the - * data from src into it. - * - * All the other fields are copied from src. - * - * @see av_packet_unref + * Copy the settings of the source AVCodecContext into the destination + * AVCodecContext. The resulting destination codec context will be + * unopened, i.e. you are required to call avcodec_open2() before you + * can use this AVCodecContext to decode/encode video/audio data. * - * @param dst Destination packet - * @param src Source packet + * @param dest target codec context, should be initialized with + * avcodec_alloc_context3(NULL), but otherwise uninitialized + * @param src source codec context + * @return AVERROR() on error (e.g. memory allocation error), 0 on success * - * @return 0 on success, a negative AVERROR on error. + * @deprecated The semantics of this function are ill-defined and it should not + * be used. If you need to transfer the stream parameters from one codec context + * to another, use an intermediate AVCodecParameters instance and the + * avcodec_parameters_from_context() / avcodec_parameters_to_context() + * functions. */ -int av_packet_ref(AVPacket *dst, const AVPacket *src); +attribute_deprecated +int avcodec_copy_context(AVCodecContext *dest, const AVCodecContext *src); +#endif /** - * Wipe the packet. - * - * Unreference the buffer referenced by the packet and reset the - * remaining packet fields to their default values. + * Fill the parameters struct based on the values from the supplied codec + * context. Any allocated fields in par are freed and replaced with duplicates + * of the corresponding fields in codec. * - * @param pkt The packet to be unreferenced. + * @return >= 0 on success, a negative AVERROR code on failure */ -void av_packet_unref(AVPacket *pkt); +int avcodec_parameters_from_context(AVCodecParameters *par, + const AVCodecContext *codec); /** - * Move every field in src to dst and reset src. - * - * @see av_packet_unref + * Fill the codec context based on the values from the supplied codec + * parameters. Any allocated fields in codec that have a corresponding field in + * par are freed and replaced with duplicates of the corresponding field in par. + * Fields in codec that do not have a counterpart in par are not touched. * - * @param src Source packet, will be reset - * @param dst Destination packet + * @return >= 0 on success, a negative AVERROR code on failure. */ -void av_packet_move_ref(AVPacket *dst, AVPacket *src); +int avcodec_parameters_to_context(AVCodecContext *codec, + const AVCodecParameters *par); /** - * Copy only "properties" fields from src to dst. + * Initialize the AVCodecContext to use the given AVCodec. Prior to using this + * function the context has to be allocated with avcodec_alloc_context3(). + * + * The functions avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(), + * avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for + * retrieving a codec. * - * Properties for the purpose of this function are all the fields - * beside those related to the packet data (buf, data, size) + * @warning This function is not thread safe! * - * @param dst Destination packet - * @param src Source packet + * @note Always call this function before using decoding routines (such as + * @ref avcodec_receive_frame()). * - * @return 0 on success AVERROR on failure. - */ -int av_packet_copy_props(AVPacket *dst, const AVPacket *src); - -/** - * Ensure the data described by a given packet is reference counted. + * @code + * avcodec_register_all(); + * av_dict_set(&opts, "b", "2.5M", 0); + * codec = avcodec_find_decoder(AV_CODEC_ID_H264); + * if (!codec) + * exit(1); * - * @note This function does not ensure that the reference will be writable. - * Use av_packet_make_writable instead for that purpose. + * context = avcodec_alloc_context3(codec); * - * @see av_packet_ref - * @see av_packet_make_writable + * if (avcodec_open2(context, codec, opts) < 0) + * exit(1); + * @endcode * - * @param pkt packet whose data should be made reference counted. + * @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. * - * @return 0 on success, a negative AVERROR on error. On failure, the - * packet is unchanged. + * @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(). */ -int av_packet_make_refcounted(AVPacket *pkt); +int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); /** - * Create a writable reference for the data described by a given packet, - * avoiding data copy if possible. + * Close a given AVCodecContext and free all the data associated with it + * (but not the AVCodecContext itself). * - * @param pkt Packet whose data should be made writable. + * Calling this function on an AVCodecContext that hasn't been opened will free + * the codec-specific data allocated in avcodec_alloc_context3() with a non-NULL + * codec. Subsequent calls will do nothing. * - * @return 0 on success, a negative AVERROR on failure. On failure, the - * packet is unchanged. + * @note 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. */ -int av_packet_make_writable(AVPacket *pkt); +int avcodec_close(AVCodecContext *avctx); /** - * Convert valid timing fields (timestamps / durations) in a packet from one - * timebase to another. Timestamps with unknown values (AV_NOPTS_VALUE) will be - * ignored. + * Free all allocated data in the given subtitle struct. * - * @param pkt packet on which the conversion will be performed - * @param tb_src source timebase, in which the timing fields in pkt are - * expressed - * @param tb_dst destination timebase, to which the timing fields will be - * converted + * @param sub AVSubtitle to free. */ -void av_packet_rescale_ts(AVPacket *pkt, AVRational tb_src, AVRational tb_dst); +void avsubtitle_free(AVSubtitle *sub); /** * @} @@ -4658,22 +2919,6 @@ void av_packet_rescale_ts(AVPacket *pkt, AVRational tb_src, AVRational tb_dst); * @{ */ -/** - * Find a registered decoder with a matching codec ID. - * - * @param id AVCodecID of the requested decoder - * @return A decoder if one was found, NULL otherwise. - */ -AVCodec *avcodec_find_decoder(enum AVCodecID id); - -/** - * Find a registered decoder with the specified name. - * - * @param name name of the requested decoder - * @return A decoder if one was found, NULL otherwise. - */ -AVCodec *avcodec_find_decoder_by_name(const char *name); - /** * The default callback for AVCodecContext.get_buffer2(). It is made public so * it can be called by custom get_buffer2() implementations for decoders without @@ -4835,7 +3080,7 @@ int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture, * If no subtitle could be decompressed, got_sub_ptr is zero. * Otherwise, the subtitle is stored in *sub. * Note that AV_CODEC_CAP_DR1 is not available for subtitle codecs. This is for - * simplicity, because the performance difference is expect to be negligible + * simplicity, because the performance difference is expected to be negligible * and reusing a get_buffer written for video codecs would probably perform badly * due to a potentially very different allocation pattern. * @@ -4851,7 +3096,7 @@ int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture, * before packets may be fed to the decoder. * * @param avctx the codec context - * @param[out] sub The Preallocated AVSubtitle in which the decoded subtitle will be stored, + * @param[out] sub The preallocated AVSubtitle in which the decoded subtitle will be stored, * must be freed with avsubtitle_free if *got_sub_ptr is set. * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed, otherwise, it is nonzero. * @param[in] avpkt The input AVPacket containing the input buffer. @@ -4968,7 +3213,7 @@ int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame); * AVERROR(EINVAL): codec not opened, refcounted_frames not set, it is a * decoder, or requires flush * AVERROR(ENOMEM): failed to add packet to internal queue, or similar - * other errors: legitimate decoding errors + * other errors: legitimate encoding errors */ int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame); @@ -4978,14 +3223,14 @@ int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame); * @param avctx codec context * @param avpkt This will be set to a reference-counted packet allocated by the * encoder. Note that the function will always call - * av_frame_unref(frame) before doing anything else. + * av_packet_unref(avpkt) before doing anything else. * @return 0 on success, otherwise negative error code: * AVERROR(EAGAIN): output is not available in the current state - user * must try to send input * AVERROR_EOF: the encoder has been fully flushed, and there will be * no more output packets - * AVERROR(EINVAL): codec not opened, or it is an encoder - * other errors: legitimate decoding errors + * AVERROR(EINVAL): codec not opened, or it is a decoder + * other errors: legitimate encoding errors */ int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt); @@ -5363,22 +3608,6 @@ void av_parser_close(AVCodecParserContext *s); * @{ */ -/** - * Find a registered encoder with a matching codec ID. - * - * @param id AVCodecID of the requested encoder - * @return An encoder if one was found, NULL otherwise. - */ -AVCodec *avcodec_find_encoder(enum AVCodecID id); - -/** - * Find a registered encoder with the specified name. - * - * @param name name of the requested encoder - * @return An encoder if one was found, NULL otherwise. - */ -AVCodec *avcodec_find_encoder_by_name(const char *name); - /** * Encode a frame of audio. * @@ -5682,13 +3911,21 @@ int avcodec_fill_audio_frame(AVFrame *frame, int nb_channels, int buf_size, int align); /** - * Reset the internal decoder state / flush internal buffers. Should be called + * Reset the internal codec state / flush internal buffers. Should be called * e.g. when seeking or when switching to a different stream. * - * @note when refcounted frames are not used (i.e. avctx->refcounted_frames is 0), - * this invalidates the frames previously returned from the decoder. When - * refcounted frames are used, the decoder just releases any references it might - * keep internally, but the caller's reference remains valid. + * @note for decoders, when refcounted frames are not used + * (i.e. avctx->refcounted_frames is 0), this invalidates the frames previously + * returned from the decoder. When refcounted frames are used, the decoder just + * releases any references it might keep internally, but the caller's reference + * remains valid. + * + * @note for encoders, this function will only do something if the encoder + * declares support for AV_CODEC_CAP_ENCODER_FLUSH. When called, the encoder + * will drain any remaining packets, and can then be re-used for a different + * stream (as opposed to sending a null frame which will leave the encoder + * in a permanent EOF state after draining). This can be desirable if the + * cost of tearing down and replacing the encoder instance is high. */ void avcodec_flush_buffers(AVCodecContext *avctx); @@ -5746,106 +3983,7 @@ typedef struct AVBitStreamFilterContext { */ char *args; } AVBitStreamFilterContext; -#endif - -typedef struct AVBSFInternal AVBSFInternal; - -/** - * The bitstream filter state. - * - * This struct must be allocated with av_bsf_alloc() and freed with - * av_bsf_free(). - * - * The fields in the struct will only be changed (by the caller or by the - * filter) as described in their documentation, and are to be considered - * immutable otherwise. - */ -typedef struct AVBSFContext { - /** - * A class for logging and AVOptions - */ - const AVClass *av_class; - - /** - * The bitstream filter this context is an instance of. - */ - const struct AVBitStreamFilter *filter; - - /** - * Opaque libavcodec internal data. Must not be touched by the caller in any - * way. - */ - AVBSFInternal *internal; - - /** - * Opaque filter-specific private data. If filter->priv_class is non-NULL, - * this is an AVOptions-enabled struct. - */ - void *priv_data; - - /** - * Parameters of the input stream. This field is allocated in - * av_bsf_alloc(), it needs to be filled by the caller before - * av_bsf_init(). - */ - AVCodecParameters *par_in; - - /** - * Parameters of the output stream. This field is allocated in - * av_bsf_alloc(), it is set by the filter in av_bsf_init(). - */ - AVCodecParameters *par_out; - /** - * The timebase used for the timestamps of the input packets. Set by the - * caller before av_bsf_init(). - */ - AVRational time_base_in; - - /** - * The timebase used for the timestamps of the output packets. Set by the - * filter in av_bsf_init(). - */ - AVRational time_base_out; -} AVBSFContext; - -typedef struct AVBitStreamFilter { - const char *name; - - /** - * A list of codec ids supported by the filter, terminated by - * AV_CODEC_ID_NONE. - * May be NULL, in that case the bitstream filter works with any codec id. - */ - const enum AVCodecID *codec_ids; - - /** - * A class for the private data, used to declare bitstream filter private - * AVOptions. This field is NULL for bitstream filters that do not declare - * any options. - * - * If this field is non-NULL, the first member of the filter private data - * must be a pointer to AVClass, which will be set by libavcodec generic - * code to this class. - */ - const AVClass *priv_class; - - /***************************************************************** - * 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. - ***************************************************************** - */ - - int priv_data_size; - int (*init)(AVBSFContext *ctx); - int (*filter)(AVBSFContext *ctx, AVPacket *pkt); - void (*close)(AVBSFContext *ctx); - void (*flush)(AVBSFContext *ctx); -} AVBitStreamFilter; - -#if FF_API_OLD_BSF /** * @deprecated the old bitstream filtering API (using AVBitStreamFilterContext) * is deprecated. Use the new bitstream filtering API (using AVBSFContext). @@ -5885,194 +4023,11 @@ attribute_deprecated const AVBitStreamFilter *av_bitstream_filter_next(const AVBitStreamFilter *f); #endif -/** - * @return a bitstream filter with the specified name or NULL if no such - * bitstream filter exists. - */ -const AVBitStreamFilter *av_bsf_get_by_name(const char *name); - -/** - * Iterate over all registered bitstream filters. - * - * @param opaque a pointer where libavcodec will store the iteration state. Must - * point to NULL to start the iteration. - * - * @return the next registered bitstream filter or NULL when the iteration is - * finished - */ -const AVBitStreamFilter *av_bsf_iterate(void **opaque); #if FF_API_NEXT attribute_deprecated const AVBitStreamFilter *av_bsf_next(void **opaque); #endif -/** - * Allocate a context for a given bitstream filter. The caller must fill in the - * context parameters as described in the documentation and then call - * av_bsf_init() before sending any data to the filter. - * - * @param filter the filter for which to allocate an instance. - * @param ctx a pointer into which the pointer to the newly-allocated context - * will be written. It must be freed with av_bsf_free() after the - * filtering is done. - * - * @return 0 on success, a negative AVERROR code on failure - */ -int av_bsf_alloc(const AVBitStreamFilter *filter, AVBSFContext **ctx); - -/** - * Prepare the filter for use, after all the parameters and options have been - * set. - */ -int av_bsf_init(AVBSFContext *ctx); - -/** - * Submit a packet for filtering. - * - * After sending each packet, the filter must be completely drained by calling - * av_bsf_receive_packet() repeatedly until it returns AVERROR(EAGAIN) or - * AVERROR_EOF. - * - * @param pkt the packet to filter. The bitstream filter will take ownership of - * the packet and reset the contents of pkt. pkt is not touched if an error occurs. - * This parameter may be NULL, which signals the end of the stream (i.e. no more - * packets will be sent). That will cause the filter to output any packets it - * may have buffered internally. - * - * @return 0 on success, a negative AVERROR on error. - */ -int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt); - -/** - * Retrieve a filtered packet. - * - * @param[out] pkt this struct will be filled with the contents of the filtered - * packet. It is owned by the caller and must be freed using - * av_packet_unref() when it is no longer needed. - * This parameter should be "clean" (i.e. freshly allocated - * with av_packet_alloc() or unreffed with av_packet_unref()) - * when this function is called. If this function returns - * successfully, the contents of pkt will be completely - * overwritten by the returned data. On failure, pkt is not - * touched. - * - * @return 0 on success. AVERROR(EAGAIN) if more packets need to be sent to the - * filter (using av_bsf_send_packet()) to get more output. AVERROR_EOF if there - * will be no further output from the filter. Another negative AVERROR value if - * an error occurs. - * - * @note one input packet may result in several output packets, so after sending - * a packet with av_bsf_send_packet(), this function needs to be called - * repeatedly until it stops returning 0. It is also possible for a filter to - * output fewer packets than were sent to it, so this function may return - * AVERROR(EAGAIN) immediately after a successful av_bsf_send_packet() call. - */ -int av_bsf_receive_packet(AVBSFContext *ctx, AVPacket *pkt); - -/** - * Reset the internal bitstream filter state / flush internal buffers. - */ -void av_bsf_flush(AVBSFContext *ctx); - -/** - * Free a bitstream filter context and everything associated with it; write NULL - * into the supplied pointer. - */ -void av_bsf_free(AVBSFContext **ctx); - -/** - * Get the AVClass for AVBSFContext. It can be used in combination with - * AV_OPT_SEARCH_FAKE_OBJ for examining options. - * - * @see av_opt_find(). - */ -const AVClass *av_bsf_get_class(void); - -/** - * Structure for chain/list of bitstream filters. - * Empty list can be allocated by av_bsf_list_alloc(). - */ -typedef struct AVBSFList AVBSFList; - -/** - * Allocate empty list of bitstream filters. - * The list must be later freed by av_bsf_list_free() - * or finalized by av_bsf_list_finalize(). - * - * @return Pointer to @ref AVBSFList on success, NULL in case of failure - */ -AVBSFList *av_bsf_list_alloc(void); - -/** - * Free list of bitstream filters. - * - * @param lst Pointer to pointer returned by av_bsf_list_alloc() - */ -void av_bsf_list_free(AVBSFList **lst); - -/** - * Append bitstream filter to the list of bitstream filters. - * - * @param lst List to append to - * @param bsf Filter context to be appended - * - * @return >=0 on success, negative AVERROR in case of failure - */ -int av_bsf_list_append(AVBSFList *lst, AVBSFContext *bsf); - -/** - * Construct new bitstream filter context given it's name and options - * and append it to the list of bitstream filters. - * - * @param lst List to append to - * @param bsf_name Name of the bitstream filter - * @param options Options for the bitstream filter, can be set to NULL - * - * @return >=0 on success, negative AVERROR in case of failure - */ -int av_bsf_list_append2(AVBSFList *lst, const char * bsf_name, AVDictionary **options); -/** - * Finalize list of bitstream filters. - * - * This function will transform @ref AVBSFList to single @ref AVBSFContext, - * so the whole chain of bitstream filters can be treated as single filter - * freshly allocated by av_bsf_alloc(). - * If the call is successful, @ref AVBSFList structure is freed and lst - * will be set to NULL. In case of failure, caller is responsible for - * freeing the structure by av_bsf_list_free() - * - * @param lst Filter list structure to be transformed - * @param[out] bsf Pointer to be set to newly created @ref AVBSFContext structure - * representing the chain of bitstream filters - * - * @return >=0 on success, negative AVERROR in case of failure - */ -int av_bsf_list_finalize(AVBSFList **lst, AVBSFContext **bsf); - -/** - * Parse string describing list of bitstream filters and create single - * @ref AVBSFContext describing the whole chain of bitstream filters. - * Resulting @ref AVBSFContext can be treated as any other @ref AVBSFContext freshly - * allocated by av_bsf_alloc(). - * - * @param str String describing chain of bitstream filters in format - * `bsf1[=opt1=val1:opt2=val2][,bsf2]` - * @param[out] bsf Pointer to be set to newly created @ref AVBSFContext structure - * representing the chain of bitstream filters - * - * @return >=0 on success, negative AVERROR in case of failure - */ -int av_bsf_list_parse_str(const char *str, AVBSFContext **bsf); - -/** - * Get null/pass-through bitstream filter. - * - * @param[out] bsf Pointer to be set to new instance of pass-through bitstream filter - * - * @return - */ -int av_bsf_get_null_filter(AVBSFContext **bsf); - /* memory */ /** @@ -6163,53 +4118,12 @@ attribute_deprecated int av_lockmgr_register(int (*cb)(void **mutex, enum AVLockOp op)); #endif -/** - * Get the type of the given codec. - */ -enum AVMediaType avcodec_get_type(enum AVCodecID codec_id); - -/** - * Get the name of a codec. - * @return a static string identifying the codec; never NULL - */ -const char *avcodec_get_name(enum AVCodecID id); - /** * @return a positive value if s is open (i.e. avcodec_open2() was called on it * with no corresponding avcodec_close()), 0 otherwise. */ int avcodec_is_open(AVCodecContext *s); -/** - * @return a non-zero number if codec is an encoder, zero otherwise - */ -int av_codec_is_encoder(const AVCodec *codec); - -/** - * @return a non-zero number if codec is a decoder, zero otherwise - */ -int av_codec_is_decoder(const AVCodec *codec); - -/** - * @return descriptor for given codec ID or NULL if no descriptor exists. - */ -const AVCodecDescriptor *avcodec_descriptor_get(enum AVCodecID id); - -/** - * Iterate over all codec descriptors known to libavcodec. - * - * @param prev previous descriptor. NULL to get the first descriptor. - * - * @return next descriptor or NULL after the last descriptor - */ -const AVCodecDescriptor *avcodec_descriptor_next(const AVCodecDescriptor *prev); - -/** - * @return codec descriptor with the given name or NULL if no such descriptor - * exists. - */ -const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name); - /** * Allocate a CPB properties structure and initialize its fields to default * values. diff --git a/libavcodec/avdct.c b/libavcodec/avdct.c index 47e5f7134ea..e8fa41f73be 100644 --- a/libavcodec/avdct.c +++ b/libavcodec/avdct.c @@ -100,7 +100,7 @@ int avcodec_dct_init(AVDCT *dsp) #if CONFIG_IDCTDSP { - IDCTDSPContext idsp; + IDCTDSPContext idsp = {0}; ff_idctdsp_init(&idsp, avctx); COPY(idsp, idct); COPY(idsp, idct_permutation); @@ -120,6 +120,7 @@ int avcodec_dct_init(AVDCT *dsp) PixblockDSPContext pdsp; ff_pixblockdsp_init(&pdsp, avctx); COPY(pdsp, get_pixels); + COPY(pdsp, get_pixels_unaligned); } #endif diff --git a/libavcodec/avdct.h b/libavcodec/avdct.h index 272422e44c9..6411fab6f63 100644 --- a/libavcodec/avdct.h +++ b/libavcodec/avdct.h @@ -67,6 +67,10 @@ typedef struct AVDCT { ptrdiff_t line_size); int bits_per_sample; + + void (*get_pixels_unaligned)(int16_t *block /* align 16 */, + const uint8_t *pixels, + ptrdiff_t line_size); } AVDCT; /** diff --git a/libavcodec/avpacket.c b/libavcodec/avpacket.c index 2b20067211b..dce26cb31a4 100644 --- a/libavcodec/avpacket.c +++ b/libavcodec/avpacket.c @@ -26,9 +26,11 @@ #include "libavutil/internal.h" #include "libavutil/mathematics.h" #include "libavutil/mem.h" -#include "avcodec.h" + #include "bytestream.h" #include "internal.h" +#include "packet.h" +#include "packet_internal.h" void av_init_packet(AVPacket *pkt) { @@ -54,7 +56,7 @@ AVPacket *av_packet_alloc(void) if (!pkt) return pkt; - av_packet_unref(pkt); + av_init_packet(pkt); return pkt; } @@ -127,7 +129,8 @@ int av_grow_packet(AVPacket *pkt, int grow_by) return AVERROR(ENOMEM); } - if (new_size + data_offset > pkt->buf->size) { + if (new_size + data_offset > pkt->buf->size || + !av_buffer_is_writable(pkt->buf)) { int ret = av_buffer_realloc(&pkt->buf, new_size + data_offset); if (ret < 0) { pkt->data = old_data; @@ -394,6 +397,9 @@ const char *av_packet_side_data_name(enum AVPacketSideDataType type) case AV_PKT_DATA_ENCRYPTION_INIT_INFO: return "Encryption initialization data"; case AV_PKT_DATA_ENCRYPTION_INFO: return "Encryption info"; case AV_PKT_DATA_AFD: return "Active Format Description data"; + case AV_PKT_DATA_PRFT: return "Producer Reference Time"; + case AV_PKT_DATA_ICC_PROFILE: return "ICC Profile"; + case AV_PKT_DATA_DOVI_CONF: return "DOVI configuration record"; } return NULL; } @@ -523,10 +529,10 @@ uint8_t *av_packet_pack_dictionary(AVDictionary *dict, int *size) int av_packet_unpack_dictionary(const uint8_t *data, int size, AVDictionary **dict) { const uint8_t *end; - int ret = 0; + int ret; if (!dict || !data || !size) - return ret; + return 0; end = data + size; if (size && end[-1]) return AVERROR_INVALIDDATA; @@ -539,11 +545,11 @@ int av_packet_unpack_dictionary(const uint8_t *data, int size, AVDictionary **di ret = av_dict_set(dict, key, val, 0); if (ret < 0) - break; + return ret; data = val + strlen(val) + 1; } - return ret; + return 0; } int av_packet_shrink_side_data(AVPacket *pkt, enum AVPacketSideDataType type, @@ -609,9 +615,11 @@ int av_packet_ref(AVPacket *dst, const AVPacket *src) { int ret; + dst->buf = NULL; + ret = av_packet_copy_props(dst, src); if (ret < 0) - return ret; + goto fail; if (!src->buf) { ret = packet_alloc(&dst->buf, src->size); @@ -635,7 +643,7 @@ int av_packet_ref(AVPacket *dst, const AVPacket *src) return 0; fail: - av_packet_free_side_data(dst); + av_packet_unref(dst); return ret; } @@ -741,3 +749,25 @@ int ff_side_data_set_encoder_stats(AVPacket *pkt, int quality, int64_t *error, i return 0; } + +int ff_side_data_set_prft(AVPacket *pkt, int64_t timestamp) +{ + AVProducerReferenceTime *prft; + uint8_t *side_data; + int side_data_size; + + side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_PRFT, &side_data_size); + if (!side_data) { + side_data_size = sizeof(AVProducerReferenceTime); + side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_PRFT, side_data_size); + } + + if (!side_data || side_data_size < sizeof(AVProducerReferenceTime)) + return AVERROR(ENOMEM); + + prft = (AVProducerReferenceTime *)side_data; + prft->wallclock = timestamp; + prft->flags = 0; + + return 0; +} diff --git a/libavcodec/avs.c b/libavcodec/avs.c index 66724d47b72..2f2361e5ea6 100644 --- a/libavcodec/avs.c +++ b/libavcodec/avs.c @@ -59,7 +59,7 @@ avs_decode_frame(AVCodecContext * avctx, AvsBlockType type; GetBitContext change_map = {0}; //init to silence warning - if ((ret = ff_reget_buffer(avctx, p)) < 0) + if ((ret = ff_reget_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_P; p->key_frame = 0; diff --git a/libavcodec/avuienc.c b/libavcodec/avuienc.c index b219906706f..2091309e7e2 100644 --- a/libavcodec/avuienc.c +++ b/libavcodec/avuienc.c @@ -98,6 +98,6 @@ AVCodec ff_avui_encoder = { .id = AV_CODEC_ID_AVUI, .init = avui_encode_init, .encode2 = avui_encode_frame, - .capabilities = AV_CODEC_CAP_EXPERIMENTAL | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_EXPERIMENTAL, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_UYVY422, AV_PIX_FMT_NONE }, }; diff --git a/libavcodec/bethsoftvideo.c b/libavcodec/bethsoftvideo.c index e5a73f55a1c..a931b549113 100644 --- a/libavcodec/bethsoftvideo.c +++ b/libavcodec/bethsoftvideo.c @@ -79,7 +79,7 @@ static int bethsoftvid_decode_frame(AVCodecContext *avctx, int code, ret; int yoffset; - if ((ret = ff_reget_buffer(avctx, vid->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, vid->frame, 0)) < 0) return ret; wrap_to_next_line = vid->frame->linesize[0] - avctx->width; diff --git a/libavcodec/bgmc.c b/libavcodec/bgmc.c index 2d59aa37adb..361f7c52e62 100644 --- a/libavcodec/bgmc.c +++ b/libavcodec/bgmc.c @@ -493,7 +493,7 @@ int ff_bgmc_decode_init(GetBitContext *gb, unsigned int *h, *h = TOP_VALUE; *l = 0; - *v = get_bits_long(gb, VALUE_BITS); + *v = get_bits(gb, VALUE_BITS); return 0; } diff --git a/libavcodec/bink.c b/libavcodec/bink.c index d18c0ceae4c..f251ab4017f 100644 --- a/libavcodec/bink.c +++ b/libavcodec/bink.c @@ -241,16 +241,19 @@ static void merge(GetBitContext *gb, uint8_t *dst, uint8_t *src, int size) * @param gb context for reading bits * @param tree pointer for storing tree data */ -static void read_tree(GetBitContext *gb, Tree *tree) +static int read_tree(GetBitContext *gb, Tree *tree) { uint8_t tmp1[16] = { 0 }, tmp2[16], *in = tmp1, *out = tmp2; int i, t, len; + if (get_bits_left(gb) < 4) + return AVERROR_INVALIDDATA; + tree->vlc_num = get_bits(gb, 4); if (!tree->vlc_num) { for (i = 0; i < 16; i++) tree->syms[i] = i; - return; + return 0; } if (get_bits1(gb)) { len = get_bits(gb, 3); @@ -273,6 +276,7 @@ static void read_tree(GetBitContext *gb, Tree *tree) } memcpy(tree->syms, in, 16); } + return 0; } /** @@ -282,19 +286,27 @@ static void read_tree(GetBitContext *gb, Tree *tree) * @param c decoder context * @param bundle_num number of the bundle to initialize */ -static void read_bundle(GetBitContext *gb, BinkContext *c, int bundle_num) +static int read_bundle(GetBitContext *gb, BinkContext *c, int bundle_num) { int i; if (bundle_num == BINK_SRC_COLORS) { - for (i = 0; i < 16; i++) - read_tree(gb, &c->col_high[i]); + for (i = 0; i < 16; i++) { + int ret = read_tree(gb, &c->col_high[i]); + if (ret < 0) + return ret; + } c->col_lastval = 0; } - if (bundle_num != BINK_SRC_INTRA_DC && bundle_num != BINK_SRC_INTER_DC) - read_tree(gb, &c->bundle[bundle_num].tree); + if (bundle_num != BINK_SRC_INTRA_DC && bundle_num != BINK_SRC_INTER_DC) { + int ret = read_tree(gb, &c->bundle[bundle_num].tree); + if (ret < 0) + return ret; + } c->bundle[bundle_num].cur_dec = c->bundle[bundle_num].cur_ptr = c->bundle[bundle_num].data; + + return 0; } /** @@ -324,6 +336,8 @@ static int read_runs(AVCodecContext *avctx, GetBitContext *gb, Bundle *b) av_log(avctx, AV_LOG_ERROR, "Run value went out of bounds\n"); return AVERROR_INVALIDDATA; } + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; if (get_bits1(gb)) { v = get_bits(gb, 4); memset(b->cur_dec, v, t); @@ -346,6 +360,8 @@ static int read_motion_values(AVCodecContext *avctx, GetBitContext *gb, Bundle * av_log(avctx, AV_LOG_ERROR, "Too many motion values\n"); return AVERROR_INVALIDDATA; } + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; if (get_bits1(gb)) { v = get_bits(gb, 4); if (v) { @@ -389,6 +405,8 @@ static int read_block_types(AVCodecContext *avctx, GetBitContext *gb, Bundle *b) av_log(avctx, AV_LOG_ERROR, "Too many block type values\n"); return AVERROR_INVALIDDATA; } + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; if (get_bits1(gb)) { v = get_bits(gb, 4); memset(b->cur_dec, v, t); @@ -424,6 +442,8 @@ static int read_patterns(AVCodecContext *avctx, GetBitContext *gb, Bundle *b) return AVERROR_INVALIDDATA; } while (b->cur_dec < dec_end) { + if (get_bits_left(gb) < 2) + return AVERROR_INVALIDDATA; v = GET_HUFF(gb, b->tree); v |= GET_HUFF(gb, b->tree) << 4; *b->cur_dec++ = v; @@ -443,6 +463,8 @@ static int read_colors(GetBitContext *gb, Bundle *b, BinkContext *c) av_log(c->avctx, AV_LOG_ERROR, "Too many color values\n"); return AVERROR_INVALIDDATA; } + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; if (get_bits1(gb)) { c->col_lastval = GET_HUFF(gb, c->col_high[c->col_lastval]); v = GET_HUFF(gb, b->tree); @@ -456,6 +478,8 @@ static int read_colors(GetBitContext *gb, Bundle *b, BinkContext *c) b->cur_dec += t; } else { while (b->cur_dec < dec_end) { + if (get_bits_left(gb) < 2) + return AVERROR_INVALIDDATA; c->col_lastval = GET_HUFF(gb, c->col_high[c->col_lastval]); v = GET_HUFF(gb, b->tree); v = (c->col_lastval << 4) | v; @@ -481,6 +505,8 @@ static int read_dcs(AVCodecContext *avctx, GetBitContext *gb, Bundle *b, int16_t *dst_end = (int16_t*)b->data_end; CHECK_READ_VAL(gb, b, len); + if (get_bits_left(gb) < start_bits - has_sign) + return AVERROR_INVALIDDATA; v = get_bits(gb, start_bits - has_sign); if (v && has_sign) { sign = -get_bits1(gb); @@ -620,6 +646,9 @@ static int read_dct_coeffs(BinkContext *c, GetBitContext *gb, int32_t block[64], int coef_count = 0; int quant_idx; + if (get_bits_left(gb) < 4) + return AVERROR_INVALIDDATA; + coef_list[list_end] = 4; mode_list[list_end++] = 0; coef_list[list_end] = 24; mode_list[list_end++] = 0; coef_list[list_end] = 44; mode_list[list_end++] = 0; @@ -1015,8 +1044,11 @@ static int bink_decode_plane(BinkContext *c, AVFrame *frame, GetBitContext *gb, } init_lengths(c, FFMAX(width, 8), bw); - for (i = 0; i < BINK_NB_SRC; i++) - read_bundle(gb, c, i); + for (i = 0; i < BINK_NB_SRC; i++) { + ret = read_bundle(gb, c, i); + if (ret < 0) + return ret; + } ref_start = c->last->data[plane_idx] ? c->last->data[plane_idx] : frame->data[plane_idx]; @@ -1066,6 +1098,8 @@ static int bink_decode_plane(BinkContext *c, AVFrame *frame, GetBitContext *gb, blk = get_value(c, BINK_SRC_SUB_BLOCK_TYPES); switch (blk) { case RUN_BLOCK: + if (get_bits_left(gb) < 4) + return AVERROR_INVALIDDATA; scan = bink_patterns[get_bits(gb, 4)]; i = 0; do { @@ -1227,7 +1261,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0) return ret; } else { - if ((ret = ff_reget_buffer(avctx, c->last)) < 0) + if ((ret = ff_reget_buffer(avctx, c->last, 0)) < 0) return ret; if ((ret = av_frame_ref(frame, c->last)) < 0) return ret; diff --git a/libavcodec/binkaudio.c b/libavcodec/binkaudio.c index 96cf968c66e..012190a9555 100644 --- a/libavcodec/binkaudio.c +++ b/libavcodec/binkaudio.c @@ -95,6 +95,8 @@ static av_cold int decode_init(AVCodecContext *avctx) if (avctx->codec->id == AV_CODEC_ID_BINKAUDIO_RDFT) { // audio is already interleaved for the RDFT format variant avctx->sample_fmt = AV_SAMPLE_FMT_FLT; + if (sample_rate > INT_MAX / avctx->channels) + return AVERROR_INVALIDDATA; sample_rate *= avctx->channels; s->channels = 1; if (!s->version_b) @@ -107,7 +109,7 @@ static av_cold int decode_init(AVCodecContext *avctx) s->frame_len = 1 << frame_len_bits; s->overlap_len = s->frame_len / 16; s->block_size = (s->frame_len - s->overlap_len) * s->channels; - sample_rate_half = (sample_rate + 1) / 2; + sample_rate_half = (sample_rate + 1LL) / 2; if (avctx->codec->id == AV_CODEC_ID_BINKAUDIO_RDFT) s->root = 2.0 / (sqrt(s->frame_len) * 32768.0); else @@ -151,7 +153,7 @@ static av_cold int decode_init(AVCodecContext *avctx) static float get_float(GetBitContext *gb) { int power = get_bits(gb, 5); - float f = ldexpf(get_bits_long(gb, 23), power - 23); + float f = ldexpf(get_bits(gb, 23), power - 23); if (get_bits1(gb)) f = -f; return f; diff --git a/libavcodec/bitpacked.c b/libavcodec/bitpacked.c index f0b417d595b..952ba73a32d 100644 --- a/libavcodec/bitpacked.c +++ b/libavcodec/bitpacked.c @@ -146,4 +146,8 @@ AVCodec ff_bitpacked_decoder = { .init = bitpacked_init_decoder, .decode = bitpacked_decode, .capabilities = AV_CODEC_CAP_EXPERIMENTAL, + .codec_tags = (const uint32_t []){ + MKTAG('U', 'Y', 'V', 'Y'), + FF_CODEC_TAGS_END, + }, }; diff --git a/libavcodec/bitstream.c b/libavcodec/bitstream.c index be8a0f634dc..53a2db7451b 100644 --- a/libavcodec/bitstream.c +++ b/libavcodec/bitstream.c @@ -162,9 +162,9 @@ static int build_table(VLC *vlc, int table_nb_bits, int nb_codes, uint32_t code; volatile VLC_TYPE (* volatile table)[2]; // the double volatile is needed to prevent an internal compiler error in gcc 4.2 - table_size = 1 << table_nb_bits; 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); ff_dlog(NULL, "new table index=%d size=%d\n", table_index, table_size); if (table_index < 0) diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index 463003966ad..a7aa5dca654 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -22,9 +22,10 @@ #include "libavutil/log.h" #include "avcodec.h" -#include "bsf.h" +#include "bsf_internal.h" extern const AVBitStreamFilter ff_aac_adtstoasc_bsf; +extern const AVBitStreamFilter ff_av1_frame_merge_bsf; extern const AVBitStreamFilter ff_av1_frame_split_bsf; extern const AVBitStreamFilter ff_av1_metadata_bsf; extern const AVBitStreamFilter ff_chomp_bsf; @@ -48,6 +49,8 @@ extern const AVBitStreamFilter ff_mpeg4_unpack_bframes_bsf; extern const AVBitStreamFilter ff_mov2textsub_bsf; extern const AVBitStreamFilter ff_noise_bsf; extern const AVBitStreamFilter ff_null_bsf; +extern const AVBitStreamFilter ff_opus_metadata_bsf; +extern const AVBitStreamFilter ff_pcm_rechunk_bsf; extern const AVBitStreamFilter ff_prores_metadata_bsf; extern const AVBitStreamFilter ff_remove_extradata_bsf; extern const AVBitStreamFilter ff_text2movsub_bsf; diff --git a/libavcodec/bmp_parser.c b/libavcodec/bmp_parser.c index cd65f02a2e0..700bf27af1d 100644 --- a/libavcodec/bmp_parser.c +++ b/libavcodec/bmp_parser.c @@ -45,6 +45,7 @@ static int bmp_parse(AVCodecParserContext *s, AVCodecContext *avctx, int i = 0; *poutbuf_size = 0; + *poutbuf = NULL; restart: if (bpc->pc.frame_start_found <= 2+4+4) { diff --git a/libavcodec/bsf.c b/libavcodec/bsf.c index 50813076033..5e1c794a76f 100644 --- a/libavcodec/bsf.c +++ b/libavcodec/bsf.c @@ -18,14 +18,19 @@ #include +#include "libavutil/avassert.h" #include "libavutil/log.h" #include "libavutil/mem.h" #include "libavutil/opt.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" +#include "codec_desc.h" +#include "codec_par.h" + +#define IS_EMPTY(pkt) (!(pkt)->data && !(pkt)->side_data_elems) struct AVBSFInternal { AVPacket *buffer_pkt; @@ -45,9 +50,8 @@ void av_bsf_free(AVBSFContext **pctx) if (ctx->filter->priv_class && ctx->priv_data) av_opt_free(ctx->priv_data); - av_opt_free(ctx); - - av_packet_free(&ctx->internal->buffer_pkt); + if (ctx->internal) + av_packet_free(&ctx->internal->buffer_pkt); av_freep(&ctx->internal); av_freep(&ctx->priv_data); @@ -65,12 +69,18 @@ static void *bsf_child_next(void *obj, void *prev) return NULL; } +static const char *bsf_to_name(void *bsf) +{ + return ((AVBSFContext *)bsf)->filter->name; +} + static const AVClass bsf_class = { .class_name = "AVBSFContext", - .item_name = av_default_item_name, + .item_name = bsf_to_name, .version = LIBAVUTIL_VERSION_INT, .child_next = bsf_child_next, .child_class_next = ff_bsf_child_class_next, + .category = AV_CLASS_CATEGORY_BITSTREAM_FILTER, }; const AVClass *av_bsf_get_class(void) @@ -81,6 +91,7 @@ const AVClass *av_bsf_get_class(void) int av_bsf_alloc(const AVBitStreamFilter *filter, AVBSFContext **pctx) { AVBSFContext *ctx; + AVBSFInternal *bsfi; int ret; ctx = av_mallocz(sizeof(*ctx)); @@ -97,20 +108,19 @@ int av_bsf_alloc(const AVBitStreamFilter *filter, AVBSFContext **pctx) goto fail; } - ctx->internal = av_mallocz(sizeof(*ctx->internal)); - if (!ctx->internal) { + bsfi = av_mallocz(sizeof(*bsfi)); + if (!bsfi) { ret = AVERROR(ENOMEM); goto fail; } + ctx->internal = bsfi; - ctx->internal->buffer_pkt = av_packet_alloc(); - if (!ctx->internal->buffer_pkt) { + bsfi->buffer_pkt = av_packet_alloc(); + if (!bsfi->buffer_pkt) { ret = AVERROR(ENOMEM); goto fail; } - av_opt_set_defaults(ctx); - /* allocate priv data and init private options */ if (filter->priv_data_size) { ctx->priv_data = av_mallocz(filter->priv_data_size); @@ -174,9 +184,11 @@ int av_bsf_init(AVBSFContext *ctx) void av_bsf_flush(AVBSFContext *ctx) { - ctx->internal->eof = 0; + AVBSFInternal *bsfi = ctx->internal; - av_packet_unref(ctx->internal->buffer_pkt); + bsfi->eof = 0; + + av_packet_unref(bsfi->buffer_pkt); if (ctx->filter->flush) ctx->filter->flush(ctx); @@ -184,26 +196,26 @@ void av_bsf_flush(AVBSFContext *ctx) int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt) { + AVBSFInternal *bsfi = ctx->internal; int ret; - if (!pkt || (!pkt->data && !pkt->side_data_elems)) { - ctx->internal->eof = 1; + if (!pkt || IS_EMPTY(pkt)) { + bsfi->eof = 1; return 0; } - if (ctx->internal->eof) { + if (bsfi->eof) { av_log(ctx, AV_LOG_ERROR, "A non-NULL packet sent after an EOF.\n"); return AVERROR(EINVAL); } - if (ctx->internal->buffer_pkt->data || - ctx->internal->buffer_pkt->side_data_elems) + if (!IS_EMPTY(bsfi->buffer_pkt)) return AVERROR(EAGAIN); ret = av_packet_make_refcounted(pkt); if (ret < 0) return ret; - av_packet_move_ref(ctx->internal->buffer_pkt, pkt); + av_packet_move_ref(bsfi->buffer_pkt, pkt); return 0; } @@ -215,38 +227,36 @@ int av_bsf_receive_packet(AVBSFContext *ctx, AVPacket *pkt) int ff_bsf_get_packet(AVBSFContext *ctx, AVPacket **pkt) { - AVBSFInternal *in = ctx->internal; + AVBSFInternal *bsfi = ctx->internal; AVPacket *tmp_pkt; - if (in->eof) + if (bsfi->eof) return AVERROR_EOF; - if (!ctx->internal->buffer_pkt->data && - !ctx->internal->buffer_pkt->side_data_elems) + if (IS_EMPTY(bsfi->buffer_pkt)) return AVERROR(EAGAIN); tmp_pkt = av_packet_alloc(); if (!tmp_pkt) return AVERROR(ENOMEM); - *pkt = ctx->internal->buffer_pkt; - ctx->internal->buffer_pkt = tmp_pkt; + *pkt = bsfi->buffer_pkt; + bsfi->buffer_pkt = tmp_pkt; return 0; } int ff_bsf_get_packet_ref(AVBSFContext *ctx, AVPacket *pkt) { - AVBSFInternal *in = ctx->internal; + AVBSFInternal *bsfi = ctx->internal; - if (in->eof) + if (bsfi->eof) return AVERROR_EOF; - if (!ctx->internal->buffer_pkt->data && - !ctx->internal->buffer_pkt->side_data_elems) + if (IS_EMPTY(bsfi->buffer_pkt)) return AVERROR(EAGAIN); - av_packet_move_ref(pkt, ctx->internal->buffer_pkt); + av_packet_move_ref(pkt, bsfi->buffer_pkt); return 0; } @@ -258,7 +268,6 @@ typedef struct BSFListContext { int nb_bsfs; unsigned idx; // index of currently processed BSF - unsigned flushed_idx; // index of BSF being flushed char * item_name; } BSFListContext; @@ -296,58 +305,43 @@ static int bsf_list_init(AVBSFContext *bsf) static int bsf_list_filter(AVBSFContext *bsf, AVPacket *out) { BSFListContext *lst = bsf->priv_data; - int ret; + int ret, eof = 0; if (!lst->nb_bsfs) return ff_bsf_get_packet_ref(bsf, out); while (1) { - if (lst->idx > lst->flushed_idx) { + /* get a packet from the previous filter up the chain */ + if (lst->idx) ret = av_bsf_receive_packet(lst->bsfs[lst->idx-1], out); - if (ret == AVERROR(EAGAIN)) { - /* no more packets from idx-1, try with previous */ - ret = 0; - lst->idx--; - continue; - } else if (ret == AVERROR_EOF) { - /* filter idx-1 is done, continue with idx...nb_bsfs */ - lst->flushed_idx = lst->idx; - continue; - }else if (ret < 0) { - /* filtering error */ - break; - } - } else { + else ret = ff_bsf_get_packet_ref(bsf, out); - if (ret == AVERROR_EOF) { - lst->idx = lst->flushed_idx; - } else if (ret < 0) - break; - } + if (ret == AVERROR(EAGAIN)) { + if (!lst->idx) + return ret; + lst->idx--; + continue; + } else if (ret == AVERROR_EOF) { + eof = 1; + } else if (ret < 0) + return ret; + /* send it to the next filter down the chain */ if (lst->idx < lst->nb_bsfs) { - AVPacket *pkt; - if (ret == AVERROR_EOF && lst->idx == lst->flushed_idx) { - /* ff_bsf_get_packet_ref returned EOF and idx is first - * filter of yet not flushed filter chain */ - pkt = NULL; - } else { - pkt = out; + ret = av_bsf_send_packet(lst->bsfs[lst->idx], eof ? NULL : out); + av_assert1(ret != AVERROR(EAGAIN)); + if (ret < 0) { + av_packet_unref(out); + return ret; } - ret = av_bsf_send_packet(lst->bsfs[lst->idx], pkt); - if (ret < 0) - break; lst->idx++; + eof = 0; + } else if (eof) { + return ret; } else { - /* The end of filter chain, break to return result */ - break; + return 0; } } - - if (ret < 0) - av_packet_unref(out); - - return ret; } static void bsf_list_flush(AVBSFContext *bsf) @@ -356,7 +350,7 @@ static void bsf_list_flush(AVBSFContext *bsf) for (int i = 0; i < lst->nb_bsfs; i++) av_bsf_flush(lst->bsfs[i]); - lst->idx = lst->flushed_idx = 0; + lst->idx = 0; } static void bsf_list_close(AVBSFContext *bsf) @@ -439,7 +433,7 @@ int av_bsf_list_append(AVBSFList *lst, AVBSFContext *bsf) return av_dynarray_add_nofree(&lst->bsfs, &lst->nb_bsfs, bsf); } -int av_bsf_list_append2(AVBSFList *lst, const char *bsf_name, AVDictionary ** options) +static int bsf_list_append_internal(AVBSFList *lst, const char *bsf_name, const char *options, AVDictionary ** options_dict) { int ret; const AVBitStreamFilter *filter; @@ -453,8 +447,20 @@ int av_bsf_list_append2(AVBSFList *lst, const char *bsf_name, AVDictionary ** op if (ret < 0) return ret; - if (options) { - ret = av_opt_set_dict2(bsf, options, AV_OPT_SEARCH_CHILDREN); + if (options && filter->priv_class) { + const AVOption *opt = av_opt_next(bsf->priv_data, NULL); + const char * shorthand[2] = {NULL}; + + if (opt) + shorthand[0] = opt->name; + + ret = av_opt_set_from_string(bsf->priv_data, options, shorthand, "=", ":"); + if (ret < 0) + goto end; + } + + if (options_dict) { + ret = av_opt_set_dict2(bsf, options_dict, AV_OPT_SEARCH_CHILDREN); if (ret < 0) goto end; } @@ -468,6 +474,11 @@ int av_bsf_list_append2(AVBSFList *lst, const char *bsf_name, AVDictionary ** op return ret; } +int av_bsf_list_append2(AVBSFList *lst, const char *bsf_name, AVDictionary ** options) +{ + return bsf_list_append_internal(lst, bsf_name, NULL, options); +} + int av_bsf_list_finalize(AVBSFList **lst, AVBSFContext **bsf) { int ret = 0; @@ -494,33 +505,15 @@ int av_bsf_list_finalize(AVBSFList **lst, AVBSFContext **bsf) return ret; } -static int bsf_parse_single(const char *str, AVBSFList *bsf_lst) +static int bsf_parse_single(char *str, AVBSFList *bsf_lst) { - char *bsf_name, *bsf_options_str, *buf; - AVDictionary *bsf_options = NULL; - int ret = 0; + char *bsf_name, *bsf_options_str; - if (!(buf = av_strdup(str))) - return AVERROR(ENOMEM); - - bsf_name = av_strtok(buf, "=", &bsf_options_str); - if (!bsf_name) { - ret = AVERROR(EINVAL); - goto end; - } - - if (bsf_options_str) { - ret = av_dict_parse_string(&bsf_options, bsf_options_str, "=", ":", 0); - if (ret < 0) - goto end; - } - - ret = av_bsf_list_append2(bsf_lst, bsf_name, &bsf_options); + bsf_name = av_strtok(str, "=", &bsf_options_str); + if (!bsf_name) + return AVERROR(EINVAL); - av_dict_free(&bsf_options); -end: - av_free(buf); - return ret; + return bsf_list_append_internal(bsf_lst, bsf_name, bsf_options_str, NULL); } int av_bsf_list_parse_str(const char *str, AVBSFContext **bsf_lst) @@ -541,11 +534,7 @@ int av_bsf_list_parse_str(const char *str, AVBSFContext **bsf_lst) goto end; } - while (1) { - bsf_str = av_strtok(buf, ",", &saveptr); - if (!bsf_str) - break; - + while (bsf_str = av_strtok(buf, ",", &saveptr)) { ret = bsf_parse_single(bsf_str, lst); if (ret < 0) goto end; diff --git a/libavcodec/bsf.h b/libavcodec/bsf.h index af035eee440..7ed51677e82 100644 --- a/libavcodec/bsf.h +++ b/libavcodec/bsf.h @@ -1,4 +1,6 @@ /* + * Bitstream filters public API + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -19,26 +21,305 @@ #ifndef AVCODEC_BSF_H #define AVCODEC_BSF_H -#include "avcodec.h" +#include "libavutil/dict.h" +#include "libavutil/log.h" +#include "libavutil/rational.h" + +#include "codec_id.h" +#include "codec_par.h" +#include "packet.h" + +/** + * @addtogroup lavc_core + * @{ + */ + +typedef struct AVBSFInternal AVBSFInternal; + +/** + * The bitstream filter state. + * + * This struct must be allocated with av_bsf_alloc() and freed with + * av_bsf_free(). + * + * The fields in the struct will only be changed (by the caller or by the + * filter) as described in their documentation, and are to be considered + * immutable otherwise. + */ +typedef struct AVBSFContext { + /** + * A class for logging and AVOptions + */ + const AVClass *av_class; + + /** + * The bitstream filter this context is an instance of. + */ + const struct AVBitStreamFilter *filter; + + /** + * Opaque libavcodec internal data. Must not be touched by the caller in any + * way. + */ + AVBSFInternal *internal; + + /** + * Opaque filter-specific private data. If filter->priv_class is non-NULL, + * this is an AVOptions-enabled struct. + */ + void *priv_data; + + /** + * Parameters of the input stream. This field is allocated in + * av_bsf_alloc(), it needs to be filled by the caller before + * av_bsf_init(). + */ + AVCodecParameters *par_in; + + /** + * Parameters of the output stream. This field is allocated in + * av_bsf_alloc(), it is set by the filter in av_bsf_init(). + */ + AVCodecParameters *par_out; + + /** + * The timebase used for the timestamps of the input packets. Set by the + * caller before av_bsf_init(). + */ + AVRational time_base_in; + + /** + * The timebase used for the timestamps of the output packets. Set by the + * filter in av_bsf_init(). + */ + AVRational time_base_out; +} AVBSFContext; + +typedef struct AVBitStreamFilter { + const char *name; + + /** + * A list of codec ids supported by the filter, terminated by + * AV_CODEC_ID_NONE. + * May be NULL, in that case the bitstream filter works with any codec id. + */ + const enum AVCodecID *codec_ids; + + /** + * A class for the private data, used to declare bitstream filter private + * AVOptions. This field is NULL for bitstream filters that do not declare + * any options. + * + * If this field is non-NULL, the first member of the filter private data + * must be a pointer to AVClass, which will be set by libavcodec generic + * code to this class. + */ + const AVClass *priv_class; + + /***************************************************************** + * 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. + ***************************************************************** + */ + + int priv_data_size; + int (*init)(AVBSFContext *ctx); + int (*filter)(AVBSFContext *ctx, AVPacket *pkt); + void (*close)(AVBSFContext *ctx); + void (*flush)(AVBSFContext *ctx); +} AVBitStreamFilter; /** - * Called by the bitstream filters to get the next packet for filtering. - * The filter is responsible for either freeing the packet or passing it to the - * caller. + * @return a bitstream filter with the specified name or NULL if no such + * bitstream filter exists. */ -int ff_bsf_get_packet(AVBSFContext *ctx, AVPacket **pkt); +const AVBitStreamFilter *av_bsf_get_by_name(const char *name); /** - * Called by bitstream filters to get packet for filtering. - * The reference to packet is moved to provided packet structure. + * Iterate over all registered bitstream filters. * - * @param ctx pointer to AVBSFContext of filter - * @param pkt pointer to packet to move reference to + * @param opaque a pointer where libavcodec will store the iteration state. Must + * point to NULL to start the iteration. * - * @return 0>= on success, negative AVERROR in case of failure + * @return the next registered bitstream filter or NULL when the iteration is + * finished + */ +const AVBitStreamFilter *av_bsf_iterate(void **opaque); + +/** + * Allocate a context for a given bitstream filter. The caller must fill in the + * context parameters as described in the documentation and then call + * av_bsf_init() before sending any data to the filter. + * + * @param filter the filter for which to allocate an instance. + * @param ctx a pointer into which the pointer to the newly-allocated context + * will be written. It must be freed with av_bsf_free() after the + * filtering is done. + * + * @return 0 on success, a negative AVERROR code on failure + */ +int av_bsf_alloc(const AVBitStreamFilter *filter, AVBSFContext **ctx); + +/** + * Prepare the filter for use, after all the parameters and options have been + * set. + */ +int av_bsf_init(AVBSFContext *ctx); + +/** + * Submit a packet for filtering. + * + * After sending each packet, the filter must be completely drained by calling + * av_bsf_receive_packet() repeatedly until it returns AVERROR(EAGAIN) or + * AVERROR_EOF. + * + * @param pkt the packet to filter. The bitstream filter will take ownership of + * the packet and reset the contents of pkt. pkt is not touched if an error occurs. + * If pkt is empty (i.e. NULL, or pkt->data is NULL and pkt->side_data_elems zero), + * it signals the end of the stream (i.e. no more non-empty packets will be sent; + * sending more empty packets does nothing) and will cause the filter to output + * any packets it may have buffered internally. + * + * @return 0 on success. AVERROR(EAGAIN) if packets need to be retrieved from the + * filter (using av_bsf_receive_packet()) before new input can be consumed. Another + * negative AVERROR value if an error occurs. + */ +int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt); + +/** + * Retrieve a filtered packet. + * + * @param[out] pkt this struct will be filled with the contents of the filtered + * packet. It is owned by the caller and must be freed using + * av_packet_unref() when it is no longer needed. + * This parameter should be "clean" (i.e. freshly allocated + * with av_packet_alloc() or unreffed with av_packet_unref()) + * when this function is called. If this function returns + * successfully, the contents of pkt will be completely + * overwritten by the returned data. On failure, pkt is not + * touched. + * + * @return 0 on success. AVERROR(EAGAIN) if more packets need to be sent to the + * filter (using av_bsf_send_packet()) to get more output. AVERROR_EOF if there + * will be no further output from the filter. Another negative AVERROR value if + * an error occurs. + * + * @note one input packet may result in several output packets, so after sending + * a packet with av_bsf_send_packet(), this function needs to be called + * repeatedly until it stops returning 0. It is also possible for a filter to + * output fewer packets than were sent to it, so this function may return + * AVERROR(EAGAIN) immediately after a successful av_bsf_send_packet() call. + */ +int av_bsf_receive_packet(AVBSFContext *ctx, AVPacket *pkt); + +/** + * Reset the internal bitstream filter state / flush internal buffers. */ -int ff_bsf_get_packet_ref(AVBSFContext *ctx, AVPacket *pkt); +void av_bsf_flush(AVBSFContext *ctx); -const AVClass *ff_bsf_child_class_next(const AVClass *prev); +/** + * Free a bitstream filter context and everything associated with it; write NULL + * into the supplied pointer. + */ +void av_bsf_free(AVBSFContext **ctx); + +/** + * Get the AVClass for AVBSFContext. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *av_bsf_get_class(void); + +/** + * Structure for chain/list of bitstream filters. + * Empty list can be allocated by av_bsf_list_alloc(). + */ +typedef struct AVBSFList AVBSFList; + +/** + * Allocate empty list of bitstream filters. + * The list must be later freed by av_bsf_list_free() + * or finalized by av_bsf_list_finalize(). + * + * @return Pointer to @ref AVBSFList on success, NULL in case of failure + */ +AVBSFList *av_bsf_list_alloc(void); + +/** + * Free list of bitstream filters. + * + * @param lst Pointer to pointer returned by av_bsf_list_alloc() + */ +void av_bsf_list_free(AVBSFList **lst); + +/** + * Append bitstream filter to the list of bitstream filters. + * + * @param lst List to append to + * @param bsf Filter context to be appended + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_append(AVBSFList *lst, AVBSFContext *bsf); + +/** + * Construct new bitstream filter context given it's name and options + * and append it to the list of bitstream filters. + * + * @param lst List to append to + * @param bsf_name Name of the bitstream filter + * @param options Options for the bitstream filter, can be set to NULL + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_append2(AVBSFList *lst, const char * bsf_name, AVDictionary **options); +/** + * Finalize list of bitstream filters. + * + * This function will transform @ref AVBSFList to single @ref AVBSFContext, + * so the whole chain of bitstream filters can be treated as single filter + * freshly allocated by av_bsf_alloc(). + * If the call is successful, @ref AVBSFList structure is freed and lst + * will be set to NULL. In case of failure, caller is responsible for + * freeing the structure by av_bsf_list_free() + * + * @param lst Filter list structure to be transformed + * @param[out] bsf Pointer to be set to newly created @ref AVBSFContext structure + * representing the chain of bitstream filters + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_finalize(AVBSFList **lst, AVBSFContext **bsf); + +/** + * Parse string describing list of bitstream filters and create single + * @ref AVBSFContext describing the whole chain of bitstream filters. + * Resulting @ref AVBSFContext can be treated as any other @ref AVBSFContext freshly + * allocated by av_bsf_alloc(). + * + * @param str String describing chain of bitstream filters in format + * `bsf1[=opt1=val1:opt2=val2][,bsf2]` + * @param[out] bsf Pointer to be set to newly created @ref AVBSFContext structure + * representing the chain of bitstream filters + * + * @return >=0 on success, negative AVERROR in case of failure + */ +int av_bsf_list_parse_str(const char *str, AVBSFContext **bsf); + +/** + * Get null/pass-through bitstream filter. + * + * @param[out] bsf Pointer to be set to new instance of pass-through bitstream filter + * + * @return + */ +int av_bsf_get_null_filter(AVBSFContext **bsf); + +/** + * @} + */ -#endif /* AVCODEC_BSF_H */ +#endif // AVCODEC_BSF_H diff --git a/libavcodec/bsf_internal.h b/libavcodec/bsf_internal.h new file mode 100644 index 00000000000..fefd5b89057 --- /dev/null +++ b/libavcodec/bsf_internal.h @@ -0,0 +1,47 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_BSF_INTERNAL_H +#define AVCODEC_BSF_INTERNAL_H + +#include "libavutil/log.h" + +#include "bsf.h" +#include "packet.h" + +/** + * Called by the bitstream filters to get the next packet for filtering. + * The filter is responsible for either freeing the packet or passing it to the + * caller. + */ +int ff_bsf_get_packet(AVBSFContext *ctx, AVPacket **pkt); + +/** + * Called by bitstream filters to get packet for filtering. + * The reference to packet is moved to provided packet structure. + * + * @param ctx pointer to AVBSFContext of filter + * @param pkt pointer to packet to move reference to + * + * @return 0 on success, negative AVERROR in case of failure + */ +int ff_bsf_get_packet_ref(AVBSFContext *ctx, AVPacket *pkt); + +const AVClass *ff_bsf_child_class_next(const AVClass *prev); + +#endif /* AVCODEC_BSF_INTERNAL_H */ diff --git a/libavcodec/bytestream.h b/libavcodec/bytestream.h index 7be7fc22fc8..0516a6e3dcd 100644 --- a/libavcodec/bytestream.h +++ b/libavcodec/bytestream.h @@ -151,12 +151,12 @@ static av_always_inline void bytestream2_init_writer(PutByteContext *p, p->eof = 0; } -static av_always_inline unsigned int bytestream2_get_bytes_left(GetByteContext *g) +static av_always_inline int bytestream2_get_bytes_left(GetByteContext *g) { return g->buffer_end - g->buffer; } -static av_always_inline unsigned int bytestream2_get_bytes_left_p(PutByteContext *p) +static av_always_inline int bytestream2_get_bytes_left_p(PutByteContext *p) { return p->buffer_end - p->buffer; } diff --git a/libavcodec/c93.c b/libavcodec/c93.c index b708659cca9..e1808150b87 100644 --- a/libavcodec/c93.c +++ b/libavcodec/c93.c @@ -138,7 +138,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, c93->currentpic ^= 1; - if ((ret = ff_reget_buffer(avctx, newpic)) < 0) + if ((ret = ff_reget_buffer(avctx, newpic, 0)) < 0) return ret; stride = newpic->linesize[0]; diff --git a/libavcodec/cabac.c b/libavcodec/cabac.c index e51139de3bb..54e9bb4d8f6 100644 --- a/libavcodec/cabac.c +++ b/libavcodec/cabac.c @@ -27,7 +27,6 @@ #include #include "libavutil/common.h" -#include "libavutil/timer.h" #include "cabac.h" #include "cabac_functions.h" diff --git a/libavcodec/cavsdec.c b/libavcodec/cavsdec.c index 1c4f71824a1..aaed8071961 100644 --- a/libavcodec/cavsdec.c +++ b/libavcodec/cavsdec.c @@ -1101,11 +1101,20 @@ static int decode_pic(AVSContext *h) do { if (check_for_slice(h)) skip_count = -1; - if (h->skip_mode_flag && (skip_count < 0)) + if (h->skip_mode_flag && (skip_count < 0)) { + if (get_bits_left(&h->gb) < 1) { + ret = AVERROR_INVALIDDATA; + break; + } skip_count = get_ue_golomb(&h->gb); + } if (h->skip_mode_flag && skip_count--) { decode_mb_p(h, P_SKIP); } else { + if (get_bits_left(&h->gb) < 1) { + ret = AVERROR_INVALIDDATA; + break; + } mb_type = get_ue_golomb(&h->gb) + P_SKIP + h->skip_mode_flag; if (mb_type > P_8X8) ret = decode_mb_i(h, mb_type - P_8X8 - 1); @@ -1119,11 +1128,20 @@ static int decode_pic(AVSContext *h) do { if (check_for_slice(h)) skip_count = -1; - if (h->skip_mode_flag && (skip_count < 0)) + if (h->skip_mode_flag && (skip_count < 0)) { + if (get_bits_left(&h->gb) < 1) { + ret = AVERROR_INVALIDDATA; + break; + } skip_count = get_ue_golomb(&h->gb); + } if (h->skip_mode_flag && skip_count--) { ret = decode_mb_b(h, B_SKIP); } else { + if (get_bits_left(&h->gb) < 1) { + ret = AVERROR_INVALIDDATA; + break; + } mb_type = get_ue_golomb(&h->gb) + B_SKIP + h->skip_mode_flag; if (mb_type > B_8X8) ret = decode_mb_i(h, mb_type - B_8X8 - 1); diff --git a/libavcodec/cavsdsp.c b/libavcodec/cavsdsp.c index 90a67e910ce..ba92121cc91 100644 --- a/libavcodec/cavsdsp.c +++ b/libavcodec/cavsdsp.c @@ -201,20 +201,20 @@ static void cavs_idct8_add_c(uint8_t *dst, int16_t *block, ptrdiff_t stride) src[0][0] += 8; for( i = 0; i < 8; i++ ) { - const int a0 = 3*src[i][1] - (src[i][7]<<1); - const int a1 = 3*src[i][3] + (src[i][5]<<1); - const int a2 = (src[i][3]<<1) - 3*src[i][5]; - const int a3 = (src[i][1]<<1) + 3*src[i][7]; + const int a0 = 3 * src[i][1] - 2 * src[i][7]; + const int a1 = 3 * src[i][3] + 2 * src[i][5]; + const int a2 = 2 * src[i][3] - 3 * src[i][5]; + const int a3 = 2 * src[i][1] + 3 * src[i][7]; - const int b4 = ((a0 + a1 + a3)<<1) + a1; - const int b5 = ((a0 - a1 + a2)<<1) + a0; - const int b6 = ((a3 - a2 - a1)<<1) + a3; - const int b7 = ((a0 - a2 - a3)<<1) - a2; + const int b4 = 2 * (a0 + a1 + a3) + a1; + const int b5 = 2 * (a0 - a1 + a2) + a0; + const int b6 = 2 * (a3 - a2 - a1) + a3; + const int b7 = 2 * (a0 - a2 - a3) - a2; - const int a7 = (src[i][2]<<2) - 10*src[i][6]; - const int a6 = (src[i][6]<<2) + 10*src[i][2]; - const int a5 = ((src[i][0] - src[i][4]) << 3) + 4; - const int a4 = ((src[i][0] + src[i][4]) << 3) + 4; + const int a7 = 4 * src[i][2] - 10 * src[i][6]; + const int a6 = 4 * src[i][6] + 10 * src[i][2]; + const int a5 = 8 * (src[i][0] - src[i][4]) + 4; + const int a4 = 8 * (src[i][0] + src[i][4]) + 4; const int b0 = a4 + a6; const int b1 = a5 + a7; @@ -231,20 +231,20 @@ static void cavs_idct8_add_c(uint8_t *dst, int16_t *block, ptrdiff_t stride) src[i][7] = (b0 - b4) >> 3; } for( i = 0; i < 8; i++ ) { - const int a0 = 3*src[1][i] - (src[7][i]<<1); - const int a1 = 3*src[3][i] + (src[5][i]<<1); - const int a2 = (src[3][i]<<1) - 3*src[5][i]; - const int a3 = (src[1][i]<<1) + 3*src[7][i]; - - const int b4 = ((a0 + a1 + a3)<<1) + a1; - const int b5 = ((a0 - a1 + a2)<<1) + a0; - const int b6 = ((a3 - a2 - a1)<<1) + a3; - const int b7 = ((a0 - a2 - a3)<<1) - a2; - - const int a7 = (src[2][i]<<2) - 10*src[6][i]; - const int a6 = (src[6][i]<<2) + 10*src[2][i]; - const int a5 = (src[0][i] - src[4][i]) << 3; - const int a4 = (src[0][i] + src[4][i]) << 3; + const int a0 = 3 * src[1][i] - 2 * src[7][i]; + const int a1 = 3 * src[3][i] + 2 * src[5][i]; + const int a2 = 2 * src[3][i] - 3 * src[5][i]; + const int a3 = 2 * src[1][i] + 3 * src[7][i]; + + const int b4 = 2 * (a0 + a1 + a3) + a1; + const int b5 = 2 * (a0 - a1 + a2) + a0; + const int b6 = 2 * (a3 - a2 - a1) + a3; + const int b7 = 2 * (a0 - a2 - a3) - a2; + + const int a7 = 4 * src[2][i] - 10 * src[6][i]; + const int a6 = 4 * src[6][i] + 10 * src[2][i]; + const int a5 = 8 * (src[0][i] - src[4][i]); + const int a4 = 8 * (src[0][i] + src[4][i]); const int b0 = a4 + a6; const int b1 = a5 + a7; diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c index 23504165017..42cb9711fa4 100644 --- a/libavcodec/cbs.c +++ b/libavcodec/cbs.c @@ -95,10 +95,12 @@ int ff_cbs_init(CodedBitstreamContext **ctx_ptr, ctx->log_ctx = log_ctx; ctx->codec = type; - ctx->priv_data = av_mallocz(ctx->codec->priv_data_size); - if (!ctx->priv_data) { - av_freep(&ctx); - return AVERROR(ENOMEM); + if (type->priv_data_size) { + ctx->priv_data = av_mallocz(ctx->codec->priv_data_size); + if (!ctx->priv_data) { + av_freep(&ctx); + return AVERROR(ENOMEM); + } } ctx->decompose_unit_types = NULL; @@ -120,6 +122,7 @@ void ff_cbs_close(CodedBitstreamContext **ctx_ptr) if (ctx->codec && ctx->codec->close) ctx->codec->close(ctx); + av_freep(&ctx->write_buffer); av_freep(&ctx->priv_data); av_freep(ctx_ptr); } @@ -280,6 +283,59 @@ int ff_cbs_read(CodedBitstreamContext *ctx, return cbs_read_fragment_content(ctx, frag); } +static int cbs_write_unit_data(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) +{ + PutBitContext pbc; + int ret; + + if (!ctx->write_buffer) { + // Initial write buffer size is 1MB. + ctx->write_buffer_size = 1024 * 1024; + + reallocate_and_try_again: + ret = av_reallocp(&ctx->write_buffer, ctx->write_buffer_size); + if (ret < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a " + "sufficiently large write buffer (last attempt " + "%"SIZE_SPECIFIER" bytes).\n", ctx->write_buffer_size); + return ret; + } + } + + init_put_bits(&pbc, ctx->write_buffer, ctx->write_buffer_size); + + ret = ctx->codec->write_unit(ctx, unit, &pbc); + if (ret < 0) { + if (ret == AVERROR(ENOSPC)) { + // Overflow. + if (ctx->write_buffer_size == INT_MAX / 8) + return AVERROR(ENOMEM); + ctx->write_buffer_size = FFMIN(2 * ctx->write_buffer_size, INT_MAX / 8); + goto reallocate_and_try_again; + } + // Write failed for some other reason. + return ret; + } + + // Overflow but we didn't notice. + av_assert0(put_bits_count(&pbc) <= 8 * ctx->write_buffer_size); + + if (put_bits_count(&pbc) % 8) + unit->data_bit_padding = 8 - put_bits_count(&pbc) % 8; + else + unit->data_bit_padding = 0; + + flush_put_bits(&pbc); + + ret = ff_cbs_alloc_unit_data(ctx, unit, put_bits_count(&pbc) / 8); + if (ret < 0) + return ret; + + memcpy(unit->data, ctx->write_buffer, unit->data_size); + + return 0; +} int ff_cbs_write_fragment_data(CodedBitstreamContext *ctx, CodedBitstreamFragment *frag) @@ -295,7 +351,7 @@ int ff_cbs_write_fragment_data(CodedBitstreamContext *ctx, av_buffer_unref(&unit->data_ref); unit->data = NULL; - err = ctx->codec->write_unit(ctx, unit); + err = cbs_write_unit_data(ctx, unit); if (err < 0) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to write unit %d " "(type %"PRIu32").\n", i, unit->type); @@ -597,7 +653,7 @@ int ff_cbs_alloc_unit_content(CodedBitstreamContext *ctx, return AVERROR(ENOMEM); unit->content_ref = av_buffer_create(unit->content, size, - free, ctx, 0); + free, NULL, 0); if (!unit->content_ref) { av_freep(&unit->content); return AVERROR(ENOMEM); @@ -637,11 +693,11 @@ static int cbs_insert_unit(CodedBitstreamContext *ctx, memmove(units + position + 1, units + position, (frag->nb_units - position) * sizeof(*units)); } else { - units = av_malloc_array(frag->nb_units + 1, sizeof(*units)); + units = av_malloc_array(frag->nb_units*2 + 1, sizeof(*units)); if (!units) return AVERROR(ENOMEM); - ++frag->nb_units_allocated; + frag->nb_units_allocated = 2*frag->nb_units_allocated + 1; if (position > 0) memcpy(units, frag->units, position * sizeof(*units)); @@ -719,8 +775,11 @@ int ff_cbs_insert_unit_data(CodedBitstreamContext *ctx, data_ref = av_buffer_ref(data_buf); else data_ref = av_buffer_create(data, data_size, NULL, NULL, 0); - if (!data_ref) + if (!data_ref) { + if (!data_buf) + av_free(data); return AVERROR(ENOMEM); + } err = cbs_insert_unit(ctx, frag, position); if (err < 0) { diff --git a/libavcodec/cbs.h b/libavcodec/cbs.h index fe57e7b2a57..9ca1fbd6090 100644 --- a/libavcodec/cbs.h +++ b/libavcodec/cbs.h @@ -210,6 +210,13 @@ typedef struct CodedBitstreamContext { * From AV_LOG_*; defaults to AV_LOG_TRACE. */ int trace_level; + + /** + * Write buffer. Used as intermediate buffer when writing units. + * For internal use of cbs only. + */ + uint8_t *write_buffer; + size_t write_buffer_size; } CodedBitstreamContext; @@ -341,7 +348,7 @@ void ff_cbs_fragment_free(CodedBitstreamContext *ctx, int ff_cbs_alloc_unit_content(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit, size_t size, - void (*free)(void *unit, uint8_t *content)); + void (*free)(void *opaque, uint8_t *content)); /** * Allocate a new internal data buffer of the given size in the unit. @@ -369,7 +376,8 @@ int ff_cbs_insert_unit_content(CodedBitstreamContext *ctx, * Insert a new unit into a fragment with the given data bitstream. * * If data_buf is not supplied then data must have been allocated with - * av_malloc() and will become owned by the unit after this call. + * av_malloc() and will on success become owned by the unit after this + * call or freed on error. */ int ff_cbs_insert_unit_data(CodedBitstreamContext *ctx, CodedBitstreamFragment *frag, diff --git a/libavcodec/cbs_av1.c b/libavcodec/cbs_av1.c index eb6b8017902..0abcba9c60c 100644 --- a/libavcodec/cbs_av1.c +++ b/libavcodec/cbs_av1.c @@ -125,8 +125,9 @@ static int cbs_av1_write_uvlc(CodedBitstreamContext *ctx, PutBitContext *pbc, put_bits(pbc, 1, 1); } else { zeroes = av_log2(value + 1); - v = value - (1 << zeroes) + 1; - put_bits(pbc, zeroes + 1, 1); + v = value - (1U << zeroes) + 1; + put_bits(pbc, zeroes, 0); + put_bits(pbc, 1, 1); put_bits(pbc, zeroes, v); } @@ -170,6 +171,9 @@ static int cbs_av1_read_leb128(CodedBitstreamContext *ctx, GetBitContext *gbc, break; } + if (value > UINT32_MAX) + return AVERROR_INVALIDDATA; + if (ctx->trace_enable) ff_cbs_trace_syntax_element(ctx, position, name, NULL, "", value); @@ -211,8 +215,8 @@ static int cbs_av1_read_ns(CodedBitstreamContext *ctx, GetBitContext *gbc, uint32_t n, const char *name, const int *subscripts, uint32_t *write_to) { - uint32_t w, m, v, extra_bit, value; - int position; + uint32_t m, v, extra_bit, value; + int position, w; av_assert0(n > 0); @@ -547,12 +551,12 @@ 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) + 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) + xf(width, name, current->name, range_min, range_max, 0, ) #define flag(name) fb(1, name) #define su(width, name) \ - xsu(width, name, current->name, 0) + xsu(width, name, current->name, 0, ) #define fbs(width, name, subs, ...) \ xf(width, name, current->name, 0, MAX_UINT_BITS(width), subs, __VA_ARGS__) @@ -565,7 +569,7 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) #define fixed(width, name, value) do { \ av_unused uint32_t fixed_value = value; \ - xf(width, name, fixed_value, value, value, 0); \ + xf(width, name, fixed_value, value, value, 0, ); \ } while (0) @@ -574,7 +578,7 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) #define RWContext GetBitContext #define xf(width, name, var, range_min, range_max, subs, ...) do { \ - uint32_t value = range_min; \ + uint32_t value; \ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), \ &value, range_min, range_max)); \ @@ -582,7 +586,7 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) } while (0) #define xsu(width, name, var, subs, ...) do { \ - int32_t value = 0; \ + int32_t value; \ CHECK(ff_cbs_read_signed(ctx, rw, width, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), &value, \ MIN_INT_BITS(width), \ @@ -591,27 +595,27 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) } while (0) #define uvlc(name, range_min, range_max) do { \ - uint32_t value = range_min; \ + uint32_t value; \ CHECK(cbs_av1_read_uvlc(ctx, rw, #name, \ &value, range_min, range_max)); \ current->name = value; \ } while (0) #define ns(max_value, name, subs, ...) do { \ - uint32_t value = 0; \ + uint32_t value; \ CHECK(cbs_av1_read_ns(ctx, rw, max_value, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), &value)); \ current->name = value; \ } while (0) #define increment(name, min, max) do { \ - uint32_t value = 0; \ + uint32_t value; \ CHECK(cbs_av1_read_increment(ctx, rw, min, max, #name, &value)); \ current->name = value; \ } while (0) #define subexp(name, max, subs, ...) do { \ - uint32_t value = 0; \ + uint32_t value; \ CHECK(cbs_av1_read_subexp(ctx, rw, max, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), &value)); \ current->name = value; \ @@ -620,16 +624,16 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) #define delta_q(name) do { \ uint8_t delta_coded; \ int8_t delta_q; \ - xf(1, name.delta_coded, delta_coded, 0, 1, 0); \ + xf(1, name.delta_coded, delta_coded, 0, 1, 0, ); \ if (delta_coded) \ - xsu(1 + 6, name.delta_q, delta_q, 0); \ + xsu(1 + 6, name.delta_q, delta_q, 0, ); \ else \ delta_q = 0; \ current->name = delta_q; \ } while (0) #define leb128(name) do { \ - uint64_t value = 0; \ + uint64_t value; \ CHECK(cbs_av1_read_leb128(ctx, rw, #name, &value)); \ current->name = value; \ } while (0) @@ -697,9 +701,9 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) } while (0) #define delta_q(name) do { \ - xf(1, name.delta_coded, current->name != 0, 0, 1, 0); \ + xf(1, name.delta_coded, current->name != 0, 0, 1, 0, ); \ if (current->name) \ - xsu(1 + 6, name.delta_q, current->name, 0); \ + xsu(1 + 6, name.delta_q, current->name, 0, ); \ } while (0) #define leb128(name) do { \ @@ -708,10 +712,11 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) #define infer(name, value) do { \ if (current->name != (value)) { \ - av_log(ctx->log_ctx, AV_LOG_WARNING, "Warning: " \ + av_log(ctx->log_ctx, AV_LOG_ERROR, \ "%s does not match inferred value: " \ "%"PRId64", but should be %"PRId64".\n", \ #name, (int64_t)current->name, (int64_t)(value)); \ + return AVERROR_INVALIDDATA; \ } \ } while (0) @@ -768,14 +773,13 @@ static int cbs_av1_split_fragment(CodedBitstreamContext *ctx, if (err < 0) goto fail; - if (get_bits_left(&gbc) < 8) { - av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid OBU: fragment " - "too short (%"SIZE_SPECIFIER" bytes).\n", size); - err = AVERROR_INVALIDDATA; - goto fail; - } - if (header.obu_has_size_field) { + if (get_bits_left(&gbc) < 8) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid OBU: fragment " + "too short (%"SIZE_SPECIFIER" bytes).\n", size); + err = AVERROR_INVALIDDATA; + goto fail; + } err = cbs_av1_read_leb128(ctx, &gbc, "obu_size", &obu_size); if (err < 0) goto fail; @@ -829,7 +833,7 @@ static void cbs_av1_free_metadata(AV1RawMetadata *md) } } -static void cbs_av1_free_obu(void *unit, uint8_t *content) +static void cbs_av1_free_obu(void *opaque, uint8_t *content) { AV1RawOBU *obu = (AV1RawOBU*)content; @@ -940,6 +944,8 @@ static int cbs_av1_read_unit(CodedBitstreamContext *ctx, priv->spatial_id = 0; } + priv->ref = (AV1ReferenceFrameState *)&priv->read_ref; + switch (obu->header.obu_type) { case AV1_OBU_SEQUENCE_HEADER: { @@ -1038,6 +1044,7 @@ static int cbs_av1_read_unit(CodedBitstreamContext *ctx, if (obu->obu_size > 0 && obu->header.obu_type != AV1_OBU_TILE_GROUP && + obu->header.obu_type != AV1_OBU_TILE_LIST && obu->header.obu_type != AV1_OBU_FRAME) { int nb_bits = obu->obu_size * 8 + start_pos - end_pos; @@ -1082,6 +1089,8 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx, td = NULL; start_pos = put_bits_count(pbc); + priv->ref = (AV1ReferenceFrameState *)&priv->write_ref; + switch (obu->header.obu_type) { case AV1_OBU_SEQUENCE_HEADER: { @@ -1200,66 +1209,19 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx, return AVERROR(ENOSPC); if (obu->obu_size > 0) { - memmove(priv->write_buffer + data_pos, - priv->write_buffer + start_pos, header_size); + memmove(pbc->buf + data_pos, + pbc->buf + start_pos, header_size); skip_put_bytes(pbc, header_size); if (td) { - memcpy(priv->write_buffer + data_pos + header_size, + memcpy(pbc->buf + data_pos + header_size, td->data, td->data_size); skip_put_bytes(pbc, td->data_size); } } - return 0; -} - -static int cbs_av1_write_unit(CodedBitstreamContext *ctx, - CodedBitstreamUnit *unit) -{ - CodedBitstreamAV1Context *priv = ctx->priv_data; - PutBitContext pbc; - int err; - - if (!priv->write_buffer) { - // Initial write buffer size is 1MB. - priv->write_buffer_size = 1024 * 1024; - - reallocate_and_try_again: - err = av_reallocp(&priv->write_buffer, priv->write_buffer_size); - if (err < 0) { - av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a " - "sufficiently large write buffer (last attempt " - "%"SIZE_SPECIFIER" bytes).\n", priv->write_buffer_size); - return err; - } - } - - init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size); - - err = cbs_av1_write_obu(ctx, unit, &pbc); - if (err == AVERROR(ENOSPC)) { - // Overflow. - priv->write_buffer_size *= 2; - goto reallocate_and_try_again; - } - if (err < 0) - return err; - - // Overflow but we didn't notice. - av_assert0(put_bits_count(&pbc) <= 8 * priv->write_buffer_size); - // OBU data must be byte-aligned. - av_assert0(put_bits_count(&pbc) % 8 == 0); - - unit->data_size = put_bits_count(&pbc) / 8; - flush_put_bits(&pbc); - - err = ff_cbs_alloc_unit_data(ctx, unit, unit->data_size); - if (err < 0) - return err; - - memcpy(unit->data, priv->write_buffer, unit->data_size); + av_assert0(put_bits_count(pbc) % 8 == 0); return 0; } @@ -1298,8 +1260,6 @@ static void cbs_av1_close(CodedBitstreamContext *ctx) av_buffer_unref(&priv->sequence_header_ref); av_buffer_unref(&priv->frame_header_ref); - - av_freep(&priv->write_buffer); } const CodedBitstreamType ff_cbs_type_av1 = { @@ -1309,7 +1269,7 @@ const CodedBitstreamType ff_cbs_type_av1 = { .split_fragment = &cbs_av1_split_fragment, .read_unit = &cbs_av1_read_unit, - .write_unit = &cbs_av1_write_unit, + .write_unit = &cbs_av1_write_obu, .assemble_fragment = &cbs_av1_assemble_fragment, .close = &cbs_av1_close, diff --git a/libavcodec/cbs_av1.h b/libavcodec/cbs_av1.h index 1fb668ada48..fdc629b00ce 100644 --- a/libavcodec/cbs_av1.h +++ b/libavcodec/cbs_av1.h @@ -105,7 +105,7 @@ typedef struct AV1RawSequenceHeader { uint8_t use_128x128_superblock; uint8_t enable_filter_intra; uint8_t enable_intra_edge_filter; - uint8_t enable_intraintra_compound; + uint8_t enable_interintra_compound; uint8_t enable_masked_compound; uint8_t enable_warped_motion; uint8_t enable_dual_filter; @@ -256,20 +256,20 @@ typedef struct AV1RawFrameHeader { uint8_t update_grain; uint8_t film_grain_params_ref_idx; uint8_t num_y_points; - uint8_t point_y_value[16]; - uint8_t point_y_scaling[16]; + uint8_t point_y_value[14]; + uint8_t point_y_scaling[14]; uint8_t chroma_scaling_from_luma; uint8_t num_cb_points; - uint8_t point_cb_value[16]; - uint8_t point_cb_scaling[16]; + uint8_t point_cb_value[10]; + uint8_t point_cb_scaling[10]; uint8_t num_cr_points; - uint8_t point_cr_value[16]; - uint8_t point_cr_scaling[16]; + uint8_t point_cr_value[10]; + uint8_t point_cr_scaling[10]; uint8_t grain_scaling_minus_8; uint8_t ar_coeff_lag; uint8_t ar_coeffs_y_plus_128[24]; - uint8_t ar_coeffs_cb_plus_128[24]; - uint8_t ar_coeffs_cr_plus_128[24]; + uint8_t ar_coeffs_cb_plus_128[25]; + uint8_t ar_coeffs_cr_plus_128[25]; uint8_t ar_coeff_shift_minus_6; uint8_t grain_scale_shift; uint8_t cb_mult; @@ -441,11 +441,9 @@ typedef struct CodedBitstreamAV1Context { int tile_cols; int tile_rows; - AV1ReferenceFrameState ref[AV1_NUM_REF_FRAMES]; - - // Write buffer. - uint8_t *write_buffer; - size_t write_buffer_size; + AV1ReferenceFrameState *ref; + AV1ReferenceFrameState read_ref[AV1_NUM_REF_FRAMES]; + AV1ReferenceFrameState write_ref[AV1_NUM_REF_FRAMES]; } CodedBitstreamAV1Context; diff --git a/libavcodec/cbs_av1_syntax_template.c b/libavcodec/cbs_av1_syntax_template.c index 806b302de6a..a315e8868a5 100644 --- a/libavcodec/cbs_av1_syntax_template.c +++ b/libavcodec/cbs_av1_syntax_template.c @@ -20,7 +20,6 @@ static int FUNC(obu_header)(CodedBitstreamContext *ctx, RWContext *rw, AV1RawOBUHeader *current) { int err; - av_unused int zero = 0; HEADER("OBU header"); @@ -268,7 +267,7 @@ static int FUNC(sequence_header_obu)(CodedBitstreamContext *ctx, RWContext *rw, flag(enable_intra_edge_filter); if (current->reduced_still_picture_header) { - infer(enable_intraintra_compound, 0); + infer(enable_interintra_compound, 0); infer(enable_masked_compound, 0); infer(enable_warped_motion, 0); infer(enable_dual_filter, 0); @@ -281,7 +280,7 @@ static int FUNC(sequence_header_obu)(CodedBitstreamContext *ctx, RWContext *rw, infer(seq_force_integer_mv, AV1_SELECT_INTEGER_MV); } else { - flag(enable_intraintra_compound); + flag(enable_interintra_compound); flag(enable_masked_compound); flag(enable_warped_motion); flag(enable_dual_filter); @@ -339,6 +338,117 @@ static int FUNC(temporal_delimiter_obu)(CodedBitstreamContext *ctx, RWContext *r return 0; } +static int FUNC(set_frame_refs)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawFrameHeader *current) +{ + CodedBitstreamAV1Context *priv = ctx->priv_data; + const AV1RawSequenceHeader *seq = priv->sequence_header; + static const uint8_t ref_frame_list[AV1_NUM_REF_FRAMES - 2] = { + AV1_REF_FRAME_LAST2, AV1_REF_FRAME_LAST3, AV1_REF_FRAME_BWDREF, + AV1_REF_FRAME_ALTREF2, AV1_REF_FRAME_ALTREF + }; + int8_t ref_frame_idx[AV1_REFS_PER_FRAME], used_frame[AV1_NUM_REF_FRAMES]; + int8_t shifted_order_hints[AV1_NUM_REF_FRAMES]; + int cur_frame_hint, latest_order_hint, earliest_order_hint, ref; + int i, j; + + for (i = 0; i < AV1_REFS_PER_FRAME; i++) + ref_frame_idx[i] = -1; + 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; + + for (i = 0; i < AV1_NUM_REF_FRAMES; i++) + used_frame[i] = 0; + used_frame[current->last_frame_idx] = 1; + used_frame[current->golden_frame_idx] = 1; + + cur_frame_hint = 1 << (seq->order_hint_bits_minus_1); + for (i = 0; i < AV1_NUM_REF_FRAMES; i++) + shifted_order_hints[i] = cur_frame_hint + + cbs_av1_get_relative_dist(seq, priv->ref[i].order_hint, + current->order_hint); + + latest_order_hint = shifted_order_hints[current->last_frame_idx]; + earliest_order_hint = shifted_order_hints[current->golden_frame_idx]; + + ref = -1; + for (i = 0; i < AV1_NUM_REF_FRAMES; i++) { + int hint = shifted_order_hints[i]; + if (!used_frame[i] && hint >= cur_frame_hint && + (ref < 0 || hint >= latest_order_hint)) { + ref = i; + latest_order_hint = hint; + } + } + if (ref >= 0) { + ref_frame_idx[AV1_REF_FRAME_ALTREF - AV1_REF_FRAME_LAST] = ref; + used_frame[ref] = 1; + } + + ref = -1; + for (i = 0; i < AV1_NUM_REF_FRAMES; i++) { + int hint = shifted_order_hints[i]; + if (!used_frame[i] && hint >= cur_frame_hint && + (ref < 0 || hint < earliest_order_hint)) { + ref = i; + earliest_order_hint = hint; + } + } + if (ref >= 0) { + ref_frame_idx[AV1_REF_FRAME_BWDREF - AV1_REF_FRAME_LAST] = ref; + used_frame[ref] = 1; + } + + ref = -1; + for (i = 0; i < AV1_NUM_REF_FRAMES; i++) { + int hint = shifted_order_hints[i]; + if (!used_frame[i] && hint >= cur_frame_hint && + (ref < 0 || hint < earliest_order_hint)) { + ref = i; + earliest_order_hint = hint; + } + } + if (ref >= 0) { + ref_frame_idx[AV1_REF_FRAME_ALTREF2 - AV1_REF_FRAME_LAST] = ref; + used_frame[ref] = 1; + } + + 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; + for (j = 0; j < AV1_NUM_REF_FRAMES; j++) { + int hint = shifted_order_hints[j]; + if (!used_frame[j] && hint < cur_frame_hint && + (ref < 0 || hint >= latest_order_hint)) { + ref = j; + latest_order_hint = hint; + } + } + if (ref >= 0) { + ref_frame_idx[ref_frame - AV1_REF_FRAME_LAST] = ref; + used_frame[ref] = 1; + } + } + } + + ref = -1; + for (i = 0; i < AV1_NUM_REF_FRAMES; i++) { + int hint = shifted_order_hints[i]; + if (ref < 0 || hint < earliest_order_hint) { + ref = i; + earliest_order_hint = hint; + } + } + for (i = 0; i < AV1_REFS_PER_FRAME; i++) { + if (ref_frame_idx[i] < 0) + ref_frame_idx[i] = ref; + infer(ref_frame_idx[i], ref_frame_idx[i]); + } + + return 0; +} + static int FUNC(superres_params)(CodedBitstreamContext *ctx, RWContext *rw, AV1RawFrameHeader *current) { @@ -419,17 +529,16 @@ static int FUNC(frame_size_with_refs)(CodedBitstreamContext *ctx, RWContext *rw, for (i = 0; i < AV1_REFS_PER_FRAME; i++) { flags(found_ref[i], 1, i); if (current->found_ref[i]) { - AV1ReferenceFrameState *ref; + AV1ReferenceFrameState *ref = + &priv->ref[current->ref_frame_idx[i]]; - if (current->ref_frame_idx[i] < 0 || - !priv->ref[current->ref_frame_idx[i]].valid) { + if (!ref->valid) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Missing reference frame needed for frame size " "(ref = %d, ref_frame_idx = %d).\n", i, current->ref_frame_idx[i]); return AVERROR_INVALIDDATA; } - ref = &priv->ref[current->ref_frame_idx[i]]; priv->upscaled_width = ref->upscaled_width; priv->frame_width = ref->frame_width; @@ -882,7 +991,7 @@ static int FUNC(skip_mode_params)(CodedBitstreamContext *ctx, RWContext *rw, forward_idx = -1; backward_idx = -1; for (i = 0; i < AV1_REFS_PER_FRAME; i++) { - ref_hint = priv->ref[i].order_hint; + ref_hint = priv->ref[current->ref_frame_idx[i]].order_hint; dist = cbs_av1_get_relative_dist(seq, ref_hint, current->order_hint); if (dist < 0) { @@ -913,7 +1022,7 @@ static int FUNC(skip_mode_params)(CodedBitstreamContext *ctx, RWContext *rw, second_forward_idx = -1; for (i = 0; i < AV1_REFS_PER_FRAME; i++) { - ref_hint = priv->ref[i].order_hint; + ref_hint = priv->ref[current->ref_frame_idx[i]].order_hint; if (cbs_av1_get_relative_dist(seq, ref_hint, forward_hint) < 0) { if (second_forward_idx < 0 || @@ -1045,9 +1154,12 @@ static int FUNC(film_grain_params)(CodedBitstreamContext *ctx, RWContext *rw, return 0; } - fb(4, num_y_points); + fc(4, num_y_points, 0, 14); for (i = 0; i < current->num_y_points; i++) { - fbs(8, point_y_value[i], 1, i); + fcs(8, point_y_value[i], + i ? current->point_y_value[i - 1] + 1 : 0, + MAX_UINT_BITS(8) - (current->num_y_points - i - 1), + 1, i); fbs(8, point_y_scaling[i], 1, i); } @@ -1064,14 +1176,20 @@ static int FUNC(film_grain_params)(CodedBitstreamContext *ctx, RWContext *rw, infer(num_cb_points, 0); infer(num_cr_points, 0); } else { - fb(4, num_cb_points); + fc(4, num_cb_points, 0, 10); for (i = 0; i < current->num_cb_points; i++) { - fbs(8, point_cb_value[i], 1, i); + fcs(8, point_cb_value[i], + i ? current->point_cb_value[i - 1] + 1 : 0, + MAX_UINT_BITS(8) - (current->num_cb_points - i - 1), + 1, i); fbs(8, point_cb_scaling[i], 1, i); } - fb(4, num_cr_points); + fc(4, num_cr_points, 0, 10); for (i = 0; i < current->num_cr_points; i++) { - fbs(8, point_cr_value[i], 1, i); + fcs(8, point_cr_value[i], + i ? current->point_cr_value[i - 1] + 1 : 0, + MAX_UINT_BITS(8) - (current->num_cr_points - i - 1), + 1, i); fbs(8, point_cr_scaling[i], 1, i); } } @@ -1307,16 +1425,7 @@ static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx, RWContext *rw, if (current->frame_refs_short_signaling) { fb(3, last_frame_idx); fb(3, golden_frame_idx); - - for (i = 0; i < AV1_REFS_PER_FRAME; i++) { - if (i == 0) - infer(ref_frame_idx[i], current->last_frame_idx); - else if (i == AV1_REF_FRAME_GOLDEN - - AV1_REF_FRAME_LAST) - infer(ref_frame_idx[i], current->golden_frame_idx); - else - infer(ref_frame_idx[i], -1); - } + CHECK(FUNC(set_frame_refs)(ctx, rw, current)); } } @@ -1500,8 +1609,6 @@ static int FUNC(frame_header_obu)(CodedBitstreamContext *ctx, RWContext *rw, else HEADER("Frame Header"); - priv->seen_frame_header = 1; - #ifdef READ start_pos = get_bits_count(rw); #else diff --git a/libavcodec/cbs_h264.h b/libavcodec/cbs_h264.h index b39e7480c98..9f7c2a0d308 100644 --- a/libavcodec/cbs_h264.h +++ b/libavcodec/cbs_h264.h @@ -468,10 +468,13 @@ typedef struct CodedBitstreamH264Context { /** * Add an SEI message to an access unit. + * + * On success, the payload will be owned by a unit in access_unit; + * on failure, the content of the payload will be freed. */ int ff_cbs_h264_add_sei_message(CodedBitstreamContext *ctx, CodedBitstreamFragment *access_unit, - const H264RawSEIPayload *payload); + H264RawSEIPayload *payload); /** * Delete an SEI message from an access unit. diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c index c95f1308e9d..64fe2c1b9be 100644 --- a/libavcodec/cbs_h2645.c +++ b/libavcodec/cbs_h2645.c @@ -233,6 +233,16 @@ static int cbs_write_se_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc, return 0; } +// payload_extension_present() - true if we are before the last 1-bit +// in the payload structure, which must be in the last byte. +static int cbs_h265_payload_extension_present(GetBitContext *gbc, uint32_t payload_size, + int cur_pos) +{ + int bits_left = payload_size * 8 - cur_pos; + return (bits_left > 0 && + (bits_left > 7 || show_bits(gbc, bits_left) & MAX_UINT_BITS(bits_left - 1))); +} + #define HEADER(name) do { \ ff_cbs_trace_header(ctx, name); \ } while (0) @@ -250,18 +260,18 @@ static int cbs_write_se_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc, #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) + 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) + 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) + xue(name, current->name, range_min, range_max, 0, ) #define i(width, name, range_min, range_max) \ - xi(width, name, current->name, range_min, range_max, 0) + xi(width, name, current->name, range_min, range_max, 0, ) #define ib(width, name) \ - xi(width, name, current->name, MIN_INT_BITS(width), MAX_INT_BITS(width), 0) + xi(width, name, current->name, MIN_INT_BITS(width), MAX_INT_BITS(width), 0, ) #define se(name, range_min, range_max) \ - xse(name, current->name, range_min, range_max, 0) + xse(name, current->name, range_min, range_max, 0, ) #define us(width, name, range_min, range_max, subs, ...) \ xu(width, name, current->name, range_min, range_max, subs, __VA_ARGS__) @@ -280,7 +290,7 @@ static int cbs_write_se_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc, #define fixed(width, name, value) do { \ av_unused uint32_t fixed_value = value; \ - xu(width, name, fixed_value, value, value, 0); \ + xu(width, name, fixed_value, value, value, 0, ); \ } while (0) @@ -289,28 +299,28 @@ static int cbs_write_se_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc, #define RWContext GetBitContext #define xu(width, name, var, range_min, range_max, subs, ...) do { \ - uint32_t value = range_min; \ + uint32_t value; \ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), \ &value, range_min, range_max)); \ var = value; \ } while (0) #define xue(name, var, range_min, range_max, subs, ...) do { \ - uint32_t value = range_min; \ + uint32_t value; \ CHECK(cbs_read_ue_golomb(ctx, rw, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), \ &value, range_min, range_max)); \ var = value; \ } while (0) #define xi(width, name, var, range_min, range_max, subs, ...) do { \ - int32_t value = range_min; \ + int32_t value; \ CHECK(ff_cbs_read_signed(ctx, rw, width, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), \ &value, range_min, range_max)); \ var = value; \ } while (0) #define xse(name, var, range_min, range_max, subs, ...) do { \ - int32_t value = range_min; \ + int32_t value; \ CHECK(cbs_read_se_golomb(ctx, rw, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), \ &value, range_min, range_max)); \ @@ -398,10 +408,11 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc) #define infer(name, value) do { \ if (current->name != (value)) { \ - av_log(ctx->log_ctx, AV_LOG_WARNING, "Warning: " \ + av_log(ctx->log_ctx, AV_LOG_ERROR, \ "%s does not match inferred value: " \ "%"PRId64", but should be %"PRId64".\n", \ #name, (int64_t)current->name, (int64_t)(value)); \ + return AVERROR_INVALIDDATA; \ } \ } while (0) @@ -443,7 +454,7 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc) #undef allocate -static void cbs_h264_free_pps(void *unit, uint8_t *content) +static void cbs_h264_free_pps(void *opaque, uint8_t *content) { H264RawPPS *pps = (H264RawPPS*)content; av_buffer_unref(&pps->slice_group_id_ref); @@ -473,7 +484,7 @@ static void cbs_h264_free_sei_payload(H264RawSEIPayload *payload) } } -static void cbs_h264_free_sei(void *unit, uint8_t *content) +static void cbs_h264_free_sei(void *opaque, uint8_t *content) { H264RawSEI *sei = (H264RawSEI*)content; int i; @@ -482,35 +493,35 @@ static void cbs_h264_free_sei(void *unit, uint8_t *content) av_freep(&content); } -static void cbs_h264_free_slice(void *unit, uint8_t *content) +static void cbs_h264_free_slice(void *opaque, uint8_t *content) { H264RawSlice *slice = (H264RawSlice*)content; av_buffer_unref(&slice->data_ref); av_freep(&content); } -static void cbs_h265_free_vps(void *unit, uint8_t *content) +static void cbs_h265_free_vps(void *opaque, uint8_t *content) { H265RawVPS *vps = (H265RawVPS*)content; av_buffer_unref(&vps->extension_data.data_ref); av_freep(&content); } -static void cbs_h265_free_sps(void *unit, uint8_t *content) +static void cbs_h265_free_sps(void *opaque, uint8_t *content) { H265RawSPS *sps = (H265RawSPS*)content; av_buffer_unref(&sps->extension_data.data_ref); av_freep(&content); } -static void cbs_h265_free_pps(void *unit, uint8_t *content) +static void cbs_h265_free_pps(void *opaque, uint8_t *content) { H265RawPPS *pps = (H265RawPPS*)content; av_buffer_unref(&pps->extension_data.data_ref); av_freep(&content); } -static void cbs_h265_free_slice(void *unit, uint8_t *content) +static void cbs_h265_free_slice(void *opaque, uint8_t *content) { H265RawSlice *slice = (H265RawSlice*)content; av_buffer_unref(&slice->data_ref); @@ -543,9 +554,10 @@ static void cbs_h265_free_sei_payload(H265RawSEIPayload *payload) av_buffer_unref(&payload->payload.other.data_ref); break; } + av_buffer_unref(&payload->extension_data.data_ref); } -static void cbs_h265_free_sei(void *unit, uint8_t *content) +static void cbs_h265_free_sei(void *opaque, uint8_t *content) { H265RawSEI *sei = (H265RawSEI*)content; int i; @@ -565,10 +577,16 @@ static int cbs_h2645_fragment_add_nals(CodedBitstreamContext *ctx, AVBufferRef *ref; size_t size = nal->size; + if (nal->nuh_layer_id > 0) + continue; + // Remove trailing zeroes. while (size > 0 && nal->data[size - 1] == 0) --size; - av_assert0(size > 0); + if (size == 0) { + av_log(ctx->log_ctx, AV_LOG_VERBOSE, "Discarding empty 0 NAL unit\n"); + continue; + } ref = (nal->data == nal->raw_data) ? frag->data_ref : packet->rbsp.rbsp_buffer_ref; @@ -610,7 +628,7 @@ static int cbs_h2645_split_fragment(CodedBitstreamContext *ctx, version = bytestream2_get_byte(&gbc); if (version != 1) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid AVCC header: " - "first byte %u.", version); + "first byte %u.\n", version); return AVERROR_INVALIDDATA; } @@ -685,7 +703,7 @@ static int cbs_h2645_split_fragment(CodedBitstreamContext *ctx, version = bytestream2_get_byte(&gbc); if (version != 1) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid HVCC header: " - "first byte %u.", version); + "first byte %u.\n", version); return AVERROR_INVALIDDATA; } @@ -748,7 +766,7 @@ static int cbs_h26 ## h26n ## _replace_ ## ps_var(CodedBitstreamContext *ctx, \ CodedBitstreamH26 ## h26n ## Context *priv = ctx->priv_data; \ H26 ## h26n ## Raw ## ps_name *ps_var = unit->content; \ unsigned int id = ps_var->id_element; \ - if (id > FF_ARRAY_ELEMS(priv->ps_var)) { \ + if (id >= FF_ARRAY_ELEMS(priv->ps_var)) { \ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid " #ps_name \ " id : %d.\n", id); \ return AVERROR_INVALIDDATA; \ @@ -855,15 +873,11 @@ static int cbs_h264_read_nal_unit(CodedBitstreamContext *ctx, 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 (!unit->data[len - 1]) { - int z; - for (z = 0; z < len && !unit->data[len - z - 1]; z++); - av_log(ctx->log_ctx, AV_LOG_DEBUG, "Deleted %d trailing zeroes " - "from slice data.\n", z); - len -= z; - } slice->data_size = len - pos / 8; slice->data_ref = av_buffer_ref(unit->data_ref); @@ -1037,15 +1051,11 @@ static int cbs_h265_read_nal_unit(CodedBitstreamContext *ctx, 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 (!unit->data[len - 1]) { - int z; - for (z = 0; z < len && !unit->data[len - z - 1]; z++); - av_log(ctx->log_ctx, AV_LOG_DEBUG, "Deleted %d trailing zeroes " - "from slice data.\n", z); - len -= z; - } slice->data_size = len - pos / 8; slice->data_ref = av_buffer_ref(unit->data_ref); @@ -1101,7 +1111,7 @@ static int cbs_h2645_write_slice_data(CodedBitstreamContext *ctx, const uint8_t *pos = data + data_bit_start / 8; av_assert0(data_bit_start >= 0 && - 8 * data_size > data_bit_start); + data_size > data_bit_start / 8); if (data_size * 8 + 8 > put_bits_left(pbc)) return AVERROR(ENOSPC); @@ -1380,65 +1390,6 @@ static int cbs_h265_write_nal_unit(CodedBitstreamContext *ctx, return 0; } -static int cbs_h2645_write_nal_unit(CodedBitstreamContext *ctx, - CodedBitstreamUnit *unit) -{ - CodedBitstreamH2645Context *priv = ctx->priv_data; - enum AVCodecID codec_id = ctx->codec->codec_id; - PutBitContext pbc; - int err; - - if (!priv->write_buffer) { - // Initial write buffer size is 1MB. - priv->write_buffer_size = 1024 * 1024; - - reallocate_and_try_again: - err = av_reallocp(&priv->write_buffer, priv->write_buffer_size); - if (err < 0) { - av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a " - "sufficiently large write buffer (last attempt " - "%"SIZE_SPECIFIER" bytes).\n", priv->write_buffer_size); - return err; - } - } - - init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size); - - if (codec_id == AV_CODEC_ID_H264) - err = cbs_h264_write_nal_unit(ctx, unit, &pbc); - else - err = cbs_h265_write_nal_unit(ctx, unit, &pbc); - - if (err == AVERROR(ENOSPC)) { - // Overflow. - priv->write_buffer_size *= 2; - goto reallocate_and_try_again; - } - // Overflow but we didn't notice. - av_assert0(put_bits_count(&pbc) <= 8 * priv->write_buffer_size); - - if (err < 0) { - // Write failed for some other reason. - return err; - } - - if (put_bits_count(&pbc) % 8) - unit->data_bit_padding = 8 - put_bits_count(&pbc) % 8; - else - unit->data_bit_padding = 0; - - unit->data_size = (put_bits_count(&pbc) + 7) / 8; - flush_put_bits(&pbc); - - err = ff_cbs_alloc_unit_data(ctx, unit, unit->data_size); - if (err < 0) - return err; - - memcpy(unit->data, priv->write_buffer, unit->data_size); - - return 0; -} - static int cbs_h2645_assemble_fragment(CodedBitstreamContext *ctx, CodedBitstreamFragment *frag) { @@ -1454,10 +1405,10 @@ static int cbs_h2645_assemble_fragment(CodedBitstreamContext *ctx, max_size = 0; for (i = 0; i < frag->nb_units; i++) { // Start code + content with worst-case emulation prevention. - max_size += 3 + frag->units[i].data_size * 3 / 2; + max_size += 4 + frag->units[i].data_size * 3 / 2; } - data = av_malloc(max_size + AV_INPUT_BUFFER_PADDING_SIZE); + data = av_realloc(NULL, max_size + AV_INPUT_BUFFER_PADDING_SIZE); if (!data) return AVERROR(ENOMEM); @@ -1533,8 +1484,6 @@ static void cbs_h264_close(CodedBitstreamContext *ctx) ff_h2645_packet_uninit(&h264->common.read_packet); - av_freep(&h264->common.write_buffer); - for (i = 0; i < FF_ARRAY_ELEMS(h264->sps); i++) av_buffer_unref(&h264->sps_ref[i]); for (i = 0; i < FF_ARRAY_ELEMS(h264->pps); i++) @@ -1548,8 +1497,6 @@ static void cbs_h265_close(CodedBitstreamContext *ctx) ff_h2645_packet_uninit(&h265->common.read_packet); - av_freep(&h265->common.write_buffer); - for (i = 0; i < FF_ARRAY_ELEMS(h265->vps); i++) av_buffer_unref(&h265->vps_ref[i]); for (i = 0; i < FF_ARRAY_ELEMS(h265->sps); i++) @@ -1565,7 +1512,7 @@ const CodedBitstreamType ff_cbs_type_h264 = { .split_fragment = &cbs_h2645_split_fragment, .read_unit = &cbs_h264_read_nal_unit, - .write_unit = &cbs_h2645_write_nal_unit, + .write_unit = &cbs_h264_write_nal_unit, .assemble_fragment = &cbs_h2645_assemble_fragment, .close = &cbs_h264_close, @@ -1578,7 +1525,7 @@ const CodedBitstreamType ff_cbs_type_h265 = { .split_fragment = &cbs_h2645_split_fragment, .read_unit = &cbs_h265_read_nal_unit, - .write_unit = &cbs_h2645_write_nal_unit, + .write_unit = &cbs_h265_write_nal_unit, .assemble_fragment = &cbs_h2645_assemble_fragment, .close = &cbs_h265_close, @@ -1586,39 +1533,42 @@ const CodedBitstreamType ff_cbs_type_h265 = { int ff_cbs_h264_add_sei_message(CodedBitstreamContext *ctx, CodedBitstreamFragment *au, - const H264RawSEIPayload *payload) + H264RawSEIPayload *payload) { - H264RawSEI *sei; - CodedBitstreamUnit *nal = NULL; + H264RawSEI *sei = NULL; int err, i; // Find an existing SEI NAL unit to add to. for (i = 0; i < au->nb_units; i++) { if (au->units[i].type == H264_NAL_SEI) { - nal = &au->units[i]; - break; + sei = au->units[i].content; + if (sei->payload_count < H264_MAX_SEI_PAYLOADS) + break; + + sei = NULL; } } - if (nal) { - sei = nal->content; - } else { + if (!sei) { // Need to make a new SEI NAL unit. Insert it before the first // slice data NAL unit; if no slice data, add at the end. AVBufferRef *sei_ref; sei = av_mallocz(sizeof(*sei)); - if (!sei) - return AVERROR(ENOMEM); + if (!sei) { + err = AVERROR(ENOMEM); + goto fail; + } sei->nal_unit_header.nal_unit_type = H264_NAL_SEI; sei->nal_unit_header.nal_ref_idc = 0; sei_ref = av_buffer_create((uint8_t*)sei, sizeof(*sei), - &cbs_h264_free_sei, ctx, 0); + &cbs_h264_free_sei, NULL, 0); if (!sei_ref) { av_freep(&sei); - return AVERROR(ENOMEM); + err = AVERROR(ENOMEM); + goto fail; } for (i = 0; i < au->nb_units; i++) { @@ -1631,19 +1581,16 @@ int ff_cbs_h264_add_sei_message(CodedBitstreamContext *ctx, sei, sei_ref); av_buffer_unref(&sei_ref); if (err < 0) - return err; - } - - if (sei->payload_count >= H264_MAX_SEI_PAYLOADS) { - av_log(ctx->log_ctx, AV_LOG_ERROR, "Too many payloads in " - "SEI NAL unit.\n"); - return AVERROR(EINVAL); + goto fail; } memcpy(&sei->payload[sei->payload_count], payload, sizeof(*payload)); ++sei->payload_count; return 0; +fail: + cbs_h264_free_sei_payload(payload); + return err; } void ff_cbs_h264_delete_sei_message(CodedBitstreamContext *ctx, diff --git a/libavcodec/cbs_h2645.h b/libavcodec/cbs_h2645.h index f4cf65bdde5..f4c987a5119 100644 --- a/libavcodec/cbs_h2645.h +++ b/libavcodec/cbs_h2645.h @@ -19,9 +19,6 @@ #ifndef AVCODEC_CBS_H2645_H #define AVCODEC_CBS_H2645_H -#include -#include - #include "h2645_parse.h" @@ -33,10 +30,6 @@ typedef struct CodedBitstreamH2645Context { int nal_length_size; // Packet reader. H2645Packet read_packet; - - // Write buffer - uint8_t *write_buffer; - size_t write_buffer_size; } CodedBitstreamH2645Context; diff --git a/libavcodec/cbs_h264_syntax_template.c b/libavcodec/cbs_h264_syntax_template.c index 26be6e590fb..b65460996b1 100644 --- a/libavcodec/cbs_h264_syntax_template.c +++ b/libavcodec/cbs_h264_syntax_template.c @@ -137,6 +137,10 @@ static int FUNC(vui_parameters)(CodedBitstreamContext *ctx, RWContext *rw, ub(8, colour_primaries); ub(8, transfer_characteristics); ub(8, matrix_coefficients); + } else { + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); } } else { infer(video_format, 5); @@ -950,6 +954,7 @@ static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw, current->payload[k].payload_type = payload_type; current->payload[k].payload_size = payload_size; + current->payload_count++; CHECK(FUNC(sei_payload)(ctx, rw, ¤t->payload[k])); if (!cbs_h2645_read_more_rbsp_data(rw)) @@ -960,7 +965,6 @@ static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw, "SEI message: found %d.\n", k); return AVERROR_INVALIDDATA; } - current->payload_count = k + 1; #else for (k = 0; k < current->payload_count; k++) { PutBitContext start_state; @@ -1362,7 +1366,7 @@ static int FUNC(slice_header)(CodedBitstreamContext *ctx, RWContext *rw, (sps->pic_height_in_map_units_minus1 + 1); max = (pic_size + pps->slice_group_change_rate_minus1) / (pps->slice_group_change_rate_minus1 + 1); - bits = av_log2(2 * max - 1); + bits = av_ceil_log2(max + 1); u(bits, slice_group_change_cycle, 0, max); } diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h index ad746bf35fa..73897f77a42 100644 --- a/libavcodec/cbs_h265.h +++ b/libavcodec/cbs_h265.h @@ -182,11 +182,11 @@ typedef struct H265RawVUI { uint8_t log2_max_mv_length_vertical; } H265RawVUI; -typedef struct H265RawPSExtensionData { +typedef struct H265RawExtensionData { uint8_t *data; size_t bit_length; AVBufferRef *data_ref; -} H265RawPSExtensionData; +} H265RawExtensionData; typedef struct H265RawVPS { H265RawNALUnitHeader nal_unit_header; @@ -221,7 +221,7 @@ typedef struct H265RawVPS { H265RawHRDParameters hrd_parameters[HEVC_MAX_LAYER_SETS]; uint8_t vps_extension_flag; - H265RawPSExtensionData extension_data; + H265RawExtensionData extension_data; } H265RawVPS; typedef struct H265RawSTRefPicSet { @@ -325,7 +325,7 @@ typedef struct H265RawSPS { uint8_t sps_scc_extension_flag; uint8_t sps_extension_4bits; - H265RawPSExtensionData extension_data; + H265RawExtensionData extension_data; // Range extension. uint8_t transform_skip_rotation_enabled_flag; @@ -413,7 +413,7 @@ typedef struct H265RawPPS { uint8_t pps_scc_extension_flag; uint8_t pps_extension_4bits; - H265RawPSExtensionData extension_data; + H265RawExtensionData extension_data; // Range extension. uint8_t log2_max_transform_skip_block_size_minus2; @@ -715,6 +715,7 @@ typedef struct H265RawSEIPayload { AVBufferRef *data_ref; } other; } payload; + H265RawExtensionData extension_data; } H265RawSEIPayload; typedef struct H265RawSEI { diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c index 54570929ec7..48fae82d048 100644 --- a/libavcodec/cbs_h265_syntax_template.c +++ b/libavcodec/cbs_h265_syntax_template.c @@ -59,7 +59,7 @@ static int FUNC(byte_alignment)(CodedBitstreamContext *ctx, RWContext *rw) } static int FUNC(extension_data)(CodedBitstreamContext *ctx, RWContext *rw, - H265RawPSExtensionData *current) + H265RawExtensionData *current) { int err; size_t k; @@ -80,7 +80,7 @@ static int FUNC(extension_data)(CodedBitstreamContext *ctx, RWContext *rw, } #else for (k = 0; k < current->bit_length; k++) - xu(1, extension_data, current->data[k / 8] >> (7 - k % 8), 0, 1, 0); + xu(1, extension_data, current->data[k / 8] >> (7 - k % 8) & 1, 0, 1, 0); #endif return 0; } @@ -522,7 +522,7 @@ static int FUNC(st_ref_pic_set)(CodedBitstreamContext *ctx, RWContext *rw, infer(inter_ref_pic_set_prediction_flag, 0); if (current->inter_ref_pic_set_prediction_flag) { - unsigned int ref_rps_idx, num_delta_pocs; + unsigned int ref_rps_idx, num_delta_pocs, num_ref_pics; const H265RawSTRefPicSet *ref; int delta_rps, d_poc; int ref_delta_poc_s0[HEVC_MAX_REFS], ref_delta_poc_s1[HEVC_MAX_REFS]; @@ -538,18 +538,28 @@ static int FUNC(st_ref_pic_set)(CodedBitstreamContext *ctx, RWContext *rw, ref_rps_idx = st_rps_idx - (current->delta_idx_minus1 + 1); ref = &sps->st_ref_pic_set[ref_rps_idx]; num_delta_pocs = ref->num_negative_pics + ref->num_positive_pics; + av_assert0(num_delta_pocs < HEVC_MAX_DPB_SIZE); flag(delta_rps_sign); ue(abs_delta_rps_minus1, 0, INT16_MAX); delta_rps = (1 - 2 * current->delta_rps_sign) * (current->abs_delta_rps_minus1 + 1); + num_ref_pics = 0; for (j = 0; j <= num_delta_pocs; j++) { flags(used_by_curr_pic_flag[j], 1, j); if (!current->used_by_curr_pic_flag[j]) flags(use_delta_flag[j], 1, j); else infer(use_delta_flag[j], 1); + if (current->use_delta_flag[j]) + ++num_ref_pics; + } + if (num_ref_pics >= HEVC_MAX_DPB_SIZE) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid stream: " + "short-term ref pic set %d " + "contains too many pictures.\n", st_rps_idx); + return AVERROR_INVALIDDATA; } // Since the stored form of an RPS here is actually the delta-step @@ -734,6 +744,32 @@ static int FUNC(sps_scc_extension)(CodedBitstreamContext *ctx, RWContext *rw, return 0; } +static int FUNC(vui_parameters_default)(CodedBitstreamContext *ctx, + RWContext *rw, H265RawVUI *current, + H265RawSPS *sps) +{ + infer(aspect_ratio_idc, 0); + + infer(video_format, 5); + infer(video_full_range_flag, 0); + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); + + infer(chroma_sample_loc_type_top_field, 0); + infer(chroma_sample_loc_type_bottom_field, 0); + + infer(tiles_fixed_structure_flag, 0); + infer(motion_vectors_over_pic_boundaries_flag, 1); + infer(min_spatial_segmentation_idc, 0); + infer(max_bytes_per_pic_denom, 2); + infer(max_bits_per_min_cu_denom, 1); + infer(log2_max_mv_length_horizontal, 15); + infer(log2_max_mv_length_vertical, 15); + + return 0; +} + static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw, H265RawSPS *current) { @@ -898,6 +934,8 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw, flag(vui_parameters_present_flag); if (current->vui_parameters_present_flag) CHECK(FUNC(vui_parameters)(ctx, rw, ¤t->vui, current)); + else + CHECK(FUNC(vui_parameters_default)(ctx, rw, ¤t->vui, current)); flag(sps_extension_present_flag); if (current->sps_extension_present_flag) { @@ -1275,7 +1313,7 @@ static int FUNC(slice_segment_header)(CodedBitstreamContext *ctx, RWContext *rw, flag(first_slice_segment_in_pic_flag); if (current->nal_unit_header.nal_unit_type >= HEVC_NAL_BLA_W_LP && - current->nal_unit_header.nal_unit_type <= HEVC_NAL_IRAP_VCL23) + current->nal_unit_header.nal_unit_type <= HEVC_NAL_RSV_IRAP_VCL23) flag(no_output_of_prior_pics_flag); ue(slice_pic_parameter_set_id, 0, 63); @@ -1367,7 +1405,7 @@ static int FUNC(slice_segment_header)(CodedBitstreamContext *ctx, RWContext *rw, infer(num_long_term_sps, 0); idx_size = 0; } - ue(num_long_term_pics, 0, HEVC_MAX_LONG_TERM_REF_PICS); + ue(num_long_term_pics, 0, HEVC_MAX_REFS - current->num_long_term_sps); for (i = 0; i < current->num_long_term_sps + current->num_long_term_pics; i++) { @@ -1560,7 +1598,8 @@ static int FUNC(slice_segment_header)(CodedBitstreamContext *ctx, RWContext *rw, static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw, H265RawSEIBufferingPeriod *current, - uint32_t *payload_size) + uint32_t *payload_size, + int *more_data) { CodedBitstreamH265Context *h265 = ctx->priv_data; const H265RawSPS *sps; @@ -1568,7 +1607,7 @@ static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw, int err, i, length; #ifdef READ - int start_pos, end_pos, bits_left; + int start_pos, end_pos; start_pos = get_bits_count(rw); #endif @@ -1647,18 +1686,22 @@ static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw, } #ifdef READ - // payload_extension_present() - true if we are before the last 1-bit - // in the payload structure, which must be in the last byte. end_pos = get_bits_count(rw); - bits_left = *payload_size * 8 - (end_pos - start_pos); - if (bits_left > 0 && - (bits_left > 7 || ff_ctz(show_bits(rw, bits_left)) < bits_left - 1)) + if (cbs_h265_payload_extension_present(rw, *payload_size, + end_pos - start_pos)) flag(use_alt_cpb_params_flag); else infer(use_alt_cpb_params_flag, 0); #else - if (current->use_alt_cpb_params_flag) + // If unknown extension data exists, then use_alt_cpb_params_flag is + // coded in the bitstream and must be written even if it's 0. + if (current->use_alt_cpb_params_flag || *more_data) { flag(use_alt_cpb_params_flag); + // Ensure this bit is not the last in the payload by making the + // more_data_in_payload() check evaluate to true, so it may not + // be mistaken as something else by decoders. + *more_data = 1; + } #endif return 0; @@ -2056,11 +2099,48 @@ static int FUNC(sei_alpha_channel_info)(CodedBitstreamContext *ctx, return 0; } +static int FUNC(payload_extension)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawExtensionData *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(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw, H265RawSEIPayload *current, int prefix) { int err, i; - int start_position, end_position; + int start_position, current_position; + int more_data = !!current->extension_data.bit_length; #ifdef READ start_position = get_bits_count(rw); @@ -2092,8 +2172,15 @@ static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw, CHECK(FUNC(sei_ ## name)(ctx, rw, ¤t->payload.name, \ ¤t->payload_size)); \ break +#define SEI_TYPE_E(type, prefix_valid, suffix_valid, name) \ + case HEVC_SEI_TYPE_ ## type: \ + SEI_TYPE_CHECK_VALID(name, prefix_valid, suffix_valid); \ + CHECK(FUNC(sei_ ## name)(ctx, rw, ¤t->payload.name, \ + ¤t->payload_size, \ + &more_data)); \ + break - SEI_TYPE_S(BUFFERING_PERIOD, 1, 0, buffering_period); + SEI_TYPE_E(BUFFERING_PERIOD, 1, 0, buffering_period); SEI_TYPE_N(PICTURE_TIMING, 1, 0, pic_timing); SEI_TYPE_N(PAN_SCAN_RECT, 1, 0, pan_scan_rect); SEI_TYPE_S(USER_DATA_REGISTERED_ITU_T_T35, @@ -2124,24 +2211,23 @@ static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw, } } - if (byte_alignment(rw)) { + // more_data_in_payload() +#ifdef READ + current_position = get_bits_count(rw) - start_position; + if (current_position < 8 * current->payload_size) { +#else + current_position = put_bits_count(rw) - start_position; + if (byte_alignment(rw) || more_data) { +#endif + CHECK(FUNC(payload_extension)(ctx, rw, ¤t->extension_data, + current->payload_size, current_position)); fixed(1, bit_equal_to_one, 1); while (byte_alignment(rw)) fixed(1, bit_equal_to_zero, 0); } -#ifdef READ - end_position = get_bits_count(rw); - if (end_position < start_position + 8 * current->payload_size) { - av_log(ctx->log_ctx, AV_LOG_ERROR, "Incorrect SEI payload length: " - "header %"PRIu32" bits, actually %d bits.\n", - 8 * current->payload_size, - end_position - start_position); - return AVERROR_INVALIDDATA; - } -#else - end_position = put_bits_count(rw); - current->payload_size = (end_position - start_position) >> 3; +#ifdef WRITE + current->payload_size = (put_bits_count(rw) - start_position) >> 3; #endif return 0; @@ -2184,6 +2270,7 @@ static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw, current->payload[k].payload_type = payload_type; current->payload[k].payload_size = payload_size; + current->payload_count++; CHECK(FUNC(sei_payload)(ctx, rw, ¤t->payload[k], prefix)); if (!cbs_h2645_read_more_rbsp_data(rw)) @@ -2194,7 +2281,6 @@ static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw, "SEI message: found %d.\n", k); return AVERROR_INVALIDDATA; } - current->payload_count = k + 1; #else for (k = 0; k < current->payload_count; k++) { PutBitContext start_state; diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h index dd4babf092a..4c5a535ca6e 100644 --- a/libavcodec/cbs_internal.h +++ b/libavcodec/cbs_internal.h @@ -44,9 +44,11 @@ typedef struct CodedBitstreamType { int (*read_unit)(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit); - // Write the unit->data bitstream from unit->content. + // Write the data bitstream from unit->content into pbc. + // Return value AVERROR(ENOSPC) indicates that pbc was too small. int (*write_unit)(CodedBitstreamContext *ctx, - CodedBitstreamUnit *unit); + CodedBitstreamUnit *unit, + PutBitContext *pbc); // Read the data from all of frag->units and assemble it into // a bitstream for the whole fragment. diff --git a/libavcodec/cbs_jpeg.c b/libavcodec/cbs_jpeg.c index 83857bbba2d..471d77074f5 100644 --- a/libavcodec/cbs_jpeg.c +++ b/libavcodec/cbs_jpeg.c @@ -34,7 +34,7 @@ #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL) #define u(width, name, range_min, range_max) \ - xu(width, name, range_min, range_max, 0) + xu(width, name, range_min, range_max, 0, ) #define us(width, name, sub, range_min, range_max) \ xu(width, name, range_min, range_max, 1, sub) @@ -45,7 +45,7 @@ #define FUNC(name) cbs_jpeg_read_ ## name #define xu(width, name, range_min, range_max, subs, ...) do { \ - uint32_t value = range_min; \ + uint32_t value; \ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), \ &value, range_min, range_max)); \ @@ -82,21 +82,21 @@ #undef xu -static void cbs_jpeg_free_application_data(void *unit, uint8_t *content) +static void cbs_jpeg_free_application_data(void *opaque, uint8_t *content) { JPEGRawApplicationData *ad = (JPEGRawApplicationData*)content; av_buffer_unref(&ad->Ap_ref); av_freep(&content); } -static void cbs_jpeg_free_comment(void *unit, uint8_t *content) +static void cbs_jpeg_free_comment(void *opaque, uint8_t *content) { JPEGRawComment *comment = (JPEGRawComment*)content; av_buffer_unref(&comment->Cm_ref); av_freep(&content); } -static void cbs_jpeg_free_scan(void *unit, uint8_t *content) +static void cbs_jpeg_free_scan(void *opaque, uint8_t *content) { JPEGRawScan *scan = (JPEGRawScan*)content; av_buffer_unref(&scan->data_ref); @@ -148,15 +148,15 @@ static int cbs_jpeg_split_fragment(CodedBitstreamContext *ctx, if (marker == JPEG_MARKER_EOI) { break; } else if (marker == JPEG_MARKER_SOS) { + next_marker = -1; + end = start; for (i = start; i + 1 < frag->data_size; i++) { if (frag->data[i] != 0xff) continue; end = i; for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++); - if (i + 1 >= frag->data_size) { - next_marker = -1; - } else { + if (i + 1 < frag->data_size) { if (frag->data[i] == 0x00) continue; next_marker = frag->data[i]; @@ -197,6 +197,9 @@ static int cbs_jpeg_split_fragment(CodedBitstreamContext *ctx, if (marker == JPEG_MARKER_SOS) { length = AV_RB16(frag->data + start); + if (length > end - start) + return AVERROR_INVALIDDATA; + data_ref = NULL; data = av_malloc(end - start + AV_INPUT_BUFFER_PADDING_SIZE); @@ -225,11 +228,8 @@ static int cbs_jpeg_split_fragment(CodedBitstreamContext *ctx, err = ff_cbs_insert_unit_data(ctx, frag, unit, marker, data, data_size, data_ref); - if (err < 0) { - if (!data_ref) - av_freep(&data); + if (err < 0) return err; - } if (next_marker == -1) break; @@ -330,7 +330,7 @@ static int cbs_jpeg_write_scan(CodedBitstreamContext *ctx, PutBitContext *pbc) { JPEGRawScan *scan = unit->content; - int i, err; + int err; err = cbs_jpeg_write_scan_header(ctx, pbc, &scan->header); if (err < 0) @@ -340,8 +340,12 @@ static int cbs_jpeg_write_scan(CodedBitstreamContext *ctx, if (scan->data_size * 8 > put_bits_left(pbc)) return AVERROR(ENOSPC); - for (i = 0; i < scan->data_size; i++) - put_bits(pbc, 8, scan->data[i]); + av_assert0(put_bits_count(pbc) % 8 == 0); + + flush_put_bits(pbc); + + memcpy(put_bits_ptr(pbc), scan->data, scan->data_size); + skip_put_bytes(pbc, scan->data_size); } return 0; @@ -377,58 +381,13 @@ static int cbs_jpeg_write_segment(CodedBitstreamContext *ctx, } static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx, - CodedBitstreamUnit *unit) + CodedBitstreamUnit *unit, + PutBitContext *pbc) { - CodedBitstreamJPEGContext *priv = ctx->priv_data; - PutBitContext pbc; - int err; - - if (!priv->write_buffer) { - // Initial write buffer size is 1MB. - priv->write_buffer_size = 1024 * 1024; - - reallocate_and_try_again: - err = av_reallocp(&priv->write_buffer, priv->write_buffer_size); - if (err < 0) { - av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a " - "sufficiently large write buffer (last attempt " - "%"SIZE_SPECIFIER" bytes).\n", priv->write_buffer_size); - return err; - } - } - - init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size); - if (unit->type == JPEG_MARKER_SOS) - err = cbs_jpeg_write_scan(ctx, unit, &pbc); + return cbs_jpeg_write_scan (ctx, unit, pbc); else - err = cbs_jpeg_write_segment(ctx, unit, &pbc); - - if (err == AVERROR(ENOSPC)) { - // Overflow. - priv->write_buffer_size *= 2; - goto reallocate_and_try_again; - } - if (err < 0) { - // Write failed for some other reason. - return err; - } - - if (put_bits_count(&pbc) % 8) - unit->data_bit_padding = 8 - put_bits_count(&pbc) % 8; - else - unit->data_bit_padding = 0; - - unit->data_size = (put_bits_count(&pbc) + 7) / 8; - flush_put_bits(&pbc); - - err = ff_cbs_alloc_unit_data(ctx, unit, unit->data_size); - if (err < 0) - return err; - - memcpy(unit->data, priv->write_buffer, unit->data_size); - - return 0; + return cbs_jpeg_write_segment(ctx, unit, pbc); } static int cbs_jpeg_assemble_fragment(CodedBitstreamContext *ctx, @@ -499,22 +458,11 @@ static int cbs_jpeg_assemble_fragment(CodedBitstreamContext *ctx, return 0; } -static void cbs_jpeg_close(CodedBitstreamContext *ctx) -{ - CodedBitstreamJPEGContext *priv = ctx->priv_data; - - av_freep(&priv->write_buffer); -} - const CodedBitstreamType ff_cbs_type_jpeg = { .codec_id = AV_CODEC_ID_MJPEG, - .priv_data_size = sizeof(CodedBitstreamJPEGContext), - .split_fragment = &cbs_jpeg_split_fragment, .read_unit = &cbs_jpeg_read_unit, .write_unit = &cbs_jpeg_write_unit, .assemble_fragment = &cbs_jpeg_assemble_fragment, - - .close = &cbs_jpeg_close, }; diff --git a/libavcodec/cbs_jpeg.h b/libavcodec/cbs_jpeg.h index 913d3f90f6f..ff1961106f4 100644 --- a/libavcodec/cbs_jpeg.h +++ b/libavcodec/cbs_jpeg.h @@ -120,11 +120,4 @@ typedef struct JPEGRawComment { } JPEGRawComment; -typedef struct CodedBitstreamJPEGContext { - // Write buffer. - uint8_t *write_buffer; - size_t write_buffer_size; -} CodedBitstreamJPEGContext; - - #endif /* AVCODEC_CBS_JPEG_H */ diff --git a/libavcodec/cbs_jpeg_syntax_template.c b/libavcodec/cbs_jpeg_syntax_template.c index d3cd9ff62e7..6eda56d623c 100644 --- a/libavcodec/cbs_jpeg_syntax_template.c +++ b/libavcodec/cbs_jpeg_syntax_template.c @@ -89,6 +89,8 @@ static int FUNC(huffman_table)(CodedBitstreamContext *ctx, RWContext *rw, ij = 0; for (i = 0; i < 16; i++) { for (j = 0; j < current->L[i]; j++) { + if (ij >= 224) + return AVERROR_INVALIDDATA; us(8, V[ij], ij, 0, 255); ++ij; } @@ -108,6 +110,9 @@ static int FUNC(dht)(CodedBitstreamContext *ctx, RWContext *rw, n = 2; for (i = 0; n < current->Lh; i++) { + if (i >= 8) + return AVERROR_INVALIDDATA; + CHECK(FUNC(huffman_table)(ctx, rw, ¤t->table[i])); ++n; diff --git a/libavcodec/cbs_mpeg2.c b/libavcodec/cbs_mpeg2.c index cb202f835bb..97f7889cbb8 100644 --- a/libavcodec/cbs_mpeg2.c +++ b/libavcodec/cbs_mpeg2.c @@ -41,24 +41,33 @@ #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) + 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) + xui(width, name, current->name, 1, MAX_UINT_BITS(width), 0, ) #define uis(width, name, subs, ...) \ xui(width, name, current->name, 0, MAX_UINT_BITS(width), subs, __VA_ARGS__) #define uirs(width, name, subs, ...) \ xui(width, name, current->name, 1, MAX_UINT_BITS(width), subs, __VA_ARGS__) +#define xui(width, name, var, range_min, range_max, subs, ...) \ + xuia(width, #name, var, range_min, range_max, subs, __VA_ARGS__) #define sis(width, name, subs, ...) \ xsi(width, name, current->name, subs, __VA_ARGS__) +#define marker_bit() \ + bit("marker_bit", 1) +#define bit(string, value) do { \ + av_unused uint32_t bit = value; \ + xuia(1, string, bit, value, value, 0, ); \ + } while (0) + #define READ #define READWRITE read #define RWContext GetBitContext -#define xui(width, name, var, range_min, range_max, subs, ...) do { \ - uint32_t value = 0; \ - CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ +#define xuia(width, string, var, range_min, range_max, subs, ...) do { \ + uint32_t value; \ + CHECK(ff_cbs_read_unsigned(ctx, rw, width, string, \ SUBSCRIPTS(subs, __VA_ARGS__), \ &value, range_min, range_max)); \ var = value; \ @@ -73,32 +82,31 @@ var = value; \ } while (0) -#define marker_bit() do { \ - av_unused uint32_t one; \ - CHECK(ff_cbs_read_unsigned(ctx, rw, 1, "marker_bit", NULL, &one, 1, 1)); \ - } while (0) - #define nextbits(width, compare, var) \ (get_bits_left(rw) >= width && \ (var = show_bits(rw, width)) == (compare)) +#define infer(name, value) do { \ + current->name = value; \ + } while (0) + #include "cbs_mpeg2_syntax_template.c" #undef READ #undef READWRITE #undef RWContext -#undef xui +#undef xuia #undef xsi -#undef marker_bit #undef nextbits +#undef infer #define WRITE #define READWRITE write #define RWContext PutBitContext -#define xui(width, name, var, range_min, range_max, subs, ...) do { \ - CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ +#define xuia(width, string, var, range_min, range_max, subs, ...) do { \ + CHECK(ff_cbs_write_unsigned(ctx, rw, width, string, \ SUBSCRIPTS(subs, __VA_ARGS__), \ var, range_min, range_max)); \ } while (0) @@ -110,34 +118,46 @@ MAX_INT_BITS(width))); \ } while (0) -#define marker_bit() do { \ - CHECK(ff_cbs_write_unsigned(ctx, rw, 1, "marker_bit", NULL, 1, 1, 1)); \ - } while (0) - #define nextbits(width, compare, var) (var) +#define infer(name, value) do { \ + if (current->name != (value)) { \ + av_log(ctx->log_ctx, AV_LOG_WARNING, "Warning: " \ + "%s does not match inferred value: " \ + "%"PRId64", but should be %"PRId64".\n", \ + #name, (int64_t)current->name, (int64_t)(value)); \ + } \ + } while (0) + #include "cbs_mpeg2_syntax_template.c" #undef WRITE #undef READWRITE #undef RWContext -#undef xui +#undef xuia #undef xsi -#undef marker_bit #undef nextbits +#undef infer -static void cbs_mpeg2_free_user_data(void *unit, uint8_t *content) +static void cbs_mpeg2_free_picture_header(void *opaque, uint8_t *content) +{ + MPEG2RawPictureHeader *picture = (MPEG2RawPictureHeader*)content; + av_buffer_unref(&picture->extra_information_picture.extra_information_ref); + av_freep(&content); +} + +static void cbs_mpeg2_free_user_data(void *opaque, uint8_t *content) { MPEG2RawUserData *user = (MPEG2RawUserData*)content; av_buffer_unref(&user->user_data_ref); av_freep(&content); } -static void cbs_mpeg2_free_slice(void *unit, uint8_t *content) +static void cbs_mpeg2_free_slice(void *opaque, uint8_t *content) { MPEG2RawSlice *slice = (MPEG2RawSlice*)content; - av_buffer_unref(&slice->header.extra_information_ref); + av_buffer_unref(&slice->header.extra_information_slice.extra_information_ref); av_buffer_unref(&slice->data_ref); av_freep(&content); } @@ -147,41 +167,54 @@ static int cbs_mpeg2_split_fragment(CodedBitstreamContext *ctx, int header) { const uint8_t *start, *end; - uint8_t *unit_data; - uint32_t start_code = -1, next_start_code = -1; + CodedBitstreamUnitType unit_type; + uint32_t start_code = -1; size_t unit_size; - int err, i, unit_type; + int err, i, final = 0; start = avpriv_find_start_code(frag->data, frag->data + frag->data_size, &start_code); - for (i = 0;; i++) { - end = avpriv_find_start_code(start, frag->data + frag->data_size, - &next_start_code); + if (start_code >> 8 != 0x000001) { + // No start code found. + return AVERROR_INVALIDDATA; + } + for (i = 0;; i++) { unit_type = start_code & 0xff; - // The start and end pointers point at to the byte following the - // start_code_identifier in the start code that they found. - if (end == frag->data + frag->data_size) { - // We didn't find a start code, so this is the final unit. - unit_size = end - (start - 1); - } else { + if (start == frag->data + frag->data_size) { + // The last four bytes form a start code which constitutes + // a unit of its own. In this situation avpriv_find_start_code + // won't modify start_code at all so modify start_code so that + // the next unit will be treated as the last unit. + start_code = 0; + } + + end = avpriv_find_start_code(start--, frag->data + frag->data_size, + &start_code); + + // start points to the byte containing the start_code_identifier + // (may be the last byte of fragment->data); end points to the byte + // following the byte containing the start code identifier (or to + // the end of fragment->data). + if (start_code >> 8 == 0x000001) { // Unit runs from start to the beginning of the start code // pointed to by end (including any padding zeroes). - unit_size = (end - 4) - (start - 1); + unit_size = (end - 4) - start; + } else { + // We didn't find a start code, so this is the final unit. + unit_size = end - start; + final = 1; } - unit_data = (uint8_t *)start - 1; - - err = ff_cbs_insert_unit_data(ctx, frag, i, unit_type, - unit_data, unit_size, frag->data_ref); + err = ff_cbs_insert_unit_data(ctx, frag, i, unit_type, (uint8_t*)start, + unit_size, frag->data_ref); if (err < 0) return err; - if (end == frag->data + frag->data_size) + if (final) break; - start_code = next_start_code; start = end; } @@ -212,6 +245,9 @@ static int cbs_mpeg2_read_unit(CodedBitstreamContext *ctx, if (err < 0) return err; + if (!get_bits_left(&gbc)) + return AVERROR_INVALIDDATA; + pos = get_bits_count(&gbc); len = unit->data_size; @@ -240,7 +276,7 @@ static int cbs_mpeg2_read_unit(CodedBitstreamContext *ctx, } \ break; START(MPEG2_START_PICTURE, MPEG2RawPictureHeader, - picture_header, NULL); + picture_header, &cbs_mpeg2_free_picture_header); START(MPEG2_START_USER_DATA, MPEG2RawUserData, user_data, &cbs_mpeg2_free_user_data); START(MPEG2_START_SEQUENCE_HEADER, MPEG2RawSequenceHeader, @@ -249,6 +285,8 @@ static int cbs_mpeg2_read_unit(CodedBitstreamContext *ctx, extension_data, NULL); START(MPEG2_START_GROUP, MPEG2RawGroupOfPicturesHeader, group_of_pictures_header, NULL); + START(MPEG2_START_SEQUENCE_END, MPEG2RawSequenceEnd, + sequence_end, NULL); #undef START default: return AVERROR(ENOSYS); @@ -275,6 +313,7 @@ static int cbs_mpeg2_write_header(CodedBitstreamContext *ctx, START(MPEG2_START_EXTENSION, MPEG2RawExtensionData, extension_data); START(MPEG2_START_GROUP, MPEG2RawGroupOfPicturesHeader, group_of_pictures_header); + START(MPEG2_START_SEQUENCE_END, MPEG2RawSequenceEnd, sequence_end); #undef START default: av_log(ctx->log_ctx, AV_LOG_ERROR, "Write unimplemented for start " @@ -301,7 +340,7 @@ static int cbs_mpeg2_write_slice(CodedBitstreamContext *ctx, uint8_t *pos = slice->data + slice->data_bit_start / 8; av_assert0(slice->data_bit_start >= 0 && - 8 * slice->data_size > slice->data_bit_start); + slice->data_size > slice->data_bit_start / 8); if (slice->data_size * 8 + 8 > put_bits_left(pbc)) return AVERROR(ENOSPC); @@ -335,58 +374,13 @@ static int cbs_mpeg2_write_slice(CodedBitstreamContext *ctx, } static int cbs_mpeg2_write_unit(CodedBitstreamContext *ctx, - CodedBitstreamUnit *unit) + CodedBitstreamUnit *unit, + PutBitContext *pbc) { - CodedBitstreamMPEG2Context *priv = ctx->priv_data; - PutBitContext pbc; - int err; - - if (!priv->write_buffer) { - // Initial write buffer size is 1MB. - priv->write_buffer_size = 1024 * 1024; - - reallocate_and_try_again: - err = av_reallocp(&priv->write_buffer, priv->write_buffer_size); - if (err < 0) { - av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a " - "sufficiently large write buffer (last attempt " - "%"SIZE_SPECIFIER" bytes).\n", priv->write_buffer_size); - return err; - } - } - - init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size); - if (MPEG2_START_IS_SLICE(unit->type)) - err = cbs_mpeg2_write_slice(ctx, unit, &pbc); - else - err = cbs_mpeg2_write_header(ctx, unit, &pbc); - - if (err == AVERROR(ENOSPC)) { - // Overflow. - priv->write_buffer_size *= 2; - goto reallocate_and_try_again; - } - if (err < 0) { - // Write failed for some other reason. - return err; - } - - if (put_bits_count(&pbc) % 8) - unit->data_bit_padding = 8 - put_bits_count(&pbc) % 8; + return cbs_mpeg2_write_slice (ctx, unit, pbc); else - unit->data_bit_padding = 0; - - unit->data_size = (put_bits_count(&pbc) + 7) / 8; - flush_put_bits(&pbc); - - err = ff_cbs_alloc_unit_data(ctx, unit, unit->data_size); - if (err < 0) - return err; - - memcpy(unit->data, priv->write_buffer, unit->data_size); - - return 0; + return cbs_mpeg2_write_header(ctx, unit, pbc); } static int cbs_mpeg2_assemble_fragment(CodedBitstreamContext *ctx, @@ -426,13 +420,6 @@ static int cbs_mpeg2_assemble_fragment(CodedBitstreamContext *ctx, return 0; } -static void cbs_mpeg2_close(CodedBitstreamContext *ctx) -{ - CodedBitstreamMPEG2Context *priv = ctx->priv_data; - - av_freep(&priv->write_buffer); -} - const CodedBitstreamType ff_cbs_type_mpeg2 = { .codec_id = AV_CODEC_ID_MPEG2VIDEO, @@ -442,6 +429,4 @@ const CodedBitstreamType ff_cbs_type_mpeg2 = { .read_unit = &cbs_mpeg2_read_unit, .write_unit = &cbs_mpeg2_write_unit, .assemble_fragment = &cbs_mpeg2_assemble_fragment, - - .close = &cbs_mpeg2_close, }; diff --git a/libavcodec/cbs_mpeg2.h b/libavcodec/cbs_mpeg2.h index 11f93b9df8f..5bcafd09f0e 100644 --- a/libavcodec/cbs_mpeg2.h +++ b/libavcodec/cbs_mpeg2.h @@ -114,6 +114,12 @@ typedef struct MPEG2RawGroupOfPicturesHeader { uint8_t broken_link; } MPEG2RawGroupOfPicturesHeader; +typedef struct MPEG2RawExtraInformation { + uint8_t *extra_information; + AVBufferRef *extra_information_ref; + size_t extra_information_length; +} MPEG2RawExtraInformation; + typedef struct MPEG2RawPictureHeader { uint8_t picture_start_code; @@ -126,7 +132,7 @@ typedef struct MPEG2RawPictureHeader { uint8_t full_pel_backward_vector; uint8_t backward_f_code; - uint8_t extra_bit_picture; + MPEG2RawExtraInformation extra_information_picture; } MPEG2RawPictureHeader; typedef struct MPEG2RawPictureCodingExtension { @@ -194,11 +200,7 @@ typedef struct MPEG2RawSliceHeader { uint8_t slice_picture_id_enable; uint8_t slice_picture_id; - uint8_t extra_bit_slice; - - size_t extra_information_length; - uint8_t *extra_information; - AVBufferRef *extra_information_ref; + MPEG2RawExtraInformation extra_information_slice; } MPEG2RawSliceHeader; typedef struct MPEG2RawSlice { @@ -210,6 +212,10 @@ typedef struct MPEG2RawSlice { AVBufferRef *data_ref; } MPEG2RawSlice; +typedef struct MPEG2RawSequenceEnd { + uint8_t sequence_end_code; +} MPEG2RawSequenceEnd; + typedef struct CodedBitstreamMPEG2Context { // Elements stored in headers which are required for other decoding. @@ -219,10 +225,6 @@ typedef struct CodedBitstreamMPEG2Context { uint8_t scalable_mode; uint8_t progressive_sequence; uint8_t number_of_frame_centre_offsets; - - // Write buffer. - uint8_t *write_buffer; - size_t write_buffer_size; } CodedBitstreamMPEG2Context; diff --git a/libavcodec/cbs_mpeg2_syntax_template.c b/libavcodec/cbs_mpeg2_syntax_template.c index e0cf7168747..5165a14cd50 100644 --- a/libavcodec/cbs_mpeg2_syntax_template.c +++ b/libavcodec/cbs_mpeg2_syntax_template.c @@ -144,6 +144,10 @@ static int FUNC(sequence_display_extension)(CodedBitstreamContext *ctx, RWContex uir(8, transfer_characteristics); uir(8, matrix_coefficients); #endif + } else { + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); } ui(14, display_horizontal_size); @@ -169,6 +173,40 @@ static int FUNC(group_of_pictures_header)(CodedBitstreamContext *ctx, RWContext return 0; } +static int FUNC(extra_information)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawExtraInformation *current, + const char *element_name, const char *marker_name) +{ + int err; + size_t k; +#ifdef READ + GetBitContext start = *rw; + uint8_t bit; + + for (k = 0; nextbits(1, 1, bit); k++) + skip_bits(rw, 1 + 8); + current->extra_information_length = k; + if (k > 0) { + *rw = start; + current->extra_information_ref = + av_buffer_allocz(k + AV_INPUT_BUFFER_PADDING_SIZE); + if (!current->extra_information_ref) + return AVERROR(ENOMEM); + current->extra_information = current->extra_information_ref->data; + } +#endif + + for (k = 0; k < current->extra_information_length; k++) { + bit(marker_name, 1); + xuia(8, element_name, + current->extra_information[k], 0, 255, 1, k); + } + + bit(marker_name, 0); + + return 0; +} + static int FUNC(picture_header)(CodedBitstreamContext *ctx, RWContext *rw, MPEG2RawPictureHeader *current) { @@ -193,7 +231,8 @@ static int FUNC(picture_header)(CodedBitstreamContext *ctx, RWContext *rw, ui(3, backward_f_code); } - ui(1, extra_bit_picture); + CHECK(FUNC(extra_information)(ctx, rw, ¤t->extra_information_picture, + "extra_information_picture[k]", "extra_bit_picture")); return 0; } @@ -365,39 +404,22 @@ static int FUNC(slice_header)(CodedBitstreamContext *ctx, RWContext *rw, ui(1, intra_slice); ui(1, slice_picture_id_enable); ui(6, slice_picture_id); - - { - size_t k; -#ifdef READ - GetBitContext start; - uint8_t bit; - start = *rw; - for (k = 0; nextbits(1, 1, bit); k++) - skip_bits(rw, 8); - current->extra_information_length = k; - if (k > 0) { - *rw = start; - current->extra_information_ref = - av_buffer_alloc(current->extra_information_length); - if (!current->extra_information_ref) - return AVERROR(ENOMEM); - current->extra_information = current->extra_information_ref->data; - for (k = 0; k < current->extra_information_length; k++) { - xui(1, extra_bit_slice, bit, 1, 1, 0); - xui(8, extra_information_slice[k], - current->extra_information[k], 0, 255, 1, k); - } - } -#else - for (k = 0; k < current->extra_information_length; k++) { - xui(1, extra_bit_slice, 1, 1, 1, 0); - xui(8, extra_information_slice[k], - current->extra_information[k], 0, 255, 1, k); - } -#endif - } } - ui(1, extra_bit_slice); + + CHECK(FUNC(extra_information)(ctx, rw, ¤t->extra_information_slice, + "extra_information_slice[k]", "extra_bit_slice")); + + return 0; +} + +static int FUNC(sequence_end)(CodedBitstreamContext *ctx, RWContext *rw, + MPEG2RawSequenceEnd *current) +{ + int err; + + HEADER("Sequence End"); + + ui(8, sequence_end_code); return 0; } diff --git a/libavcodec/cbs_vp9.c b/libavcodec/cbs_vp9.c index 5579d9b0af0..eef603bfb27 100644 --- a/libavcodec/cbs_vp9.c +++ b/libavcodec/cbs_vp9.c @@ -253,28 +253,27 @@ 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) + xf(width, name, current->name, 0, ) #define s(width, name) \ - xs(width, name, current->name, 0) + xs(width, name, current->name, 0, ) #define fs(width, name, subs, ...) \ xf(width, name, current->name, subs, __VA_ARGS__) #define ss(width, name, subs, ...) \ xs(width, name, current->name, subs, __VA_ARGS__) - #define READ #define READWRITE read #define RWContext GetBitContext #define xf(width, name, var, subs, ...) do { \ - uint32_t value = 0; \ + uint32_t value; \ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), \ &value, 0, (1 << width) - 1)); \ var = value; \ } while (0) #define xs(width, name, var, subs, ...) do { \ - int32_t value = 0; \ + int32_t value; \ CHECK(cbs_vp9_read_s(ctx, rw, width, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), &value)); \ var = value; \ @@ -282,7 +281,7 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, #define increment(name, min, max) do { \ - uint32_t value = 0; \ + uint32_t value; \ CHECK(cbs_vp9_read_increment(ctx, rw, min, max, #name, &value)); \ current->name = value; \ } while (0) @@ -295,9 +294,9 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, #define delta_q(name) do { \ uint8_t delta_coded; \ int8_t delta_q; \ - xf(1, name.delta_coded, delta_coded, 0); \ + xf(1, name.delta_coded, delta_coded, 0, ); \ if (delta_coded) \ - xs(4, name.delta_q, delta_q, 0); \ + xs(4, name.delta_q, delta_q, 0, ); \ else \ delta_q = 0; \ current->name = delta_q; \ @@ -315,7 +314,7 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, } while (0) #define fixed(width, name, value) do { \ - av_unused uint32_t fixed_value = value; \ + av_unused uint32_t fixed_value; \ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ 0, &fixed_value, value, value)); \ } while (0) @@ -366,9 +365,9 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, } while (0) #define delta_q(name) do { \ - xf(1, name.delta_coded, !!current->name, 0); \ + xf(1, name.delta_coded, !!current->name, 0, ); \ if (current->name) \ - xs(4, name.delta_q, current->name, 0); \ + xs(4, name.delta_q, current->name, 0, ); \ } while (0) #define prob(name, subs, ...) do { \ @@ -416,6 +415,9 @@ static int cbs_vp9_split_fragment(CodedBitstreamContext *ctx, uint8_t superframe_header; int err; + if (frag->data_size == 0) + return AVERROR_INVALIDDATA; + // Last byte in the packet. superframe_header = frag->data[frag->data_size - 1]; @@ -428,6 +430,9 @@ static int cbs_vp9_split_fragment(CodedBitstreamContext *ctx, index_size = 2 + (((superframe_header & 0x18) >> 3) + 1) * ((superframe_header & 0x07) + 1); + if (index_size > frag->data_size) + return AVERROR_INVALIDDATA; + err = init_get_bits(&gbc, frag->data + frag->data_size - index_size, 8 * index_size); if (err < 0) @@ -474,7 +479,7 @@ static int cbs_vp9_split_fragment(CodedBitstreamContext *ctx, return 0; } -static void cbs_vp9_free_frame(void *unit, uint8_t *content) +static void cbs_vp9_free_frame(void *opaque, uint8_t *content) { VP9RawFrame *frame = (VP9RawFrame*)content; av_buffer_unref(&frame->data_ref); @@ -522,62 +527,28 @@ static int cbs_vp9_read_unit(CodedBitstreamContext *ctx, } static int cbs_vp9_write_unit(CodedBitstreamContext *ctx, - CodedBitstreamUnit *unit) + CodedBitstreamUnit *unit, + PutBitContext *pbc) { - CodedBitstreamVP9Context *priv = ctx->priv_data; VP9RawFrame *frame = unit->content; - PutBitContext pbc; int err; - if (!priv->write_buffer) { - // Initial write buffer size is 1MB. - priv->write_buffer_size = 1024 * 1024; - - reallocate_and_try_again: - err = av_reallocp(&priv->write_buffer, priv->write_buffer_size); - if (err < 0) { - av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a " - "sufficiently large write buffer (last attempt " - "%"SIZE_SPECIFIER" bytes).\n", priv->write_buffer_size); - return err; - } - } - - init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size); - - err = cbs_vp9_write_frame(ctx, &pbc, frame); - if (err == AVERROR(ENOSPC)) { - priv->write_buffer_size *= 2; - goto reallocate_and_try_again; - } + err = cbs_vp9_write_frame(ctx, pbc, frame); if (err < 0) return err; // Frame must be byte-aligned. - av_assert0(put_bits_count(&pbc) % 8 == 0); - - unit->data_size = put_bits_count(&pbc) / 8; - unit->data_bit_padding = 0; - flush_put_bits(&pbc); + av_assert0(put_bits_count(pbc) % 8 == 0); if (frame->data) { - if (unit->data_size + frame->data_size > - priv->write_buffer_size) { - priv->write_buffer_size *= 2; - goto reallocate_and_try_again; - } + if (frame->data_size > put_bits_left(pbc) / 8) + return AVERROR(ENOSPC); - memcpy(priv->write_buffer + unit->data_size, - frame->data, frame->data_size); - unit->data_size += frame->data_size; + flush_put_bits(pbc); + memcpy(put_bits_ptr(pbc), frame->data, frame->data_size); + skip_put_bytes(pbc, frame->data_size); } - err = ff_cbs_alloc_unit_data(ctx, unit, unit->data_size); - if (err < 0) - return err; - - memcpy(unit->data, priv->write_buffer, unit->data_size); - return 0; } @@ -671,13 +642,6 @@ static int cbs_vp9_assemble_fragment(CodedBitstreamContext *ctx, return 0; } -static void cbs_vp9_close(CodedBitstreamContext *ctx) -{ - CodedBitstreamVP9Context *priv = ctx->priv_data; - - av_freep(&priv->write_buffer); -} - const CodedBitstreamType ff_cbs_type_vp9 = { .codec_id = AV_CODEC_ID_VP9, @@ -687,6 +651,4 @@ const CodedBitstreamType ff_cbs_type_vp9 = { .read_unit = &cbs_vp9_read_unit, .write_unit = &cbs_vp9_write_unit, .assemble_fragment = &cbs_vp9_assemble_fragment, - - .close = &cbs_vp9_close, }; diff --git a/libavcodec/cbs_vp9.h b/libavcodec/cbs_vp9.h index 4c9b2f880d3..40e62476ed7 100644 --- a/libavcodec/cbs_vp9.h +++ b/libavcodec/cbs_vp9.h @@ -207,10 +207,6 @@ typedef struct CodedBitstreamVP9Context { int bit_depth; VP9ReferenceFrameState ref[VP9_NUM_REF_FRAMES]; - - // Write buffer. - uint8_t *write_buffer; - size_t write_buffer_size; } CodedBitstreamVP9Context; diff --git a/libavcodec/cbs_vp9_syntax_template.c b/libavcodec/cbs_vp9_syntax_template.c index 125eb025895..2f08eccf180 100644 --- a/libavcodec/cbs_vp9_syntax_template.c +++ b/libavcodec/cbs_vp9_syntax_template.c @@ -19,23 +19,11 @@ static int FUNC(frame_sync_code)(CodedBitstreamContext *ctx, RWContext *rw, VP9RawFrameHeader *current) { - uint8_t frame_sync_byte_0 = VP9_FRAME_SYNC_0; - uint8_t frame_sync_byte_1 = VP9_FRAME_SYNC_1; - uint8_t frame_sync_byte_2 = VP9_FRAME_SYNC_2; int err; - xf(8, frame_sync_byte_0, frame_sync_byte_0, 0); - xf(8, frame_sync_byte_1, frame_sync_byte_1, 0); - xf(8, frame_sync_byte_2, frame_sync_byte_2, 0); - - if (frame_sync_byte_0 != VP9_FRAME_SYNC_0 || - frame_sync_byte_1 != VP9_FRAME_SYNC_1 || - frame_sync_byte_2 != VP9_FRAME_SYNC_2) { - av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid frame sync code: " - "%02x %02x %02x.\n", frame_sync_byte_0, - frame_sync_byte_1, frame_sync_byte_2); - return AVERROR_INVALIDDATA; - } + fixed(8, frame_sync_byte_0, VP9_FRAME_SYNC_0); + fixed(8, frame_sync_byte_1, VP9_FRAME_SYNC_1); + fixed(8, frame_sync_byte_2, VP9_FRAME_SYNC_2); return 0; } @@ -396,9 +384,8 @@ static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx, RWContext *rw, static int FUNC(trailing_bits)(CodedBitstreamContext *ctx, RWContext *rw) { int err; - av_unused int zero = 0; while (byte_alignment(rw) != 0) - xf(1, zero_bit, zero, 0); + fixed(1, zero_bit, 0); return 0; } diff --git a/libavcodec/cdgraphics.c b/libavcodec/cdgraphics.c index cf3f01a417b..469128964c2 100644 --- a/libavcodec/cdgraphics.c +++ b/libavcodec/cdgraphics.c @@ -283,7 +283,7 @@ static int cdg_decode_frame(AVCodecContext *avctx, bytestream2_init(&gb, avpkt->data, avpkt->size); - if ((ret = ff_reget_buffer(avctx, cc->frame)) < 0) + 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); diff --git a/libavcodec/cdtoons.c b/libavcodec/cdtoons.c new file mode 100644 index 00000000000..a8609815c12 --- /dev/null +++ b/libavcodec/cdtoons.c @@ -0,0 +1,456 @@ +/* + * CDToons video decoder + * Copyright (C) 2020 Alyssa Milburn + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 + * CDToons video decoder + * @author Alyssa Milburn + */ + +#include + +#include "libavutil/attributes.h" +#include "libavutil/internal.h" +#include "avcodec.h" +#include "bytestream.h" +#include "internal.h" + +#define CDTOONS_HEADER_SIZE 44 +#define CDTOONS_MAX_SPRITES 1200 + +typedef struct CDToonsSprite { + uint16_t flags; + uint16_t owner_frame; + uint16_t start_frame; + uint16_t end_frame; + unsigned int alloc_size; + uint32_t size; + uint8_t *data; + int active; +} CDToonsSprite; + +typedef struct CDToonsContext { + AVFrame *frame; + + uint16_t last_pal_id; ///< The index of the active palette sprite. + uint32_t pal[256]; ///< The currently-used palette data. + CDToonsSprite sprites[CDTOONS_MAX_SPRITES]; +} CDToonsContext; + +static int cdtoons_render_sprite(AVCodecContext *avctx, const uint8_t *data, + uint32_t data_size, + int dst_x, int dst_y, int width, int height) +{ + CDToonsContext *c = avctx->priv_data; + const uint8_t *next_line = data; + const uint8_t *end = data + data_size; + uint16_t line_size; + uint8_t *dest; + int skip = 0, to_skip, x; + + if (dst_x + width > avctx->width) + width = avctx->width - dst_x; + if (dst_y + height > avctx->height) + height = avctx->height - dst_y; + + if (dst_x < 0) { + /* we need to skip the start of the scanlines */ + skip = -dst_x; + if (width <= skip) + return 0; + dst_x = 0; + } + + for (int y = 0; y < height; y++) { + /* one scanline at a time, size is provided */ + data = next_line; + if (end - data < 2) + return 1; + line_size = bytestream_get_be16(&data); + if (end - data < line_size) + return 1; + next_line = data + line_size; + if (dst_y + y < 0) + continue; + + dest = c->frame->data[0] + (dst_y + y) * c->frame->linesize[0] + dst_x; + + to_skip = skip; + x = 0; + while (x < width - skip) { + int raw, size, step; + uint8_t val; + + if (data >= end) + return 1; + + val = bytestream_get_byte(&data); + raw = !(val & 0x80); + size = (int)(val & 0x7F) + 1; + + /* skip the start of a scanline if it is off-screen */ + if (to_skip >= size) { + to_skip -= size; + if (raw) { + step = size; + } else { + step = 1; + } + if (next_line - data < step) + return 1; + data += step; + continue; + } else if (to_skip) { + size -= to_skip; + if (raw) { + if (next_line - data < to_skip) + return 1; + data += to_skip; + } + to_skip = 0; + } + + if (x + size >= width - skip) + size = width - skip - x; + + /* either raw data, or a run of a single color */ + if (raw) { + if (next_line - data < size) + return 1; + memcpy(dest + x, data, size); + data += size; + } else { + uint8_t color = bytestream_get_byte(&data); + /* ignore transparent runs */ + if (color) + memset(dest + x, color, size); + } + x += size; + } + } + + return 0; +} + +static int cdtoons_decode_frame(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *avpkt) +{ + CDToonsContext *c = avctx->priv_data; + const uint8_t *buf = avpkt->data; + const uint8_t *eod = avpkt->data + avpkt->size; + const int buf_size = avpkt->size; + uint16_t frame_id; + uint8_t background_color; + uint16_t sprite_count, sprite_offset; + uint8_t referenced_count; + uint16_t palette_id; + uint8_t palette_set; + int ret; + int saw_embedded_sprites = 0; + + if (buf_size < CDTOONS_HEADER_SIZE) + return AVERROR_INVALIDDATA; + + if ((ret = ff_reget_buffer(avctx, c->frame, 0)) < 0) + return ret; + + /* a lot of the header is useless junk in the absence of + * dirty rectangling etc */ + buf += 2; /* version? (always 9?) */ + frame_id = bytestream_get_be16(&buf); + buf += 2; /* blocks_valid_until */ + buf += 1; + background_color = bytestream_get_byte(&buf); + buf += 16; /* clip rect, dirty rect */ + buf += 4; /* flags */ + sprite_count = bytestream_get_be16(&buf); + sprite_offset = bytestream_get_be16(&buf); + buf += 2; /* max block id? */ + referenced_count = bytestream_get_byte(&buf); + buf += 1; + palette_id = bytestream_get_be16(&buf); + palette_set = bytestream_get_byte(&buf); + buf += 5; + + if (sprite_offset > buf_size) + return AVERROR_INVALIDDATA; + + /* read new sprites introduced in this frame */ + buf = avpkt->data + sprite_offset; + while (sprite_count--) { + uint32_t size; + uint16_t sprite_id; + + if (buf + 14 > eod) + return AVERROR_INVALIDDATA; + + sprite_id = bytestream_get_be16(&buf); + if (sprite_id >= CDTOONS_MAX_SPRITES) { + av_log(avctx, AV_LOG_ERROR, + "Sprite ID %d is too high.\n", sprite_id); + return AVERROR_INVALIDDATA; + } + if (c->sprites[sprite_id].active) { + av_log(avctx, AV_LOG_ERROR, + "Sprite ID %d is a duplicate.\n", sprite_id); + return AVERROR_INVALIDDATA; + } + + c->sprites[sprite_id].flags = bytestream_get_be16(&buf); + size = bytestream_get_be32(&buf); + if (size < 14) { + av_log(avctx, AV_LOG_ERROR, + "Sprite only has %d bytes of data.\n", size); + return AVERROR_INVALIDDATA; + } + size -= 14; + c->sprites[sprite_id].size = size; + c->sprites[sprite_id].owner_frame = frame_id; + c->sprites[sprite_id].start_frame = bytestream_get_be16(&buf); + c->sprites[sprite_id].end_frame = bytestream_get_be16(&buf); + buf += 2; + + if (size > buf_size || buf + size > eod) + return AVERROR_INVALIDDATA; + + av_fast_padded_malloc(&c->sprites[sprite_id].data, &c->sprites[sprite_id].alloc_size, size); + if (!c->sprites[sprite_id].data) + return AVERROR(ENOMEM); + + c->sprites[sprite_id].active = 1; + + bytestream_get_buffer(&buf, c->sprites[sprite_id].data, size); + } + + /* render any embedded sprites */ + while (buf < eod) { + uint32_t tag, size; + if (buf + 8 > eod) { + av_log(avctx, AV_LOG_WARNING, "Ran (seriously) out of data for embedded sprites.\n"); + return AVERROR_INVALIDDATA; + } + tag = bytestream_get_be32(&buf); + size = bytestream_get_be32(&buf); + if (tag == MKBETAG('D', 'i', 'f', 'f')) { + uint16_t diff_count; + if (buf + 10 > eod) { + av_log(avctx, AV_LOG_WARNING, "Ran (seriously) out of data for Diff frame.\n"); + return AVERROR_INVALIDDATA; + } + diff_count = bytestream_get_be16(&buf); + buf += 8; /* clip rect? */ + for (int i = 0; i < diff_count; i++) { + int16_t top, left; + uint16_t diff_size, width, height; + + if (buf + 16 > eod) { + av_log(avctx, AV_LOG_WARNING, "Ran (seriously) out of data for Diff frame header.\n"); + return AVERROR_INVALIDDATA; + } + + top = bytestream_get_be16(&buf); + left = bytestream_get_be16(&buf); + buf += 4; /* bottom, right */ + diff_size = bytestream_get_be32(&buf); + width = bytestream_get_be16(&buf); + height = bytestream_get_be16(&buf); + if (diff_size < 8 || diff_size - 4 > eod - buf) { + av_log(avctx, AV_LOG_WARNING, "Ran (seriously) out of data for Diff frame data.\n"); + return AVERROR_INVALIDDATA; + } + if (cdtoons_render_sprite(avctx, buf + 4, diff_size - 8, + left, top, width, height)) { + av_log(avctx, AV_LOG_WARNING, "Ran beyond end of sprite while rendering.\n"); + } + buf += diff_size - 4; + } + saw_embedded_sprites = 1; + } else { + /* we don't care about any other entries */ + if (size < 8 || size - 8 > eod - buf) { + av_log(avctx, AV_LOG_WARNING, "Ran out of data for ignored entry (size %X, %d left).\n", size, (int)(eod - buf)); + return AVERROR_INVALIDDATA; + } + buf += (size - 8); + } + } + + /* was an intra frame? */ + if (saw_embedded_sprites) + goto done; + + /* render any referenced sprites */ + buf = avpkt->data + CDTOONS_HEADER_SIZE; + eod = avpkt->data + sprite_offset; + for (int i = 0; i < referenced_count; i++) { + const uint8_t *block_data; + uint16_t sprite_id, width, height; + int16_t top, left, right; + + if (buf + 10 > eod) { + av_log(avctx, AV_LOG_WARNING, "Ran (seriously) out of data when rendering.\n"); + return AVERROR_INVALIDDATA; + } + + sprite_id = bytestream_get_be16(&buf); + top = bytestream_get_be16(&buf); + left = bytestream_get_be16(&buf); + buf += 2; /* bottom */ + right = bytestream_get_be16(&buf); + + if ((i == 0) && (sprite_id == 0)) { + /* clear background */ + memset(c->frame->data[0], background_color, + c->frame->linesize[0] * avctx->height); + } + + if (!right) + continue; + if (sprite_id >= CDTOONS_MAX_SPRITES) { + av_log(avctx, AV_LOG_ERROR, + "Sprite ID %d is too high.\n", sprite_id); + return AVERROR_INVALIDDATA; + } + + block_data = c->sprites[sprite_id].data; + if (!c->sprites[sprite_id].active) { + /* this can happen when seeking around */ + av_log(avctx, AV_LOG_WARNING, "Sprite %d is missing.\n", sprite_id); + continue; + } + if (c->sprites[sprite_id].size < 14) { + av_log(avctx, AV_LOG_ERROR, "Sprite %d is too small.\n", sprite_id); + continue; + } + + height = bytestream_get_be16(&block_data); + width = bytestream_get_be16(&block_data); + block_data += 10; + if (cdtoons_render_sprite(avctx, block_data, + c->sprites[sprite_id].size - 14, + left, top, width, height)) { + av_log(avctx, AV_LOG_WARNING, "Ran beyond end of sprite while rendering.\n"); + } + } + + if (palette_id && (palette_id != c->last_pal_id)) { + if (palette_id >= CDTOONS_MAX_SPRITES) { + av_log(avctx, AV_LOG_ERROR, + "Palette ID %d is too high.\n", palette_id); + return AVERROR_INVALIDDATA; + } + if (!c->sprites[palette_id].active) { + /* this can happen when seeking around */ + av_log(avctx, AV_LOG_WARNING, + "Palette ID %d is missing.\n", palette_id); + goto done; + } + if (c->sprites[palette_id].size != 256 * 2 * 3) { + av_log(avctx, AV_LOG_ERROR, + "Palette ID %d is wrong size (%d).\n", + palette_id, c->sprites[palette_id].size); + return AVERROR_INVALIDDATA; + } + c->last_pal_id = palette_id; + if (!palette_set) { + uint8_t *palette_data = c->sprites[palette_id].data; + for (int i = 0; i < 256; i++) { + /* QuickTime-ish palette: 16-bit RGB components */ + unsigned r, g, b; + r = *palette_data; + g = *(palette_data + 2); + b = *(palette_data + 4); + c->pal[i] = (0xFFU << 24) | (r << 16) | (g << 8) | b; + palette_data += 6; + } + /* first palette entry indicates transparency */ + c->pal[0] = 0; + c->frame->palette_has_changed = 1; + } + } + +done: + /* discard outdated blocks */ + for (int i = 0; i < CDTOONS_MAX_SPRITES; i++) { + if (c->sprites[i].end_frame > frame_id) + continue; + c->sprites[i].active = 0; + } + + memcpy(c->frame->data[1], c->pal, AVPALETTE_SIZE); + + if ((ret = av_frame_ref(data, c->frame)) < 0) + return ret; + + *got_frame = 1; + + /* always report that the buffer was completely consumed */ + return buf_size; +} + +static av_cold int cdtoons_decode_init(AVCodecContext *avctx) +{ + CDToonsContext *c = avctx->priv_data; + + avctx->pix_fmt = AV_PIX_FMT_PAL8; + c->last_pal_id = 0; + c->frame = av_frame_alloc(); + if (!c->frame) + return AVERROR(ENOMEM); + + return 0; +} + +static void cdtoons_flush(AVCodecContext *avctx) +{ + CDToonsContext *c = avctx->priv_data; + + c->last_pal_id = 0; + for (int i = 0; i < CDTOONS_MAX_SPRITES; i++) + c->sprites[i].active = 0; +} + +static av_cold int cdtoons_decode_end(AVCodecContext *avctx) +{ + CDToonsContext *c = avctx->priv_data; + + for (int i = 0; i < CDTOONS_MAX_SPRITES; i++) { + av_freep(&c->sprites[i].data); + c->sprites[i].active = 0; + } + + av_frame_free(&c->frame); + + return 0; +} + +AVCodec ff_cdtoons_decoder = { + .name = "cdtoons", + .long_name = NULL_IF_CONFIG_SMALL("CDToons video"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_CDTOONS, + .priv_data_size = sizeof(CDToonsContext), + .init = cdtoons_decode_init, + .close = cdtoons_decode_end, + .decode = cdtoons_decode_frame, + .capabilities = AV_CODEC_CAP_DR1, + .flush = cdtoons_flush, +}; diff --git a/libavcodec/cfhd.c b/libavcodec/cfhd.c index 49a5a2c30a9..7956367b499 100644 --- a/libavcodec/cfhd.c +++ b/libavcodec/cfhd.c @@ -150,6 +150,49 @@ static inline void process_alpha(int16_t *alpha, int width) } } +static inline void process_bayer(AVFrame *frame) +{ + const int linesize = frame->linesize[0]; + uint16_t *r = (uint16_t *)frame->data[0]; + uint16_t *g1 = (uint16_t *)(frame->data[0] + 2); + uint16_t *g2 = (uint16_t *)(frame->data[0] + frame->linesize[0]); + uint16_t *b = (uint16_t *)(frame->data[0] + frame->linesize[0] + 2); + const int mid = 2048; + + for (int y = 0; y < frame->height >> 1; y++) { + for (int x = 0; x < frame->width; x += 2) { + int R, G1, G2, B; + int g, rg, bg, gd; + + g = r[x]; + rg = g1[x]; + bg = g2[x]; + gd = b[x]; + gd -= mid; + + R = (rg - mid) * 2 + g; + G1 = g + gd; + G2 = g - gd; + B = (bg - mid) * 2 + g; + + R = av_clip_uintp2(R * 16, 16); + G1 = av_clip_uintp2(G1 * 16, 16); + G2 = av_clip_uintp2(G2 * 16, 16); + B = av_clip_uintp2(B * 16, 16); + + r[x] = R; + g1[x] = G1; + g2[x] = G2; + b[x] = B; + } + + r += linesize; + g1 += linesize; + g2 += linesize; + b += linesize; + } +} + static inline void filter(int16_t *output, ptrdiff_t out_stride, int16_t *low, ptrdiff_t low_stride, int16_t *high, ptrdiff_t high_stride, @@ -217,6 +260,12 @@ static void horiz_filter_clip(int16_t *output, int16_t *low, int16_t *high, filter(output, 1, low, 1, high, 1, width, clip); } +static void horiz_filter_clip_bayer(int16_t *output, int16_t *low, int16_t *high, + int width, int clip) +{ + filter(output, 2, low, 1, high, 1, width, clip); +} + static void vert_filter(int16_t *output, ptrdiff_t out_stride, int16_t *low, ptrdiff_t low_stride, int16_t *high, ptrdiff_t high_stride, int len) @@ -249,6 +298,11 @@ static int alloc_buffers(AVCodecContext *avctx) int chroma_x_shift, chroma_y_shift; unsigned k; + if (s->coded_format == AV_PIX_FMT_BAYER_RGGB16) { + s->coded_width *= 2; + s->coded_height *= 2; + } + if ((ret = ff_set_dimensions(avctx, s->coded_width, s->coded_height)) < 0) return ret; avctx->pix_fmt = s->coded_format; @@ -258,6 +312,11 @@ static int alloc_buffers(AVCodecContext *avctx) &chroma_y_shift)) < 0) return ret; planes = av_pix_fmt_count_planes(s->coded_format); + if (s->coded_format == AV_PIX_FMT_BAYER_RGGB16) { + planes = 4; + chroma_x_shift = 1; + chroma_y_shift = 1; + } for (i = 0; i < planes; i++) { int w8, h8, w4, h4, w2, h2; @@ -519,18 +578,20 @@ static int cfhd_decode(AVCodecContext *avctx, void *data, int *got_frame, s->bpc = data; } else if (tag == 84) { av_log(avctx, AV_LOG_DEBUG, "Sample format? %i\n", data); - if (data == 1) + if (data == 1) { s->coded_format = AV_PIX_FMT_YUV422P10; - else if (data == 3) + } else if (data == 2) { + s->coded_format = AV_PIX_FMT_BAYER_RGGB16; + } else if (data == 3) { s->coded_format = AV_PIX_FMT_GBRP12; - else if (data == 4) + } else if (data == 4) { s->coded_format = AV_PIX_FMT_GBRAP12; - else { + } else { avpriv_report_missing_feature(avctx, "Sample format of %"PRIu16, data); ret = AVERROR_PATCHWELCOME; break; } - planes = av_pix_fmt_count_planes(s->coded_format); + planes = data == 2 ? 4 : av_pix_fmt_count_planes(s->coded_format); } else if (tag == -85) { av_log(avctx, AV_LOG_DEBUG, "Cropped height %"PRIu16"\n", data); s->cropped_height = data; @@ -564,8 +625,12 @@ static int cfhd_decode(AVCodecContext *avctx, void *data, int *got_frame, ret = ff_set_dimensions(avctx, s->coded_width, s->coded_height); if (ret < 0) return ret; - if (s->cropped_height) - avctx->height = s->cropped_height; + if (s->cropped_height) { + unsigned height = s->cropped_height << (avctx->pix_fmt == AV_PIX_FMT_BAYER_RGGB16); + if (avctx->height < height) + return AVERROR_INVALIDDATA; + avctx->height = height; + } frame.f->width = frame.f->height = 0; @@ -735,14 +800,28 @@ static int cfhd_decode(AVCodecContext *avctx, void *data, int *got_frame, } planes = av_pix_fmt_count_planes(avctx->pix_fmt); + if (avctx->pix_fmt == AV_PIX_FMT_BAYER_RGGB16) { + if (!s->progressive) + return AVERROR_INVALIDDATA; + planes = 4; + } + for (plane = 0; plane < planes && !ret; plane++) { /* level 1 */ int lowpass_height = s->plane[plane].band[0][0].height; int lowpass_width = s->plane[plane].band[0][0].width; int highpass_stride = s->plane[plane].band[0][1].stride; int act_plane = plane == 1 ? 2 : plane == 2 ? 1 : plane; + ptrdiff_t dst_linesize; int16_t *low, *high, *output, *dst; + if (avctx->pix_fmt == AV_PIX_FMT_BAYER_RGGB16) { + act_plane = 0; + dst_linesize = pic->linesize[act_plane]; + } else { + dst_linesize = pic->linesize[act_plane] / 2; + } + if (lowpass_height > s->plane[plane].band[0][0].a_height || lowpass_width > s->plane[plane].band[0][0].a_width || !highpass_stride || s->plane[plane].band[0][1].width > s->plane[plane].band[0][1].a_width) { av_log(avctx, AV_LOG_ERROR, "Invalid plane dimensions\n"); @@ -880,15 +959,33 @@ static int cfhd_decode(AVCodecContext *avctx, void *data, int *got_frame, } dst = (int16_t *)pic->data[act_plane]; + if (avctx->pix_fmt == AV_PIX_FMT_BAYER_RGGB16) { + if (plane & 1) + dst++; + if (plane > 1) + dst += pic->linesize[act_plane] >> 1; + } low = s->plane[plane].l_h[6]; high = s->plane[plane].l_h[7]; + + if (avctx->pix_fmt == AV_PIX_FMT_BAYER_RGGB16 && + (lowpass_height * 2 > avctx->coded_height / 2 || + lowpass_width * 2 > avctx->coded_width / 2 ) + ) { + ret = AVERROR_INVALIDDATA; + goto end; + } + for (i = 0; i < lowpass_height * 2; i++) { - horiz_filter_clip(dst, low, high, lowpass_width, s->bpc); + if (avctx->pix_fmt == AV_PIX_FMT_BAYER_RGGB16) + horiz_filter_clip_bayer(dst, low, high, lowpass_width, s->bpc); + else + horiz_filter_clip(dst, low, high, lowpass_width, s->bpc); if (avctx->pix_fmt == AV_PIX_FMT_GBRAP12 && act_plane == 3) process_alpha(dst, lowpass_width * 2); low += lowpass_width; high += lowpass_width; - dst += pic->linesize[act_plane] / 2; + dst += dst_linesize; } } else { av_log(avctx, AV_LOG_DEBUG, "interlaced frame ? %d", pic->interlaced_frame); @@ -926,6 +1023,8 @@ static int cfhd_decode(AVCodecContext *avctx, void *data, int *got_frame, } + if (avctx->pix_fmt == AV_PIX_FMT_BAYER_RGGB16) + process_bayer(pic); end: if (ret < 0) return ret; @@ -940,10 +1039,8 @@ static av_cold int cfhd_close(AVCodecContext *avctx) free_buffers(s); - if (!avctx->internal->is_copy) { - ff_free_vlc(&s->vlc_9); - ff_free_vlc(&s->vlc_18); - } + ff_free_vlc(&s->vlc_9); + ff_free_vlc(&s->vlc_18); return 0; } diff --git a/libavcodec/chomp_bsf.c b/libavcodec/chomp_bsf.c index 3ba45f3e06d..48b9336466c 100644 --- a/libavcodec/chomp_bsf.c +++ b/libavcodec/chomp_bsf.c @@ -19,9 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "avcodec.h" #include "bsf.h" -#include "internal.h" +#include "bsf_internal.h" static int chomp_filter(AVBSFContext *ctx, AVPacket *pkt) { diff --git a/libavcodec/cinepak.c b/libavcodec/cinepak.c index aeb15de0ed9..9c5b254231b 100644 --- a/libavcodec/cinepak.c +++ b/libavcodec/cinepak.c @@ -473,7 +473,7 @@ static int cinepak_decode_frame(AVCodecContext *avctx, return ret; } - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; if (s->palette_video) { diff --git a/libavcodec/cinepakenc.c b/libavcodec/cinepakenc.c index 93917fafe89..6024df0fba5 100644 --- a/libavcodec/cinepakenc.c +++ b/libavcodec/cinepakenc.c @@ -544,8 +544,9 @@ static int encode_mode(CinepakEncContext *s, int h, uint8_t *last_data[4], int last_linesize[4], strip_info *info, unsigned char *buf) { - int x, y, z, flags, bits, temp_size, header_ofs, ret = 0, mb_count = s->w * h / MB_AREA; + int x, y, z, bits, temp_size, header_ofs, ret = 0, mb_count = s->w * h / MB_AREA; int needs_extra_bit, should_write_temp; + uint32_t flags; unsigned char temp[64]; // 32/2 = 16 V4 blocks at 4 B each -> 64 B mb_info *mb; uint8_t *sub_scratch_data[4] = { 0 }, *sub_last_data[4] = { 0 }; @@ -599,7 +600,7 @@ static int encode_mode(CinepakEncContext *s, int h, flags = 0; for (y = x; y < FFMIN(x + 32, mb_count); y++) if (s->mb[y].best_encoding == ENC_V4) - flags |= 1 << (31 - y + x); + flags |= 1U << (31 - y + x); AV_WB32(&buf[ret], flags); ret += 4; @@ -626,13 +627,13 @@ static int encode_mode(CinepakEncContext *s, int h, for (x = 0; x < mb_count; x++) { mb = &s->mb[x]; - flags |= (mb->best_encoding != ENC_SKIP) << (31 - bits++); + flags |= (uint32_t)(mb->best_encoding != ENC_SKIP) << (31 - bits++); needs_extra_bit = 0; should_write_temp = 0; if (mb->best_encoding != ENC_SKIP) { if (bits < 32) - flags |= (mb->best_encoding == ENC_V4) << (31 - bits++); + flags |= (uint32_t)(mb->best_encoding == ENC_V4) << (31 - bits++); else needs_extra_bit = 1; } @@ -651,7 +652,7 @@ static int encode_mode(CinepakEncContext *s, int h, } if (needs_extra_bit) { - flags = (mb->best_encoding == ENC_V4) << 31; + flags = (uint32_t)(mb->best_encoding == ENC_V4) << 31; bits = 1; } diff --git a/libavcodec/clearvideo.c b/libavcodec/clearvideo.c index 26cdfb2731d..65bf1404016 100644 --- a/libavcodec/clearvideo.c +++ b/libavcodec/clearvideo.c @@ -524,7 +524,7 @@ static int clv_decode_frame(AVCodecContext *avctx, void *data, return AVERROR_INVALIDDATA; } - if ((ret = ff_reget_buffer(avctx, c->pic)) < 0) + if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; c->pic->key_frame = 1; @@ -558,7 +558,7 @@ static int clv_decode_frame(AVCodecContext *avctx, void *data, if (c->pmb_width * c->pmb_height > 8LL*(buf_size - bytestream2_tell(&gb))) return AVERROR_INVALIDDATA; - if ((ret = ff_reget_buffer(avctx, c->pic)) < 0) + if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; ret = av_frame_copy(c->pic, c->prev); diff --git a/libavcodec/cllc.c b/libavcodec/cllc.c index af0f6da2e99..1f2c98ef735 100644 --- a/libavcodec/cllc.c +++ b/libavcodec/cllc.c @@ -483,19 +483,6 @@ static int cllc_decode_frame(AVCodecContext *avctx, void *data, return avpkt->size; } -#if HAVE_THREADS -static int cllc_init_thread_copy(AVCodecContext *avctx) -{ - CLLCContext *ctx = avctx->priv_data; - - ctx->avctx = avctx; - ctx->swapped_buf = NULL; - ctx->swapped_buf_size = 0; - - return 0; -} -#endif - static av_cold int cllc_decode_close(AVCodecContext *avctx) { CLLCContext *ctx = avctx->priv_data; @@ -526,7 +513,6 @@ AVCodec ff_cllc_decoder = { .id = AV_CODEC_ID_CLLC, .priv_data_size = sizeof(CLLCContext), .init = cllc_decode_init, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(cllc_init_thread_copy), .decode = cllc_decode_frame, .close = cllc_decode_close, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, diff --git a/libavcodec/cngdec.c b/libavcodec/cngdec.c index 43b96fea1bb..747ab49cd0d 100644 --- a/libavcodec/cngdec.c +++ b/libavcodec/cngdec.c @@ -173,7 +173,7 @@ AVCodec ff_comfortnoise_decoder = { .close = cng_decode_close, .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1, + .capabilities = AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/codec.h b/libavcodec/codec.h new file mode 100644 index 00000000000..1fda619ee78 --- /dev/null +++ b/libavcodec/codec.h @@ -0,0 +1,462 @@ +/* + * AVCodec public API + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_CODEC_H +#define AVCODEC_CODEC_H + +#include + +#include "libavutil/avutil.h" +#include "libavutil/hwcontext.h" +#include "libavutil/log.h" +#include "libavutil/pixfmt.h" +#include "libavutil/rational.h" +#include "libavutil/samplefmt.h" + +#include "libavcodec/codec_id.h" +#include "libavcodec/version.h" + +/** + * @addtogroup lavc_core + * @{ + */ + +/** + * Decoder can use draw_horiz_band callback. + */ +#define AV_CODEC_CAP_DRAW_HORIZ_BAND (1 << 0) +/** + * Codec uses get_buffer() for allocating buffers and supports custom allocators. + * If not set, it might not use get_buffer() at all or use operations that + * assume the buffer was allocated by avcodec_default_get_buffer. + */ +#define AV_CODEC_CAP_DR1 (1 << 1) +#define AV_CODEC_CAP_TRUNCATED (1 << 3) +/** + * Encoder or decoder requires flushing with NULL input at the end in order to + * give the complete and correct output. + * + * NOTE: If this flag is not set, the codec is guaranteed to never be fed with + * with NULL data. The user can still send NULL data to the public encode + * or decode function, but libavcodec will not pass it along to the codec + * unless this flag is set. + * + * Decoders: + * The decoder has a non-zero delay and needs to be fed with avpkt->data=NULL, + * avpkt->size=0 at the end to get the delayed data until the decoder no longer + * returns frames. + * + * Encoders: + * The encoder needs to be fed with NULL data at the end of encoding until the + * encoder no longer returns data. + * + * NOTE: For encoders implementing the AVCodec.encode2() function, setting this + * flag also means that the encoder must set the pts and duration for + * each output packet. If this flag is not set, the pts and duration will + * be determined by libavcodec from the input frame. + */ +#define AV_CODEC_CAP_DELAY (1 << 5) +/** + * Codec can be fed a final frame with a smaller size. + * This can be used to prevent truncation of the last audio samples. + */ +#define AV_CODEC_CAP_SMALL_LAST_FRAME (1 << 6) + +/** + * Codec can output multiple frames per AVPacket + * Normally demuxers return one frame at a time, demuxers which do not do + * are connected to a parser to split what they return into proper frames. + * This flag is reserved to the very rare category of codecs which have a + * bitstream that cannot be split into frames without timeconsuming + * operations like full decoding. Demuxers carrying such bitstreams thus + * may return multiple frames in a packet. This has many disadvantages like + * prohibiting stream copy in many cases thus it should only be considered + * as a last resort. + */ +#define AV_CODEC_CAP_SUBFRAMES (1 << 8) +/** + * Codec is experimental and is thus avoided in favor of non experimental + * encoders + */ +#define AV_CODEC_CAP_EXPERIMENTAL (1 << 9) +/** + * Codec should fill in channel configuration and samplerate instead of container + */ +#define AV_CODEC_CAP_CHANNEL_CONF (1 << 10) +/** + * Codec supports frame-level multithreading. + */ +#define AV_CODEC_CAP_FRAME_THREADS (1 << 12) +/** + * Codec supports slice-based (or partition-based) multithreading. + */ +#define AV_CODEC_CAP_SLICE_THREADS (1 << 13) +/** + * Codec supports changed parameters at any point. + */ +#define AV_CODEC_CAP_PARAM_CHANGE (1 << 14) +/** + * Codec supports avctx->thread_count == 0 (auto). + */ +#define AV_CODEC_CAP_AUTO_THREADS (1 << 15) +/** + * Audio encoder supports receiving a different number of samples in each call. + */ +#define AV_CODEC_CAP_VARIABLE_FRAME_SIZE (1 << 16) +/** + * Decoder is not a preferred choice for probing. + * This indicates that the decoder is not a good choice for probing. + * It could for example be an expensive to spin up hardware decoder, + * or it could simply not provide a lot of useful information about + * the stream. + * A decoder marked with this flag should only be used as last resort + * choice for probing. + */ +#define AV_CODEC_CAP_AVOID_PROBING (1 << 17) + +#if FF_API_UNUSED_CODEC_CAPS +/** + * Deprecated and unused. Use AVCodecDescriptor.props instead + */ +#define AV_CODEC_CAP_INTRA_ONLY 0x40000000 +/** + * Deprecated and unused. Use AVCodecDescriptor.props instead + */ +#define AV_CODEC_CAP_LOSSLESS 0x80000000 +#endif + +/** + * Codec is backed by a hardware implementation. Typically used to + * identify a non-hwaccel hardware decoder. For information about hwaccels, use + * avcodec_get_hw_config() instead. + */ +#define AV_CODEC_CAP_HARDWARE (1 << 18) + +/** + * Codec is potentially backed by a hardware implementation, but not + * necessarily. This is used instead of AV_CODEC_CAP_HARDWARE, if the + * implementation provides some sort of internal fallback. + */ +#define AV_CODEC_CAP_HYBRID (1 << 19) + +/** + * This codec takes the reordered_opaque field from input AVFrames + * and returns it in the corresponding field in AVCodecContext after + * encoding. + */ +#define AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE (1 << 20) + +/** + * This encoder can be flushed using avcodec_flush_buffers(). If this flag is + * not set, the encoder must be closed and reopened to ensure that no frames + * remain pending. + */ +#define AV_CODEC_CAP_ENCODER_FLUSH (1 << 21) + +/** + * AVProfile. + */ +typedef struct AVProfile { + int profile; + const char *name; ///< short name for the profile +} AVProfile; + +typedef struct AVCodecDefault AVCodecDefault; + +struct AVCodecContext; +struct AVSubtitle; +struct AVPacket; + +/** + * AVCodec. + */ +typedef struct AVCodec { + /** + * Name of the codec implementation. + * The name is globally unique among encoders and among decoders (but an + * encoder and a decoder can share the same name). + * This is the primary way to find a codec from the user perspective. + */ + const char *name; + /** + * Descriptive name for the codec, meant to be more human readable than name. + * You should use the NULL_IF_CONFIG_SMALL() macro to define it. + */ + const char *long_name; + enum AVMediaType type; + enum AVCodecID id; + /** + * Codec capabilities. + * see AV_CODEC_CAP_* + */ + int capabilities; + const AVRational *supported_framerates; ///< array of supported framerates, or NULL if any, array is terminated by {0,0} + 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 + const uint64_t *channel_layouts; ///< array of support channel layouts, or NULL if unknown. array is terminated by 0 + uint8_t max_lowres; ///< maximum value for lowres supported by the decoder + 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} + + /** + * Group name of the codec implementation. + * This is a short symbolic name of the wrapper backing this codec. A + * wrapper uses some kind of external implementation for the codec, such + * as an external library, or a codec implementation provided by the OS or + * the hardware. + * If this field is NULL, this is a builtin, libavcodec native codec. + * If non-NULL, this will be the suffix in AVCodec.name in most cases + * (usually AVCodec.name will be of the form "_"). + */ + const char *wrapper_name; + + /***************************************************************** + * 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. + ***************************************************************** + */ + int priv_data_size; + struct AVCodec *next; + /** + * @name Frame-level threading support functions + * @{ + */ + /** + * Copy necessary context variables from a previous thread context to the current one. + * If not defined, the next thread will start automatically; otherwise, the codec + * must call ff_thread_finish_setup(). + * + * dst and src will (rarely) point to the same context, in which case memcpy should be skipped. + */ + int (*update_thread_context)(struct AVCodecContext *dst, const struct AVCodecContext *src); + /** @} */ + + /** + * Private codec-specific defaults. + */ + const AVCodecDefault *defaults; + + /** + * Initialize codec static data, called from avcodec_register(). + * + * This is not intended for time consuming operations as it is + * run for every codec regardless of that codec being used. + */ + void (*init_static_data)(struct AVCodec *codec); + + int (*init)(struct AVCodecContext *); + int (*encode_sub)(struct AVCodecContext *, uint8_t *buf, int buf_size, + const struct AVSubtitle *sub); + /** + * Encode data to an AVPacket. + * + * @param avctx codec context + * @param avpkt output AVPacket (may contain a user-provided buffer) + * @param[in] frame AVFrame containing the raw data to be encoded + * @param[out] got_packet_ptr encoder sets to 0 or 1 to indicate that a + * non-empty packet was returned in avpkt. + * @return 0 on success, negative error code on failure + */ + int (*encode2)(struct AVCodecContext *avctx, struct AVPacket *avpkt, + const struct AVFrame *frame, int *got_packet_ptr); + int (*decode)(struct AVCodecContext *, void *outdata, int *outdata_size, struct AVPacket *avpkt); + int (*close)(struct AVCodecContext *); + /** + * Encode API with decoupled packet/frame dataflow. The API is the + * same as the avcodec_ prefixed APIs (avcodec_send_frame() etc.), except + * that: + * - never called if the codec is closed or the wrong type, + * - if AV_CODEC_CAP_DELAY is not set, drain frames are never sent, + * - only one drain frame is ever passed down, + */ + int (*send_frame)(struct AVCodecContext *avctx, const struct AVFrame *frame); + int (*receive_packet)(struct AVCodecContext *avctx, struct AVPacket *avpkt); + + /** + * Decode API with decoupled packet/frame dataflow. This function is called + * to get one output frame. It should call ff_decode_get_packet() to obtain + * input data. + */ + int (*receive_frame)(struct AVCodecContext *avctx, struct AVFrame *frame); + /** + * Flush buffers. + * Will be called when seeking + */ + void (*flush)(struct AVCodecContext *); + /** + * Internal codec capabilities. + * See FF_CODEC_CAP_* in internal.h + */ + int caps_internal; + + /** + * Decoding only, a comma-separated list of bitstream filters to apply to + * packets before decoding. + */ + const char *bsfs; + + /** + * Array of pointers to hardware configurations supported by the codec, + * or NULL if no hardware supported. The array is terminated by a NULL + * pointer. + * + * The user can only access this field via avcodec_get_hw_config(). + */ + const struct AVCodecHWConfigInternal **hw_configs; + + /** + * List of supported codec_tags, terminated by FF_CODEC_TAGS_END. + */ + const uint32_t *codec_tags; +} AVCodec; + +/** + * Iterate over all registered codecs. + * + * @param opaque a pointer where libavcodec will store the iteration state. Must + * point to NULL to start the iteration. + * + * @return the next registered codec or NULL when the iteration is + * finished + */ +const AVCodec *av_codec_iterate(void **opaque); + +/** + * Find a registered decoder with a matching codec ID. + * + * @param id AVCodecID of the requested decoder + * @return A decoder if one was found, NULL otherwise. + */ +AVCodec *avcodec_find_decoder(enum AVCodecID id); + +/** + * Find a registered decoder with the specified name. + * + * @param name name of the requested decoder + * @return A decoder if one was found, NULL otherwise. + */ +AVCodec *avcodec_find_decoder_by_name(const char *name); + +/** + * Find a registered encoder with a matching codec ID. + * + * @param id AVCodecID of the requested encoder + * @return An encoder if one was found, NULL otherwise. + */ +AVCodec *avcodec_find_encoder(enum AVCodecID id); + +/** + * Find a registered encoder with the specified name. + * + * @param name name of the requested encoder + * @return An encoder if one was found, NULL otherwise. + */ +AVCodec *avcodec_find_encoder_by_name(const char *name); +/** + * @return a non-zero number if codec is an encoder, zero otherwise + */ +int av_codec_is_encoder(const AVCodec *codec); + +/** + * @return a non-zero number if codec is a decoder, zero otherwise + */ +int av_codec_is_decoder(const AVCodec *codec); + +enum { + /** + * The codec supports this format via the hw_device_ctx interface. + * + * When selecting this format, AVCodecContext.hw_device_ctx should + * have been set to a device of the specified type before calling + * avcodec_open2(). + */ + AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX = 0x01, + /** + * The codec supports this format via the hw_frames_ctx interface. + * + * When selecting this format for a decoder, + * AVCodecContext.hw_frames_ctx should be set to a suitable frames + * context inside the get_format() callback. The frames context + * must have been created on a device of the specified type. + * + * When selecting this format for an encoder, + * AVCodecContext.hw_frames_ctx should be set to the context which + * will be used for the input frames before calling avcodec_open2(). + */ + AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX = 0x02, + /** + * The codec supports this format by some internal method. + * + * This format can be selected without any additional configuration - + * no device or frames context is required. + */ + AV_CODEC_HW_CONFIG_METHOD_INTERNAL = 0x04, + /** + * The codec supports this format by some ad-hoc method. + * + * Additional settings and/or function calls are required. See the + * codec-specific documentation for details. (Methods requiring + * this sort of configuration are deprecated and others should be + * used in preference.) + */ + AV_CODEC_HW_CONFIG_METHOD_AD_HOC = 0x08, +}; + +typedef struct AVCodecHWConfig { + /** + * For decoders, a hardware pixel format which that decoder may be + * able to decode to if suitable hardware is available. + * + * For encoders, a pixel format which the encoder may be able to + * accept. If set to AV_PIX_FMT_NONE, this applies to all pixel + * formats supported by the codec. + */ + enum AVPixelFormat pix_fmt; + /** + * Bit set of AV_CODEC_HW_CONFIG_METHOD_* flags, describing the possible + * setup methods which can be used with this configuration. + */ + int methods; + /** + * The device type associated with the configuration. + * + * Must be set for AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX and + * AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX, otherwise unused. + */ + enum AVHWDeviceType device_type; +} AVCodecHWConfig; + +/** + * Retrieve supported hardware configurations for a codec. + * + * Values of index from zero to some maximum return the indexed configuration + * descriptor; all other values return NULL. If the codec does not support + * any hardware configurations then it will always return NULL. + */ +const AVCodecHWConfig *avcodec_get_hw_config(const AVCodec *codec, int index); + +/** + * @} + */ + +#endif /* AVCODEC_CODEC_H */ diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 4d033c20ff5..9f8847544ff 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -23,7 +23,9 @@ #include "libavutil/common.h" #include "libavutil/internal.h" -#include "avcodec.h" + +#include "codec_id.h" +#include "codec_desc.h" #include "profiles.h" #include "version.h" @@ -1726,6 +1728,55 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("On2 VP4"), .props = AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_IMM5, + .type = AVMEDIA_TYPE_VIDEO, + .name = "imm5", + .long_name = NULL_IF_CONFIG_SMALL("Infinity IMM5"), + .props = AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_MVDV, + .type = AVMEDIA_TYPE_VIDEO, + .name = "mvdv", + .long_name = NULL_IF_CONFIG_SMALL("MidiVid VQ"), + .props = AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_MVHA, + .type = AVMEDIA_TYPE_VIDEO, + .name = "mvha", + .long_name = NULL_IF_CONFIG_SMALL("MidiVid Archive Codec"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_CDTOONS, + .type = AVMEDIA_TYPE_VIDEO, + .name = "cdtoons", + .long_name = NULL_IF_CONFIG_SMALL("CDToons video"), + .props = AV_CODEC_PROP_LOSSLESS, + }, + { + .id = AV_CODEC_ID_MV30, + .type = AVMEDIA_TYPE_VIDEO, + .name = "mv30", + .long_name = NULL_IF_CONFIG_SMALL("MidiVid 3.0"), + .props = AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_NOTCHLC, + .type = AVMEDIA_TYPE_VIDEO, + .name = "notchlc", + .long_name = NULL_IF_CONFIG_SMALL("NotchLC"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_PFM, + .type = AVMEDIA_TYPE_VIDEO, + .name = "pfm", + .long_name = NULL_IF_CONFIG_SMALL("PFM (Portable FloatMap) image"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, + }, /* various PCM "codecs" */ { @@ -1733,252 +1784,245 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s16le", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 16-bit little-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_S16BE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s16be", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 16-bit big-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_U16LE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_u16le", .long_name = NULL_IF_CONFIG_SMALL("PCM unsigned 16-bit little-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_U16BE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_u16be", .long_name = NULL_IF_CONFIG_SMALL("PCM unsigned 16-bit big-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_S8, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s8", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 8-bit"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_U8, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_u8", .long_name = NULL_IF_CONFIG_SMALL("PCM unsigned 8-bit"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_MULAW, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_mulaw", .long_name = NULL_IF_CONFIG_SMALL("PCM mu-law / G.711 mu-law"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_PCM_ALAW, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_alaw", .long_name = NULL_IF_CONFIG_SMALL("PCM A-law / G.711 A-law"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_PCM_S32LE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s32le", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 32-bit little-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_S32BE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s32be", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 32-bit big-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_U32LE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_u32le", .long_name = NULL_IF_CONFIG_SMALL("PCM unsigned 32-bit little-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_U32BE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_u32be", .long_name = NULL_IF_CONFIG_SMALL("PCM unsigned 32-bit big-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_S24LE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s24le", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 24-bit little-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_S24BE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s24be", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 24-bit big-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_U24LE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_u24le", .long_name = NULL_IF_CONFIG_SMALL("PCM unsigned 24-bit little-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_U24BE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_u24be", .long_name = NULL_IF_CONFIG_SMALL("PCM unsigned 24-bit big-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_S24DAUD, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s24daud", .long_name = NULL_IF_CONFIG_SMALL("PCM D-Cinema audio signed 24-bit"), - .props = AV_CODEC_PROP_LOSSLESS, - }, - { - .id = AV_CODEC_ID_PCM_ZORK, - .type = AVMEDIA_TYPE_AUDIO, - .name = "pcm_zork", - .long_name = NULL_IF_CONFIG_SMALL("PCM Zork"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_S16LE_PLANAR, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s16le_planar", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 16-bit little-endian planar"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_DVD, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_dvd", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 20|24-bit big-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_F32BE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_f32be", .long_name = NULL_IF_CONFIG_SMALL("PCM 32-bit floating point big-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_F32LE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_f32le", .long_name = NULL_IF_CONFIG_SMALL("PCM 32-bit floating point little-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_F64BE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_f64be", .long_name = NULL_IF_CONFIG_SMALL("PCM 64-bit floating point big-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_F64LE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_f64le", .long_name = NULL_IF_CONFIG_SMALL("PCM 64-bit floating point little-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_BLURAY, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_bluray", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 16|20|24-bit big-endian for Blu-ray media"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_LXF, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_lxf", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 20-bit little-endian planar"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_S302M, .type = AVMEDIA_TYPE_AUDIO, .name = "s302m", .long_name = NULL_IF_CONFIG_SMALL("SMPTE 302M"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_S8_PLANAR, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s8_planar", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 8-bit planar"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_S24LE_PLANAR, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s24le_planar", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 24-bit little-endian planar"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_S32LE_PLANAR, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s32le_planar", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 32-bit little-endian planar"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_S16BE_PLANAR, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s16be_planar", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 16-bit big-endian planar"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_S64LE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s64le", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 64-bit little-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_S64BE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_s64be", .long_name = NULL_IF_CONFIG_SMALL("PCM signed 64-bit big-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_F16LE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_f16le", .long_name = NULL_IF_CONFIG_SMALL("PCM 16.8 floating point little-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_F24LE, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_f24le", .long_name = NULL_IF_CONFIG_SMALL("PCM 24.0 floating point little-endian"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_PCM_VIDC, .type = AVMEDIA_TYPE_AUDIO, .name = "pcm_vidc", .long_name = NULL_IF_CONFIG_SMALL("PCM Archimedes VIDC"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, /* various ADPCM codecs */ @@ -1987,294 +2031,343 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ima_qt", .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA QuickTime"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_IMA_WAV, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ima_wav", .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA WAV"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_IMA_DK3, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ima_dk3", .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Duck DK3"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_IMA_DK4, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ima_dk4", .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Duck DK4"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_IMA_WS, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ima_ws", .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Westwood"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_IMA_SMJPEG, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ima_smjpeg", .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Loki SDL MJPEG"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_MS, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ms", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Microsoft"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_4XM, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_4xm", .long_name = NULL_IF_CONFIG_SMALL("ADPCM 4X Movie"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_XA, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_xa", .long_name = NULL_IF_CONFIG_SMALL("ADPCM CDROM XA"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_ADX, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_adx", .long_name = NULL_IF_CONFIG_SMALL("SEGA CRI ADX ADPCM"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_EA, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ea", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Electronic Arts"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_G726, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_g726", .long_name = NULL_IF_CONFIG_SMALL("G.726 ADPCM"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_CT, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ct", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Creative Technology"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_SWF, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_swf", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Shockwave Flash"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_YAMAHA, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_yamaha", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Yamaha"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_SBPRO_4, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_sbpro_4", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Sound Blaster Pro 4-bit"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_SBPRO_3, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_sbpro_3", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Sound Blaster Pro 2.6-bit"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_SBPRO_2, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_sbpro_2", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Sound Blaster Pro 2-bit"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_THP, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_thp", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Nintendo THP"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_IMA_AMV, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ima_amv", .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA AMV"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_EA_R1, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ea_r1", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Electronic Arts R1"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_EA_R3, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ea_r3", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Electronic Arts R3"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_EA_R2, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ea_r2", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Electronic Arts R2"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_IMA_EA_SEAD, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ima_ea_sead", .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Electronic Arts SEAD"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_IMA_EA_EACS, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ima_ea_eacs", .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Electronic Arts EACS"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_EA_XAS, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ea_xas", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Electronic Arts XAS"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_EA_MAXIS_XA, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ea_maxis_xa", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Electronic Arts Maxis CDROM XA"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_IMA_ISS, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ima_iss", .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Funcom ISS"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_G722, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_g722", .long_name = NULL_IF_CONFIG_SMALL("G.722 ADPCM"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_IMA_APC, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ima_apc", .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA CRYO APC"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_VIMA, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_vima", .long_name = NULL_IF_CONFIG_SMALL("LucasArts VIMA audio"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_AFC, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_afc", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Nintendo Gamecube AFC"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_IMA_OKI, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ima_oki", .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Dialogic OKI"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_DTK, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_dtk", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Nintendo Gamecube DTK"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_IMA_RAD, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ima_rad", .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Radical"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_G726LE, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_g726le", .long_name = NULL_IF_CONFIG_SMALL("G.726 ADPCM little-endian"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_THP_LE, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_thp_le", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Nintendo THP (Little-Endian)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_PSX, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_psx", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Playstation"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_AICA, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_aica", .long_name = NULL_IF_CONFIG_SMALL("ADPCM Yamaha AICA"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_IMA_DAT4, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_ima_dat4", .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Eurocom DAT4"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_MTAF, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_mtaf", .long_name = NULL_IF_CONFIG_SMALL("ADPCM MTAF"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ADPCM_AGM, .type = AVMEDIA_TYPE_AUDIO, .name = "adpcm_agm", .long_name = NULL_IF_CONFIG_SMALL("ADPCM AmuseGraphics Movie AGM"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_ADPCM_ARGO, + .type = AVMEDIA_TYPE_AUDIO, + .name = "adpcm_argo", + .long_name = NULL_IF_CONFIG_SMALL("ADPCM Argonaut Games"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_ADPCM_IMA_SSI, + .type = AVMEDIA_TYPE_AUDIO, + .name = "adpcm_ima_ssi", + .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Simon & Schuster Interactive"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_ADPCM_ZORK, + .type = AVMEDIA_TYPE_AUDIO, + .name = "adpcm_zork", + .long_name = NULL_IF_CONFIG_SMALL("ADPCM Zork"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_ADPCM_IMA_APM, + .type = AVMEDIA_TYPE_AUDIO, + .name = "adpcm_ima_apm", + .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Ubisoft APM"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_ADPCM_IMA_ALP, + .type = AVMEDIA_TYPE_AUDIO, + .name = "adpcm_ima_alp", + .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA High Voltage Software ALP"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_ADPCM_IMA_MTF, + .type = AVMEDIA_TYPE_AUDIO, + .name = "adpcm_ima_mtf", + .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Capcom's MT Framework"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_ADPCM_IMA_CUNNING, + .type = AVMEDIA_TYPE_AUDIO, + .name = "adpcm_ima_cunning", + .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Cunning Developments"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, /* AMR */ @@ -2283,14 +2376,14 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "amr_nb", .long_name = NULL_IF_CONFIG_SMALL("AMR-NB (Adaptive Multi-Rate NarrowBand)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_AMR_WB, .type = AVMEDIA_TYPE_AUDIO, .name = "amr_wb", .long_name = NULL_IF_CONFIG_SMALL("AMR-WB (Adaptive Multi-Rate WideBand)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, /* RealAudio codecs*/ @@ -2299,14 +2392,14 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "ra_144", .long_name = NULL_IF_CONFIG_SMALL("RealAudio 1.0 (14.4K)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_RA_288, .type = AVMEDIA_TYPE_AUDIO, .name = "ra_288", .long_name = NULL_IF_CONFIG_SMALL("RealAudio 2.0 (28.8K)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, /* various DPCM codecs */ @@ -2315,42 +2408,49 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "roq_dpcm", .long_name = NULL_IF_CONFIG_SMALL("DPCM id RoQ"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_INTERPLAY_DPCM, .type = AVMEDIA_TYPE_AUDIO, .name = "interplay_dpcm", .long_name = NULL_IF_CONFIG_SMALL("DPCM Interplay"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_XAN_DPCM, .type = AVMEDIA_TYPE_AUDIO, .name = "xan_dpcm", .long_name = NULL_IF_CONFIG_SMALL("DPCM Xan"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_SOL_DPCM, .type = AVMEDIA_TYPE_AUDIO, .name = "sol_dpcm", .long_name = NULL_IF_CONFIG_SMALL("DPCM Sol"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_SDX2_DPCM, .type = AVMEDIA_TYPE_AUDIO, .name = "sdx2_dpcm", .long_name = NULL_IF_CONFIG_SMALL("DPCM Squareroot-Delta-Exact"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_GREMLIN_DPCM, .type = AVMEDIA_TYPE_AUDIO, .name = "gremlin_dpcm", .long_name = NULL_IF_CONFIG_SMALL("DPCM Gremlin"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_DERF_DPCM, + .type = AVMEDIA_TYPE_AUDIO, + .name = "derf_dpcm", + .long_name = NULL_IF_CONFIG_SMALL("DPCM Xilam DERF"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, /* audio codecs */ @@ -2359,21 +2459,21 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "mp2", .long_name = NULL_IF_CONFIG_SMALL("MP2 (MPEG audio layer 2)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_MP3, .type = AVMEDIA_TYPE_AUDIO, .name = "mp3", .long_name = NULL_IF_CONFIG_SMALL("MP3 (MPEG audio layer 3)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_AAC, .type = AVMEDIA_TYPE_AUDIO, .name = "aac", .long_name = NULL_IF_CONFIG_SMALL("AAC (Advanced Audio Coding)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, .profiles = NULL_IF_CONFIG_SMALL(ff_aac_profiles), }, { @@ -2381,14 +2481,14 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "ac3", .long_name = NULL_IF_CONFIG_SMALL("ATSC A/52A (AC-3)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_DTS, .type = AVMEDIA_TYPE_AUDIO, .name = "dts", .long_name = NULL_IF_CONFIG_SMALL("DCA (DTS Coherent Acoustics)"), - .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_LOSSLESS, .profiles = NULL_IF_CONFIG_SMALL(ff_dca_profiles), }, { @@ -2396,49 +2496,49 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "vorbis", .long_name = NULL_IF_CONFIG_SMALL("Vorbis"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_DVAUDIO, .type = AVMEDIA_TYPE_AUDIO, .name = "dvaudio", .long_name = NULL_IF_CONFIG_SMALL("DV audio"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_WMAV1, .type = AVMEDIA_TYPE_AUDIO, .name = "wmav1", .long_name = NULL_IF_CONFIG_SMALL("Windows Media Audio 1"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_WMAV2, .type = AVMEDIA_TYPE_AUDIO, .name = "wmav2", .long_name = NULL_IF_CONFIG_SMALL("Windows Media Audio 2"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_MACE3, .type = AVMEDIA_TYPE_AUDIO, .name = "mace3", .long_name = NULL_IF_CONFIG_SMALL("MACE (Macintosh Audio Compression/Expansion) 3:1"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_MACE6, .type = AVMEDIA_TYPE_AUDIO, .name = "mace6", .long_name = NULL_IF_CONFIG_SMALL("MACE (Macintosh Audio Compression/Expansion) 6:1"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_VMDAUDIO, .type = AVMEDIA_TYPE_AUDIO, .name = "vmdaudio", .long_name = NULL_IF_CONFIG_SMALL("Sierra VMD audio"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_FLAC, @@ -2452,21 +2552,21 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "mp3adu", .long_name = NULL_IF_CONFIG_SMALL("ADU (Application Data Unit) MP3 (MPEG audio layer 3)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_MP3ON4, .type = AVMEDIA_TYPE_AUDIO, .name = "mp3on4", .long_name = NULL_IF_CONFIG_SMALL("MP3onMP4"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_SHORTEN, .type = AVMEDIA_TYPE_AUDIO, .name = "shorten", .long_name = NULL_IF_CONFIG_SMALL("Shorten"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_ALAC, @@ -2480,35 +2580,35 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "westwood_snd1", .long_name = NULL_IF_CONFIG_SMALL("Westwood Audio (SND1)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_GSM, .type = AVMEDIA_TYPE_AUDIO, .name = "gsm", .long_name = NULL_IF_CONFIG_SMALL("GSM"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_QDM2, .type = AVMEDIA_TYPE_AUDIO, .name = "qdm2", .long_name = NULL_IF_CONFIG_SMALL("QDesign Music Codec 2"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_COOK, .type = AVMEDIA_TYPE_AUDIO, .name = "cook", .long_name = NULL_IF_CONFIG_SMALL("Cook / Cooker / Gecko (RealAudio G2)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_TRUESPEECH, .type = AVMEDIA_TYPE_AUDIO, .name = "truespeech", .long_name = NULL_IF_CONFIG_SMALL("DSP Group TrueSpeech"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_TTA, @@ -2522,14 +2622,14 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "smackaudio", .long_name = NULL_IF_CONFIG_SMALL("Smacker audio"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_QCELP, .type = AVMEDIA_TYPE_AUDIO, .name = "qcelp", .long_name = NULL_IF_CONFIG_SMALL("QCELP / PureVoice"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_WAVPACK, @@ -2544,126 +2644,126 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "dsicinaudio", .long_name = NULL_IF_CONFIG_SMALL("Delphine Software International CIN audio"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_IMC, .type = AVMEDIA_TYPE_AUDIO, .name = "imc", .long_name = NULL_IF_CONFIG_SMALL("IMC (Intel Music Coder)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_MUSEPACK7, .type = AVMEDIA_TYPE_AUDIO, .name = "musepack7", .long_name = NULL_IF_CONFIG_SMALL("Musepack SV7"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_MLP, .type = AVMEDIA_TYPE_AUDIO, .name = "mlp", .long_name = NULL_IF_CONFIG_SMALL("MLP (Meridian Lossless Packing)"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_GSM_MS, .type = AVMEDIA_TYPE_AUDIO, .name = "gsm_ms", .long_name = NULL_IF_CONFIG_SMALL("GSM Microsoft variant"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ATRAC3, .type = AVMEDIA_TYPE_AUDIO, .name = "atrac3", .long_name = NULL_IF_CONFIG_SMALL("ATRAC3 (Adaptive TRansform Acoustic Coding 3)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_APE, .type = AVMEDIA_TYPE_AUDIO, .name = "ape", .long_name = NULL_IF_CONFIG_SMALL("Monkey's Audio"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_NELLYMOSER, .type = AVMEDIA_TYPE_AUDIO, .name = "nellymoser", .long_name = NULL_IF_CONFIG_SMALL("Nellymoser Asao"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_MUSEPACK8, .type = AVMEDIA_TYPE_AUDIO, .name = "musepack8", .long_name = NULL_IF_CONFIG_SMALL("Musepack SV8"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_SPEEX, .type = AVMEDIA_TYPE_AUDIO, .name = "speex", .long_name = NULL_IF_CONFIG_SMALL("Speex"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_WMAVOICE, .type = AVMEDIA_TYPE_AUDIO, .name = "wmavoice", .long_name = NULL_IF_CONFIG_SMALL("Windows Media Audio Voice"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_WMAPRO, .type = AVMEDIA_TYPE_AUDIO, .name = "wmapro", .long_name = NULL_IF_CONFIG_SMALL("Windows Media Audio 9 Professional"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_WMALOSSLESS, .type = AVMEDIA_TYPE_AUDIO, .name = "wmalossless", .long_name = NULL_IF_CONFIG_SMALL("Windows Media Audio Lossless"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_ATRAC3P, .type = AVMEDIA_TYPE_AUDIO, .name = "atrac3p", .long_name = NULL_IF_CONFIG_SMALL("ATRAC3+ (Adaptive TRansform Acoustic Coding 3+)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_EAC3, .type = AVMEDIA_TYPE_AUDIO, .name = "eac3", .long_name = NULL_IF_CONFIG_SMALL("ATSC A/52B (AC-3, E-AC-3)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_SIPR, .type = AVMEDIA_TYPE_AUDIO, .name = "sipr", .long_name = NULL_IF_CONFIG_SMALL("RealAudio SIPR / ACELP.NET"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_MP1, .type = AVMEDIA_TYPE_AUDIO, .name = "mp1", .long_name = NULL_IF_CONFIG_SMALL("MP1 (MPEG audio layer 1)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_TWINVQ, .type = AVMEDIA_TYPE_AUDIO, .name = "twinvq", .long_name = NULL_IF_CONFIG_SMALL("VQF TwinVQ"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_TRUEHD, @@ -2677,35 +2777,35 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "mp4als", .long_name = NULL_IF_CONFIG_SMALL("MPEG-4 Audio Lossless Coding (ALS)"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_ATRAC1, .type = AVMEDIA_TYPE_AUDIO, .name = "atrac1", .long_name = NULL_IF_CONFIG_SMALL("ATRAC1 (Adaptive TRansform Acoustic Coding)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_BINKAUDIO_RDFT, .type = AVMEDIA_TYPE_AUDIO, .name = "binkaudio_rdft", .long_name = NULL_IF_CONFIG_SMALL("Bink Audio (RDFT)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_BINKAUDIO_DCT, .type = AVMEDIA_TYPE_AUDIO, .name = "binkaudio_dct", .long_name = NULL_IF_CONFIG_SMALL("Bink Audio (DCT)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_AAC_LATM, .type = AVMEDIA_TYPE_AUDIO, .name = "aac_latm", .long_name = NULL_IF_CONFIG_SMALL("AAC LATM (Advanced Audio Coding LATM syntax)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, .profiles = NULL_IF_CONFIG_SMALL(ff_aac_profiles), }, { @@ -2713,84 +2813,84 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "qdmc", .long_name = NULL_IF_CONFIG_SMALL("QDesign Music"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_CELT, .type = AVMEDIA_TYPE_AUDIO, .name = "celt", .long_name = NULL_IF_CONFIG_SMALL("Constrained Energy Lapped Transform (CELT)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_G723_1, .type = AVMEDIA_TYPE_AUDIO, .name = "g723_1", .long_name = NULL_IF_CONFIG_SMALL("G.723.1"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_G729, .type = AVMEDIA_TYPE_AUDIO, .name = "g729", .long_name = NULL_IF_CONFIG_SMALL("G.729"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_8SVX_EXP, .type = AVMEDIA_TYPE_AUDIO, .name = "8svx_exp", .long_name = NULL_IF_CONFIG_SMALL("8SVX exponential"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_8SVX_FIB, .type = AVMEDIA_TYPE_AUDIO, .name = "8svx_fib", .long_name = NULL_IF_CONFIG_SMALL("8SVX fibonacci"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_BMV_AUDIO, .type = AVMEDIA_TYPE_AUDIO, .name = "bmv_audio", .long_name = NULL_IF_CONFIG_SMALL("Discworld II BMV audio"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_RALF, .type = AVMEDIA_TYPE_AUDIO, .name = "ralf", .long_name = NULL_IF_CONFIG_SMALL("RealAudio Lossless"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_IAC, .type = AVMEDIA_TYPE_AUDIO, .name = "iac", .long_name = NULL_IF_CONFIG_SMALL("IAC (Indeo Audio Coder)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ILBC, .type = AVMEDIA_TYPE_AUDIO, .name = "ilbc", .long_name = NULL_IF_CONFIG_SMALL("iLBC (Internet Low Bitrate Codec)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_OPUS, .type = AVMEDIA_TYPE_AUDIO, .name = "opus", .long_name = NULL_IF_CONFIG_SMALL("Opus (Opus Interactive Audio Codec)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_COMFORT_NOISE, .type = AVMEDIA_TYPE_AUDIO, .name = "comfortnoise", .long_name = NULL_IF_CONFIG_SMALL("RFC 3389 Comfort Noise"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_TAK, @@ -2804,187 +2904,218 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_AUDIO, .name = "metasound", .long_name = NULL_IF_CONFIG_SMALL("Voxware MetaSound"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_PAF_AUDIO, .type = AVMEDIA_TYPE_AUDIO, .name = "paf_audio", .long_name = NULL_IF_CONFIG_SMALL("Amazing Studio Packed Animation File Audio"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ON2AVC, .type = AVMEDIA_TYPE_AUDIO, .name = "avc", .long_name = NULL_IF_CONFIG_SMALL("On2 Audio for Video Codec"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_DSS_SP, .type = AVMEDIA_TYPE_AUDIO, .name = "dss_sp", .long_name = NULL_IF_CONFIG_SMALL("Digital Speech Standard - Standard Play mode (DSS SP)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_CODEC2, .type = AVMEDIA_TYPE_AUDIO, .name = "codec2", .long_name = NULL_IF_CONFIG_SMALL("codec2 (very low bitrate speech codec)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_FFWAVESYNTH, .type = AVMEDIA_TYPE_AUDIO, .name = "wavesynth", .long_name = NULL_IF_CONFIG_SMALL("Wave synthesis pseudo-codec"), + .props = AV_CODEC_PROP_INTRA_ONLY, }, { .id = AV_CODEC_ID_SONIC, .type = AVMEDIA_TYPE_AUDIO, .name = "sonic", .long_name = NULL_IF_CONFIG_SMALL("Sonic"), + .props = AV_CODEC_PROP_INTRA_ONLY, }, { .id = AV_CODEC_ID_SONIC_LS, .type = AVMEDIA_TYPE_AUDIO, .name = "sonicls", .long_name = NULL_IF_CONFIG_SMALL("Sonic lossless"), + .props = AV_CODEC_PROP_INTRA_ONLY, }, { .id = AV_CODEC_ID_EVRC, .type = AVMEDIA_TYPE_AUDIO, .name = "evrc", .long_name = NULL_IF_CONFIG_SMALL("EVRC (Enhanced Variable Rate Codec)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_SMV, .type = AVMEDIA_TYPE_AUDIO, .name = "smv", .long_name = NULL_IF_CONFIG_SMALL("SMV (Selectable Mode Vocoder)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_DSD_LSBF, .type = AVMEDIA_TYPE_AUDIO, .name = "dsd_lsbf", .long_name = NULL_IF_CONFIG_SMALL("DSD (Direct Stream Digital), least significant bit first"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_DSD_MSBF, .type = AVMEDIA_TYPE_AUDIO, .name = "dsd_msbf", .long_name = NULL_IF_CONFIG_SMALL("DSD (Direct Stream Digital), most significant bit first"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_DSD_LSBF_PLANAR, .type = AVMEDIA_TYPE_AUDIO, .name = "dsd_lsbf_planar", .long_name = NULL_IF_CONFIG_SMALL("DSD (Direct Stream Digital), least significant bit first, planar"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_DSD_MSBF_PLANAR, .type = AVMEDIA_TYPE_AUDIO, .name = "dsd_msbf_planar", .long_name = NULL_IF_CONFIG_SMALL("DSD (Direct Stream Digital), most significant bit first, planar"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_4GV, .type = AVMEDIA_TYPE_AUDIO, .name = "4gv", .long_name = NULL_IF_CONFIG_SMALL("4GV (Fourth Generation Vocoder)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_INTERPLAY_ACM, .type = AVMEDIA_TYPE_AUDIO, .name = "interplayacm", .long_name = NULL_IF_CONFIG_SMALL("Interplay ACM"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_XMA1, .type = AVMEDIA_TYPE_AUDIO, .name = "xma1", .long_name = NULL_IF_CONFIG_SMALL("Xbox Media Audio 1"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_XMA2, .type = AVMEDIA_TYPE_AUDIO, .name = "xma2", .long_name = NULL_IF_CONFIG_SMALL("Xbox Media Audio 2"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_DST, .type = AVMEDIA_TYPE_AUDIO, .name = "dst", .long_name = NULL_IF_CONFIG_SMALL("DST (Direct Stream Transfer)"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_ATRAC3AL, .type = AVMEDIA_TYPE_AUDIO, .name = "atrac3al", .long_name = NULL_IF_CONFIG_SMALL("ATRAC3 AL (Adaptive TRansform Acoustic Coding 3 Advanced Lossless)"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_ATRAC3PAL, .type = AVMEDIA_TYPE_AUDIO, .name = "atrac3pal", .long_name = NULL_IF_CONFIG_SMALL("ATRAC3+ AL (Adaptive TRansform Acoustic Coding 3+ Advanced Lossless)"), - .props = AV_CODEC_PROP_LOSSLESS, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, { .id = AV_CODEC_ID_DOLBY_E, .type = AVMEDIA_TYPE_AUDIO, .name = "dolby_e", .long_name = NULL_IF_CONFIG_SMALL("Dolby E"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_APTX, .type = AVMEDIA_TYPE_AUDIO, .name = "aptx", .long_name = NULL_IF_CONFIG_SMALL("aptX (Audio Processing Technology for Bluetooth)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_APTX_HD, .type = AVMEDIA_TYPE_AUDIO, .name = "aptx_hd", .long_name = NULL_IF_CONFIG_SMALL("aptX HD (Audio Processing Technology for Bluetooth)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_SBC, .type = AVMEDIA_TYPE_AUDIO, .name = "sbc", .long_name = NULL_IF_CONFIG_SMALL("SBC (low-complexity subband codec)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_ATRAC9, .type = AVMEDIA_TYPE_AUDIO, .name = "atrac9", .long_name = NULL_IF_CONFIG_SMALL("ATRAC9 (Adaptive TRansform Acoustic Coding 9)"), - .props = AV_CODEC_PROP_LOSSY, + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, { .id = AV_CODEC_ID_HCOM, .type = AVMEDIA_TYPE_AUDIO, .name = "hcom", .long_name = NULL_IF_CONFIG_SMALL("HCOM Audio"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_ACELP_KELVIN, + .type = AVMEDIA_TYPE_AUDIO, + .name = "acelp.kelvin", + .long_name = NULL_IF_CONFIG_SMALL("Sipro ACELP.KELVIN"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_MPEGH_3D_AUDIO, + .type = AVMEDIA_TYPE_AUDIO, + .name = "mpegh_3d_audio", + .long_name = NULL_IF_CONFIG_SMALL("MPEG-H 3D Audio"), .props = AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_SIREN, + .type = AVMEDIA_TYPE_AUDIO, + .name = "siren", + .long_name = NULL_IF_CONFIG_SMALL("Siren"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_HCA, + .type = AVMEDIA_TYPE_AUDIO, + .name = "hca", + .long_name = NULL_IF_CONFIG_SMALL("CRI HCA"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, /* subtitle codecs */ { @@ -3184,6 +3315,12 @@ static const AVCodecDescriptor codec_descriptors[] = { .name = "scte_35", .long_name = NULL_IF_CONFIG_SMALL("SCTE 35 Message Queue"), }, + { + .id = AV_CODEC_ID_EPG, + .type = AVMEDIA_TYPE_DATA, + .name = "epg", + .long_name = NULL_IF_CONFIG_SMALL("Electronic Program Guide"), + }, { .id = AV_CODEC_ID_BINTEXT, .type = AVMEDIA_TYPE_VIDEO, diff --git a/libavcodec/codec_desc.h b/libavcodec/codec_desc.h new file mode 100644 index 00000000000..126b52df476 --- /dev/null +++ b/libavcodec/codec_desc.h @@ -0,0 +1,128 @@ +/* + * Codec descriptors public API + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_CODEC_DESC_H +#define AVCODEC_CODEC_DESC_H + +#include "libavutil/avutil.h" + +#include "codec_id.h" + +/** + * @addtogroup lavc_core + * @{ + */ + +/** + * This struct describes the properties of a single codec described by an + * AVCodecID. + * @see avcodec_descriptor_get() + */ +typedef struct AVCodecDescriptor { + enum AVCodecID id; + enum AVMediaType type; + /** + * Name of the codec described by this descriptor. It is non-empty and + * unique for each codec descriptor. It should contain alphanumeric + * characters and '_' only. + */ + const char *name; + /** + * A more descriptive name for this codec. May be NULL. + */ + const char *long_name; + /** + * Codec properties, a combination of AV_CODEC_PROP_* flags. + */ + int props; + /** + * MIME type(s) associated with the codec. + * May be NULL; if not, a NULL-terminated array of MIME types. + * The first item is always non-NULL and is the preferred MIME type. + */ + const char *const *mime_types; + /** + * If non-NULL, an array of profiles recognized for this codec. + * Terminated with FF_PROFILE_UNKNOWN. + */ + const struct AVProfile *profiles; +} AVCodecDescriptor; + +/** + * Codec uses only intra compression. + * Video and audio codecs only. + */ +#define AV_CODEC_PROP_INTRA_ONLY (1 << 0) +/** + * Codec supports lossy compression. Audio and video codecs only. + * @note a codec may support both lossy and lossless + * compression modes + */ +#define AV_CODEC_PROP_LOSSY (1 << 1) +/** + * Codec supports lossless compression. Audio and video codecs only. + */ +#define AV_CODEC_PROP_LOSSLESS (1 << 2) +/** + * Codec supports frame reordering. That is, the coded order (the order in which + * the encoded packets are output by the encoders / stored / input to the + * decoders) may be different from the presentation order of the corresponding + * frames. + * + * For codecs that do not have this property set, PTS and DTS should always be + * equal. + */ +#define AV_CODEC_PROP_REORDER (1 << 3) +/** + * Subtitle codec is bitmap based + * Decoded AVSubtitle data can be read from the AVSubtitleRect->pict field. + */ +#define AV_CODEC_PROP_BITMAP_SUB (1 << 16) +/** + * Subtitle codec is text based. + * Decoded AVSubtitle data can be read from the AVSubtitleRect->ass field. + */ +#define AV_CODEC_PROP_TEXT_SUB (1 << 17) + +/** + * @return descriptor for given codec ID or NULL if no descriptor exists. + */ +const AVCodecDescriptor *avcodec_descriptor_get(enum AVCodecID id); + +/** + * Iterate over all codec descriptors known to libavcodec. + * + * @param prev previous descriptor. NULL to get the first descriptor. + * + * @return next descriptor or NULL after the last descriptor + */ +const AVCodecDescriptor *avcodec_descriptor_next(const AVCodecDescriptor *prev); + +/** + * @return codec descriptor with the given name or NULL if no such descriptor + * exists. + */ +const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name); + +/** + * @} + */ + +#endif // AVCODEC_CODEC_DESC_H diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h new file mode 100644 index 00000000000..d885962c9c3 --- /dev/null +++ b/libavcodec/codec_id.h @@ -0,0 +1,577 @@ +/* + * Codec IDs + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_CODEC_ID_H +#define AVCODEC_CODEC_ID_H + +#include "libavutil/avutil.h" + +/** + * @addtogroup lavc_core + * @{ + */ + +/** + * Identify the syntax and semantics of the bitstream. + * The principle is roughly: + * Two decoders with the same ID can decode the same streams. + * Two encoders with the same ID can encode compatible streams. + * There may be slight deviations from the principle due to implementation + * details. + * + * If you add a codec ID to this list, add it so that + * 1. no value of an existing codec ID changes (that would break ABI), + * 2. it is as close as possible to similar codecs + * + * After adding new codec IDs, do not forget to add an entry to the codec + * descriptor list and bump libavcodec minor version. + */ +enum AVCodecID { + AV_CODEC_ID_NONE, + + /* video codecs */ + AV_CODEC_ID_MPEG1VIDEO, + AV_CODEC_ID_MPEG2VIDEO, ///< preferred ID for MPEG-1/2 video decoding + AV_CODEC_ID_H261, + AV_CODEC_ID_H263, + AV_CODEC_ID_RV10, + AV_CODEC_ID_RV20, + AV_CODEC_ID_MJPEG, + AV_CODEC_ID_MJPEGB, + AV_CODEC_ID_LJPEG, + AV_CODEC_ID_SP5X, + AV_CODEC_ID_JPEGLS, + AV_CODEC_ID_MPEG4, + AV_CODEC_ID_RAWVIDEO, + AV_CODEC_ID_MSMPEG4V1, + AV_CODEC_ID_MSMPEG4V2, + AV_CODEC_ID_MSMPEG4V3, + AV_CODEC_ID_WMV1, + AV_CODEC_ID_WMV2, + AV_CODEC_ID_H263P, + AV_CODEC_ID_H263I, + AV_CODEC_ID_FLV1, + AV_CODEC_ID_SVQ1, + AV_CODEC_ID_SVQ3, + AV_CODEC_ID_DVVIDEO, + AV_CODEC_ID_HUFFYUV, + AV_CODEC_ID_CYUV, + AV_CODEC_ID_H264, + AV_CODEC_ID_INDEO3, + AV_CODEC_ID_VP3, + AV_CODEC_ID_THEORA, + AV_CODEC_ID_ASV1, + AV_CODEC_ID_ASV2, + AV_CODEC_ID_FFV1, + AV_CODEC_ID_4XM, + AV_CODEC_ID_VCR1, + AV_CODEC_ID_CLJR, + AV_CODEC_ID_MDEC, + AV_CODEC_ID_ROQ, + AV_CODEC_ID_INTERPLAY_VIDEO, + AV_CODEC_ID_XAN_WC3, + AV_CODEC_ID_XAN_WC4, + AV_CODEC_ID_RPZA, + AV_CODEC_ID_CINEPAK, + AV_CODEC_ID_WS_VQA, + AV_CODEC_ID_MSRLE, + AV_CODEC_ID_MSVIDEO1, + AV_CODEC_ID_IDCIN, + AV_CODEC_ID_8BPS, + AV_CODEC_ID_SMC, + AV_CODEC_ID_FLIC, + AV_CODEC_ID_TRUEMOTION1, + AV_CODEC_ID_VMDVIDEO, + AV_CODEC_ID_MSZH, + AV_CODEC_ID_ZLIB, + AV_CODEC_ID_QTRLE, + AV_CODEC_ID_TSCC, + AV_CODEC_ID_ULTI, + AV_CODEC_ID_QDRAW, + AV_CODEC_ID_VIXL, + AV_CODEC_ID_QPEG, + AV_CODEC_ID_PNG, + AV_CODEC_ID_PPM, + AV_CODEC_ID_PBM, + AV_CODEC_ID_PGM, + AV_CODEC_ID_PGMYUV, + AV_CODEC_ID_PAM, + AV_CODEC_ID_FFVHUFF, + AV_CODEC_ID_RV30, + AV_CODEC_ID_RV40, + AV_CODEC_ID_VC1, + AV_CODEC_ID_WMV3, + AV_CODEC_ID_LOCO, + AV_CODEC_ID_WNV1, + AV_CODEC_ID_AASC, + AV_CODEC_ID_INDEO2, + AV_CODEC_ID_FRAPS, + AV_CODEC_ID_TRUEMOTION2, + AV_CODEC_ID_BMP, + AV_CODEC_ID_CSCD, + AV_CODEC_ID_MMVIDEO, + AV_CODEC_ID_ZMBV, + AV_CODEC_ID_AVS, + AV_CODEC_ID_SMACKVIDEO, + AV_CODEC_ID_NUV, + AV_CODEC_ID_KMVC, + AV_CODEC_ID_FLASHSV, + AV_CODEC_ID_CAVS, + AV_CODEC_ID_JPEG2000, + AV_CODEC_ID_VMNC, + AV_CODEC_ID_VP5, + AV_CODEC_ID_VP6, + AV_CODEC_ID_VP6F, + AV_CODEC_ID_TARGA, + AV_CODEC_ID_DSICINVIDEO, + AV_CODEC_ID_TIERTEXSEQVIDEO, + AV_CODEC_ID_TIFF, + AV_CODEC_ID_GIF, + AV_CODEC_ID_DXA, + AV_CODEC_ID_DNXHD, + AV_CODEC_ID_THP, + AV_CODEC_ID_SGI, + AV_CODEC_ID_C93, + AV_CODEC_ID_BETHSOFTVID, + AV_CODEC_ID_PTX, + AV_CODEC_ID_TXD, + AV_CODEC_ID_VP6A, + AV_CODEC_ID_AMV, + AV_CODEC_ID_VB, + AV_CODEC_ID_PCX, + AV_CODEC_ID_SUNRAST, + AV_CODEC_ID_INDEO4, + AV_CODEC_ID_INDEO5, + AV_CODEC_ID_MIMIC, + AV_CODEC_ID_RL2, + AV_CODEC_ID_ESCAPE124, + AV_CODEC_ID_DIRAC, + AV_CODEC_ID_BFI, + AV_CODEC_ID_CMV, + AV_CODEC_ID_MOTIONPIXELS, + AV_CODEC_ID_TGV, + AV_CODEC_ID_TGQ, + AV_CODEC_ID_TQI, + AV_CODEC_ID_AURA, + AV_CODEC_ID_AURA2, + AV_CODEC_ID_V210X, + AV_CODEC_ID_TMV, + AV_CODEC_ID_V210, + AV_CODEC_ID_DPX, + AV_CODEC_ID_MAD, + AV_CODEC_ID_FRWU, + AV_CODEC_ID_FLASHSV2, + AV_CODEC_ID_CDGRAPHICS, + AV_CODEC_ID_R210, + AV_CODEC_ID_ANM, + AV_CODEC_ID_BINKVIDEO, + AV_CODEC_ID_IFF_ILBM, +#define AV_CODEC_ID_IFF_BYTERUN1 AV_CODEC_ID_IFF_ILBM + AV_CODEC_ID_KGV1, + AV_CODEC_ID_YOP, + AV_CODEC_ID_VP8, + AV_CODEC_ID_PICTOR, + AV_CODEC_ID_ANSI, + AV_CODEC_ID_A64_MULTI, + AV_CODEC_ID_A64_MULTI5, + AV_CODEC_ID_R10K, + AV_CODEC_ID_MXPEG, + AV_CODEC_ID_LAGARITH, + AV_CODEC_ID_PRORES, + AV_CODEC_ID_JV, + AV_CODEC_ID_DFA, + AV_CODEC_ID_WMV3IMAGE, + AV_CODEC_ID_VC1IMAGE, + AV_CODEC_ID_UTVIDEO, + AV_CODEC_ID_BMV_VIDEO, + AV_CODEC_ID_VBLE, + AV_CODEC_ID_DXTORY, + AV_CODEC_ID_V410, + AV_CODEC_ID_XWD, + AV_CODEC_ID_CDXL, + AV_CODEC_ID_XBM, + AV_CODEC_ID_ZEROCODEC, + AV_CODEC_ID_MSS1, + AV_CODEC_ID_MSA1, + AV_CODEC_ID_TSCC2, + AV_CODEC_ID_MTS2, + AV_CODEC_ID_CLLC, + AV_CODEC_ID_MSS2, + AV_CODEC_ID_VP9, + AV_CODEC_ID_AIC, + AV_CODEC_ID_ESCAPE130, + AV_CODEC_ID_G2M, + AV_CODEC_ID_WEBP, + AV_CODEC_ID_HNM4_VIDEO, + AV_CODEC_ID_HEVC, +#define AV_CODEC_ID_H265 AV_CODEC_ID_HEVC + AV_CODEC_ID_FIC, + AV_CODEC_ID_ALIAS_PIX, + AV_CODEC_ID_BRENDER_PIX, + AV_CODEC_ID_PAF_VIDEO, + AV_CODEC_ID_EXR, + AV_CODEC_ID_VP7, + AV_CODEC_ID_SANM, + AV_CODEC_ID_SGIRLE, + AV_CODEC_ID_MVC1, + AV_CODEC_ID_MVC2, + AV_CODEC_ID_HQX, + AV_CODEC_ID_TDSC, + AV_CODEC_ID_HQ_HQA, + AV_CODEC_ID_HAP, + AV_CODEC_ID_DDS, + AV_CODEC_ID_DXV, + AV_CODEC_ID_SCREENPRESSO, + AV_CODEC_ID_RSCC, + AV_CODEC_ID_AVS2, + + AV_CODEC_ID_Y41P = 0x8000, + AV_CODEC_ID_AVRP, + AV_CODEC_ID_012V, + AV_CODEC_ID_AVUI, + AV_CODEC_ID_AYUV, + AV_CODEC_ID_TARGA_Y216, + AV_CODEC_ID_V308, + AV_CODEC_ID_V408, + AV_CODEC_ID_YUV4, + AV_CODEC_ID_AVRN, + AV_CODEC_ID_CPIA, + AV_CODEC_ID_XFACE, + AV_CODEC_ID_SNOW, + AV_CODEC_ID_SMVJPEG, + AV_CODEC_ID_APNG, + AV_CODEC_ID_DAALA, + AV_CODEC_ID_CFHD, + AV_CODEC_ID_TRUEMOTION2RT, + AV_CODEC_ID_M101, + AV_CODEC_ID_MAGICYUV, + AV_CODEC_ID_SHEERVIDEO, + AV_CODEC_ID_YLC, + AV_CODEC_ID_PSD, + AV_CODEC_ID_PIXLET, + AV_CODEC_ID_SPEEDHQ, + AV_CODEC_ID_FMVC, + AV_CODEC_ID_SCPR, + AV_CODEC_ID_CLEARVIDEO, + AV_CODEC_ID_XPM, + AV_CODEC_ID_AV1, + AV_CODEC_ID_BITPACKED, + AV_CODEC_ID_MSCC, + AV_CODEC_ID_SRGC, + AV_CODEC_ID_SVG, + AV_CODEC_ID_GDV, + AV_CODEC_ID_FITS, + AV_CODEC_ID_IMM4, + AV_CODEC_ID_PROSUMER, + AV_CODEC_ID_MWSC, + AV_CODEC_ID_WCMV, + AV_CODEC_ID_RASC, + AV_CODEC_ID_HYMT, + AV_CODEC_ID_ARBC, + AV_CODEC_ID_AGM, + AV_CODEC_ID_LSCR, + AV_CODEC_ID_VP4, + AV_CODEC_ID_IMM5, + AV_CODEC_ID_MVDV, + AV_CODEC_ID_MVHA, + AV_CODEC_ID_CDTOONS, + AV_CODEC_ID_MV30, + AV_CODEC_ID_NOTCHLC, + AV_CODEC_ID_PFM, + + /* various PCM "codecs" */ + AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs + AV_CODEC_ID_PCM_S16LE = 0x10000, + AV_CODEC_ID_PCM_S16BE, + AV_CODEC_ID_PCM_U16LE, + AV_CODEC_ID_PCM_U16BE, + AV_CODEC_ID_PCM_S8, + AV_CODEC_ID_PCM_U8, + AV_CODEC_ID_PCM_MULAW, + AV_CODEC_ID_PCM_ALAW, + AV_CODEC_ID_PCM_S32LE, + AV_CODEC_ID_PCM_S32BE, + AV_CODEC_ID_PCM_U32LE, + AV_CODEC_ID_PCM_U32BE, + AV_CODEC_ID_PCM_S24LE, + AV_CODEC_ID_PCM_S24BE, + AV_CODEC_ID_PCM_U24LE, + AV_CODEC_ID_PCM_U24BE, + AV_CODEC_ID_PCM_S24DAUD, + AV_CODEC_ID_PCM_ZORK, + AV_CODEC_ID_PCM_S16LE_PLANAR, + AV_CODEC_ID_PCM_DVD, + AV_CODEC_ID_PCM_F32BE, + AV_CODEC_ID_PCM_F32LE, + AV_CODEC_ID_PCM_F64BE, + AV_CODEC_ID_PCM_F64LE, + AV_CODEC_ID_PCM_BLURAY, + AV_CODEC_ID_PCM_LXF, + AV_CODEC_ID_S302M, + AV_CODEC_ID_PCM_S8_PLANAR, + AV_CODEC_ID_PCM_S24LE_PLANAR, + AV_CODEC_ID_PCM_S32LE_PLANAR, + AV_CODEC_ID_PCM_S16BE_PLANAR, + + AV_CODEC_ID_PCM_S64LE = 0x10800, + AV_CODEC_ID_PCM_S64BE, + AV_CODEC_ID_PCM_F16LE, + AV_CODEC_ID_PCM_F24LE, + AV_CODEC_ID_PCM_VIDC, + + /* various ADPCM codecs */ + AV_CODEC_ID_ADPCM_IMA_QT = 0x11000, + AV_CODEC_ID_ADPCM_IMA_WAV, + AV_CODEC_ID_ADPCM_IMA_DK3, + AV_CODEC_ID_ADPCM_IMA_DK4, + AV_CODEC_ID_ADPCM_IMA_WS, + AV_CODEC_ID_ADPCM_IMA_SMJPEG, + AV_CODEC_ID_ADPCM_MS, + AV_CODEC_ID_ADPCM_4XM, + AV_CODEC_ID_ADPCM_XA, + AV_CODEC_ID_ADPCM_ADX, + AV_CODEC_ID_ADPCM_EA, + AV_CODEC_ID_ADPCM_G726, + AV_CODEC_ID_ADPCM_CT, + AV_CODEC_ID_ADPCM_SWF, + AV_CODEC_ID_ADPCM_YAMAHA, + AV_CODEC_ID_ADPCM_SBPRO_4, + AV_CODEC_ID_ADPCM_SBPRO_3, + AV_CODEC_ID_ADPCM_SBPRO_2, + AV_CODEC_ID_ADPCM_THP, + AV_CODEC_ID_ADPCM_IMA_AMV, + AV_CODEC_ID_ADPCM_EA_R1, + AV_CODEC_ID_ADPCM_EA_R3, + AV_CODEC_ID_ADPCM_EA_R2, + AV_CODEC_ID_ADPCM_IMA_EA_SEAD, + AV_CODEC_ID_ADPCM_IMA_EA_EACS, + AV_CODEC_ID_ADPCM_EA_XAS, + AV_CODEC_ID_ADPCM_EA_MAXIS_XA, + AV_CODEC_ID_ADPCM_IMA_ISS, + AV_CODEC_ID_ADPCM_G722, + AV_CODEC_ID_ADPCM_IMA_APC, + AV_CODEC_ID_ADPCM_VIMA, + + AV_CODEC_ID_ADPCM_AFC = 0x11800, + AV_CODEC_ID_ADPCM_IMA_OKI, + AV_CODEC_ID_ADPCM_DTK, + AV_CODEC_ID_ADPCM_IMA_RAD, + AV_CODEC_ID_ADPCM_G726LE, + AV_CODEC_ID_ADPCM_THP_LE, + AV_CODEC_ID_ADPCM_PSX, + AV_CODEC_ID_ADPCM_AICA, + AV_CODEC_ID_ADPCM_IMA_DAT4, + AV_CODEC_ID_ADPCM_MTAF, + AV_CODEC_ID_ADPCM_AGM, + AV_CODEC_ID_ADPCM_ARGO, + AV_CODEC_ID_ADPCM_IMA_SSI, + AV_CODEC_ID_ADPCM_ZORK, + AV_CODEC_ID_ADPCM_IMA_APM, + AV_CODEC_ID_ADPCM_IMA_ALP, + AV_CODEC_ID_ADPCM_IMA_MTF, + AV_CODEC_ID_ADPCM_IMA_CUNNING, + + /* AMR */ + AV_CODEC_ID_AMR_NB = 0x12000, + AV_CODEC_ID_AMR_WB, + + /* RealAudio codecs*/ + AV_CODEC_ID_RA_144 = 0x13000, + AV_CODEC_ID_RA_288, + + /* various DPCM codecs */ + AV_CODEC_ID_ROQ_DPCM = 0x14000, + AV_CODEC_ID_INTERPLAY_DPCM, + AV_CODEC_ID_XAN_DPCM, + AV_CODEC_ID_SOL_DPCM, + + AV_CODEC_ID_SDX2_DPCM = 0x14800, + AV_CODEC_ID_GREMLIN_DPCM, + AV_CODEC_ID_DERF_DPCM, + + /* audio codecs */ + AV_CODEC_ID_MP2 = 0x15000, + AV_CODEC_ID_MP3, ///< preferred ID for decoding MPEG audio layer 1, 2 or 3 + AV_CODEC_ID_AAC, + AV_CODEC_ID_AC3, + AV_CODEC_ID_DTS, + AV_CODEC_ID_VORBIS, + AV_CODEC_ID_DVAUDIO, + AV_CODEC_ID_WMAV1, + AV_CODEC_ID_WMAV2, + AV_CODEC_ID_MACE3, + AV_CODEC_ID_MACE6, + AV_CODEC_ID_VMDAUDIO, + AV_CODEC_ID_FLAC, + AV_CODEC_ID_MP3ADU, + AV_CODEC_ID_MP3ON4, + AV_CODEC_ID_SHORTEN, + AV_CODEC_ID_ALAC, + AV_CODEC_ID_WESTWOOD_SND1, + AV_CODEC_ID_GSM, ///< as in Berlin toast format + AV_CODEC_ID_QDM2, + AV_CODEC_ID_COOK, + AV_CODEC_ID_TRUESPEECH, + AV_CODEC_ID_TTA, + AV_CODEC_ID_SMACKAUDIO, + AV_CODEC_ID_QCELP, + AV_CODEC_ID_WAVPACK, + AV_CODEC_ID_DSICINAUDIO, + AV_CODEC_ID_IMC, + AV_CODEC_ID_MUSEPACK7, + AV_CODEC_ID_MLP, + AV_CODEC_ID_GSM_MS, /* as found in WAV */ + AV_CODEC_ID_ATRAC3, + AV_CODEC_ID_APE, + AV_CODEC_ID_NELLYMOSER, + AV_CODEC_ID_MUSEPACK8, + AV_CODEC_ID_SPEEX, + AV_CODEC_ID_WMAVOICE, + AV_CODEC_ID_WMAPRO, + AV_CODEC_ID_WMALOSSLESS, + AV_CODEC_ID_ATRAC3P, + AV_CODEC_ID_EAC3, + AV_CODEC_ID_SIPR, + AV_CODEC_ID_MP1, + AV_CODEC_ID_TWINVQ, + AV_CODEC_ID_TRUEHD, + AV_CODEC_ID_MP4ALS, + AV_CODEC_ID_ATRAC1, + AV_CODEC_ID_BINKAUDIO_RDFT, + AV_CODEC_ID_BINKAUDIO_DCT, + AV_CODEC_ID_AAC_LATM, + AV_CODEC_ID_QDMC, + AV_CODEC_ID_CELT, + AV_CODEC_ID_G723_1, + AV_CODEC_ID_G729, + AV_CODEC_ID_8SVX_EXP, + AV_CODEC_ID_8SVX_FIB, + AV_CODEC_ID_BMV_AUDIO, + AV_CODEC_ID_RALF, + AV_CODEC_ID_IAC, + AV_CODEC_ID_ILBC, + AV_CODEC_ID_OPUS, + AV_CODEC_ID_COMFORT_NOISE, + AV_CODEC_ID_TAK, + AV_CODEC_ID_METASOUND, + AV_CODEC_ID_PAF_AUDIO, + AV_CODEC_ID_ON2AVC, + AV_CODEC_ID_DSS_SP, + AV_CODEC_ID_CODEC2, + + AV_CODEC_ID_FFWAVESYNTH = 0x15800, + AV_CODEC_ID_SONIC, + AV_CODEC_ID_SONIC_LS, + AV_CODEC_ID_EVRC, + AV_CODEC_ID_SMV, + AV_CODEC_ID_DSD_LSBF, + AV_CODEC_ID_DSD_MSBF, + AV_CODEC_ID_DSD_LSBF_PLANAR, + AV_CODEC_ID_DSD_MSBF_PLANAR, + AV_CODEC_ID_4GV, + AV_CODEC_ID_INTERPLAY_ACM, + AV_CODEC_ID_XMA1, + AV_CODEC_ID_XMA2, + AV_CODEC_ID_DST, + AV_CODEC_ID_ATRAC3AL, + AV_CODEC_ID_ATRAC3PAL, + AV_CODEC_ID_DOLBY_E, + AV_CODEC_ID_APTX, + AV_CODEC_ID_APTX_HD, + AV_CODEC_ID_SBC, + AV_CODEC_ID_ATRAC9, + AV_CODEC_ID_HCOM, + AV_CODEC_ID_ACELP_KELVIN, + AV_CODEC_ID_MPEGH_3D_AUDIO, + AV_CODEC_ID_SIREN, + AV_CODEC_ID_HCA, + + /* subtitle codecs */ + AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs. + AV_CODEC_ID_DVD_SUBTITLE = 0x17000, + AV_CODEC_ID_DVB_SUBTITLE, + AV_CODEC_ID_TEXT, ///< raw UTF-8 text + AV_CODEC_ID_XSUB, + AV_CODEC_ID_SSA, + AV_CODEC_ID_MOV_TEXT, + AV_CODEC_ID_HDMV_PGS_SUBTITLE, + AV_CODEC_ID_DVB_TELETEXT, + AV_CODEC_ID_SRT, + + AV_CODEC_ID_MICRODVD = 0x17800, + AV_CODEC_ID_EIA_608, + AV_CODEC_ID_JACOSUB, + AV_CODEC_ID_SAMI, + AV_CODEC_ID_REALTEXT, + AV_CODEC_ID_STL, + AV_CODEC_ID_SUBVIEWER1, + AV_CODEC_ID_SUBVIEWER, + AV_CODEC_ID_SUBRIP, + AV_CODEC_ID_WEBVTT, + AV_CODEC_ID_MPL2, + AV_CODEC_ID_VPLAYER, + AV_CODEC_ID_PJS, + AV_CODEC_ID_ASS, + AV_CODEC_ID_HDMV_TEXT_SUBTITLE, + AV_CODEC_ID_TTML, + AV_CODEC_ID_ARIB_CAPTION, + + /* other specific kind of codecs (generally used for attachments) */ + AV_CODEC_ID_FIRST_UNKNOWN = 0x18000, ///< A dummy ID pointing at the start of various fake codecs. + AV_CODEC_ID_TTF = 0x18000, + + AV_CODEC_ID_SCTE_35, ///< Contain timestamp estimated through PCR of program stream. + AV_CODEC_ID_EPG, + AV_CODEC_ID_BINTEXT = 0x18800, + AV_CODEC_ID_XBIN, + AV_CODEC_ID_IDF, + AV_CODEC_ID_OTF, + AV_CODEC_ID_SMPTE_KLV, + AV_CODEC_ID_DVD_NAV, + AV_CODEC_ID_TIMED_ID3, + AV_CODEC_ID_BIN_DATA, + + + AV_CODEC_ID_PROBE = 0x19000, ///< codec_id is not known (like AV_CODEC_ID_NONE) but lavf should attempt to identify it + + AV_CODEC_ID_MPEG2TS = 0x20000, /**< _FAKE_ codec to indicate a raw MPEG-2 TS + * stream (only used by libavformat) */ + AV_CODEC_ID_MPEG4SYSTEMS = 0x20001, /**< _FAKE_ codec to indicate a MPEG-4 Systems + * stream (only used by libavformat) */ + AV_CODEC_ID_FFMETADATA = 0x21000, ///< Dummy codec for streams containing only metadata information. + AV_CODEC_ID_WRAPPED_AVFRAME = 0x21001, ///< Passthrough codec, AVFrames wrapped in AVPacket +}; + +/** + * Get the type of the given codec. + */ +enum AVMediaType avcodec_get_type(enum AVCodecID codec_id); + +/** + * Get the name of a codec. + * @return a static string identifying the codec; never NULL + */ +const char *avcodec_get_name(enum AVCodecID id); + +/** + * @} + */ + +#endif // AVCODEC_CODEC_ID_H diff --git a/libavcodec/codec_par.h b/libavcodec/codec_par.h new file mode 100644 index 00000000000..948758e2373 --- /dev/null +++ b/libavcodec/codec_par.h @@ -0,0 +1,229 @@ +/* + * Codec parameters public API + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_CODEC_PAR_H +#define AVCODEC_CODEC_PAR_H + +#include + +#include "libavutil/avutil.h" +#include "libavutil/rational.h" +#include "libavutil/pixfmt.h" + +#include "codec_id.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. + * + * sizeof(AVCodecParameters) is not a part of the public ABI, this struct must + * be allocated with avcodec_parameters_alloc() and freed with + * avcodec_parameters_free(). + */ +typedef struct AVCodecParameters { + /** + * General type of the encoded data. + */ + enum AVMediaType codec_type; + /** + * Specific type of the encoded data (the codec used). + */ + enum AVCodecID codec_id; + /** + * Additional information about the codec (corresponds to the AVI FOURCC). + */ + uint32_t codec_tag; + + /** + * Extra binary data needed for initializing the decoder, codec-dependent. + * + * Must be allocated with av_malloc() and will be freed by + * avcodec_parameters_free(). The allocated size of extradata must be at + * least extradata_size + AV_INPUT_BUFFER_PADDING_SIZE, with the padding + * bytes zeroed. + */ + uint8_t *extradata; + /** + * Size of the extradata content in bytes. + */ + int extradata_size; + + /** + * - video: the pixel format, the value corresponds to enum AVPixelFormat. + * - audio: the sample format, the value corresponds to enum AVSampleFormat. + */ + int format; + + /** + * The average bitrate of the encoded data (in bits per second). + */ + int64_t bit_rate; + + /** + * The number of bits per sample in the codedwords. + * + * This is basically the bitrate per sample. It is mandatory for a bunch of + * formats to actually decode them. It's the number of bits for one sample in + * the actual coded bitstream. + * + * This could be for example 4 for ADPCM + * For PCM formats this matches bits_per_raw_sample + * Can be 0 + */ + int bits_per_coded_sample; + + /** + * This is the number of valid bits in each output sample. If the + * sample format has more bits, the least significant bits are additional + * padding bits, which are always 0. Use right shifts to reduce the sample + * to its actual size. For example, audio formats with 24 bit samples will + * have bits_per_raw_sample set to 24, and format set to AV_SAMPLE_FMT_S32. + * To get the original sample use "(int32_t)sample >> 8"." + * + * For ADPCM this might be 12 or 16 or similar + * Can be 0 + */ + int bits_per_raw_sample; + + /** + * Codec-specific bitstream restrictions that the stream conforms to. + */ + int profile; + int level; + + /** + * Video only. The dimensions of the video frame in pixels. + */ + int width; + int height; + + /** + * Video only. The aspect ratio (width / height) which a single pixel + * should have when displayed. + * + * When the aspect ratio is unknown / undefined, the numerator should be + * set to 0 (the denominator may have any value). + */ + AVRational sample_aspect_ratio; + + /** + * Video only. The order of the fields in interlaced video. + */ + enum AVFieldOrder field_order; + + /** + * Video only. Additional colorspace characteristics. + */ + enum AVColorRange color_range; + enum AVColorPrimaries color_primaries; + enum AVColorTransferCharacteristic color_trc; + enum AVColorSpace color_space; + enum AVChromaLocation chroma_location; + + /** + * Video only. Number of delayed frames. + */ + int video_delay; + + /** + * 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. + */ + uint64_t channel_layout; + /** + * Audio only. The number of audio channels. + */ + int channels; + /** + * Audio only. The number of audio samples per second. + */ + int sample_rate; + /** + * Audio only. The number of bytes per coded audio frame, required by some + * formats. + * + * Corresponds to nBlockAlign in WAVEFORMATEX. + */ + int block_align; + /** + * Audio only. Audio frame size, if known. Required by some formats to be static. + */ + int frame_size; + + /** + * Audio only. The amount of padding (in samples) 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. + */ + 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. + */ + int trailing_padding; + /** + * Audio only. Number of samples to skip after a discontinuity. + */ + int seek_preroll; +} AVCodecParameters; + +/** + * Allocate a new AVCodecParameters and set its fields to default values + * (unknown/invalid/0). The returned struct must be freed with + * avcodec_parameters_free(). + */ +AVCodecParameters *avcodec_parameters_alloc(void); + +/** + * Free an AVCodecParameters instance and everything associated with it and + * write NULL to the supplied pointer. + */ +void avcodec_parameters_free(AVCodecParameters **par); + +/** + * Copy the contents of src to dst. Any allocated fields in dst are freed and + * replaced with newly allocated duplicates of the corresponding fields in src. + * + * @return >= 0 on success, a negative AVERROR code on failure. + */ +int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src); + + +/** + * @} + */ + +#endif // AVCODEC_CODEC_PAR_H diff --git a/libavcodec/cook.c b/libavcodec/cook.c index c5f68c98ba6..d0b41a2431d 100644 --- a/libavcodec/cook.c +++ b/libavcodec/cook.c @@ -60,7 +60,7 @@ #define MONO 0x1000001 #define STEREO 0x1000002 #define JOINT_STEREO 0x1000003 -#define MC_COOK 0x2000000 // multichannel Cook, not supported +#define MC_COOK 0x2000000 #define SUBBAND_SIZE 20 #define MAX_SUBPACKETS 5 @@ -143,7 +143,7 @@ typedef struct cook { /* generate tables and related variables */ int gain_size_factor; - float gain_table[23]; + float gain_table[31]; /* data buffers */ @@ -185,8 +185,8 @@ static av_cold void init_gain_table(COOKContext *q) { int i; q->gain_size_factor = q->samples_per_channel / 8; - for (i = 0; i < 23; i++) - q->gain_table[i] = pow(pow2tab[i + 52], + for (i = 0; i < 31; i++) + q->gain_table[i] = pow(pow2tab[i + 48], (1.0 / (double) q->gain_size_factor)); } @@ -670,7 +670,7 @@ static void interpolate_float(COOKContext *q, float *buffer, for (i = 0; i < q->gain_size_factor; i++) buffer[i] *= fc1; } else { // smooth gain - fc2 = q->gain_table[11 + (gain_index_next - gain_index)]; + fc2 = q->gain_table[15 + (gain_index_next - gain_index)]; for (i = 0; i < q->gain_size_factor; i++) { buffer[i] *= fc1; fc1 *= fc2; @@ -759,7 +759,7 @@ static int decouple_info(COOKContext *q, COOKSubpacket *p, int *decouple_tab) for (i = 0; i < length; i++) decouple_tab[start + i] = get_vlc2(&q->gb, p->channel_coupling.table, - p->channel_coupling.bits, 2); + p->channel_coupling.bits, 3); else for (i = 0; i < length; i++) { int v = get_bits(&q->gb, p->js_vlc_bits); @@ -1075,6 +1075,9 @@ static av_cold int cook_decode_init(AVCodecContext *avctx) return AVERROR_INVALIDDATA; } + if (avctx->block_align >= INT_MAX / 8) + return AVERROR(EINVAL); + /* Initialize RNG. */ av_lfg_init(&q->random_state, 0); @@ -1217,6 +1220,15 @@ static av_cold int cook_decode_init(AVCodecContext *avctx) return AVERROR_PATCHWELCOME; } } + + /* Try to catch some obviously faulty streams, otherwise it might be exploitable */ + if (q->samples_per_channel != 256 && q->samples_per_channel != 512 && + q->samples_per_channel != 1024) { + avpriv_request_sample(avctx, "samples_per_channel = %d", + q->samples_per_channel); + return AVERROR_PATCHWELCOME; + } + /* Generate tables */ init_pow2table(); init_gain_table(q); @@ -1225,10 +1237,6 @@ static av_cold int cook_decode_init(AVCodecContext *avctx) if ((ret = init_cook_vlc_tables(q))) return ret; - - if (avctx->block_align >= UINT_MAX / 2) - return AVERROR(EINVAL); - /* Pad the databuffer with: DECODE_BYTES_PAD1 or DECODE_BYTES_PAD2 for decode_bytes(), AV_INPUT_BUFFER_PADDING_SIZE, for the bitstreamreader. */ @@ -1252,14 +1260,6 @@ static av_cold int cook_decode_init(AVCodecContext *avctx) q->saturate_output = saturate_output_float; } - /* Try to catch some obviously faulty streams, otherwise it might be exploitable */ - if (q->samples_per_channel != 256 && q->samples_per_channel != 512 && - q->samples_per_channel != 1024) { - avpriv_request_sample(avctx, "samples_per_channel = %d", - q->samples_per_channel); - return AVERROR_PATCHWELCOME; - } - avctx->sample_fmt = AV_SAMPLE_FMT_FLTP; if (channel_mask) avctx->channel_layout = channel_mask; diff --git a/libavcodec/cpia.c b/libavcodec/cpia.c index f6d7332606f..bf09e1a5db7 100644 --- a/libavcodec/cpia.c +++ b/libavcodec/cpia.c @@ -100,7 +100,7 @@ static int cpia_decode_frame(AVCodecContext *avctx, } // Get buffer filled with previous frame - if ((ret = ff_reget_buffer(avctx, frame)) < 0) + if ((ret = ff_reget_buffer(avctx, frame, 0)) < 0) return ret; diff --git a/libavcodec/cscd.c b/libavcodec/cscd.c index 8781df110ca..d50ddd6258c 100644 --- a/libavcodec/cscd.c +++ b/libavcodec/cscd.c @@ -77,7 +77,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, return AVERROR_INVALIDDATA; } - if ((ret = ff_reget_buffer(avctx, c->pic)) < 0) + if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; // decompress data diff --git a/libavcodec/cuviddec.c b/libavcodec/cuviddec.c index acee78cf2cb..bce584c9c60 100644 --- a/libavcodec/cuviddec.c +++ b/libavcodec/cuviddec.c @@ -33,7 +33,7 @@ #include "avcodec.h" #include "decode.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "nvdec.h" #include "internal.h" @@ -70,8 +70,6 @@ typedef struct CuvidContext AVBufferRef *hwdevice; AVBufferRef *hwframe; - AVBSFContext *bsf; - AVFifoBuffer *frame_queue; int deint_mode; @@ -387,8 +385,6 @@ static int cuvid_decode_packet(AVCodecContext *avctx, const AVPacket *avpkt) AVCUDADeviceContext *device_hwctx = device_ctx->hwctx; CUcontext dummy, cuda_ctx = device_hwctx->cuda_ctx; CUVIDSOURCEDATAPACKET cupkt; - AVPacket filter_packet = { 0 }; - AVPacket filtered_packet = { 0 }; int ret = 0, eret = 0, is_flush = ctx->decoder_flushing; av_log(avctx, AV_LOG_TRACE, "cuvid_decode_packet\n"); @@ -399,29 +395,8 @@ static int cuvid_decode_packet(AVCodecContext *avctx, const AVPacket *avpkt) if (cuvid_is_buffer_full(avctx) && avpkt && avpkt->size) return AVERROR(EAGAIN); - if (ctx->bsf && avpkt && avpkt->size) { - if ((ret = av_packet_ref(&filter_packet, avpkt)) < 0) { - av_log(avctx, AV_LOG_ERROR, "av_packet_ref failed\n"); - return ret; - } - - if ((ret = av_bsf_send_packet(ctx->bsf, &filter_packet)) < 0) { - av_log(avctx, AV_LOG_ERROR, "av_bsf_send_packet failed\n"); - av_packet_unref(&filter_packet); - return ret; - } - - if ((ret = av_bsf_receive_packet(ctx->bsf, &filtered_packet)) < 0) { - av_log(avctx, AV_LOG_ERROR, "av_bsf_receive_packet failed\n"); - return ret; - } - - avpkt = &filtered_packet; - } - ret = CHECK_CU(ctx->cudl->cuCtxPushCurrent(cuda_ctx)); if (ret < 0) { - av_packet_unref(&filtered_packet); return ret; } @@ -445,8 +420,6 @@ static int cuvid_decode_packet(AVCodecContext *avctx, const AVPacket *avpkt) ret = CHECK_CU(ctx->cvdl->cuvidParseVideoData(ctx->cuparser, &cupkt)); - av_packet_unref(&filtered_packet); - if (ret < 0) goto error; @@ -699,9 +672,6 @@ static av_cold int cuvid_decode_end(AVCodecContext *avctx) av_fifo_freep(&ctx->frame_queue); - if (ctx->bsf) - av_bsf_free(&ctx->bsf); - if (ctx->cuparser) ctx->cvdl->cuvidDestroyVideoParser(ctx->cuparser); @@ -823,7 +793,6 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx) CUVIDSOURCEDATAPACKET seq_pkt; CUcontext cuda_ctx = NULL; CUcontext dummy; - const AVBitStreamFilter *bsf; int ret = 0; enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_CUDA, @@ -976,28 +945,12 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx) return AVERROR_BUG; } - if (avctx->codec->id == AV_CODEC_ID_H264 || avctx->codec->id == AV_CODEC_ID_HEVC) { - if (avctx->codec->id == AV_CODEC_ID_H264) - bsf = av_bsf_get_by_name("h264_mp4toannexb"); - else - bsf = av_bsf_get_by_name("hevc_mp4toannexb"); - - if (!bsf) { - ret = AVERROR_BSF_NOT_FOUND; - goto error; - } - if (ret = av_bsf_alloc(bsf, &ctx->bsf)) { - goto error; - } - if (((ret = avcodec_parameters_from_context(ctx->bsf->par_in, avctx)) < 0) || ((ret = av_bsf_init(ctx->bsf)) < 0)) { - av_bsf_free(&ctx->bsf); - goto error; - } - - ctx->cuparse_ext.format.seqhdr_data_length = ctx->bsf->par_out->extradata_size; + if (avctx->codec->bsfs) { + const AVCodecParameters *par = avctx->internal->bsf->par_out; + ctx->cuparse_ext.format.seqhdr_data_length = par->extradata_size; memcpy(ctx->cuparse_ext.raw_seqhdr_data, - ctx->bsf->par_out->extradata, - FFMIN(sizeof(ctx->cuparse_ext.raw_seqhdr_data), ctx->bsf->par_out->extradata_size)); + par->extradata, + FFMIN(sizeof(ctx->cuparse_ext.raw_seqhdr_data), par->extradata_size)); } else if (avctx->extradata_size > 0) { ctx->cuparse_ext.format.seqhdr_data_length = avctx->extradata_size; memcpy(ctx->cuparse_ext.raw_seqhdr_data, @@ -1142,7 +1095,7 @@ static const AVCodecHWConfigInternal *cuvid_hw_configs[] = { NULL }; -#define DEFINE_CUVID_CODEC(x, X) \ +#define DEFINE_CUVID_CODEC(x, X, bsf_name) \ static const AVClass x##_cuvid_class = { \ .class_name = #x "_cuvid", \ .item_name = av_default_item_name, \ @@ -1161,6 +1114,7 @@ static const AVCodecHWConfigInternal *cuvid_hw_configs[] = { .decode = cuvid_decode_frame, \ .receive_frame = cuvid_output_frame, \ .flush = cuvid_flush, \ + .bsfs = bsf_name, \ .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, \ .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, \ AV_PIX_FMT_NV12, \ @@ -1172,37 +1126,37 @@ static const AVCodecHWConfigInternal *cuvid_hw_configs[] = { }; #if CONFIG_HEVC_CUVID_DECODER -DEFINE_CUVID_CODEC(hevc, HEVC) +DEFINE_CUVID_CODEC(hevc, HEVC, "hevc_mp4toannexb") #endif #if CONFIG_H264_CUVID_DECODER -DEFINE_CUVID_CODEC(h264, H264) +DEFINE_CUVID_CODEC(h264, H264, "h264_mp4toannexb") #endif #if CONFIG_MJPEG_CUVID_DECODER -DEFINE_CUVID_CODEC(mjpeg, MJPEG) +DEFINE_CUVID_CODEC(mjpeg, MJPEG, NULL) #endif #if CONFIG_MPEG1_CUVID_DECODER -DEFINE_CUVID_CODEC(mpeg1, MPEG1VIDEO) +DEFINE_CUVID_CODEC(mpeg1, MPEG1VIDEO, NULL) #endif #if CONFIG_MPEG2_CUVID_DECODER -DEFINE_CUVID_CODEC(mpeg2, MPEG2VIDEO) +DEFINE_CUVID_CODEC(mpeg2, MPEG2VIDEO, NULL) #endif #if CONFIG_MPEG4_CUVID_DECODER -DEFINE_CUVID_CODEC(mpeg4, MPEG4) +DEFINE_CUVID_CODEC(mpeg4, MPEG4, NULL) #endif #if CONFIG_VP8_CUVID_DECODER -DEFINE_CUVID_CODEC(vp8, VP8) +DEFINE_CUVID_CODEC(vp8, VP8, NULL) #endif #if CONFIG_VP9_CUVID_DECODER -DEFINE_CUVID_CODEC(vp9, VP9) +DEFINE_CUVID_CODEC(vp9, VP9, NULL) #endif #if CONFIG_VC1_CUVID_DECODER -DEFINE_CUVID_CODEC(vc1, VC1) +DEFINE_CUVID_CODEC(vc1, VC1, NULL) #endif diff --git a/libavcodec/dca_core_bsf.c b/libavcodec/dca_core_bsf.c index 8565796951e..b92e6a1ba6e 100644 --- a/libavcodec/dca_core_bsf.c +++ b/libavcodec/dca_core_bsf.c @@ -18,11 +18,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "bytestream.h" #include "dca_syncwords.h" -#include "libavutil/mem.h" static int dca_core_filter(AVBSFContext *ctx, AVPacket *pkt) { diff --git a/libavcodec/dca_lbr.c b/libavcodec/dca_lbr.c index 3b50a99cf6d..747fdafd3e4 100644 --- a/libavcodec/dca_lbr.c +++ b/libavcodec/dca_lbr.c @@ -154,7 +154,7 @@ static int parse_lfe_24(DCALbrDecoder *s) step_i = get_bits(&s->gb, 8); if (step_i > step_max) { av_log(s->avctx, AV_LOG_ERROR, "Invalid LFE step size index\n"); - return -1; + return AVERROR_INVALIDDATA; } step = ff_dca_lfe_step_size_24[step_i]; @@ -208,7 +208,7 @@ static int parse_lfe_16(DCALbrDecoder *s) step_i = get_bits(&s->gb, 8); if (step_i > step_max) { av_log(s->avctx, AV_LOG_ERROR, "Invalid LFE step size index\n"); - return -1; + return AVERROR_INVALIDDATA; } step = ff_dca_lfe_step_size_16[step_i]; @@ -246,14 +246,17 @@ static int parse_lfe_16(DCALbrDecoder *s) static int parse_lfe_chunk(DCALbrDecoder *s, LBRChunk *chunk) { + int ret; + if (!(s->flags & LBR_FLAG_LFE_PRESENT)) return 0; if (!chunk->len) return 0; - if (init_get_bits8(&s->gb, chunk->data, chunk->len) < 0) - return -1; + ret = init_get_bits8(&s->gb, chunk->data, chunk->len); + if (ret < 0) + return ret; // Determine bit depth from chunk size if (chunk->len >= 52) @@ -262,7 +265,7 @@ static int parse_lfe_chunk(DCALbrDecoder *s, LBRChunk *chunk) return parse_lfe_16(s); av_log(s->avctx, AV_LOG_ERROR, "LFE chunk too short\n"); - return -1; + return AVERROR_INVALIDDATA; } static inline int parse_vlc(GetBitContext *s, VLC *vlc, int max_depth) @@ -291,13 +294,13 @@ static int parse_tonal(DCALbrDecoder *s, int group) for (freq = 1;; freq++) { if (get_bits_left(&s->gb) < 1) { av_log(s->avctx, AV_LOG_ERROR, "Tonal group chunk too short\n"); - return -1; + return AVERROR_INVALIDDATA; } diff = parse_vlc(&s->gb, &ff_dca_vlc_tnl_grp[group], 2); if (diff >= FF_ARRAY_ELEMS(ff_dca_fst_amp)) { av_log(s->avctx, AV_LOG_ERROR, "Invalid tonal frequency diff\n"); - return -1; + return AVERROR_INVALIDDATA; } diff = get_bitsz(&s->gb, diff >> 2) + ff_dca_fst_amp[diff]; @@ -307,7 +310,7 @@ static int parse_tonal(DCALbrDecoder *s, int group) freq += diff - 2; if (freq >> (5 - group) > s->nsubbands * 4 - 6) { av_log(s->avctx, AV_LOG_ERROR, "Invalid spectral line offset\n"); - return -1; + return AVERROR_INVALIDDATA; } // Main channel @@ -358,19 +361,21 @@ static int parse_tonal(DCALbrDecoder *s, int group) static int parse_tonal_chunk(DCALbrDecoder *s, LBRChunk *chunk) { - int sb, group; + int sb, group, ret; if (!chunk->len) return 0; - if (init_get_bits8(&s->gb, chunk->data, chunk->len) < 0) - return -1; + ret = init_get_bits8(&s->gb, chunk->data, chunk->len); + + if (ret < 0) + return ret; // Scale factors if (chunk->id == LBR_CHUNK_SCF || chunk->id == LBR_CHUNK_TONAL_SCF) { if (get_bits_left(&s->gb) < 36) { av_log(s->avctx, AV_LOG_ERROR, "Tonal scale factor chunk too short\n"); - return -1; + return AVERROR_INVALIDDATA; } for (sb = 0; sb < 6; sb++) s->tonal_scf[sb] = get_bits(&s->gb, 6); @@ -378,20 +383,25 @@ static int parse_tonal_chunk(DCALbrDecoder *s, LBRChunk *chunk) // Tonal groups if (chunk->id == LBR_CHUNK_TONAL || chunk->id == LBR_CHUNK_TONAL_SCF) - for (group = 0; group < 5; group++) - if (parse_tonal(s, group) < 0) - return -1; + for (group = 0; group < 5; group++) { + ret = parse_tonal(s, group); + if (ret < 0) + return ret; + } return 0; } static int parse_tonal_group(DCALbrDecoder *s, LBRChunk *chunk) { + int ret; + if (!chunk->len) return 0; - if (init_get_bits8(&s->gb, chunk->data, chunk->len) < 0) - return -1; + ret = init_get_bits8(&s->gb, chunk->data, chunk->len); + if (ret < 0) + return ret; return parse_tonal(s, chunk->id); } @@ -404,7 +414,7 @@ static int ensure_bits(GetBitContext *s, int n) { int left = get_bits_left(s); if (left < 0) - return -1; + return AVERROR_INVALIDDATA; if (left < n) { skip_bits_long(s, left); return 1; @@ -433,7 +443,7 @@ static int parse_scale_factors(DCALbrDecoder *s, uint8_t *scf) dist = parse_vlc(&s->gb, &ff_dca_vlc_rsd_apprx, 1) + 1; if (dist > 7 - sf) { av_log(s->avctx, AV_LOG_ERROR, "Invalid scale factor distance\n"); - return -1; + return AVERROR_INVALIDDATA; } if (ensure_bits(&s->gb, 20)) @@ -498,22 +508,26 @@ static int parse_st_code(GetBitContext *s, int min_v) static int parse_grid_1_chunk(DCALbrDecoder *s, LBRChunk *chunk, int ch1, int ch2) { - int ch, sb, sf, nsubbands; + int ch, sb, sf, nsubbands, ret; if (!chunk->len) return 0; - if (init_get_bits8(&s->gb, chunk->data, chunk->len) < 0) - return -1; + ret = init_get_bits8(&s->gb, chunk->data, chunk->len); + if (ret < 0) + return ret; // Scale factors nsubbands = ff_dca_scf_to_grid_1[s->nsubbands - 1] + 1; for (sb = 2; sb < nsubbands; sb++) { - if (parse_scale_factors(s, s->grid_1_scf[ch1][sb]) < 0) - return -1; - if (ch1 != ch2 && ff_dca_grid_1_to_scf[sb] < s->min_mono_subband - && parse_scale_factors(s, s->grid_1_scf[ch2][sb]) < 0) - return -1; + ret = parse_scale_factors(s, s->grid_1_scf[ch1][sb]); + if (ret < 0) + return ret; + if (ch1 != ch2 && ff_dca_grid_1_to_scf[sb] < s->min_mono_subband) { + ret = parse_scale_factors(s, s->grid_1_scf[ch2][sb]); + if (ret < 0) + return ret; + } } if (get_bits_left(&s->gb) < 1) @@ -532,7 +546,7 @@ static int parse_grid_1_chunk(DCALbrDecoder *s, LBRChunk *chunk, int ch1, int ch if (get_bits_left(&s->gb) < 0) { av_log(s->avctx, AV_LOG_ERROR, "First grid chunk too short\n"); - return -1; + return AVERROR_INVALIDDATA; } // Stereo image for partial mono mode @@ -562,14 +576,16 @@ static int parse_grid_1_chunk(DCALbrDecoder *s, LBRChunk *chunk, int ch1, int ch static int parse_grid_1_sec_ch(DCALbrDecoder *s, int ch2) { - int sb, nsubbands; + int sb, nsubbands, ret; // Scale factors nsubbands = ff_dca_scf_to_grid_1[s->nsubbands - 1] + 1; for (sb = 2; sb < nsubbands; sb++) { - if (ff_dca_grid_1_to_scf[sb] >= s->min_mono_subband - && parse_scale_factors(s, s->grid_1_scf[ch2][sb]) < 0) - return -1; + if (ff_dca_grid_1_to_scf[sb] >= s->min_mono_subband) { + ret = parse_scale_factors(s, s->grid_1_scf[ch2][sb]); + if (ret < 0) + return ret; + } } // Average values for third grid @@ -709,7 +725,7 @@ static int parse_ts(DCALbrDecoder *s, int ch1, int ch2, s->sb_indices[sb] = sb_reorder; } if (sb_reorder >= s->nsubbands) - return -1; + return AVERROR_INVALIDDATA; // Third grid scale factors if (sb == 12) { @@ -731,7 +747,7 @@ static int parse_ts(DCALbrDecoder *s, int ch1, int ch2, quant_level = s->quant_levels[ch1 / 2][sb]; if (!quant_level) - return -1; + return AVERROR_INVALIDDATA; // Time samples for one or both channels if (sb < s->max_mono_subband && sb_reorder >= s->min_mono_subband) { @@ -792,13 +808,14 @@ static int parse_lpc(DCALbrDecoder *s, int ch1, int ch2, int start_sb, int end_s static int parse_high_res_grid(DCALbrDecoder *s, LBRChunk *chunk, int ch1, int ch2) { int quant_levels[DCA_LBR_SUBBANDS]; - int sb, ch, ol, st, max_sb, profile; + int sb, ch, ol, st, max_sb, profile, ret; if (!chunk->len) return 0; - if (init_get_bits8(&s->gb, chunk->data, chunk->len) < 0) - return -1; + ret = init_get_bits8(&s->gb, chunk->data, chunk->len); + if (ret < 0) + return ret; // Quantizer profile profile = get_bits(&s->gb, 8); @@ -832,18 +849,20 @@ static int parse_high_res_grid(DCALbrDecoder *s, LBRChunk *chunk, int ch1, int c s->quant_levels[ch1 / 2][sb] = quant_levels[sb]; // LPC for the first two subbands - if (parse_lpc(s, ch1, ch2, 0, 2) < 0) - return -1; + ret = parse_lpc(s, ch1, ch2, 0, 2); + if (ret < 0) + return ret; // Time-samples for the first two subbands of main channel - if (parse_ts(s, ch1, ch2, 0, 2, 0) < 0) - return -1; + ret = parse_ts(s, ch1, ch2, 0, 2, 0); + if (ret < 0) + return ret; // First two bands of the first grid for (sb = 0; sb < 2; sb++) for (ch = ch1; ch <= ch2; ch++) - if (parse_scale_factors(s, s->grid_1_scf[ch][sb]) < 0) - return -1; + if ((ret = parse_scale_factors(s, s->grid_1_scf[ch][sb])) < 0) + return ret; return 0; } @@ -892,39 +911,42 @@ static int parse_grid_2(DCALbrDecoder *s, int ch1, int ch2, static int parse_ts1_chunk(DCALbrDecoder *s, LBRChunk *chunk, int ch1, int ch2) { + int ret; if (!chunk->len) return 0; - if (init_get_bits8(&s->gb, chunk->data, chunk->len) < 0) - return -1; - if (parse_lpc(s, ch1, ch2, 2, 3) < 0) - return -1; - if (parse_ts(s, ch1, ch2, 2, 4, 0) < 0) - return -1; - if (parse_grid_2(s, ch1, ch2, 0, 1, 0) < 0) - return -1; - if (parse_ts(s, ch1, ch2, 4, 6, 0) < 0) - return -1; + if ((ret = init_get_bits8(&s->gb, chunk->data, chunk->len)) < 0) + return ret; + if ((ret = parse_lpc(s, ch1, ch2, 2, 3)) < 0) + return ret; + if ((ret = parse_ts(s, ch1, ch2, 2, 4, 0)) < 0) + return ret; + if ((ret = parse_grid_2(s, ch1, ch2, 0, 1, 0)) < 0) + return ret; + if ((ret = parse_ts(s, ch1, ch2, 4, 6, 0)) < 0) + return ret; return 0; } static int parse_ts2_chunk(DCALbrDecoder *s, LBRChunk *chunk, int ch1, int ch2) { + int ret; + if (!chunk->len) return 0; - if (init_get_bits8(&s->gb, chunk->data, chunk->len) < 0) - return -1; - if (parse_grid_2(s, ch1, ch2, 1, 3, 0) < 0) - return -1; - if (parse_ts(s, ch1, ch2, 6, s->max_mono_subband, 0) < 0) - return -1; + if ((ret = init_get_bits8(&s->gb, chunk->data, chunk->len)) < 0) + return ret; + if ((ret = parse_grid_2(s, ch1, ch2, 1, 3, 0)) < 0) + return ret; + if ((ret = parse_ts(s, ch1, ch2, 6, s->max_mono_subband, 0)) < 0) + return ret; if (ch1 != ch2) { - if (parse_grid_1_sec_ch(s, ch2) < 0) - return -1; - if (parse_grid_2(s, ch1, ch2, 0, 3, 1) < 0) - return -1; + if ((ret = parse_grid_1_sec_ch(s, ch2)) < 0) + return ret; + if ((ret = parse_grid_2(s, ch1, ch2, 0, 3, 1)) < 0) + return ret; } - if (parse_ts(s, ch1, ch2, s->min_mono_subband, s->nsubbands, 1) < 0) - return -1; + if ((ret = parse_ts(s, ch1, ch2, s->min_mono_subband, s->nsubbands, 1)) < 0) + return ret; return 0; } @@ -932,11 +954,13 @@ static int init_sample_rate(DCALbrDecoder *s) { double scale = (-1.0 / (1 << 17)) * sqrt(1 << (2 - s->limited_range)); int i, br_per_ch = s->bit_rate_scaled / s->nchannels_total; + int ret; ff_mdct_end(&s->imdct); - if (ff_mdct_init(&s->imdct, s->freq_range + 6, 1, scale) < 0) - return -1; + ret = ff_mdct_init(&s->imdct, s->freq_range + 6, 1, scale); + if (ret < 0) + return ret; for (i = 0; i < 32 << s->freq_range; i++) s->window[i] = ff_dca_long_window[i << (2 - s->freq_range)]; @@ -975,7 +999,7 @@ static int alloc_sample_buffer(DCALbrDecoder *s) // Reallocate time sample buffer av_fast_mallocz(&s->ts_buffer, &s->ts_size, nsamples * sizeof(float)); if (!s->ts_buffer) - return -1; + return AVERROR(ENOMEM); ptr = s->ts_buffer + DCA_LBR_TIME_HISTORY; for (ch = 0; ch < s->nchannels; ch++) { @@ -1796,7 +1820,7 @@ av_cold int ff_dca_lbr_init(DCALbrDecoder *s) init_tables(); if (!(s->fdsp = avpriv_float_dsp_alloc(0))) - return -1; + return AVERROR(ENOMEM); s->lbr_rand = 1; return 0; diff --git a/libavcodec/decode.c b/libavcodec/decode.c index 6c31166ec28..a4e50c0d031 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -41,10 +41,29 @@ #include "avcodec.h" #include "bytestream.h" #include "decode.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "internal.h" #include "thread.h" +typedef struct FramePool { + /** + * Pools for each data plane. For audio all the planes have the same size, + * so only pools[0] is used. + */ + AVBufferPool *pools[4]; + + /* + * Pool parameters + */ + int format; + int width, height; + int stride_align[AV_NUM_DATA_POINTERS]; + int linesize[4]; + int planes; + int channels; + int samples; +} FramePool; + static int apply_param_change(AVCodecContext *avctx, const AVPacket *avpkt) { int size = 0, ret; @@ -185,147 +204,37 @@ static int unrefcount_frame(AVCodecInternal *avci, AVFrame *frame) int ff_decode_bsfs_init(AVCodecContext *avctx) { AVCodecInternal *avci = avctx->internal; - DecodeFilterContext *s = &avci->filter; - const char *bsfs_str; int ret; - if (s->nb_bsfs) + if (avci->bsf) return 0; - bsfs_str = avctx->codec->bsfs ? avctx->codec->bsfs : "null"; - while (bsfs_str && *bsfs_str) { - AVBSFContext **tmp; - const AVBitStreamFilter *filter; - char *bsf, *bsf_options_str, *bsf_name; - - bsf = av_get_token(&bsfs_str, ","); - if (!bsf) { - ret = AVERROR(ENOMEM); - goto fail; - } - bsf_name = av_strtok(bsf, "=", &bsf_options_str); - if (!bsf_name) { - av_freep(&bsf); - ret = AVERROR(ENOMEM); - goto fail; - } - - filter = av_bsf_get_by_name(bsf_name); - if (!filter) { - av_log(avctx, AV_LOG_ERROR, "A non-existing bitstream filter %s " - "requested by a decoder. This is a bug, please report it.\n", - bsf_name); - av_freep(&bsf); + ret = av_bsf_list_parse_str(avctx->codec->bsfs, &avci->bsf); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error parsing decoder bitstream filters '%s': %s\n", avctx->codec->bsfs, av_err2str(ret)); + if (ret != AVERROR(ENOMEM)) ret = AVERROR_BUG; - goto fail; - } - - tmp = av_realloc_array(s->bsfs, s->nb_bsfs + 1, sizeof(*s->bsfs)); - if (!tmp) { - av_freep(&bsf); - ret = AVERROR(ENOMEM); - goto fail; - } - s->bsfs = tmp; - s->nb_bsfs++; - - ret = av_bsf_alloc(filter, &s->bsfs[s->nb_bsfs - 1]); - if (ret < 0) { - av_freep(&bsf); - goto fail; - } - - if (s->nb_bsfs == 1) { - /* We do not currently have an API for passing the input timebase into decoders, - * but no filters used here should actually need it. - * So we make up some plausible-looking number (the MPEG 90kHz timebase) */ - s->bsfs[s->nb_bsfs - 1]->time_base_in = (AVRational){ 1, 90000 }; - ret = avcodec_parameters_from_context(s->bsfs[s->nb_bsfs - 1]->par_in, - avctx); - } else { - s->bsfs[s->nb_bsfs - 1]->time_base_in = s->bsfs[s->nb_bsfs - 2]->time_base_out; - ret = avcodec_parameters_copy(s->bsfs[s->nb_bsfs - 1]->par_in, - s->bsfs[s->nb_bsfs - 2]->par_out); - } - if (ret < 0) { - av_freep(&bsf); - goto fail; - } - - if (bsf_options_str && filter->priv_class) { - const AVOption *opt = av_opt_next(s->bsfs[s->nb_bsfs - 1]->priv_data, NULL); - const char * shorthand[2] = {NULL}; - - if (opt) - shorthand[0] = opt->name; - - ret = av_opt_set_from_string(s->bsfs[s->nb_bsfs - 1]->priv_data, bsf_options_str, shorthand, "=", ":"); - if (ret < 0) { - if (ret != AVERROR(ENOMEM)) { - av_log(avctx, AV_LOG_ERROR, "Invalid options for bitstream filter %s " - "requested by the decoder. This is a bug, please report it.\n", - bsf_name); - ret = AVERROR_BUG; - } - av_freep(&bsf); - goto fail; - } - } - av_freep(&bsf); + goto fail; + } - ret = av_bsf_init(s->bsfs[s->nb_bsfs - 1]); - if (ret < 0) - goto fail; + /* We do not currently have an API for passing the input timebase into decoders, + * but no filters used here should actually need it. + * So we make up some plausible-looking number (the MPEG 90kHz timebase) */ + avci->bsf->time_base_in = (AVRational){ 1, 90000 }; + ret = avcodec_parameters_from_context(avci->bsf->par_in, avctx); + if (ret < 0) + goto fail; - if (*bsfs_str) - bsfs_str++; - } + ret = av_bsf_init(avci->bsf); + if (ret < 0) + goto fail; return 0; fail: - ff_decode_bsfs_uninit(avctx); + av_bsf_free(&avci->bsf); return ret; } -/* try to get one output packet from the filter chain */ -static int bsfs_poll(AVCodecContext *avctx, AVPacket *pkt) -{ - DecodeFilterContext *s = &avctx->internal->filter; - int idx, ret; - - /* start with the last filter in the chain */ - idx = s->nb_bsfs - 1; - while (idx >= 0) { - /* request a packet from the currently selected filter */ - ret = av_bsf_receive_packet(s->bsfs[idx], pkt); - if (ret == AVERROR(EAGAIN)) { - /* no packets available, try the next filter up the chain */ - ret = 0; - idx--; - continue; - } else if (ret < 0 && ret != AVERROR_EOF) { - return ret; - } - - /* got a packet or EOF -- pass it to the caller or to the next filter - * down the chain */ - if (idx == s->nb_bsfs - 1) { - return ret; - } else { - idx++; - ret = av_bsf_send_packet(s->bsfs[idx], ret < 0 ? NULL : pkt); - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, - "Error pre-processing a packet before decoding\n"); - av_packet_unref(pkt); - return ret; - } - } - } - - return AVERROR(EAGAIN); -} - int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt) { AVCodecInternal *avci = avctx->internal; @@ -334,7 +243,7 @@ int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt) if (avci->draining) return AVERROR_EOF; - ret = bsfs_poll(avctx, pkt); + ret = av_bsf_receive_packet(avci->bsf, pkt); if (ret == AVERROR_EOF) avci->draining = 1; if (ret < 0) @@ -399,7 +308,7 @@ static int64_t guess_correct_pts(AVCodecContext *ctx, * returning any output, so this function needs to be called in a loop until it * returns EAGAIN. **/ -static int decode_simple_internal(AVCodecContext *avctx, AVFrame *frame) +static inline int decode_simple_internal(AVCodecContext *avctx, AVFrame *frame) { AVCodecInternal *avci = avctx->internal; DecodeSimpleContext *ds = &avci->ds; @@ -480,32 +389,32 @@ static int decode_simple_internal(AVCodecContext *avctx, AVFrame *frame) side= av_packet_get_side_data(avci->last_pkt_props, AV_PKT_DATA_SKIP_SAMPLES, &side_size); if(side && side_size>=10) { - avctx->internal->skip_samples = AV_RL32(side) * avctx->internal->skip_samples_multiplier; + avci->skip_samples = AV_RL32(side) * avci->skip_samples_multiplier; discard_padding = AV_RL32(side + 4); av_log(avctx, AV_LOG_DEBUG, "skip %d / discard %d samples due to side data\n", - avctx->internal->skip_samples, (int)discard_padding); + 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)) { - avctx->internal->skip_samples = FFMAX(0, avctx->internal->skip_samples - frame->nb_samples); + avci->skip_samples = FFMAX(0, avci->skip_samples - frame->nb_samples); got_frame = 0; } - if (avctx->internal->skip_samples > 0 && got_frame && + if (avci->skip_samples > 0 && got_frame && !(avctx->flags2 & AV_CODEC_FLAG2_SKIP_MANUAL)) { - if(frame->nb_samples <= avctx->internal->skip_samples){ + if(frame->nb_samples <= avci->skip_samples){ got_frame = 0; - avctx->internal->skip_samples -= frame->nb_samples; + avci->skip_samples -= frame->nb_samples; av_log(avctx, AV_LOG_DEBUG, "skip whole frame, skip left: %d\n", - avctx->internal->skip_samples); + avci->skip_samples); } else { - av_samples_copy(frame->extended_data, frame->extended_data, 0, avctx->internal->skip_samples, - frame->nb_samples - avctx->internal->skip_samples, avctx->channels, frame->format); + av_samples_copy(frame->extended_data, frame->extended_data, 0, avci->skip_samples, + frame->nb_samples - avci->skip_samples, avctx->channels, frame->format); if(avctx->pkt_timebase.num && avctx->sample_rate) { - int64_t diff_ts = av_rescale_q(avctx->internal->skip_samples, + int64_t diff_ts = av_rescale_q(avci->skip_samples, (AVRational){1, avctx->sample_rate}, avctx->pkt_timebase); if(frame->pts!=AV_NOPTS_VALUE) @@ -524,9 +433,9 @@ FF_ENABLE_DEPRECATION_WARNINGS 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", - avctx->internal->skip_samples, frame->nb_samples); - frame->nb_samples -= avctx->internal->skip_samples; - avctx->internal->skip_samples = 0; + avci->skip_samples, frame->nb_samples); + frame->nb_samples -= avci->skip_samples; + avci->skip_samples = 0; } } @@ -552,11 +461,11 @@ FF_ENABLE_DEPRECATION_WARNINGS 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, avctx->internal->skip_samples); + 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); - avctx->internal->skip_samples = 0; + avci->skip_samples = 0; } } } @@ -581,7 +490,7 @@ FF_ENABLE_DEPRECATION_WARNINGS /* 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 (avctx->internal->draining && !actual_got_frame) { + if (avci->draining && !actual_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 */ @@ -695,7 +604,7 @@ int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacke return ret; } - ret = av_bsf_send_packet(avci->filter.bsfs[0], avci->buffer_pkt); + ret = av_bsf_send_packet(avci->bsf, avci->buffer_pkt); if (ret < 0) { av_packet_unref(avci->buffer_pkt); return ret; @@ -786,7 +695,7 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr if (avctx->frame_number > 1) { changed = avci->initial_format != frame->format; - switch(avctx->codec_type) { + switch(avctx->codec_type) { case AVMEDIA_TYPE_VIDEO: changed |= avci->initial_width != frame->width || avci->initial_height != frame->height; @@ -827,7 +736,6 @@ static int compat_decode(AVCodecContext *avctx, AVFrame *frame, } *got_frame = 0; - avci->compat_decode = 1; if (avci->compat_decode_partial_size > 0 && avci->compat_decode_partial_size != pkt->size) { @@ -1505,10 +1413,61 @@ int ff_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt) return ret; } +static void frame_pool_free(void *opaque, uint8_t *data) +{ + FramePool *pool = (FramePool*)data; + 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; - int i, ret; + FramePool *pool = avctx->internal->pool ? + (FramePool*)avctx->internal->pool->data : NULL; + AVBufferRef *pool_buf; + int i, ret, ch, planes; + + if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) { + int planar = av_sample_fmt_is_planar(frame->format); + ch = frame->channels; + planes = planar ? ch : 1; + } + + if (pool && pool->format == frame->format) { + if (avctx->codec_type == AVMEDIA_TYPE_VIDEO && + pool->width == frame->width && pool->height == frame->height) + return 0; + if (avctx->codec_type == AVMEDIA_TYPE_AUDIO && pool->planes == planes && + pool->channels == ch && frame->nb_samples == pool->samples) + return 0; + } + + pool_buf = frame_pool_alloc(); + if (!pool_buf) + return AVERROR(ENOMEM); + pool = (FramePool*)pool_buf->data; switch (avctx->codec_type) { case AVMEDIA_TYPE_VIDEO: { @@ -1519,10 +1478,6 @@ static int update_frame_pool(AVCodecContext *avctx, AVFrame *frame) int h = frame->height; int tmpsize, unaligned; - if (pool->format == frame->format && - pool->width == frame->width && pool->height == frame->height) - return 0; - avcodec_align_dimensions2(avctx, &w, &h, pool->stride_align); do { @@ -1530,7 +1485,7 @@ static int update_frame_pool(AVCodecContext *avctx, AVFrame *frame) // that linesize[0] == 2*linesize[1] in the MPEG-encoder for 4:2:2 ret = av_image_fill_linesizes(linesize, avctx->pix_fmt, w); if (ret < 0) - return ret; + goto fail; // increase alignment of w for next try (rhs gives the lowest bit set in w) w += w & ~(w - 1); @@ -1541,15 +1496,16 @@ static int update_frame_pool(AVCodecContext *avctx, AVFrame *frame) tmpsize = av_image_fill_pointers(data, avctx->pix_fmt, h, NULL, linesize); - if (tmpsize < 0) - return tmpsize; + if (tmpsize < 0) { + ret = tmpsize; + goto fail; + } for (i = 0; i < 3 && data[i + 1]; i++) size[i] = data[i + 1] - data[i]; size[i] = tmpsize - (data[i] - data[0]); for (i = 0; i < 4; i++) { - av_buffer_pool_uninit(&pool->pools[i]); pool->linesize[i] = linesize[i]; if (size[i]) { pool->pools[i] = av_buffer_pool_init(size[i] + 16 + STRIDE_ALIGN - 1, @@ -1569,15 +1525,6 @@ static int update_frame_pool(AVCodecContext *avctx, AVFrame *frame) break; } case AVMEDIA_TYPE_AUDIO: { - int ch = frame->channels; //av_get_channel_layout_nb_channels(frame->channel_layout); - int planar = av_sample_fmt_is_planar(frame->format); - int planes = planar ? ch : 1; - - if (pool->format == frame->format && pool->planes == planes && - pool->channels == ch && frame->nb_samples == pool->samples) - return 0; - - av_buffer_pool_uninit(&pool->pools[0]); ret = av_samples_get_buffer_size(&pool->linesize[0], ch, frame->nb_samples, frame->format, 0); if (ret < 0) @@ -1597,19 +1544,19 @@ static int update_frame_pool(AVCodecContext *avctx, AVFrame *frame) } default: av_assert0(0); } + + av_buffer_unref(&avctx->internal->pool); + avctx->internal->pool = pool_buf; + return 0; fail: - for (i = 0; i < 4; i++) - av_buffer_pool_uninit(&pool->pools[i]); - pool->format = -1; - pool->planes = pool->channels = pool->samples = 0; - pool->width = pool->height = 0; + av_buffer_unref(&pool_buf); return ret; } static int audio_get_buffer(AVCodecContext *avctx, AVFrame *frame) { - FramePool *pool = avctx->internal->pool; + FramePool *pool = (FramePool*)avctx->internal->pool->data; int planes = pool->planes; int i; @@ -1654,7 +1601,7 @@ static int audio_get_buffer(AVCodecContext *avctx, AVFrame *frame) static int video_get_buffer(AVCodecContext *s, AVFrame *pic) { - FramePool *pool = s->internal->pool; + FramePool *pool = (FramePool*)s->internal->pool->data; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pic->format); int i; @@ -1751,6 +1698,7 @@ int ff_decode_frame_props(AVCodecContext *avctx, AVFrame *frame) { 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 }, }; if (pkt) { @@ -1903,7 +1851,7 @@ int ff_attach_decode_data(AVFrame *frame) return 0; } -static int get_buffer_internal(AVCodecContext *avctx, AVFrame *frame, int flags) +int ff_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags) { const AVHWAccel *hwaccel = avctx->hwaccel; int override_dimensions = 1; @@ -1912,7 +1860,8 @@ static int get_buffer_internal(AVCodecContext *avctx, AVFrame *frame, int flags) if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) { if ((ret = av_image_check_size2(FFALIGN(avctx->width, STRIDE_ALIGN), avctx->height, avctx->max_pixels, AV_PIX_FMT_NONE, 0, avctx)) < 0 || avctx->pix_fmt<0) { av_log(avctx, AV_LOG_ERROR, "video_get_buffer: image parameters invalid\n"); - return AVERROR(EINVAL); + ret = AVERROR(EINVAL); + goto fail; } if (frame->width <= 0 || frame->height <= 0) { @@ -1923,12 +1872,19 @@ static int get_buffer_internal(AVCodecContext *avctx, AVFrame *frame, int flags) if (frame->data[0] || frame->data[1] || frame->data[2] || frame->data[3]) { av_log(avctx, AV_LOG_ERROR, "pic->data[*]!=NULL in get_buffer_internal\n"); - return AVERROR(EINVAL); + ret = AVERROR(EINVAL); + goto fail; + } + } else if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) { + if (frame->nb_samples * (int64_t)avctx->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); + goto fail; } } ret = ff_decode_frame_props(avctx, frame); if (ret < 0) - return ret; + goto fail; if (hwaccel) { if (hwaccel->alloc_frame) { @@ -1940,13 +1896,13 @@ static int get_buffer_internal(AVCodecContext *avctx, AVFrame *frame, int flags) ret = avctx->get_buffer2(avctx, frame, flags); if (ret < 0) - goto end; + goto fail; validate_avframe_allocation(avctx, frame); ret = ff_attach_decode_data(frame); if (ret < 0) - goto end; + goto fail; end: if (avctx->codec_type == AVMEDIA_TYPE_VIDEO && !override_dimensions && @@ -1955,23 +1911,16 @@ static int get_buffer_internal(AVCodecContext *avctx, AVFrame *frame, int flags) frame->height = avctx->height; } - if (ret < 0) - av_frame_unref(frame); - - return ret; -} - -int ff_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags) -{ - int ret = get_buffer_internal(avctx, frame, flags); +fail: if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); - frame->width = frame->height = 0; + av_frame_unref(frame); } + return ret; } -static int reget_buffer_internal(AVCodecContext *avctx, AVFrame *frame) +static int reget_buffer_internal(AVCodecContext *avctx, AVFrame *frame, int flags) { AVFrame *tmp; int ret; @@ -1987,7 +1936,7 @@ static int reget_buffer_internal(AVCodecContext *avctx, AVFrame *frame) if (!frame->data[0]) return ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF); - if (av_frame_is_writable(frame)) + if ((flags & FF_REGET_BUFFER_FLAG_READONLY) || av_frame_is_writable(frame)) return ff_decode_frame_props(avctx, frame); tmp = av_frame_alloc(); @@ -2008,55 +1957,10 @@ static int reget_buffer_internal(AVCodecContext *avctx, AVFrame *frame) return 0; } -int ff_reget_buffer(AVCodecContext *avctx, AVFrame *frame) +int ff_reget_buffer(AVCodecContext *avctx, AVFrame *frame, int flags) { - int ret = reget_buffer_internal(avctx, frame); + int ret = reget_buffer_internal(avctx, frame, flags); if (ret < 0) av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); return ret; } - -static void bsfs_flush(AVCodecContext *avctx) -{ - DecodeFilterContext *s = &avctx->internal->filter; - - for (int i = 0; i < s->nb_bsfs; i++) - av_bsf_flush(s->bsfs[i]); -} - -void avcodec_flush_buffers(AVCodecContext *avctx) -{ - avctx->internal->draining = 0; - avctx->internal->draining_done = 0; - avctx->internal->nb_draining_errors = 0; - av_frame_unref(avctx->internal->buffer_frame); - av_frame_unref(avctx->internal->compat_decode_frame); - av_packet_unref(avctx->internal->buffer_pkt); - avctx->internal->buffer_pkt_valid = 0; - - av_packet_unref(avctx->internal->ds.in_pkt); - - if (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME) - ff_thread_flush(avctx); - else if (avctx->codec->flush) - avctx->codec->flush(avctx); - - avctx->pts_correction_last_pts = - avctx->pts_correction_last_dts = INT64_MIN; - - bsfs_flush(avctx); - - if (!avctx->refcounted_frames) - av_frame_unref(avctx->internal->to_free); -} - -void ff_decode_bsfs_uninit(AVCodecContext *avctx) -{ - DecodeFilterContext *s = &avctx->internal->filter; - int i; - - for (i = 0; i < s->nb_bsfs; i++) - av_bsf_free(&s->bsfs[i]); - av_freep(&s->bsfs); - s->nb_bsfs = 0; -} diff --git a/libavcodec/decode.h b/libavcodec/decode.h index c3e0e82f4cd..5565346f962 100644 --- a/libavcodec/decode.h +++ b/libavcodec/decode.h @@ -64,10 +64,12 @@ typedef struct FrameDecodeData { */ int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt); +/** + * Called during avcodec_open2() to initialize avctx->internal->bsf. + * The bsf should be freed with av_bsf_free(). + */ int ff_decode_bsfs_init(AVCodecContext *avctx); -void ff_decode_bsfs_uninit(AVCodecContext *avctx); - /** * Make sure avctx.hw_frames_ctx is set. If it's not set, the function will * try to allocate it from hw_device_ctx. If that is not possible, an error diff --git a/libavcodec/dfa.c b/libavcodec/dfa.c index c6106b9397d..31c6c390894 100644 --- a/libavcodec/dfa.c +++ b/libavcodec/dfa.c @@ -332,7 +332,7 @@ static const chunk_decoder decoder[8] = { decode_tdlt, decode_dsw1, decode_blck, decode_dds1, }; -static const char * const chunk_name[8] = { +static const char chunk_name[8][5] = { "COPY", "TSW1", "BDLT", "WDLT", "TDLT", "DSW1", "BLCK", "DDS1" }; diff --git a/libavcodec/dirac_vlc.c b/libavcodec/dirac_vlc.c index 496d8177cd8..2e2fa7ea639 100644 --- a/libavcodec/dirac_vlc.c +++ b/libavcodec/dirac_vlc.c @@ -1,7 +1,4 @@ /* - * Copyright (C) 2016 Open Broadcast Systems Ltd. - * Author 2016 Rostislav Pehlivanov - * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -21,232 +18,1114 @@ #include "dirac_vlc.h" -#define LUT_SIZE (1 << LUT_BITS) -#define RSIZE_BITS (CHAR_BIT*sizeof(residual)) - -#define CONVERT_TO_RESIDUE(a, b) \ - (((residual)(a)) << (RSIZE_BITS - (b))) - -#define INIT_RESIDUE(N) \ - residual N = 0; \ - av_unused int32_t N ## _bits = 0 - -#define SET_RESIDUE(N, I, B) \ - N = CONVERT_TO_RESIDUE(I, B); \ - N ## _bits = B - -#define APPEND_RESIDUE(N, M) \ - N |= M >> (N ## _bits); \ - N ## _bits = (N ## _bits + (M ## _bits)) & 0x3F - -int ff_dirac_golomb_read_32bit(DiracGolombLUT *lut_ctx, const uint8_t *buf, - int bytes, uint8_t *_dst, int coeffs) -{ - int i, b, c_idx = 0; - int32_t *dst = (int32_t *)_dst; - DiracGolombLUT *future[4], *l = &lut_ctx[2*LUT_SIZE + buf[0]]; - INIT_RESIDUE(res); - - for (b = 1; b <= bytes; b++) { - future[0] = &lut_ctx[buf[b]]; - future[1] = future[0] + 1*LUT_SIZE; - future[2] = future[0] + 2*LUT_SIZE; - future[3] = future[0] + 3*LUT_SIZE; - - if ((c_idx + 1) > coeffs) - return c_idx; - - /* res_bits is a hint for better branch prediction */ - if (res_bits && l->sign) { - int32_t coeff = 1; - APPEND_RESIDUE(res, l->preamble); - for (i = 0; i < (res_bits >> 1) - 1; i++) { - coeff <<= 1; - coeff |= (res >> (RSIZE_BITS - 2*i - 2)) & 1; - } - dst[c_idx++] = l->sign * (coeff - 1); - res_bits = res = 0; - } - - memcpy(&dst[c_idx], l->ready, LUT_BITS*sizeof(int32_t)); - c_idx += l->ready_num; +enum { + /* Next byte contains an exactly aligned start to a new symbol (even bit) */ + STATE_START = 0, + /* Next byte should end the current value on an odd bit */ + STATE_FOLLOW = 256, + /* Byte is completely data and doesn't end nor start a value */ + STATE_DATA = 512, + /* Byte has the current value's sign bit and starts a new value */ + STATE_SIGN = 768, +}; - APPEND_RESIDUE(res, l->leftover); +/* Exactly 128 bits */ +typedef struct LUTState { + int16_t val0; /* Bits to which to add after applying preshift */ + int16_t val1; + int16_t val2; + int16_t val3; + int16_t val4; + uint8_t val0_bits; /* The size of val0 in bits */ + int8_t sign; /* Sign of the current value (0 == zero the value) */ + int8_t num; /* Number of values in this byte */ + uint8_t val; /* Init value in case current value was terminated */ + uint16_t state; /* Expected state for the next byte */ +} LUTState; - l = future[l->need_s ? 3 : !res_bits ? 2 : res_bits & 1]; - } +const DECLARE_ALIGNED(32, LUTState, ff_dirac_golomb_lut)[1024] = { + { +16, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +17, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +8, 0, 0, 0, 0, 4, +1, 1, 0, STATE_START }, + { +8, 0, 0, 0, 0, 4, -1, 1, 0, STATE_START }, + { +18, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +19, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +9, 0, 0, 0, 0, 4, +1, 1, 0, STATE_START }, + { +9, 0, 0, 0, 0, 4, -1, 1, 0, STATE_START }, + { +4, 0, 0, 0, 0, 3, +1, 1, 2, STATE_FOLLOW }, + { +4, 0, 0, 0, 0, 3, +1, 1, 3, STATE_FOLLOW }, + { +4, 0, 0, 0, 0, 3, +1, 2, 1, STATE_DATA }, + { +4, 0, 0, 0, 0, 3, +1, 3, 0, STATE_START }, + { +4, 0, 0, 0, 0, 3, -1, 1, 2, STATE_FOLLOW }, + { +4, 0, 0, 0, 0, 3, -1, 1, 3, STATE_FOLLOW }, + { +4, 0, 0, 0, 0, 3, -1, 2, 1, STATE_DATA }, + { +4, 0, 0, 0, 0, 3, -1, 3, 0, STATE_START }, + { +20, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +21, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +10, 0, 0, 0, 0, 4, +1, 1, 0, STATE_START }, + { +10, 0, 0, 0, 0, 4, -1, 1, 0, STATE_START }, + { +22, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +23, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +11, 0, 0, 0, 0, 4, +1, 1, 0, STATE_START }, + { +11, 0, 0, 0, 0, 4, -1, 1, 0, STATE_START }, + { +5, 0, 0, 0, 0, 3, +1, 1, 2, STATE_FOLLOW }, + { +5, 0, 0, 0, 0, 3, +1, 1, 3, STATE_FOLLOW }, + { +5, 0, 0, 0, 0, 3, +1, 2, 1, STATE_DATA }, + { +5, 0, 0, 0, 0, 3, +1, 3, 0, STATE_START }, + { +5, 0, 0, 0, 0, 3, -1, 1, 2, STATE_FOLLOW }, + { +5, 0, 0, 0, 0, 3, -1, 1, 3, STATE_FOLLOW }, + { +5, 0, 0, 0, 0, 3, -1, 2, 1, STATE_DATA }, + { +5, 0, 0, 0, 0, 3, -1, 3, 0, STATE_START }, + { +2, 0, 0, 0, 0, 2, +1, 1, 4, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, +1, 1, 5, STATE_FOLLOW }, + { +2, +1, 0, 0, 0, 2, +1, 2, 0, STATE_START }, + { +2, -1, 0, 0, 0, 2, +1, 2, 0, STATE_START }, + { +2, 0, 0, 0, 0, 2, +1, 1, 6, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, +1, 1, 7, STATE_FOLLOW }, + { +2, +2, 0, 0, 0, 2, +1, 2, 0, STATE_START }, + { +2, -2, 0, 0, 0, 2, +1, 2, 0, STATE_START }, + { +2, 0, 0, 0, 0, 2, +1, 2, 2, STATE_DATA }, + { +2, 0, 0, 0, 0, 2, +1, 2, 2, STATE_SIGN }, + { +2, 0, 0, 0, 0, 2, +1, 2, 3, STATE_DATA }, + { +2, 0, 0, 0, 0, 2, +1, 2, 3, STATE_SIGN }, + { +2, 0, 0, 0, 0, 2, +1, 3, 2, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, +1, 3, 3, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, +1, 4, 1, STATE_DATA }, + { +2, 0, 0, 0, 0, 2, +1, 5, 0, STATE_START }, + { +2, 0, 0, 0, 0, 2, -1, 1, 4, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, -1, 1, 5, STATE_FOLLOW }, + { +2, +1, 0, 0, 0, 2, -1, 2, 0, STATE_START }, + { +2, -1, 0, 0, 0, 2, -1, 2, 0, STATE_START }, + { +2, 0, 0, 0, 0, 2, -1, 1, 6, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, -1, 1, 7, STATE_FOLLOW }, + { +2, +2, 0, 0, 0, 2, -1, 2, 0, STATE_START }, + { +2, -2, 0, 0, 0, 2, -1, 2, 0, STATE_START }, + { +2, 0, 0, 0, 0, 2, -1, 2, 2, STATE_DATA }, + { +2, 0, 0, 0, 0, 2, -1, 2, 2, STATE_SIGN }, + { +2, 0, 0, 0, 0, 2, -1, 2, 3, STATE_DATA }, + { +2, 0, 0, 0, 0, 2, -1, 2, 3, STATE_SIGN }, + { +2, 0, 0, 0, 0, 2, -1, 3, 2, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, -1, 3, 3, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, -1, 4, 1, STATE_DATA }, + { +2, 0, 0, 0, 0, 2, -1, 5, 0, STATE_START }, + { +24, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +25, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +12, 0, 0, 0, 0, 4, +1, 1, 0, STATE_START }, + { +12, 0, 0, 0, 0, 4, -1, 1, 0, STATE_START }, + { +26, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +27, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +13, 0, 0, 0, 0, 4, +1, 1, 0, STATE_START }, + { +13, 0, 0, 0, 0, 4, -1, 1, 0, STATE_START }, + { +6, 0, 0, 0, 0, 3, +1, 1, 2, STATE_FOLLOW }, + { +6, 0, 0, 0, 0, 3, +1, 1, 3, STATE_FOLLOW }, + { +6, 0, 0, 0, 0, 3, +1, 2, 1, STATE_DATA }, + { +6, 0, 0, 0, 0, 3, +1, 3, 0, STATE_START }, + { +6, 0, 0, 0, 0, 3, -1, 1, 2, STATE_FOLLOW }, + { +6, 0, 0, 0, 0, 3, -1, 1, 3, STATE_FOLLOW }, + { +6, 0, 0, 0, 0, 3, -1, 2, 1, STATE_DATA }, + { +6, 0, 0, 0, 0, 3, -1, 3, 0, STATE_START }, + { +28, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +29, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +14, 0, 0, 0, 0, 4, +1, 1, 0, STATE_START }, + { +14, 0, 0, 0, 0, 4, -1, 1, 0, STATE_START }, + { +30, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +31, 0, 0, 0, 0, 5, +1, 0, 0, STATE_FOLLOW }, + { +15, 0, 0, 0, 0, 4, +1, 1, 0, STATE_START }, + { +15, 0, 0, 0, 0, 4, -1, 1, 0, STATE_START }, + { +7, 0, 0, 0, 0, 3, +1, 1, 2, STATE_FOLLOW }, + { +7, 0, 0, 0, 0, 3, +1, 1, 3, STATE_FOLLOW }, + { +7, 0, 0, 0, 0, 3, +1, 2, 1, STATE_DATA }, + { +7, 0, 0, 0, 0, 3, +1, 3, 0, STATE_START }, + { +7, 0, 0, 0, 0, 3, -1, 1, 2, STATE_FOLLOW }, + { +7, 0, 0, 0, 0, 3, -1, 1, 3, STATE_FOLLOW }, + { +7, 0, 0, 0, 0, 3, -1, 2, 1, STATE_DATA }, + { +7, 0, 0, 0, 0, 3, -1, 3, 0, STATE_START }, + { +3, 0, 0, 0, 0, 2, +1, 1, 4, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, +1, 1, 5, STATE_FOLLOW }, + { +3, +1, 0, 0, 0, 2, +1, 2, 0, STATE_START }, + { +3, -1, 0, 0, 0, 2, +1, 2, 0, STATE_START }, + { +3, 0, 0, 0, 0, 2, +1, 1, 6, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, +1, 1, 7, STATE_FOLLOW }, + { +3, +2, 0, 0, 0, 2, +1, 2, 0, STATE_START }, + { +3, -2, 0, 0, 0, 2, +1, 2, 0, STATE_START }, + { +3, 0, 0, 0, 0, 2, +1, 2, 2, STATE_DATA }, + { +3, 0, 0, 0, 0, 2, +1, 2, 2, STATE_SIGN }, + { +3, 0, 0, 0, 0, 2, +1, 2, 3, STATE_DATA }, + { +3, 0, 0, 0, 0, 2, +1, 2, 3, STATE_SIGN }, + { +3, 0, 0, 0, 0, 2, +1, 3, 2, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, +1, 3, 3, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, +1, 4, 1, STATE_DATA }, + { +3, 0, 0, 0, 0, 2, +1, 5, 0, STATE_START }, + { +3, 0, 0, 0, 0, 2, -1, 1, 4, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, -1, 1, 5, STATE_FOLLOW }, + { +3, +1, 0, 0, 0, 2, -1, 2, 0, STATE_START }, + { +3, -1, 0, 0, 0, 2, -1, 2, 0, STATE_START }, + { +3, 0, 0, 0, 0, 2, -1, 1, 6, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, -1, 1, 7, STATE_FOLLOW }, + { +3, +2, 0, 0, 0, 2, -1, 2, 0, STATE_START }, + { +3, -2, 0, 0, 0, 2, -1, 2, 0, STATE_START }, + { +3, 0, 0, 0, 0, 2, -1, 2, 2, STATE_DATA }, + { +3, 0, 0, 0, 0, 2, -1, 2, 2, STATE_SIGN }, + { +3, 0, 0, 0, 0, 2, -1, 2, 3, STATE_DATA }, + { +3, 0, 0, 0, 0, 2, -1, 2, 3, STATE_SIGN }, + { +3, 0, 0, 0, 0, 2, -1, 3, 2, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, -1, 3, 3, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, -1, 4, 1, STATE_DATA }, + { +3, 0, 0, 0, 0, 2, -1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, 0, 1, 8, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 1, 8, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, 0, 1, 9, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 1, 9, STATE_SIGN }, + { 0, +3, 0, 0, 0, 0, 0, 2, 1, STATE_DATA }, + { 0, +3, 0, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, -3, 0, 0, 0, 0, 0, 2, 1, STATE_DATA }, + { 0, -3, 0, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, 0, 1, 10, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 1, 10, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, 0, 1, 11, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 1, 11, STATE_SIGN }, + { 0, +4, 0, 0, 0, 0, 0, 2, 1, STATE_DATA }, + { 0, +4, 0, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, -4, 0, 0, 0, 0, 0, 2, 1, STATE_DATA }, + { 0, -4, 0, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, +1, 0, 0, 0, 0, 0, 2, 2, STATE_DATA }, + { 0, +1, 0, 0, 0, 0, 0, 2, 2, STATE_SIGN }, + { 0, +1, 0, 0, 0, 0, 0, 2, 3, STATE_DATA }, + { 0, +1, 0, 0, 0, 0, 0, 2, 3, STATE_SIGN }, + { 0, +1, 0, 0, 0, 0, 0, 3, 2, STATE_FOLLOW }, + { 0, +1, 0, 0, 0, 0, 0, 3, 3, STATE_FOLLOW }, + { 0, +1, 0, 0, 0, 0, 0, 4, 1, STATE_DATA }, + { 0, +1, 0, 0, 0, 0, 0, 5, 0, STATE_START }, + { 0, -1, 0, 0, 0, 0, 0, 2, 2, STATE_DATA }, + { 0, -1, 0, 0, 0, 0, 0, 2, 2, STATE_SIGN }, + { 0, -1, 0, 0, 0, 0, 0, 2, 3, STATE_DATA }, + { 0, -1, 0, 0, 0, 0, 0, 2, 3, STATE_SIGN }, + { 0, -1, 0, 0, 0, 0, 0, 3, 2, STATE_FOLLOW }, + { 0, -1, 0, 0, 0, 0, 0, 3, 3, STATE_FOLLOW }, + { 0, -1, 0, 0, 0, 0, 0, 4, 1, STATE_DATA }, + { 0, -1, 0, 0, 0, 0, 0, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, 0, 1, 12, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 1, 12, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, 0, 1, 13, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 1, 13, STATE_SIGN }, + { 0, +5, 0, 0, 0, 0, 0, 2, 1, STATE_DATA }, + { 0, +5, 0, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, -5, 0, 0, 0, 0, 0, 2, 1, STATE_DATA }, + { 0, -5, 0, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, 0, 1, 14, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 1, 14, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, 0, 1, 15, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 1, 15, STATE_SIGN }, + { 0, +6, 0, 0, 0, 0, 0, 2, 1, STATE_DATA }, + { 0, +6, 0, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, -6, 0, 0, 0, 0, 0, 2, 1, STATE_DATA }, + { 0, -6, 0, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, +2, 0, 0, 0, 0, 0, 2, 2, STATE_DATA }, + { 0, +2, 0, 0, 0, 0, 0, 2, 2, STATE_SIGN }, + { 0, +2, 0, 0, 0, 0, 0, 2, 3, STATE_DATA }, + { 0, +2, 0, 0, 0, 0, 0, 2, 3, STATE_SIGN }, + { 0, +2, 0, 0, 0, 0, 0, 3, 2, STATE_FOLLOW }, + { 0, +2, 0, 0, 0, 0, 0, 3, 3, STATE_FOLLOW }, + { 0, +2, 0, 0, 0, 0, 0, 4, 1, STATE_DATA }, + { 0, +2, 0, 0, 0, 0, 0, 5, 0, STATE_START }, + { 0, -2, 0, 0, 0, 0, 0, 2, 2, STATE_DATA }, + { 0, -2, 0, 0, 0, 0, 0, 2, 2, STATE_SIGN }, + { 0, -2, 0, 0, 0, 0, 0, 2, 3, STATE_DATA }, + { 0, -2, 0, 0, 0, 0, 0, 2, 3, STATE_SIGN }, + { 0, -2, 0, 0, 0, 0, 0, 3, 2, STATE_FOLLOW }, + { 0, -2, 0, 0, 0, 0, 0, 3, 3, STATE_FOLLOW }, + { 0, -2, 0, 0, 0, 0, 0, 4, 1, STATE_DATA }, + { 0, -2, 0, 0, 0, 0, 0, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, 0, 2, 8, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, 0, 2, 9, STATE_FOLLOW }, + { 0, 0, +3, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, 0, -3, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, 0, 2, 10, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, 0, 2, 11, STATE_FOLLOW }, + { 0, 0, +4, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, 0, -4, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, 0, +1, 0, 0, 0, 0, 3, 2, STATE_FOLLOW }, + { 0, 0, +1, 0, 0, 0, 0, 3, 3, STATE_FOLLOW }, + { 0, 0, +1, 0, 0, 0, 0, 4, 1, STATE_DATA }, + { 0, 0, +1, 0, 0, 0, 0, 5, 0, STATE_START }, + { 0, 0, -1, 0, 0, 0, 0, 3, 2, STATE_FOLLOW }, + { 0, 0, -1, 0, 0, 0, 0, 3, 3, STATE_FOLLOW }, + { 0, 0, -1, 0, 0, 0, 0, 4, 1, STATE_DATA }, + { 0, 0, -1, 0, 0, 0, 0, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, 0, 2, 12, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, 0, 2, 13, STATE_FOLLOW }, + { 0, 0, +5, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, 0, -5, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, 0, 2, 14, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, 0, 2, 15, STATE_FOLLOW }, + { 0, 0, +6, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, 0, -6, 0, 0, 0, 0, 3, 0, STATE_START }, + { 0, 0, +2, 0, 0, 0, 0, 3, 2, STATE_FOLLOW }, + { 0, 0, +2, 0, 0, 0, 0, 3, 3, STATE_FOLLOW }, + { 0, 0, +2, 0, 0, 0, 0, 4, 1, STATE_DATA }, + { 0, 0, +2, 0, 0, 0, 0, 5, 0, STATE_START }, + { 0, 0, -2, 0, 0, 0, 0, 3, 2, STATE_FOLLOW }, + { 0, 0, -2, 0, 0, 0, 0, 3, 3, STATE_FOLLOW }, + { 0, 0, -2, 0, 0, 0, 0, 4, 1, STATE_DATA }, + { 0, 0, -2, 0, 0, 0, 0, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, 0, 3, 4, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 3, 4, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, 0, 3, 5, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 3, 5, STATE_SIGN }, + { 0, 0, 0, +1, 0, 0, 0, 4, 1, STATE_DATA }, + { 0, 0, 0, +1, 0, 0, 0, 5, 0, STATE_START }, + { 0, 0, 0, -1, 0, 0, 0, 4, 1, STATE_DATA }, + { 0, 0, 0, -1, 0, 0, 0, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, 0, 3, 6, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 3, 6, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, 0, 3, 7, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 3, 7, STATE_SIGN }, + { 0, 0, 0, +2, 0, 0, 0, 4, 1, STATE_DATA }, + { 0, 0, 0, +2, 0, 0, 0, 5, 0, STATE_START }, + { 0, 0, 0, -2, 0, 0, 0, 4, 1, STATE_DATA }, + { 0, 0, 0, -2, 0, 0, 0, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, 0, 4, 4, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, 0, 4, 5, STATE_FOLLOW }, + { 0, 0, 0, 0, +1, 0, 0, 5, 0, STATE_START }, + { 0, 0, 0, 0, -1, 0, 0, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, 0, 4, 6, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, 0, 4, 7, STATE_FOLLOW }, + { 0, 0, 0, 0, +2, 0, 0, 5, 0, STATE_START }, + { 0, 0, 0, 0, -2, 0, 0, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, 0, 5, 2, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 5, 2, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, 0, 5, 3, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 5, 3, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, 0, 6, 2, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, 0, 6, 3, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, 0, 7, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, 0, 8, 0, STATE_START }, + { 0, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 3, +1, 1, 0, STATE_START }, + { 0, 0, 0, 0, 0, 3, -1, 1, 0, STATE_START }, + { +2, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 3, +1, 1, 0, STATE_START }, + { +1, 0, 0, 0, 0, 3, -1, 1, 0, STATE_START }, + { 0, 0, 0, 0, 0, 2, +1, 1, 2, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 2, +1, 1, 3, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 2, +1, 2, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 2, +1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 2, -1, 1, 2, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 2, -1, 1, 3, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 2, -1, 2, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 2, -1, 3, 0, STATE_START }, + { +4, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +5, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 3, +1, 1, 0, STATE_START }, + { +2, 0, 0, 0, 0, 3, -1, 1, 0, STATE_START }, + { +6, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +7, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 3, +1, 1, 0, STATE_START }, + { +3, 0, 0, 0, 0, 3, -1, 1, 0, STATE_START }, + { +1, 0, 0, 0, 0, 2, +1, 1, 2, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 2, +1, 1, 3, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 2, +1, 2, 1, STATE_DATA }, + { +1, 0, 0, 0, 0, 2, +1, 3, 0, STATE_START }, + { +1, 0, 0, 0, 0, 2, -1, 1, 2, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 2, -1, 1, 3, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 2, -1, 2, 1, STATE_DATA }, + { +1, 0, 0, 0, 0, 2, -1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, +1, 1, 4, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, +1, 1, 5, STATE_FOLLOW }, + { 0, +1, 0, 0, 0, 1, +1, 2, 0, STATE_START }, + { 0, -1, 0, 0, 0, 1, +1, 2, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, +1, 1, 6, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, +1, 1, 7, STATE_FOLLOW }, + { 0, +2, 0, 0, 0, 1, +1, 2, 0, STATE_START }, + { 0, -2, 0, 0, 0, 1, +1, 2, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, +1, 2, 2, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, +1, 2, 2, STATE_SIGN }, + { 0, 0, 0, 0, 0, 1, +1, 2, 3, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, +1, 2, 3, STATE_SIGN }, + { 0, 0, 0, 0, 0, 1, +1, 3, 2, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, +1, 3, 3, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, +1, 4, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, +1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, -1, 1, 4, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, -1, 1, 5, STATE_FOLLOW }, + { 0, +1, 0, 0, 0, 1, -1, 2, 0, STATE_START }, + { 0, -1, 0, 0, 0, 1, -1, 2, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, -1, 1, 6, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, -1, 1, 7, STATE_FOLLOW }, + { 0, +2, 0, 0, 0, 1, -1, 2, 0, STATE_START }, + { 0, -2, 0, 0, 0, 1, -1, 2, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, -1, 2, 2, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, -1, 2, 2, STATE_SIGN }, + { 0, 0, 0, 0, 0, 1, -1, 2, 3, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, -1, 2, 3, STATE_SIGN }, + { 0, 0, 0, 0, 0, 1, -1, 3, 2, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, -1, 3, 3, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, -1, 4, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, -1, 5, 0, STATE_START }, + { +8, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +9, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +4, 0, 0, 0, 0, 3, +1, 1, 0, STATE_START }, + { +4, 0, 0, 0, 0, 3, -1, 1, 0, STATE_START }, + { +10, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +11, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +5, 0, 0, 0, 0, 3, +1, 1, 0, STATE_START }, + { +5, 0, 0, 0, 0, 3, -1, 1, 0, STATE_START }, + { +2, 0, 0, 0, 0, 2, +1, 1, 2, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, +1, 1, 3, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, +1, 2, 1, STATE_DATA }, + { +2, 0, 0, 0, 0, 2, +1, 3, 0, STATE_START }, + { +2, 0, 0, 0, 0, 2, -1, 1, 2, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, -1, 1, 3, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, -1, 2, 1, STATE_DATA }, + { +2, 0, 0, 0, 0, 2, -1, 3, 0, STATE_START }, + { +12, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +13, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +6, 0, 0, 0, 0, 3, +1, 1, 0, STATE_START }, + { +6, 0, 0, 0, 0, 3, -1, 1, 0, STATE_START }, + { +14, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +15, 0, 0, 0, 0, 4, +1, 0, 0, STATE_FOLLOW }, + { +7, 0, 0, 0, 0, 3, +1, 1, 0, STATE_START }, + { +7, 0, 0, 0, 0, 3, -1, 1, 0, STATE_START }, + { +3, 0, 0, 0, 0, 2, +1, 1, 2, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, +1, 1, 3, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, +1, 2, 1, STATE_DATA }, + { +3, 0, 0, 0, 0, 2, +1, 3, 0, STATE_START }, + { +3, 0, 0, 0, 0, 2, -1, 1, 2, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, -1, 1, 3, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, -1, 2, 1, STATE_DATA }, + { +3, 0, 0, 0, 0, 2, -1, 3, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, +1, 1, 4, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, +1, 1, 5, STATE_FOLLOW }, + { +1, +1, 0, 0, 0, 1, +1, 2, 0, STATE_START }, + { +1, -1, 0, 0, 0, 1, +1, 2, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, +1, 1, 6, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, +1, 1, 7, STATE_FOLLOW }, + { +1, +2, 0, 0, 0, 1, +1, 2, 0, STATE_START }, + { +1, -2, 0, 0, 0, 1, +1, 2, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, +1, 2, 2, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, +1, 2, 2, STATE_SIGN }, + { +1, 0, 0, 0, 0, 1, +1, 2, 3, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, +1, 2, 3, STATE_SIGN }, + { +1, 0, 0, 0, 0, 1, +1, 3, 2, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, +1, 3, 3, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, +1, 4, 1, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, +1, 5, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, -1, 1, 4, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, -1, 1, 5, STATE_FOLLOW }, + { +1, +1, 0, 0, 0, 1, -1, 2, 0, STATE_START }, + { +1, -1, 0, 0, 0, 1, -1, 2, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, -1, 1, 6, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, -1, 1, 7, STATE_FOLLOW }, + { +1, +2, 0, 0, 0, 1, -1, 2, 0, STATE_START }, + { +1, -2, 0, 0, 0, 1, -1, 2, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, -1, 2, 2, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, -1, 2, 2, STATE_SIGN }, + { +1, 0, 0, 0, 0, 1, -1, 2, 3, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, -1, 2, 3, STATE_SIGN }, + { +1, 0, 0, 0, 0, 1, -1, 3, 2, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, -1, 3, 3, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, -1, 4, 1, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, -1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 1, 8, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 1, 9, STATE_FOLLOW }, + { 0, +3, 0, 0, 0, 0, +1, 2, 0, STATE_START }, + { 0, -3, 0, 0, 0, 0, +1, 2, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 1, 10, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 1, 11, STATE_FOLLOW }, + { 0, +4, 0, 0, 0, 0, +1, 2, 0, STATE_START }, + { 0, -4, 0, 0, 0, 0, +1, 2, 0, STATE_START }, + { 0, +1, 0, 0, 0, 0, +1, 2, 2, STATE_FOLLOW }, + { 0, +1, 0, 0, 0, 0, +1, 2, 3, STATE_FOLLOW }, + { 0, +1, 0, 0, 0, 0, +1, 3, 1, STATE_DATA }, + { 0, +1, 0, 0, 0, 0, +1, 4, 0, STATE_START }, + { 0, -1, 0, 0, 0, 0, +1, 2, 2, STATE_FOLLOW }, + { 0, -1, 0, 0, 0, 0, +1, 2, 3, STATE_FOLLOW }, + { 0, -1, 0, 0, 0, 0, +1, 3, 1, STATE_DATA }, + { 0, -1, 0, 0, 0, 0, +1, 4, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 1, 12, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 1, 13, STATE_FOLLOW }, + { 0, +5, 0, 0, 0, 0, +1, 2, 0, STATE_START }, + { 0, -5, 0, 0, 0, 0, +1, 2, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 1, 14, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 1, 15, STATE_FOLLOW }, + { 0, +6, 0, 0, 0, 0, +1, 2, 0, STATE_START }, + { 0, -6, 0, 0, 0, 0, +1, 2, 0, STATE_START }, + { 0, +2, 0, 0, 0, 0, +1, 2, 2, STATE_FOLLOW }, + { 0, +2, 0, 0, 0, 0, +1, 2, 3, STATE_FOLLOW }, + { 0, +2, 0, 0, 0, 0, +1, 3, 1, STATE_DATA }, + { 0, +2, 0, 0, 0, 0, +1, 4, 0, STATE_START }, + { 0, -2, 0, 0, 0, 0, +1, 2, 2, STATE_FOLLOW }, + { 0, -2, 0, 0, 0, 0, +1, 2, 3, STATE_FOLLOW }, + { 0, -2, 0, 0, 0, 0, +1, 3, 1, STATE_DATA }, + { 0, -2, 0, 0, 0, 0, +1, 4, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 2, 4, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 2, 4, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, +1, 2, 5, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 2, 5, STATE_SIGN }, + { 0, 0, +1, 0, 0, 0, +1, 3, 1, STATE_DATA }, + { 0, 0, +1, 0, 0, 0, +1, 4, 0, STATE_START }, + { 0, 0, -1, 0, 0, 0, +1, 3, 1, STATE_DATA }, + { 0, 0, -1, 0, 0, 0, +1, 4, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 2, 6, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 2, 6, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, +1, 2, 7, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 2, 7, STATE_SIGN }, + { 0, 0, +2, 0, 0, 0, +1, 3, 1, STATE_DATA }, + { 0, 0, +2, 0, 0, 0, +1, 4, 0, STATE_START }, + { 0, 0, -2, 0, 0, 0, +1, 3, 1, STATE_DATA }, + { 0, 0, -2, 0, 0, 0, +1, 4, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 3, 4, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 3, 5, STATE_FOLLOW }, + { 0, 0, 0, +1, 0, 0, +1, 4, 0, STATE_START }, + { 0, 0, 0, -1, 0, 0, +1, 4, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 3, 6, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 3, 7, STATE_FOLLOW }, + { 0, 0, 0, +2, 0, 0, +1, 4, 0, STATE_START }, + { 0, 0, 0, -2, 0, 0, +1, 4, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 4, 2, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 4, 2, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, +1, 4, 3, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 4, 3, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, +1, 5, 2, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 5, 3, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 6, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 7, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 1, 8, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 1, 9, STATE_FOLLOW }, + { 0, +3, 0, 0, 0, 0, -1, 2, 0, STATE_START }, + { 0, -3, 0, 0, 0, 0, -1, 2, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 1, 10, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 1, 11, STATE_FOLLOW }, + { 0, +4, 0, 0, 0, 0, -1, 2, 0, STATE_START }, + { 0, -4, 0, 0, 0, 0, -1, 2, 0, STATE_START }, + { 0, +1, 0, 0, 0, 0, -1, 2, 2, STATE_FOLLOW }, + { 0, +1, 0, 0, 0, 0, -1, 2, 3, STATE_FOLLOW }, + { 0, +1, 0, 0, 0, 0, -1, 3, 1, STATE_DATA }, + { 0, +1, 0, 0, 0, 0, -1, 4, 0, STATE_START }, + { 0, -1, 0, 0, 0, 0, -1, 2, 2, STATE_FOLLOW }, + { 0, -1, 0, 0, 0, 0, -1, 2, 3, STATE_FOLLOW }, + { 0, -1, 0, 0, 0, 0, -1, 3, 1, STATE_DATA }, + { 0, -1, 0, 0, 0, 0, -1, 4, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 1, 12, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 1, 13, STATE_FOLLOW }, + { 0, +5, 0, 0, 0, 0, -1, 2, 0, STATE_START }, + { 0, -5, 0, 0, 0, 0, -1, 2, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 1, 14, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 1, 15, STATE_FOLLOW }, + { 0, +6, 0, 0, 0, 0, -1, 2, 0, STATE_START }, + { 0, -6, 0, 0, 0, 0, -1, 2, 0, STATE_START }, + { 0, +2, 0, 0, 0, 0, -1, 2, 2, STATE_FOLLOW }, + { 0, +2, 0, 0, 0, 0, -1, 2, 3, STATE_FOLLOW }, + { 0, +2, 0, 0, 0, 0, -1, 3, 1, STATE_DATA }, + { 0, +2, 0, 0, 0, 0, -1, 4, 0, STATE_START }, + { 0, -2, 0, 0, 0, 0, -1, 2, 2, STATE_FOLLOW }, + { 0, -2, 0, 0, 0, 0, -1, 2, 3, STATE_FOLLOW }, + { 0, -2, 0, 0, 0, 0, -1, 3, 1, STATE_DATA }, + { 0, -2, 0, 0, 0, 0, -1, 4, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 2, 4, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 2, 4, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, -1, 2, 5, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 2, 5, STATE_SIGN }, + { 0, 0, +1, 0, 0, 0, -1, 3, 1, STATE_DATA }, + { 0, 0, +1, 0, 0, 0, -1, 4, 0, STATE_START }, + { 0, 0, -1, 0, 0, 0, -1, 3, 1, STATE_DATA }, + { 0, 0, -1, 0, 0, 0, -1, 4, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 2, 6, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 2, 6, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, -1, 2, 7, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 2, 7, STATE_SIGN }, + { 0, 0, +2, 0, 0, 0, -1, 3, 1, STATE_DATA }, + { 0, 0, +2, 0, 0, 0, -1, 4, 0, STATE_START }, + { 0, 0, -2, 0, 0, 0, -1, 3, 1, STATE_DATA }, + { 0, 0, -2, 0, 0, 0, -1, 4, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 3, 4, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 3, 5, STATE_FOLLOW }, + { 0, 0, 0, +1, 0, 0, -1, 4, 0, STATE_START }, + { 0, 0, 0, -1, 0, 0, -1, 4, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 3, 6, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 3, 7, STATE_FOLLOW }, + { 0, 0, 0, +2, 0, 0, -1, 4, 0, STATE_START }, + { 0, 0, 0, -2, 0, 0, -1, 4, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 4, 2, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 4, 2, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, -1, 4, 3, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 4, 3, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, -1, 5, 2, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 5, 3, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 6, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 7, 0, STATE_START }, + { 0, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { 0, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +1, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +1, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { 0, 0, 0, 0, 0, 3, +1, 1, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 3, +1, 2, 0, STATE_START }, + { 0, 0, 0, 0, 0, 3, -1, 1, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 3, -1, 2, 0, STATE_START }, + { +2, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +2, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +3, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +3, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +1, 0, 0, 0, 0, 3, +1, 1, 1, STATE_DATA }, + { +1, 0, 0, 0, 0, 3, +1, 2, 0, STATE_START }, + { +1, 0, 0, 0, 0, 3, -1, 1, 1, STATE_DATA }, + { +1, 0, 0, 0, 0, 3, -1, 2, 0, STATE_START }, + { 0, 0, 0, 0, 0, 2, +1, 1, 2, STATE_DATA }, + { 0, 0, 0, 0, 0, 2, +1, 1, 2, STATE_SIGN }, + { 0, 0, 0, 0, 0, 2, +1, 1, 3, STATE_DATA }, + { 0, 0, 0, 0, 0, 2, +1, 1, 3, STATE_SIGN }, + { 0, 0, 0, 0, 0, 2, +1, 2, 2, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 2, +1, 2, 3, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 2, +1, 3, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 2, +1, 4, 0, STATE_START }, + { 0, 0, 0, 0, 0, 2, -1, 1, 2, STATE_DATA }, + { 0, 0, 0, 0, 0, 2, -1, 1, 2, STATE_SIGN }, + { 0, 0, 0, 0, 0, 2, -1, 1, 3, STATE_DATA }, + { 0, 0, 0, 0, 0, 2, -1, 1, 3, STATE_SIGN }, + { 0, 0, 0, 0, 0, 2, -1, 2, 2, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 2, -1, 2, 3, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 2, -1, 3, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 2, -1, 4, 0, STATE_START }, + { +4, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +4, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +5, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +5, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +2, 0, 0, 0, 0, 3, +1, 1, 1, STATE_DATA }, + { +2, 0, 0, 0, 0, 3, +1, 2, 0, STATE_START }, + { +2, 0, 0, 0, 0, 3, -1, 1, 1, STATE_DATA }, + { +2, 0, 0, 0, 0, 3, -1, 2, 0, STATE_START }, + { +6, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +6, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +7, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +7, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +3, 0, 0, 0, 0, 3, +1, 1, 1, STATE_DATA }, + { +3, 0, 0, 0, 0, 3, +1, 2, 0, STATE_START }, + { +3, 0, 0, 0, 0, 3, -1, 1, 1, STATE_DATA }, + { +3, 0, 0, 0, 0, 3, -1, 2, 0, STATE_START }, + { +1, 0, 0, 0, 0, 2, +1, 1, 2, STATE_DATA }, + { +1, 0, 0, 0, 0, 2, +1, 1, 2, STATE_SIGN }, + { +1, 0, 0, 0, 0, 2, +1, 1, 3, STATE_DATA }, + { +1, 0, 0, 0, 0, 2, +1, 1, 3, STATE_SIGN }, + { +1, 0, 0, 0, 0, 2, +1, 2, 2, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 2, +1, 2, 3, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 2, +1, 3, 1, STATE_DATA }, + { +1, 0, 0, 0, 0, 2, +1, 4, 0, STATE_START }, + { +1, 0, 0, 0, 0, 2, -1, 1, 2, STATE_DATA }, + { +1, 0, 0, 0, 0, 2, -1, 1, 2, STATE_SIGN }, + { +1, 0, 0, 0, 0, 2, -1, 1, 3, STATE_DATA }, + { +1, 0, 0, 0, 0, 2, -1, 1, 3, STATE_SIGN }, + { +1, 0, 0, 0, 0, 2, -1, 2, 2, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 2, -1, 2, 3, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 2, -1, 3, 1, STATE_DATA }, + { +1, 0, 0, 0, 0, 2, -1, 4, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, +1, 1, 4, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, +1, 1, 4, STATE_SIGN }, + { 0, 0, 0, 0, 0, 1, +1, 1, 5, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, +1, 1, 5, STATE_SIGN }, + { 0, +1, 0, 0, 0, 1, +1, 2, 1, STATE_DATA }, + { 0, +1, 0, 0, 0, 1, +1, 3, 0, STATE_START }, + { 0, -1, 0, 0, 0, 1, +1, 2, 1, STATE_DATA }, + { 0, -1, 0, 0, 0, 1, +1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, +1, 1, 6, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, +1, 1, 6, STATE_SIGN }, + { 0, 0, 0, 0, 0, 1, +1, 1, 7, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, +1, 1, 7, STATE_SIGN }, + { 0, +2, 0, 0, 0, 1, +1, 2, 1, STATE_DATA }, + { 0, +2, 0, 0, 0, 1, +1, 3, 0, STATE_START }, + { 0, -2, 0, 0, 0, 1, +1, 2, 1, STATE_DATA }, + { 0, -2, 0, 0, 0, 1, +1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, +1, 2, 4, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, +1, 2, 5, STATE_FOLLOW }, + { 0, 0, +1, 0, 0, 1, +1, 3, 0, STATE_START }, + { 0, 0, -1, 0, 0, 1, +1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, +1, 2, 6, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, +1, 2, 7, STATE_FOLLOW }, + { 0, 0, +2, 0, 0, 1, +1, 3, 0, STATE_START }, + { 0, 0, -2, 0, 0, 1, +1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, +1, 3, 2, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, +1, 3, 2, STATE_SIGN }, + { 0, 0, 0, 0, 0, 1, +1, 3, 3, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, +1, 3, 3, STATE_SIGN }, + { 0, 0, 0, 0, 0, 1, +1, 4, 2, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, +1, 4, 3, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, +1, 5, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, +1, 6, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, -1, 1, 4, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, -1, 1, 4, STATE_SIGN }, + { 0, 0, 0, 0, 0, 1, -1, 1, 5, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, -1, 1, 5, STATE_SIGN }, + { 0, +1, 0, 0, 0, 1, -1, 2, 1, STATE_DATA }, + { 0, +1, 0, 0, 0, 1, -1, 3, 0, STATE_START }, + { 0, -1, 0, 0, 0, 1, -1, 2, 1, STATE_DATA }, + { 0, -1, 0, 0, 0, 1, -1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, -1, 1, 6, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, -1, 1, 6, STATE_SIGN }, + { 0, 0, 0, 0, 0, 1, -1, 1, 7, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, -1, 1, 7, STATE_SIGN }, + { 0, +2, 0, 0, 0, 1, -1, 2, 1, STATE_DATA }, + { 0, +2, 0, 0, 0, 1, -1, 3, 0, STATE_START }, + { 0, -2, 0, 0, 0, 1, -1, 2, 1, STATE_DATA }, + { 0, -2, 0, 0, 0, 1, -1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, -1, 2, 4, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, -1, 2, 5, STATE_FOLLOW }, + { 0, 0, +1, 0, 0, 1, -1, 3, 0, STATE_START }, + { 0, 0, -1, 0, 0, 1, -1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, -1, 2, 6, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, -1, 2, 7, STATE_FOLLOW }, + { 0, 0, +2, 0, 0, 1, -1, 3, 0, STATE_START }, + { 0, 0, -2, 0, 0, 1, -1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 1, -1, 3, 2, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, -1, 3, 2, STATE_SIGN }, + { 0, 0, 0, 0, 0, 1, -1, 3, 3, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, -1, 3, 3, STATE_SIGN }, + { 0, 0, 0, 0, 0, 1, -1, 4, 2, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, -1, 4, 3, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 1, -1, 5, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 1, -1, 6, 0, STATE_START }, + { +8, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +8, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +9, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +9, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +4, 0, 0, 0, 0, 3, +1, 1, 1, STATE_DATA }, + { +4, 0, 0, 0, 0, 3, +1, 2, 0, STATE_START }, + { +4, 0, 0, 0, 0, 3, -1, 1, 1, STATE_DATA }, + { +4, 0, 0, 0, 0, 3, -1, 2, 0, STATE_START }, + { +10, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +10, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +11, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +11, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +5, 0, 0, 0, 0, 3, +1, 1, 1, STATE_DATA }, + { +5, 0, 0, 0, 0, 3, +1, 2, 0, STATE_START }, + { +5, 0, 0, 0, 0, 3, -1, 1, 1, STATE_DATA }, + { +5, 0, 0, 0, 0, 3, -1, 2, 0, STATE_START }, + { +2, 0, 0, 0, 0, 2, +1, 1, 2, STATE_DATA }, + { +2, 0, 0, 0, 0, 2, +1, 1, 2, STATE_SIGN }, + { +2, 0, 0, 0, 0, 2, +1, 1, 3, STATE_DATA }, + { +2, 0, 0, 0, 0, 2, +1, 1, 3, STATE_SIGN }, + { +2, 0, 0, 0, 0, 2, +1, 2, 2, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, +1, 2, 3, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, +1, 3, 1, STATE_DATA }, + { +2, 0, 0, 0, 0, 2, +1, 4, 0, STATE_START }, + { +2, 0, 0, 0, 0, 2, -1, 1, 2, STATE_DATA }, + { +2, 0, 0, 0, 0, 2, -1, 1, 2, STATE_SIGN }, + { +2, 0, 0, 0, 0, 2, -1, 1, 3, STATE_DATA }, + { +2, 0, 0, 0, 0, 2, -1, 1, 3, STATE_SIGN }, + { +2, 0, 0, 0, 0, 2, -1, 2, 2, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, -1, 2, 3, STATE_FOLLOW }, + { +2, 0, 0, 0, 0, 2, -1, 3, 1, STATE_DATA }, + { +2, 0, 0, 0, 0, 2, -1, 4, 0, STATE_START }, + { +12, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +12, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +13, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +13, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +6, 0, 0, 0, 0, 3, +1, 1, 1, STATE_DATA }, + { +6, 0, 0, 0, 0, 3, +1, 2, 0, STATE_START }, + { +6, 0, 0, 0, 0, 3, -1, 1, 1, STATE_DATA }, + { +6, 0, 0, 0, 0, 3, -1, 2, 0, STATE_START }, + { +14, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +14, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +15, 0, 0, 0, 0, 4, +1, 0, 0, STATE_DATA }, + { +15, 0, 0, 0, 0, 4, +1, 0, 0, STATE_SIGN }, + { +7, 0, 0, 0, 0, 3, +1, 1, 1, STATE_DATA }, + { +7, 0, 0, 0, 0, 3, +1, 2, 0, STATE_START }, + { +7, 0, 0, 0, 0, 3, -1, 1, 1, STATE_DATA }, + { +7, 0, 0, 0, 0, 3, -1, 2, 0, STATE_START }, + { +3, 0, 0, 0, 0, 2, +1, 1, 2, STATE_DATA }, + { +3, 0, 0, 0, 0, 2, +1, 1, 2, STATE_SIGN }, + { +3, 0, 0, 0, 0, 2, +1, 1, 3, STATE_DATA }, + { +3, 0, 0, 0, 0, 2, +1, 1, 3, STATE_SIGN }, + { +3, 0, 0, 0, 0, 2, +1, 2, 2, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, +1, 2, 3, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, +1, 3, 1, STATE_DATA }, + { +3, 0, 0, 0, 0, 2, +1, 4, 0, STATE_START }, + { +3, 0, 0, 0, 0, 2, -1, 1, 2, STATE_DATA }, + { +3, 0, 0, 0, 0, 2, -1, 1, 2, STATE_SIGN }, + { +3, 0, 0, 0, 0, 2, -1, 1, 3, STATE_DATA }, + { +3, 0, 0, 0, 0, 2, -1, 1, 3, STATE_SIGN }, + { +3, 0, 0, 0, 0, 2, -1, 2, 2, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, -1, 2, 3, STATE_FOLLOW }, + { +3, 0, 0, 0, 0, 2, -1, 3, 1, STATE_DATA }, + { +3, 0, 0, 0, 0, 2, -1, 4, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, +1, 1, 4, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, +1, 1, 4, STATE_SIGN }, + { +1, 0, 0, 0, 0, 1, +1, 1, 5, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, +1, 1, 5, STATE_SIGN }, + { +1, +1, 0, 0, 0, 1, +1, 2, 1, STATE_DATA }, + { +1, +1, 0, 0, 0, 1, +1, 3, 0, STATE_START }, + { +1, -1, 0, 0, 0, 1, +1, 2, 1, STATE_DATA }, + { +1, -1, 0, 0, 0, 1, +1, 3, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, +1, 1, 6, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, +1, 1, 6, STATE_SIGN }, + { +1, 0, 0, 0, 0, 1, +1, 1, 7, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, +1, 1, 7, STATE_SIGN }, + { +1, +2, 0, 0, 0, 1, +1, 2, 1, STATE_DATA }, + { +1, +2, 0, 0, 0, 1, +1, 3, 0, STATE_START }, + { +1, -2, 0, 0, 0, 1, +1, 2, 1, STATE_DATA }, + { +1, -2, 0, 0, 0, 1, +1, 3, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, +1, 2, 4, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, +1, 2, 5, STATE_FOLLOW }, + { +1, 0, +1, 0, 0, 1, +1, 3, 0, STATE_START }, + { +1, 0, -1, 0, 0, 1, +1, 3, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, +1, 2, 6, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, +1, 2, 7, STATE_FOLLOW }, + { +1, 0, +2, 0, 0, 1, +1, 3, 0, STATE_START }, + { +1, 0, -2, 0, 0, 1, +1, 3, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, +1, 3, 2, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, +1, 3, 2, STATE_SIGN }, + { +1, 0, 0, 0, 0, 1, +1, 3, 3, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, +1, 3, 3, STATE_SIGN }, + { +1, 0, 0, 0, 0, 1, +1, 4, 2, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, +1, 4, 3, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, +1, 5, 1, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, +1, 6, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, -1, 1, 4, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, -1, 1, 4, STATE_SIGN }, + { +1, 0, 0, 0, 0, 1, -1, 1, 5, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, -1, 1, 5, STATE_SIGN }, + { +1, +1, 0, 0, 0, 1, -1, 2, 1, STATE_DATA }, + { +1, +1, 0, 0, 0, 1, -1, 3, 0, STATE_START }, + { +1, -1, 0, 0, 0, 1, -1, 2, 1, STATE_DATA }, + { +1, -1, 0, 0, 0, 1, -1, 3, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, -1, 1, 6, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, -1, 1, 6, STATE_SIGN }, + { +1, 0, 0, 0, 0, 1, -1, 1, 7, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, -1, 1, 7, STATE_SIGN }, + { +1, +2, 0, 0, 0, 1, -1, 2, 1, STATE_DATA }, + { +1, +2, 0, 0, 0, 1, -1, 3, 0, STATE_START }, + { +1, -2, 0, 0, 0, 1, -1, 2, 1, STATE_DATA }, + { +1, -2, 0, 0, 0, 1, -1, 3, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, -1, 2, 4, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, -1, 2, 5, STATE_FOLLOW }, + { +1, 0, +1, 0, 0, 1, -1, 3, 0, STATE_START }, + { +1, 0, -1, 0, 0, 1, -1, 3, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, -1, 2, 6, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, -1, 2, 7, STATE_FOLLOW }, + { +1, 0, +2, 0, 0, 1, -1, 3, 0, STATE_START }, + { +1, 0, -2, 0, 0, 1, -1, 3, 0, STATE_START }, + { +1, 0, 0, 0, 0, 1, -1, 3, 2, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, -1, 3, 2, STATE_SIGN }, + { +1, 0, 0, 0, 0, 1, -1, 3, 3, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, -1, 3, 3, STATE_SIGN }, + { +1, 0, 0, 0, 0, 1, -1, 4, 2, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, -1, 4, 3, STATE_FOLLOW }, + { +1, 0, 0, 0, 0, 1, -1, 5, 1, STATE_DATA }, + { +1, 0, 0, 0, 0, 1, -1, 6, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 1, 8, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 1, 8, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, +1, 1, 9, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 1, 9, STATE_SIGN }, + { 0, +3, 0, 0, 0, 0, +1, 2, 1, STATE_DATA }, + { 0, +3, 0, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, -3, 0, 0, 0, 0, +1, 2, 1, STATE_DATA }, + { 0, -3, 0, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 1, 10, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 1, 10, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, +1, 1, 11, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 1, 11, STATE_SIGN }, + { 0, +4, 0, 0, 0, 0, +1, 2, 1, STATE_DATA }, + { 0, +4, 0, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, -4, 0, 0, 0, 0, +1, 2, 1, STATE_DATA }, + { 0, -4, 0, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, +1, 0, 0, 0, 0, +1, 2, 2, STATE_DATA }, + { 0, +1, 0, 0, 0, 0, +1, 2, 2, STATE_SIGN }, + { 0, +1, 0, 0, 0, 0, +1, 2, 3, STATE_DATA }, + { 0, +1, 0, 0, 0, 0, +1, 2, 3, STATE_SIGN }, + { 0, +1, 0, 0, 0, 0, +1, 3, 2, STATE_FOLLOW }, + { 0, +1, 0, 0, 0, 0, +1, 3, 3, STATE_FOLLOW }, + { 0, +1, 0, 0, 0, 0, +1, 4, 1, STATE_DATA }, + { 0, +1, 0, 0, 0, 0, +1, 5, 0, STATE_START }, + { 0, -1, 0, 0, 0, 0, +1, 2, 2, STATE_DATA }, + { 0, -1, 0, 0, 0, 0, +1, 2, 2, STATE_SIGN }, + { 0, -1, 0, 0, 0, 0, +1, 2, 3, STATE_DATA }, + { 0, -1, 0, 0, 0, 0, +1, 2, 3, STATE_SIGN }, + { 0, -1, 0, 0, 0, 0, +1, 3, 2, STATE_FOLLOW }, + { 0, -1, 0, 0, 0, 0, +1, 3, 3, STATE_FOLLOW }, + { 0, -1, 0, 0, 0, 0, +1, 4, 1, STATE_DATA }, + { 0, -1, 0, 0, 0, 0, +1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 1, 12, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 1, 12, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, +1, 1, 13, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 1, 13, STATE_SIGN }, + { 0, +5, 0, 0, 0, 0, +1, 2, 1, STATE_DATA }, + { 0, +5, 0, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, -5, 0, 0, 0, 0, +1, 2, 1, STATE_DATA }, + { 0, -5, 0, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 1, 14, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 1, 14, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, +1, 1, 15, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 1, 15, STATE_SIGN }, + { 0, +6, 0, 0, 0, 0, +1, 2, 1, STATE_DATA }, + { 0, +6, 0, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, -6, 0, 0, 0, 0, +1, 2, 1, STATE_DATA }, + { 0, -6, 0, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, +2, 0, 0, 0, 0, +1, 2, 2, STATE_DATA }, + { 0, +2, 0, 0, 0, 0, +1, 2, 2, STATE_SIGN }, + { 0, +2, 0, 0, 0, 0, +1, 2, 3, STATE_DATA }, + { 0, +2, 0, 0, 0, 0, +1, 2, 3, STATE_SIGN }, + { 0, +2, 0, 0, 0, 0, +1, 3, 2, STATE_FOLLOW }, + { 0, +2, 0, 0, 0, 0, +1, 3, 3, STATE_FOLLOW }, + { 0, +2, 0, 0, 0, 0, +1, 4, 1, STATE_DATA }, + { 0, +2, 0, 0, 0, 0, +1, 5, 0, STATE_START }, + { 0, -2, 0, 0, 0, 0, +1, 2, 2, STATE_DATA }, + { 0, -2, 0, 0, 0, 0, +1, 2, 2, STATE_SIGN }, + { 0, -2, 0, 0, 0, 0, +1, 2, 3, STATE_DATA }, + { 0, -2, 0, 0, 0, 0, +1, 2, 3, STATE_SIGN }, + { 0, -2, 0, 0, 0, 0, +1, 3, 2, STATE_FOLLOW }, + { 0, -2, 0, 0, 0, 0, +1, 3, 3, STATE_FOLLOW }, + { 0, -2, 0, 0, 0, 0, +1, 4, 1, STATE_DATA }, + { 0, -2, 0, 0, 0, 0, +1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 2, 8, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 2, 9, STATE_FOLLOW }, + { 0, 0, +3, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, 0, -3, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 2, 10, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 2, 11, STATE_FOLLOW }, + { 0, 0, +4, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, 0, -4, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, 0, +1, 0, 0, 0, +1, 3, 2, STATE_FOLLOW }, + { 0, 0, +1, 0, 0, 0, +1, 3, 3, STATE_FOLLOW }, + { 0, 0, +1, 0, 0, 0, +1, 4, 1, STATE_DATA }, + { 0, 0, +1, 0, 0, 0, +1, 5, 0, STATE_START }, + { 0, 0, -1, 0, 0, 0, +1, 3, 2, STATE_FOLLOW }, + { 0, 0, -1, 0, 0, 0, +1, 3, 3, STATE_FOLLOW }, + { 0, 0, -1, 0, 0, 0, +1, 4, 1, STATE_DATA }, + { 0, 0, -1, 0, 0, 0, +1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 2, 12, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 2, 13, STATE_FOLLOW }, + { 0, 0, +5, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, 0, -5, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 2, 14, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 2, 15, STATE_FOLLOW }, + { 0, 0, +6, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, 0, -6, 0, 0, 0, +1, 3, 0, STATE_START }, + { 0, 0, +2, 0, 0, 0, +1, 3, 2, STATE_FOLLOW }, + { 0, 0, +2, 0, 0, 0, +1, 3, 3, STATE_FOLLOW }, + { 0, 0, +2, 0, 0, 0, +1, 4, 1, STATE_DATA }, + { 0, 0, +2, 0, 0, 0, +1, 5, 0, STATE_START }, + { 0, 0, -2, 0, 0, 0, +1, 3, 2, STATE_FOLLOW }, + { 0, 0, -2, 0, 0, 0, +1, 3, 3, STATE_FOLLOW }, + { 0, 0, -2, 0, 0, 0, +1, 4, 1, STATE_DATA }, + { 0, 0, -2, 0, 0, 0, +1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 3, 4, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 3, 4, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, +1, 3, 5, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 3, 5, STATE_SIGN }, + { 0, 0, 0, +1, 0, 0, +1, 4, 1, STATE_DATA }, + { 0, 0, 0, +1, 0, 0, +1, 5, 0, STATE_START }, + { 0, 0, 0, -1, 0, 0, +1, 4, 1, STATE_DATA }, + { 0, 0, 0, -1, 0, 0, +1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 3, 6, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 3, 6, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, +1, 3, 7, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 3, 7, STATE_SIGN }, + { 0, 0, 0, +2, 0, 0, +1, 4, 1, STATE_DATA }, + { 0, 0, 0, +2, 0, 0, +1, 5, 0, STATE_START }, + { 0, 0, 0, -2, 0, 0, +1, 4, 1, STATE_DATA }, + { 0, 0, 0, -2, 0, 0, +1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 4, 4, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 4, 5, STATE_FOLLOW }, + { 0, 0, 0, 0, +1, 0, +1, 5, 0, STATE_START }, + { 0, 0, 0, 0, -1, 0, +1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 4, 6, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 4, 7, STATE_FOLLOW }, + { 0, 0, 0, 0, +2, 0, +1, 5, 0, STATE_START }, + { 0, 0, 0, 0, -2, 0, +1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, +1, 5, 2, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 5, 2, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, +1, 5, 3, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 5, 3, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, +1, 6, 2, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 6, 3, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, +1, 7, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, +1, 8, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 1, 8, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 1, 8, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, -1, 1, 9, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 1, 9, STATE_SIGN }, + { 0, +3, 0, 0, 0, 0, -1, 2, 1, STATE_DATA }, + { 0, +3, 0, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, -3, 0, 0, 0, 0, -1, 2, 1, STATE_DATA }, + { 0, -3, 0, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 1, 10, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 1, 10, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, -1, 1, 11, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 1, 11, STATE_SIGN }, + { 0, +4, 0, 0, 0, 0, -1, 2, 1, STATE_DATA }, + { 0, +4, 0, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, -4, 0, 0, 0, 0, -1, 2, 1, STATE_DATA }, + { 0, -4, 0, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, +1, 0, 0, 0, 0, -1, 2, 2, STATE_DATA }, + { 0, +1, 0, 0, 0, 0, -1, 2, 2, STATE_SIGN }, + { 0, +1, 0, 0, 0, 0, -1, 2, 3, STATE_DATA }, + { 0, +1, 0, 0, 0, 0, -1, 2, 3, STATE_SIGN }, + { 0, +1, 0, 0, 0, 0, -1, 3, 2, STATE_FOLLOW }, + { 0, +1, 0, 0, 0, 0, -1, 3, 3, STATE_FOLLOW }, + { 0, +1, 0, 0, 0, 0, -1, 4, 1, STATE_DATA }, + { 0, +1, 0, 0, 0, 0, -1, 5, 0, STATE_START }, + { 0, -1, 0, 0, 0, 0, -1, 2, 2, STATE_DATA }, + { 0, -1, 0, 0, 0, 0, -1, 2, 2, STATE_SIGN }, + { 0, -1, 0, 0, 0, 0, -1, 2, 3, STATE_DATA }, + { 0, -1, 0, 0, 0, 0, -1, 2, 3, STATE_SIGN }, + { 0, -1, 0, 0, 0, 0, -1, 3, 2, STATE_FOLLOW }, + { 0, -1, 0, 0, 0, 0, -1, 3, 3, STATE_FOLLOW }, + { 0, -1, 0, 0, 0, 0, -1, 4, 1, STATE_DATA }, + { 0, -1, 0, 0, 0, 0, -1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 1, 12, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 1, 12, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, -1, 1, 13, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 1, 13, STATE_SIGN }, + { 0, +5, 0, 0, 0, 0, -1, 2, 1, STATE_DATA }, + { 0, +5, 0, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, -5, 0, 0, 0, 0, -1, 2, 1, STATE_DATA }, + { 0, -5, 0, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 1, 14, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 1, 14, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, -1, 1, 15, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 1, 15, STATE_SIGN }, + { 0, +6, 0, 0, 0, 0, -1, 2, 1, STATE_DATA }, + { 0, +6, 0, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, -6, 0, 0, 0, 0, -1, 2, 1, STATE_DATA }, + { 0, -6, 0, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, +2, 0, 0, 0, 0, -1, 2, 2, STATE_DATA }, + { 0, +2, 0, 0, 0, 0, -1, 2, 2, STATE_SIGN }, + { 0, +2, 0, 0, 0, 0, -1, 2, 3, STATE_DATA }, + { 0, +2, 0, 0, 0, 0, -1, 2, 3, STATE_SIGN }, + { 0, +2, 0, 0, 0, 0, -1, 3, 2, STATE_FOLLOW }, + { 0, +2, 0, 0, 0, 0, -1, 3, 3, STATE_FOLLOW }, + { 0, +2, 0, 0, 0, 0, -1, 4, 1, STATE_DATA }, + { 0, +2, 0, 0, 0, 0, -1, 5, 0, STATE_START }, + { 0, -2, 0, 0, 0, 0, -1, 2, 2, STATE_DATA }, + { 0, -2, 0, 0, 0, 0, -1, 2, 2, STATE_SIGN }, + { 0, -2, 0, 0, 0, 0, -1, 2, 3, STATE_DATA }, + { 0, -2, 0, 0, 0, 0, -1, 2, 3, STATE_SIGN }, + { 0, -2, 0, 0, 0, 0, -1, 3, 2, STATE_FOLLOW }, + { 0, -2, 0, 0, 0, 0, -1, 3, 3, STATE_FOLLOW }, + { 0, -2, 0, 0, 0, 0, -1, 4, 1, STATE_DATA }, + { 0, -2, 0, 0, 0, 0, -1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 2, 8, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 2, 9, STATE_FOLLOW }, + { 0, 0, +3, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, 0, -3, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 2, 10, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 2, 11, STATE_FOLLOW }, + { 0, 0, +4, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, 0, -4, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, 0, +1, 0, 0, 0, -1, 3, 2, STATE_FOLLOW }, + { 0, 0, +1, 0, 0, 0, -1, 3, 3, STATE_FOLLOW }, + { 0, 0, +1, 0, 0, 0, -1, 4, 1, STATE_DATA }, + { 0, 0, +1, 0, 0, 0, -1, 5, 0, STATE_START }, + { 0, 0, -1, 0, 0, 0, -1, 3, 2, STATE_FOLLOW }, + { 0, 0, -1, 0, 0, 0, -1, 3, 3, STATE_FOLLOW }, + { 0, 0, -1, 0, 0, 0, -1, 4, 1, STATE_DATA }, + { 0, 0, -1, 0, 0, 0, -1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 2, 12, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 2, 13, STATE_FOLLOW }, + { 0, 0, +5, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, 0, -5, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 2, 14, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 2, 15, STATE_FOLLOW }, + { 0, 0, +6, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, 0, -6, 0, 0, 0, -1, 3, 0, STATE_START }, + { 0, 0, +2, 0, 0, 0, -1, 3, 2, STATE_FOLLOW }, + { 0, 0, +2, 0, 0, 0, -1, 3, 3, STATE_FOLLOW }, + { 0, 0, +2, 0, 0, 0, -1, 4, 1, STATE_DATA }, + { 0, 0, +2, 0, 0, 0, -1, 5, 0, STATE_START }, + { 0, 0, -2, 0, 0, 0, -1, 3, 2, STATE_FOLLOW }, + { 0, 0, -2, 0, 0, 0, -1, 3, 3, STATE_FOLLOW }, + { 0, 0, -2, 0, 0, 0, -1, 4, 1, STATE_DATA }, + { 0, 0, -2, 0, 0, 0, -1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 3, 4, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 3, 4, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, -1, 3, 5, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 3, 5, STATE_SIGN }, + { 0, 0, 0, +1, 0, 0, -1, 4, 1, STATE_DATA }, + { 0, 0, 0, +1, 0, 0, -1, 5, 0, STATE_START }, + { 0, 0, 0, -1, 0, 0, -1, 4, 1, STATE_DATA }, + { 0, 0, 0, -1, 0, 0, -1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 3, 6, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 3, 6, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, -1, 3, 7, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 3, 7, STATE_SIGN }, + { 0, 0, 0, +2, 0, 0, -1, 4, 1, STATE_DATA }, + { 0, 0, 0, +2, 0, 0, -1, 5, 0, STATE_START }, + { 0, 0, 0, -2, 0, 0, -1, 4, 1, STATE_DATA }, + { 0, 0, 0, -2, 0, 0, -1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 4, 4, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 4, 5, STATE_FOLLOW }, + { 0, 0, 0, 0, +1, 0, -1, 5, 0, STATE_START }, + { 0, 0, 0, 0, -1, 0, -1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 4, 6, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 4, 7, STATE_FOLLOW }, + { 0, 0, 0, 0, +2, 0, -1, 5, 0, STATE_START }, + { 0, 0, 0, 0, -2, 0, -1, 5, 0, STATE_START }, + { 0, 0, 0, 0, 0, 0, -1, 5, 2, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 5, 2, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, -1, 5, 3, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 5, 3, STATE_SIGN }, + { 0, 0, 0, 0, 0, 0, -1, 6, 2, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 6, 3, STATE_FOLLOW }, + { 0, 0, 0, 0, 0, 0, -1, 7, 1, STATE_DATA }, + { 0, 0, 0, 0, 0, 0, -1, 8, 0, STATE_START }, +}; - return c_idx; -} +#define PROCESS_VALS \ + do { \ + val <<= lut.val0_bits; \ + val |= lut.val0; \ + dst[0] = (val - 1) * lut.sign; \ + dst[1] = lut.val1; \ + dst[2] = lut.val2; \ + dst[3] = lut.val3; \ + dst[4] = lut.val4; \ + dst[5] = 0; \ + dst[6] = 0; \ + dst[7] = 0; \ + if (lut.num) \ + val = lut.val; \ + dst += lut.num; \ + if (dst >= last) \ + return coeffs; \ + lut = ff_dirac_golomb_lut[lut.state + *buf++]; \ + } while (0) -int ff_dirac_golomb_read_16bit(DiracGolombLUT *lut_ctx, const uint8_t *buf, - int bytes, uint8_t *_dst, int coeffs) +int ff_dirac_golomb_read_16bit(const uint8_t *buf, int bytes, + uint8_t *_dst, int coeffs) { - int i, b, c_idx = 0; - int16_t *dst = (int16_t *)_dst; - DiracGolombLUT *future[4], *l = &lut_ctx[2*LUT_SIZE + buf[0]]; - INIT_RESIDUE(res); - - for (b = 1; b <= bytes; b++) { - future[0] = &lut_ctx[buf[b]]; - future[1] = future[0] + 1*LUT_SIZE; - future[2] = future[0] + 2*LUT_SIZE; - future[3] = future[0] + 3*LUT_SIZE; - - if ((c_idx + 1) > coeffs) - return c_idx; + LUTState lut = ff_dirac_golomb_lut[*buf++]; + int16_t *dst = (int16_t *)_dst, *last = dst + coeffs; + uint16_t val = 0; - if (res_bits && l->sign) { - int32_t coeff = 1; - APPEND_RESIDUE(res, l->preamble); - for (i = 0; i < (res_bits >> 1) - 1; i++) { - coeff <<= 1; - coeff |= (res >> (RSIZE_BITS - 2*i - 2)) & 1; - } - dst[c_idx++] = l->sign * (coeff - 1); - res_bits = res = 0; - } + for (int i = 1; i < bytes; i++) + PROCESS_VALS; - for (i = 0; i < LUT_BITS; i++) - dst[c_idx + i] = l->ready[i]; - c_idx += l->ready_num; + /* Reader needs to be flushed */ + PROCESS_VALS; - APPEND_RESIDUE(res, l->leftover); + /* Still short of coeffs - try to guess and at least output what we have */ + if (lut.state != STATE_START) + *dst++ = -((lut.state != STATE_SIGN ? (val << 1) | 1 : val) - 1); - l = future[l->need_s ? 3 : !res_bits ? 2 : res_bits & 1]; - } - - return c_idx; + return coeffs - (int)(last - dst); } -/* Searches for golomb codes in a residue */ -static inline void search_for_golomb(DiracGolombLUT *l, residual r, int bits) +int ff_dirac_golomb_read_32bit(const uint8_t *buf, int bytes, + uint8_t *_dst, int coeffs) { - int r_count = RSIZE_BITS - 1; - int bits_start, bits_tot = bits, need_sign = 0; - -#define READ_BIT(N) (((N) >> (N ## _count--)) & 1) - - while (1) { - int32_t coef = 1; - bits_start = (RSIZE_BITS - 1) - r_count; + LUTState lut = ff_dirac_golomb_lut[*buf++]; + int32_t *dst = (int32_t *)_dst, *last = dst + coeffs; + uint32_t val = 0; - while (1) { - if (!bits--) - goto leftover; - if (READ_BIT(r)) - break; + for (int i = 1; i < bytes; i++) + PROCESS_VALS; - coef <<= 1; + /* Reader needs to be flushed */ + PROCESS_VALS; - if (!bits--) - goto leftover; - coef |= READ_BIT(r); - } + /* Still short of coeffs - try to guess and at least output what we have */ + if (lut.state != STATE_START) + *dst++ = -((lut.state != STATE_SIGN ? (val << 1) | 1 : val) - 1); - l->ready[l->ready_num] = coef - 1; - if (l->ready[l->ready_num]) { - if (!bits--) { - need_sign = 1; - goto leftover; - } - l->ready[l->ready_num] *= READ_BIT(r) ? -1 : +1; - } - l->ready_num++; - - if (!bits) - return; - } - - leftover: - l->leftover = r << bits_start; - l->leftover_bits = bits_tot - bits_start; - l->need_s = need_sign; -} - -/* Parity LUTs - even and odd bit end positions */ -static void generate_parity_lut(DiracGolombLUT *lut, int even) -{ - int idx; - for (idx = 0; idx < LUT_SIZE; idx++) { - DiracGolombLUT *l = &lut[idx]; - int symbol_end_loc = -1; - uint32_t code; - int i; - - INIT_RESIDUE(res); - SET_RESIDUE(res, idx, LUT_BITS); - - for (i = 0; i < LUT_BITS; i++) { - const int cond = even ? (i & 1) : !(i & 1); - if (((res >> (RSIZE_BITS - i - 1)) & 1) && cond) { - symbol_end_loc = i + 2; - break; - } - } - - if (symbol_end_loc < 0 || symbol_end_loc > LUT_BITS) { - l->preamble = 0; - l->preamble_bits = 0; - l->leftover_bits = LUT_BITS; - l->leftover = CONVERT_TO_RESIDUE(idx, l->leftover_bits); - if (even) - l->need_s = idx & 1; - continue; - } - - /* Gets bits 0 through to (symbol_end_loc - 1) inclusive */ - code = idx >> ((LUT_BITS - 1) - (symbol_end_loc - 1)); - code &= ((1 << LUT_BITS) - 1) >> (LUT_BITS - symbol_end_loc); - l->preamble_bits = symbol_end_loc; - l->preamble = CONVERT_TO_RESIDUE(code, l->preamble_bits); - l->sign = ((l->preamble >> (RSIZE_BITS - l->preamble_bits)) & 1) ? -1 : +1; - - search_for_golomb(l, res << symbol_end_loc, LUT_BITS - symbol_end_loc); - } -} - -/* Reset (off == 0) and needs-one-more-bit (off == 1) LUTs */ -static void generate_offset_lut(DiracGolombLUT *lut, int off) -{ - int idx; - for (idx = 0; idx < LUT_SIZE; idx++) { - DiracGolombLUT *l = &lut[idx]; - - INIT_RESIDUE(res); - SET_RESIDUE(res, idx, LUT_BITS); - - l->preamble_bits = off; - if (off) { - l->preamble = CONVERT_TO_RESIDUE(res >> (RSIZE_BITS - off), off); - l->sign = ((l->preamble >> (RSIZE_BITS - l->preamble_bits)) & 1) ? -1 : +1; - } else { - l->preamble = 0; - l->sign = 1; - } - - search_for_golomb(l, res << off, LUT_BITS - off); - } -} - -av_cold int ff_dirac_golomb_reader_init(DiracGolombLUT **lut_ctx) -{ - DiracGolombLUT *lut; - - if (!(lut = av_calloc(4*LUT_SIZE, sizeof(DiracGolombLUT)))) - return AVERROR(ENOMEM); - - generate_parity_lut(&lut[0*LUT_SIZE], 0); - generate_parity_lut(&lut[1*LUT_SIZE], 1); - generate_offset_lut(&lut[2*LUT_SIZE], 0); - generate_offset_lut(&lut[3*LUT_SIZE], 1); - - *lut_ctx = lut; - - return 0; -} - -av_cold void ff_dirac_golomb_reader_end(DiracGolombLUT **lut_ctx) -{ - av_freep(lut_ctx); + return coeffs - (int)(last - dst); } diff --git a/libavcodec/dirac_vlc.h b/libavcodec/dirac_vlc.h index 42ae41b00a7..bfcfa136a11 100644 --- a/libavcodec/dirac_vlc.h +++ b/libavcodec/dirac_vlc.h @@ -1,7 +1,4 @@ /* - * Copyright (C) 2016 Open Broadcast Systems Ltd. - * Author 2016 Rostislav Pehlivanov - * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -24,28 +21,9 @@ #include "libavutil/avutil.h" -/* Can be 32 bits wide for some performance gain on some machines, but it will - * incorrectly decode very long coefficients (usually only 1 or 2 per frame) */ -typedef uint64_t residual; - -#define LUT_BITS 8 - -/* Exactly 64 bytes */ -typedef struct DiracGolombLUT { - residual preamble, leftover; - int32_t ready[LUT_BITS]; - int32_t preamble_bits, leftover_bits, ready_num; - int8_t need_s, sign; -} DiracGolombLUT; - -av_cold int ff_dirac_golomb_reader_init(DiracGolombLUT **lut_ctx); - -int ff_dirac_golomb_read_32bit(DiracGolombLUT *lut_ctx, const uint8_t *buf, - int bytes, uint8_t *dst, int coeffs); - -int ff_dirac_golomb_read_16bit(DiracGolombLUT *lut_ctx, const uint8_t *buf, - int bytes, uint8_t *_dst, int coeffs); - -av_cold void ff_dirac_golomb_reader_end(DiracGolombLUT **lut_ctx); +int ff_dirac_golomb_read_16bit(const uint8_t *buf, int bytes, + uint8_t *_dst, int coeffs); +int ff_dirac_golomb_read_32bit(const uint8_t *buf, int bytes, + uint8_t *_dst, int coeffs); #endif /* AVCODEC_DIRAC_VLC_H */ diff --git a/libavcodec/diracdec.c b/libavcodec/diracdec.c index 22ec913bf7d..ed42bc366a9 100644 --- a/libavcodec/diracdec.c +++ b/libavcodec/diracdec.c @@ -136,7 +136,6 @@ typedef struct DiracContext { MpegvideoEncDSPContext mpvencdsp; VideoDSPContext vdsp; DiracDSPContext diracdsp; - DiracGolombLUT *reader_ctx; DiracVersionInfo version; GetBitContext gb; AVDiracSeqHeader seq; @@ -395,7 +394,6 @@ static av_cold int dirac_decode_init(AVCodecContext *avctx) s->threads_num_buf = -1; s->thread_buf_size = -1; - ff_dirac_golomb_reader_init(&s->reader_ctx); ff_diracdsp_init(&s->diracdsp); ff_mpegvideoencdsp_init(&s->mpvencdsp, avctx); ff_videodsp_init(&s->vdsp, 8); @@ -428,8 +426,6 @@ static av_cold int dirac_decode_end(AVCodecContext *avctx) DiracContext *s = avctx->priv_data; int i; - ff_dirac_golomb_reader_end(&s->reader_ctx); - dirac_decode_flush(avctx); for (i = 0; i < MAX_FRAMES; i++) av_frame_free(&s->all_frames[i].avframe); @@ -881,11 +877,11 @@ static int decode_hq_slice(DiracContext *s, DiracSlice *slice, uint8_t *tmp_buf) coef_num = subband_coeffs(s, slice->slice_x, slice->slice_y, i, coeffs_num); if (s->pshift) - coef_par = ff_dirac_golomb_read_32bit(s->reader_ctx, addr, - length, tmp_buf, coef_num); + coef_par = ff_dirac_golomb_read_32bit(addr, length, + tmp_buf, coef_num); else - coef_par = ff_dirac_golomb_read_16bit(s->reader_ctx, addr, - length, tmp_buf, coef_num); + coef_par = ff_dirac_golomb_read_16bit(addr, length, + tmp_buf, coef_num); if (coef_num > coef_par) { const int start_b = coef_par * (1 << (s->pshift + 1)); diff --git a/libavcodec/dnxhddec.c b/libavcodec/dnxhddec.c index 1e950866967..e5d01e2e71e 100644 --- a/libavcodec/dnxhddec.c +++ b/libavcodec/dnxhddec.c @@ -25,7 +25,6 @@ */ #include "libavutil/imgutils.h" -#include "libavutil/timer.h" #include "avcodec.h" #include "blockdsp.h" #define UNCHECKED_BITSTREAM_READER 1 @@ -145,21 +144,6 @@ static int dnxhd_init_vlc(DNXHDContext *ctx, uint32_t cid, int bitdepth) return 0; } -static av_cold int dnxhd_decode_init_thread_copy(AVCodecContext *avctx) -{ - DNXHDContext *ctx = avctx->priv_data; - - ctx->avctx = avctx; - // make sure VLC tables will be loaded when cid is parsed - ctx->cid = -1; - - ctx->rows = av_mallocz_array(avctx->thread_count, sizeof(RowContext)); - if (!ctx->rows) - return AVERROR(ENOMEM); - - return 0; -} - static int dnxhd_get_profile(int cid) { switch(cid) { @@ -235,7 +219,14 @@ static int dnxhd_decode_header(DNXHDContext *ctx, AVFrame *frame, av_log(ctx->avctx, AV_LOG_WARNING, "Adaptive MB interlace flag in an unsupported profile.\n"); - ctx->act = buf[0x2C] & 7; + switch ((buf[0x2C] >> 1) & 3) { + case 0: frame->colorspace = AVCOL_SPC_BT709; break; + case 1: frame->colorspace = AVCOL_SPC_BT2020_NCL; break; + case 2: frame->colorspace = AVCOL_SPC_BT2020_CL; break; + case 3: frame->colorspace = AVCOL_SPC_UNSPECIFIED; break; + } + + ctx->act = buf[0x2C] & 1; if (ctx->act && ctx->cid_table->cid != 1256 && ctx->cid_table->cid != 1270) av_log(ctx->avctx, AV_LOG_WARNING, "Adaptive color transform in an unsupported profile.\n"); @@ -600,13 +591,11 @@ static int dnxhd_decode_row(AVCodecContext *avctx, void *data, return ret; } for (x = 0; x < ctx->mb_width; x++) { - //START_TIMER; int ret = dnxhd_decode_macroblock(ctx, row, data, x, rownb); if (ret < 0) { row->errors++; return ret; } - //STOP_TIMER("decode macroblock"); } return 0; @@ -736,6 +725,5 @@ AVCodec ff_dnxhd_decoder = { .decode = dnxhd_decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(dnxhd_decode_init_thread_copy), .profiles = NULL_IF_CONFIG_SMALL(ff_dnxhd_profiles), }; diff --git a/libavcodec/dnxhdenc.c b/libavcodec/dnxhdenc.c index 41b8079a09c..5d5f1ffc0aa 100644 --- a/libavcodec/dnxhdenc.c +++ b/libavcodec/dnxhdenc.c @@ -26,7 +26,6 @@ #include "libavutil/attributes.h" #include "libavutil/internal.h" #include "libavutil/opt.h" -#include "libavutil/timer.h" #include "avcodec.h" #include "blockdsp.h" @@ -34,6 +33,7 @@ #include "internal.h" #include "mpegvideo.h" #include "pixblockdsp.h" +#include "packet_internal.h" #include "profiles.h" #include "dnxhdenc.h" @@ -220,7 +220,7 @@ static av_cold int dnxhd_init_vlc(DNXHDEncContext *ctx) ctx->vlc_bits = ctx->orig_vlc_bits + max_level * 2; for (level = -max_level; level < max_level; level++) { for (run = 0; run < 2; run++) { - int index = (level << 1) | run; + int index = level * (1 << 1) | run; int sign, offset = 0, alevel = level; MASK_ABS(sign, alevel); @@ -542,6 +542,8 @@ FF_ENABLE_DEPRECATION_WARNINGS if (avctx->active_thread_type == FF_THREAD_SLICE) { for (i = 1; i < avctx->thread_count; i++) { ctx->thread[i] = av_malloc(sizeof(DNXHDEncContext)); + if (!ctx->thread[i]) + goto fail; memcpy(ctx->thread[i], ctx, sizeof(DNXHDEncContext)); } } @@ -616,7 +618,7 @@ void dnxhd_encode_block(DNXHDEncContext *ctx, int16_t *block, slevel = block[j]; if (slevel) { int run_level = i - last_non_zero - 1; - int rlevel = (slevel << 1) | !!run_level; + int rlevel = slevel * (1 << 1) | !!run_level; put_bits(&ctx->m.pb, ctx->vlc_bits[rlevel], ctx->vlc_codes[rlevel]); if (run_level) put_bits(&ctx->m.pb, ctx->run_bits[run_level], @@ -696,7 +698,7 @@ int dnxhd_calc_ac_bits(DNXHDEncContext *ctx, int16_t *block, int last_index) level = block[j]; if (level) { int run_level = i - last_non_zero - 1; - bits += ctx->vlc_bits[(level << 1) | + bits += ctx->vlc_bits[level * (1 << 1) | !!run_level] + ctx->run_bits[run_level]; last_non_zero = i; } @@ -931,9 +933,8 @@ static int dnxhd_encode_thread(AVCodecContext *avctx, void *arg, int last_index = ctx->m.dct_quantize(&ctx->m, block, ctx->is_444 ? (((i >> 1) % 3) < 1 ? 0 : 4): 4 & (2*i), qscale, &overflow); - // START_TIMER; + dnxhd_encode_block(ctx, block, last_index, n); - // STOP_TIMER("encode_block"); } } if (put_bits_count(&ctx->m.pb) & 31) @@ -1396,7 +1397,7 @@ AVCodec ff_dnxhd_encoder = { .init = dnxhd_encode_init, .encode2 = dnxhd_encode_picture, .close = dnxhd_encode_end, - .capabilities = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV422P, diff --git a/libavcodec/dpcm.c b/libavcodec/dpcm.c index 7d3934ee35f..7078419f080 100644 --- a/libavcodec/dpcm.c +++ b/libavcodec/dpcm.c @@ -49,6 +49,21 @@ typedef struct DPCMContext { const int8_t *sol_table; ///< delta table for SOL_DPCM } DPCMContext; +static const int32_t derf_steps[96] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 16, + 17, 19, 21, 23, 25, 28, 31, 34, + 37, 41, 45, 50, 55, 60, 66, 73, + 80, 88, 97, 107, 118, 130, 143, 157, + 173, 190, 209, 230, 253, 279, 307, 337, + 371, 408, 449, 494, 544, 598, 658, 724, + 796, 876, 963, 1060, 1166, 1282, 1411, 1552, + 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, + 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, + 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, + 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767, +}; + static const int16_t interplay_delta_table[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, @@ -225,6 +240,7 @@ static int dpcm_decode_frame(AVCodecContext *avctx, void *data, else out = buf_size; break; + case AV_CODEC_ID_DERF_DPCM: case AV_CODEC_ID_GREMLIN_DPCM: case AV_CODEC_ID_SDX2_DPCM: out = buf_size; @@ -305,9 +321,8 @@ static int dpcm_decode_frame(AVCodecContext *avctx, void *data, shift[ch] -= (2 * n); diff = sign_extend((diff &~ 3) << 8, 16); - /* saturate the shifter to a lower limit of 0 */ - if (shift[ch] < 0) - shift[ch] = 0; + /* saturate the shifter to 0..31 */ + shift[ch] = av_clip_uintp2(shift[ch], 5); diff >>= shift[ch]; predictor[ch] += diff; @@ -367,11 +382,26 @@ static int dpcm_decode_frame(AVCodecContext *avctx, void *data, while (output_samples < samples_end) { uint8_t n = bytestream2_get_byteu(&gb); - *output_samples++ = s->sample[idx] += s->array[n]; + *output_samples++ = s->sample[idx] += (unsigned)s->array[n]; idx ^= 1; } } break; + + case AV_CODEC_ID_DERF_DPCM: { + int idx = 0; + + while (output_samples < samples_end) { + uint8_t n = bytestream2_get_byteu(&gb); + int index = FFMIN(n & 0x7f, 95); + + s->sample[idx] += (n & 0x80 ? -1: 1) * derf_steps[index]; + s->sample[idx] = av_clip_int16(s->sample[idx]); + *output_samples++ = s->sample[idx]; + idx ^= stereo; + } + } + break; } *got_frame_ptr = 1; @@ -391,6 +421,7 @@ AVCodec ff_ ## name_ ## _decoder = { \ .capabilities = AV_CODEC_CAP_DR1, \ } +DPCM_DECODER(AV_CODEC_ID_DERF_DPCM, derf_dpcm, "DPCM Xilam DERF"); DPCM_DECODER(AV_CODEC_ID_GREMLIN_DPCM, gremlin_dpcm, "DPCM Gremlin"); DPCM_DECODER(AV_CODEC_ID_INTERPLAY_DPCM, interplay_dpcm, "DPCM Interplay"); DPCM_DECODER(AV_CODEC_ID_ROQ_DPCM, roq_dpcm, "DPCM id RoQ"); diff --git a/libavcodec/dsd.c b/libavcodec/dsd.c index 9104f384767..d48f87fa0ff 100644 --- a/libavcodec/dsd.c +++ b/libavcodec/dsd.c @@ -53,26 +53,29 @@ av_cold void ff_init_dsd_data(void) } void ff_dsd2pcm_translate(DSDContext* s, size_t samples, int lsbf, - const unsigned char *src, ptrdiff_t src_stride, + const uint8_t *src, ptrdiff_t src_stride, float *dst, ptrdiff_t dst_stride) { + uint8_t buf[FIFOSIZE]; unsigned pos, i; - unsigned char* p; + uint8_t* p; double sum; pos = s->pos; + memcpy(buf, s->buf, sizeof(buf)); + while (samples-- > 0) { - s->buf[pos] = lsbf ? ff_reverse[*src] : *src; + buf[pos] = lsbf ? ff_reverse[*src] : *src; src += src_stride; - p = s->buf + ((pos - CTABLES) & FIFOMASK); + p = buf + ((pos - CTABLES) & FIFOMASK); *p = ff_reverse[*p]; sum = 0.0; for (i = 0; i < CTABLES; i++) { - unsigned char a = s->buf[(pos - i) & FIFOMASK]; - unsigned char b = s->buf[(pos - (CTABLES*2 - 1) + i) & FIFOMASK]; + uint8_t a = buf[(pos - i) & FIFOMASK]; + uint8_t b = buf[(pos - (CTABLES*2 - 1) + i) & FIFOMASK]; sum += ctables[i][a] + ctables[i][b]; } @@ -83,4 +86,5 @@ void ff_dsd2pcm_translate(DSDContext* s, size_t samples, int lsbf, } s->pos = pos; + memcpy(s->buf, buf, sizeof(buf)); } diff --git a/libavcodec/dsd.h b/libavcodec/dsd.h index 5ca45748375..ed09cb9b12c 100644 --- a/libavcodec/dsd.h +++ b/libavcodec/dsd.h @@ -40,13 +40,13 @@ * Per-channel buffer */ typedef struct DSDContext { - unsigned char buf[FIFOSIZE]; + uint8_t buf[FIFOSIZE]; unsigned pos; } DSDContext; void ff_init_dsd_data(void); void ff_dsd2pcm_translate(DSDContext* s, size_t samples, int lsbf, - const unsigned char *src, ptrdiff_t src_stride, + const uint8_t *src, ptrdiff_t src_stride, float *dst, ptrdiff_t dst_stride); #endif /* AVCODEC_DSD_H */ diff --git a/libavcodec/dsddec.c b/libavcodec/dsddec.c index 2c5c357acc5..39837a5ad97 100644 --- a/libavcodec/dsddec.c +++ b/libavcodec/dsddec.c @@ -44,6 +44,9 @@ static av_cold int decode_init(AVCodecContext *avctx) int i; uint8_t silence; + if (!avctx->channels) + return AVERROR_INVALIDDATA; + ff_init_dsd_data(); s = av_malloc_array(sizeof(DSDContext), avctx->channels); @@ -61,17 +64,20 @@ static av_cold int decode_init(AVCodecContext *avctx) return 0; } -static int decode_frame(AVCodecContext *avctx, void *data, - int *got_frame_ptr, AVPacket *avpkt) +typedef struct ThreadData { + AVFrame *frame; + AVPacket *avpkt; +} ThreadData; + +static int dsd_channel(AVCodecContext *avctx, void *tdata, int j, int threadnr) { - DSDContext * s = avctx->priv_data; - AVFrame *frame = data; - int ret, i; int lsbf = avctx->codec_id == AV_CODEC_ID_DSD_LSBF || avctx->codec_id == AV_CODEC_ID_DSD_LSBF_PLANAR; - int src_next; - int src_stride; - - frame->nb_samples = avpkt->size / avctx->channels; + DSDContext *s = avctx->priv_data; + ThreadData *td = tdata; + AVFrame *frame = td->frame; + AVPacket *avpkt = td->avpkt; + int src_next, src_stride; + float *dst = ((float **)frame->extended_data)[j]; if (avctx->codec_id == AV_CODEC_ID_DSD_LSBF_PLANAR || avctx->codec_id == AV_CODEC_ID_DSD_MSBF_PLANAR) { src_next = frame->nb_samples; @@ -81,15 +87,28 @@ static int decode_frame(AVCodecContext *avctx, void *data, src_stride = avctx->channels; } + ff_dsd2pcm_translate(&s[j], frame->nb_samples, lsbf, + avpkt->data + j * src_next, src_stride, + dst, 1); + + return 0; +} + +static int decode_frame(AVCodecContext *avctx, void *data, + int *got_frame_ptr, AVPacket *avpkt) +{ + ThreadData td; + AVFrame *frame = data; + int ret; + + frame->nb_samples = avpkt->size / avctx->channels; + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) return ret; - for (i = 0; i < avctx->channels; i++) { - float * dst = ((float **)frame->extended_data)[i]; - ff_dsd2pcm_translate(&s[i], frame->nb_samples, lsbf, - avpkt->data + i * src_next, src_stride, - dst, 1); - } + td.frame = frame; + td.avpkt = avpkt; + avctx->execute2(avctx, dsd_channel, &td, NULL, avctx->channels); *got_frame_ptr = 1; return frame->nb_samples * avctx->channels; @@ -103,6 +122,7 @@ AVCodec ff_##name_##_decoder = { \ .id = AV_CODEC_ID_##id_, \ .init = decode_init, \ .decode = decode_frame, \ + .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS, \ .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLTP, \ AV_SAMPLE_FMT_NONE }, \ }; diff --git a/libavcodec/dsicinvideo.c b/libavcodec/dsicinvideo.c index d422df068df..7f74808e6db 100644 --- a/libavcodec/dsicinvideo.c +++ b/libavcodec/dsicinvideo.c @@ -290,7 +290,7 @@ static int cinvideo_decode_frame(AVCodecContext *avctx, break; } - if ((res = ff_reget_buffer(avctx, cin->frame)) < 0) + if ((res = ff_reget_buffer(avctx, cin->frame, 0)) < 0) return res; memcpy(cin->frame->data[1], cin->palette, sizeof(cin->palette)); diff --git a/libavcodec/dstdec.c b/libavcodec/dstdec.c index 0614c99c4bb..41e761d7e56 100644 --- a/libavcodec/dstdec.c +++ b/libavcodec/dstdec.c @@ -37,7 +37,7 @@ #define DST_MAX_CHANNELS 6 #define DST_MAX_ELEMENTS (2 * DST_MAX_CHANNELS) -#define DSD_FS44(sample_rate) (sample_rate * 8 / 44100) +#define DSD_FS44(sample_rate) (sample_rate * 8LL / 44100) #define DST_SAMPLES_PER_FRAME(sample_rate) (588 * DSD_FS44(sample_rate)) @@ -85,6 +85,16 @@ static av_cold int decode_init(AVCodecContext *avctx) return AVERROR_PATCHWELCOME; } + // the sample rate is only allowed to be 64,128,256 * 44100 by ISO/IEC 14496-3:2005(E) + // We are a bit more tolerant here, but this check is needed to bound the size and duration + if (avctx->sample_rate > 512 * 44100) + return AVERROR_INVALIDDATA; + + + if (DST_SAMPLES_PER_FRAME(avctx->sample_rate) & 7) { + return AVERROR_PATCHWELCOME; + } + avctx->sample_fmt = AV_SAMPLE_FMT_FLT; for (i = 0; i < avctx->channels; i++) @@ -120,7 +130,7 @@ static int read_map(GetBitContext *gb, Table *t, unsigned int map[DST_MAX_CHANNE static av_always_inline int get_sr_golomb_dst(GetBitContext *gb, unsigned int k) { - int v = get_ur_golomb(gb, k, get_bits_left(gb), 0); + int v = get_ur_golomb_jpegls(gb, k, get_bits_left(gb), 0); if (v && get_bits1(gb)) v = -v; return v; @@ -155,12 +165,16 @@ static int read_table(GetBitContext *gb, Table *t, const int8_t code_pred_coeff[ for (j = method + 1; j < t->length[i]; j++) { int c, x = 0; for (k = 0; k < method + 1; k++) - x += code_pred_coeff[method][k] * t->coeff[i][j - k - 1]; + x += code_pred_coeff[method][k] * (unsigned)t->coeff[i][j - k - 1]; c = get_sr_golomb_dst(gb, lsb_size); if (x >= 0) c -= (x + 4) / 8; else c += (-x + 3) / 8; + if (!is_signed) { + if (c < offset || c >= offset + (1<coeff[i][j] = c; } } @@ -254,7 +268,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, skip_bits1(gb); if (get_bits(gb, 6)) return AVERROR_INVALIDDATA; - memcpy(frame->data[0], avpkt->data + 1, FFMIN(avpkt->size - 1, frame->nb_samples * avctx->channels)); + memcpy(frame->data[0], avpkt->data + 1, FFMIN(avpkt->size - 1, frame->nb_samples * channels)); goto dsd; } @@ -279,7 +293,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, same_map = get_bits1(gb); - if ((ret = read_map(gb, &s->fsets, map_ch_to_felem, avctx->channels)) < 0) + if ((ret = read_map(gb, &s->fsets, map_ch_to_felem, channels)) < 0) return ret; if (same_map) { @@ -287,22 +301,26 @@ static int decode_frame(AVCodecContext *avctx, void *data, memcpy(map_ch_to_pelem, map_ch_to_felem, sizeof(map_ch_to_felem)); } else { avpriv_request_sample(avctx, "Not Same Mapping"); - if ((ret = read_map(gb, &s->probs, map_ch_to_pelem, avctx->channels)) < 0) + if ((ret = read_map(gb, &s->probs, map_ch_to_pelem, channels)) < 0) return ret; } /* Half Probability (10.10) */ - for (ch = 0; ch < avctx->channels; ch++) + for (ch = 0; ch < channels; ch++) half_prob[ch] = get_bits1(gb); /* Filter Coef Sets (10.12) */ - read_table(gb, &s->fsets, fsets_code_pred_coeff, 7, 9, 1, 0); + ret = read_table(gb, &s->fsets, fsets_code_pred_coeff, 7, 9, 1, 0); + if (ret < 0) + return ret; /* Probability Tables (10.13) */ - read_table(gb, &s->probs, probs_code_pred_coeff, 6, 7, 0, 1); + ret = read_table(gb, &s->probs, probs_code_pred_coeff, 6, 7, 0, 1); + if (ret < 0) + return ret; /* Arithmetic Coded Data (10.11) */ @@ -313,7 +331,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, build_filter(s->filter, &s->fsets); memset(s->status, 0xAA, sizeof(s->status)); - memset(dsd, 0, frame->nb_samples * 4 * avctx->channels); + memset(dsd, 0, frame->nb_samples * 4 * channels); ac_get(ac, gb, prob_dst_x_bit(s->fsets.coeff[0][0]), &dst_x_bit); @@ -349,10 +367,10 @@ static int decode_frame(AVCodecContext *avctx, void *data, } dsd: - for (i = 0; i < avctx->channels; i++) { + for (i = 0; i < channels; i++) { ff_dsd2pcm_translate(&s->dsdctx[i], frame->nb_samples, 0, frame->data[0] + i * 4, - avctx->channels * 4, pcm + i, avctx->channels); + channels * 4, pcm + i, channels); } *got_frame_ptr = 1; diff --git a/libavcodec/dump_extradata_bsf.c b/libavcodec/dump_extradata_bsf.c index b6415082343..b6ef8b3e6b7 100644 --- a/libavcodec/dump_extradata_bsf.c +++ b/libavcodec/dump_extradata_bsf.c @@ -20,11 +20,10 @@ #include -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "libavutil/log.h" -#include "libavutil/mem.h" #include "libavutil/opt.h" enum DumpFreq { @@ -51,8 +50,8 @@ static int dump_extradata(AVBSFContext *ctx, AVPacket *out) if (ctx->par_in->extradata && (s->freq == DUMP_FREQ_ALL || (s->freq == DUMP_FREQ_KEYFRAME && in->flags & AV_PKT_FLAG_KEY)) && - in->size >= ctx->par_in->extradata_size && - memcmp(in->data, ctx->par_in->extradata, ctx->par_in->extradata_size)) { + (in->size < ctx->par_in->extradata_size || + memcmp(in->data, ctx->par_in->extradata, ctx->par_in->extradata_size))) { if (in->size >= INT_MAX - ctx->par_in->extradata_size) { ret = AVERROR(ERANGE); goto fail; diff --git a/libavcodec/dv.h b/libavcodec/dv.h index 0e97bb200ea..0205d723475 100644 --- a/libavcodec/dv.h +++ b/libavcodec/dv.h @@ -31,6 +31,7 @@ #include "dv_profile.h" #include "me_cmp.h" #include "vlc.h" +#include "idctdsp.h" typedef struct DVwork_chunk { uint16_t buf_offset; @@ -52,6 +53,7 @@ typedef struct DVVideoContext { me_cmp_func ildct_cmp; DVwork_chunk work_chunks[4 * 12 * 27]; uint32_t idct_factor[2 * 4 * 16 * 64]; + IDCTDSPContext idsp; int quant_deadzone; } DVVideoContext; @@ -81,6 +83,7 @@ enum dv_pack_type { #define DV_PROFILE_IS_HD(p) ((p)->video_stype & 0x10) #define DV_PROFILE_IS_1080i50(p) (((p)->video_stype == 0x14) && ((p)->dsf == 1)) +#define DV_PROFILE_IS_1080i60(p) (((p)->video_stype == 0x14) && ((p)->dsf == 0)) #define DV_PROFILE_IS_720p50(p) (((p)->video_stype == 0x18) && ((p)->dsf == 1)) /** diff --git a/libavcodec/dvbsub.c b/libavcodec/dvbsub.c index a8d43d81d6f..5c081f2b710 100644 --- a/libavcodec/dvbsub.c +++ b/libavcodec/dvbsub.c @@ -37,11 +37,11 @@ typedef struct DVBSubtitleContext { }\ } -static void dvb_encode_rle2(uint8_t **pq, - const uint8_t *bitmap, int linesize, - int w, int h) +static int dvb_encode_rle2(uint8_t **pq, int buf_size, + const uint8_t *bitmap, int linesize, + int w, int h) { - uint8_t *q; + uint8_t *q, *line_begin; unsigned int bitbuf; int bitcnt; int x, y, len, x1, v, color; @@ -49,6 +49,10 @@ static void dvb_encode_rle2(uint8_t **pq, q = *pq; for(y = 0; y < h; y++) { + // Worst case line is 3 bits per value + 4 bytes overhead + if (buf_size * 8 < w * 3 + 32) + return AVERROR_BUFFER_TOO_SMALL; + line_begin = q; *q++ = 0x10; bitbuf = 0; bitcnt = 6; @@ -109,8 +113,11 @@ static void dvb_encode_rle2(uint8_t **pq, } *q++ = 0xf0; bitmap += linesize; + buf_size -= q - line_begin; } + len = q - *pq; *pq = q; + return len; } #define PUTBITS4(val)\ @@ -125,11 +132,11 @@ static void dvb_encode_rle2(uint8_t **pq, } /* some DVB decoders only implement 4 bits/pixel */ -static void dvb_encode_rle4(uint8_t **pq, - const uint8_t *bitmap, int linesize, - int w, int h) +static int dvb_encode_rle4(uint8_t **pq, int buf_size, + const uint8_t *bitmap, int linesize, + int w, int h) { - uint8_t *q; + uint8_t *q, *line_begin; unsigned int bitbuf; int bitcnt; int x, y, len, x1, v, color; @@ -137,6 +144,10 @@ static void dvb_encode_rle4(uint8_t **pq, q = *pq; for(y = 0; y < h; y++) { + // Worst case line is 6 bits per value, + 4 bytes overhead + if (buf_size * 8 < w * 6 + 32) + return AVERROR_BUFFER_TOO_SMALL; + line_begin = q; *q++ = 0x11; bitbuf = 0; bitcnt = 4; @@ -189,20 +200,27 @@ static void dvb_encode_rle4(uint8_t **pq, } *q++ = 0xf0; bitmap += linesize; + buf_size -= q - line_begin; } + len = q - *pq; *pq = q; + return len; } -static void dvb_encode_rle8(uint8_t **pq, - const uint8_t *bitmap, int linesize, - int w, int h) +static int dvb_encode_rle8(uint8_t **pq, int buf_size, + const uint8_t *bitmap, int linesize, + int w, int h) { - uint8_t *q; + uint8_t *q, *line_begin; int x, y, len, x1, color; q = *pq; for (y = 0; y < h; y++) { + // Worst case line is 12 bits per value, + 3 bytes overhead + if (buf_size * 8 < w * 12 + 24) + return AVERROR_BUFFER_TOO_SMALL; + line_begin = q; *q++ = 0x12; x = 0; @@ -243,12 +261,16 @@ static void dvb_encode_rle8(uint8_t **pq, *q++ = 0x00; *q++ = 0xf0; bitmap += linesize; + buf_size -= q - line_begin; } + len = q - *pq; *pq = q; + return len; } static int encode_dvb_subtitles(AVCodecContext *avctx, - uint8_t *outbuf, const AVSubtitle *h) + uint8_t *outbuf, int buf_size, + const AVSubtitle *h) { DVBSubtitleContext *s = avctx->priv_data; uint8_t *q, *pseg_len; @@ -260,9 +282,11 @@ static int encode_dvb_subtitles(AVCodecContext *avctx, page_id = 1; if (h->num_rects && !h->rects) - return -1; + return AVERROR(EINVAL); if (avctx->width > 0 && avctx->height > 0) { + if (buf_size < 11) + return AVERROR_BUFFER_TOO_SMALL; /* display definition segment */ *q++ = 0x0f; /* sync_byte */ *q++ = 0x14; /* segment_type */ @@ -273,10 +297,13 @@ static int encode_dvb_subtitles(AVCodecContext *avctx, bytestream_put_be16(&q, avctx->width - 1); /* display width */ bytestream_put_be16(&q, avctx->height - 1); /* display height */ bytestream_put_be16(&pseg_len, q - pseg_len - 2); + buf_size -= 11; } /* page composition segment */ + if (buf_size < 8 + h->num_rects * 6) + return AVERROR_BUFFER_TOO_SMALL; *q++ = 0x0f; /* sync_byte */ *q++ = 0x10; /* segment_type */ bytestream_put_be16(&q, page_id); @@ -295,9 +322,12 @@ static int encode_dvb_subtitles(AVCodecContext *avctx, } bytestream_put_be16(&pseg_len, q - pseg_len - 2); + buf_size -= 8 + h->num_rects * 6; if (h->num_rects) { for (clut_id = 0; clut_id < h->num_rects; clut_id++) { + if (buf_size < 6 + h->rects[clut_id]->nb_colors * 6) + return AVERROR_BUFFER_TOO_SMALL; /* CLUT segment */ @@ -311,7 +341,7 @@ static int encode_dvb_subtitles(AVCodecContext *avctx, /* 8 bpp, standard encoding */ bpp_index = 2; } else { - return -1; + return AVERROR(EINVAL); } @@ -343,9 +373,12 @@ static int encode_dvb_subtitles(AVCodecContext *avctx, } bytestream_put_be16(&pseg_len, q - pseg_len - 2); + buf_size -= 6 + h->rects[clut_id]->nb_colors * 6; } } + if (buf_size < h->num_rects * 22) + return AVERROR_BUFFER_TOO_SMALL; for (region_id = 0; region_id < h->num_rects; region_id++) { /* region composition segment */ @@ -360,7 +393,7 @@ static int encode_dvb_subtitles(AVCodecContext *avctx, /* 8 bpp, standard encoding */ bpp_index = 2; } else { - return -1; + return AVERROR(EINVAL); } *q++ = 0x0f; /* sync_byte */ @@ -385,13 +418,17 @@ static int encode_dvb_subtitles(AVCodecContext *avctx, bytestream_put_be16(&pseg_len, q - pseg_len - 2); } + buf_size -= h->num_rects * 22; if (h->num_rects) { for (object_id = 0; object_id < h->num_rects; object_id++) { - void (*dvb_encode_rle)(uint8_t **pq, - const uint8_t *bitmap, int linesize, - int w, int h); + int (*dvb_encode_rle)(uint8_t **pq, int buf_size, + const uint8_t *bitmap, int linesize, + int w, int h); + + if (buf_size < 13) + return AVERROR_BUFFER_TOO_SMALL; /* bpp_index maths */ if (h->rects[object_id]->nb_colors <= 4) { @@ -404,7 +441,7 @@ static int encode_dvb_subtitles(AVCodecContext *avctx, /* 8 bpp, standard encoding */ dvb_encode_rle = dvb_encode_rle8; } else { - return -1; + return AVERROR(EINVAL); } /* Object Data segment */ @@ -420,19 +457,32 @@ static int encode_dvb_subtitles(AVCodecContext *avctx, non_modifying_color_flag */ { uint8_t *ptop_field_len, *pbottom_field_len, *top_ptr, *bottom_ptr; + int ret; ptop_field_len = q; q += 2; pbottom_field_len = q; q += 2; + buf_size -= 13; top_ptr = q; - dvb_encode_rle(&q, h->rects[object_id]->data[0], h->rects[object_id]->w * 2, - h->rects[object_id]->w, h->rects[object_id]->h >> 1); + ret = dvb_encode_rle(&q, buf_size, + h->rects[object_id]->data[0], + h->rects[object_id]->w * 2, + h->rects[object_id]->w, + h->rects[object_id]->h >> 1); + if (ret < 0) + return ret; + buf_size -= ret; bottom_ptr = q; - dvb_encode_rle(&q, h->rects[object_id]->data[0] + h->rects[object_id]->w, - h->rects[object_id]->w * 2, h->rects[object_id]->w, - h->rects[object_id]->h >> 1); + ret = dvb_encode_rle(&q, buf_size, + h->rects[object_id]->data[0] + h->rects[object_id]->w, + h->rects[object_id]->w * 2, + h->rects[object_id]->w, + h->rects[object_id]->h >> 1); + if (ret < 0) + return ret; + buf_size -= ret; bytestream_put_be16(&ptop_field_len, bottom_ptr - top_ptr); bytestream_put_be16(&pbottom_field_len, q - bottom_ptr); @@ -444,6 +494,8 @@ static int encode_dvb_subtitles(AVCodecContext *avctx, /* end of display set segment */ + if (buf_size < 6) + return AVERROR_BUFFER_TOO_SMALL; *q++ = 0x0f; /* sync_byte */ *q++ = 0x80; /* segment_type */ bytestream_put_be16(&q, page_id); @@ -451,6 +503,7 @@ static int encode_dvb_subtitles(AVCodecContext *avctx, q += 2; /* segment length */ bytestream_put_be16(&pseg_len, q - pseg_len - 2); + buf_size -= 6; s->object_version = (s->object_version + 1) & 0xf; return q - outbuf; @@ -462,7 +515,7 @@ static int dvbsub_encode(AVCodecContext *avctx, { int ret; - ret = encode_dvb_subtitles(avctx, buf, sub); + ret = encode_dvb_subtitles(avctx, buf, buf_size, sub); return ret; } diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c index 6e7e13b6eb2..f63a1f3bf69 100644 --- a/libavcodec/dvbsubdec.c +++ b/libavcodec/dvbsubdec.c @@ -1610,7 +1610,7 @@ static int dvbsub_display_end_segment(AVCodecContext *avctx, const uint8_t *buf, } static int dvbsub_decode(AVCodecContext *avctx, - void *data, int *data_size, + void *data, int *got_sub_ptr, AVPacket *avpkt) { const uint8_t *buf = avpkt->data; @@ -1668,7 +1668,7 @@ static int dvbsub_decode(AVCodecContext *avctx, int ret = 0; switch (segment_type) { case DVBSUB_PAGE_SEGMENT: - ret = dvbsub_parse_page_segment(avctx, p, segment_length, sub, data_size); + ret = dvbsub_parse_page_segment(avctx, p, segment_length, sub, got_sub_ptr); got_segment |= 1; break; case DVBSUB_REGION_SEGMENT: @@ -1690,7 +1690,7 @@ static int dvbsub_decode(AVCodecContext *avctx, got_dds = 1; break; case DVBSUB_DISPLAY_SEGMENT: - ret = dvbsub_display_end_segment(avctx, p, segment_length, sub, data_size); + ret = dvbsub_display_end_segment(avctx, p, segment_length, sub, got_sub_ptr); if (got_segment == 15 && !got_dds && !avctx->width && !avctx->height) { // Default from ETSI EN 300 743 V1.3.1 (7.2.1) avctx->width = 720; @@ -1713,12 +1713,12 @@ static int dvbsub_decode(AVCodecContext *avctx, // segments then we need no further data. if (got_segment == 15) { av_log(avctx, AV_LOG_DEBUG, "Missing display_end_segment, emulating\n"); - dvbsub_display_end_segment(avctx, p, 0, sub, data_size); + dvbsub_display_end_segment(avctx, p, 0, sub, got_sub_ptr); } end: if(ret < 0) { - *data_size = 0; + *got_sub_ptr = 0; avsubtitle_free(sub); return ret; } else { diff --git a/libavcodec/dvdec.c b/libavcodec/dvdec.c index 89864f2edc3..c526091eb4e 100644 --- a/libavcodec/dvdec.c +++ b/libavcodec/dvdec.c @@ -45,7 +45,6 @@ #include "dv_profile_internal.h" #include "dvdata.h" #include "get_bits.h" -#include "idctdsp.h" #include "internal.h" #include "put_bits.h" #include "simple_idct.h" @@ -177,24 +176,22 @@ static void dv_init_weight_tables(DVVideoContext *ctx, const AVDVProfile *d) static av_cold int dvvideo_decode_init(AVCodecContext *avctx) { DVVideoContext *s = avctx->priv_data; - IDCTDSPContext idsp; int i; - memset(&idsp,0, sizeof(idsp)); - ff_idctdsp_init(&idsp, avctx); + ff_idctdsp_init(&s->idsp, avctx); for (i = 0; i < 64; i++) - s->dv_zigzag[0][i] = idsp.idct_permutation[ff_zigzag_direct[i]]; + s->dv_zigzag[0][i] = s->idsp.idct_permutation[ff_zigzag_direct[i]]; if (avctx->lowres){ for (i = 0; i < 64; i++){ int j = ff_dv_zigzag248_direct[i]; - s->dv_zigzag[1][i] = idsp.idct_permutation[(j & 7) + (j & 8) * 4 + (j & 48) / 2]; + s->dv_zigzag[1][i] = s->idsp.idct_permutation[(j & 7) + (j & 8) * 4 + (j & 48) / 2]; } }else memcpy(s->dv_zigzag[1], ff_dv_zigzag248_direct, sizeof(s->dv_zigzag[1])); - s->idct_put[0] = idsp.idct_put; + s->idct_put[0] = s->idsp.idct_put; s->idct_put[1] = ff_simple_idct248_put; return ff_dvvideo_init(avctx); @@ -272,6 +269,48 @@ 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) +{ + int i, j; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 8; j++) + p[j] = av_clip_uint8(block[j]); + block += 8; + p += stride; + } +} + +static void dv100_idct_put_last_row_field_chroma(DVVideoContext *s, uint8_t *data, + int stride, int16_t *blocks) +{ + s->idsp.idct(blocks + 0*64); + s->idsp.idct(blocks + 1*64); + + put_block_8x4(blocks+0*64, data, stride<<1); + put_block_8x4(blocks+0*64 + 4*8, data + 8, stride<<1); + put_block_8x4(blocks+1*64, data + stride, stride<<1); + put_block_8x4(blocks+1*64 + 4*8, data + 8 + stride, stride<<1); +} + +static void dv100_idct_put_last_row_field_luma(DVVideoContext *s, uint8_t *data, + int stride, int16_t *blocks) +{ + s->idsp.idct(blocks + 0*64); + s->idsp.idct(blocks + 1*64); + s->idsp.idct(blocks + 2*64); + s->idsp.idct(blocks + 3*64); + + put_block_8x4(blocks+0*64, data, stride<<1); + put_block_8x4(blocks+0*64 + 4*8, data + 16, stride<<1); + put_block_8x4(blocks+1*64, data + 8, stride<<1); + put_block_8x4(blocks+1*64 + 4*8, data + 24, stride<<1); + put_block_8x4(blocks+2*64, data + stride, stride<<1); + put_block_8x4(blocks+2*64 + 4*8, data + 16 + stride, stride<<1); + put_block_8x4(blocks+3*64, data + 8 + stride, stride<<1); + put_block_8x4(blocks+3*64 + 4*8, data + 24 + stride, stride<<1); +} + /* mb_x and mb_y are in units of 8 pixels */ static int dv_decode_video_segment(AVCodecContext *avctx, void *arg) { @@ -443,14 +482,18 @@ static int dv_decode_video_segment(AVCodecContext *avctx, void *arg) } y_ptr = s->frame->data[0] + ((mb_y * s->frame->linesize[0] + mb_x) << log2_blocksize); - linesize = s->frame->linesize[0] << is_field_mode[mb_index]; - mb[0].idct_put(y_ptr, linesize, block + 0 * 64); - if (s->sys->video_stype == 4) { /* SD 422 */ - mb[2].idct_put(y_ptr + (1 << log2_blocksize), linesize, block + 2 * 64); + if (mb_y == 134 && is_field_mode[mb_index]) { + dv100_idct_put_last_row_field_luma(s, y_ptr, s->frame->linesize[0], block); } else { - mb[1].idct_put(y_ptr + (1 << log2_blocksize), linesize, block + 1 * 64); - mb[2].idct_put(y_ptr + y_stride, linesize, block + 2 * 64); - mb[3].idct_put(y_ptr + (1 << log2_blocksize) + y_stride, linesize, block + 3 * 64); + linesize = s->frame->linesize[0] << is_field_mode[mb_index]; + mb[0].idct_put(y_ptr, linesize, block + 0 * 64); + if (s->sys->video_stype == 4) { /* SD 422 */ + mb[2].idct_put(y_ptr + (1 << log2_blocksize), linesize, block + 2 * 64); + } else { + mb[1].idct_put(y_ptr + (1 << log2_blocksize), linesize, block + 1 * 64); + mb[2].idct_put(y_ptr + y_stride, linesize, block + 2 * 64); + mb[3].idct_put(y_ptr + (1 << log2_blocksize) + y_stride, linesize, block + 3 * 64); + } } mb += 4; block += 4 * 64; @@ -478,13 +521,19 @@ static int dv_decode_video_segment(AVCodecContext *avctx, void *arg) mb++; } else { y_stride = (mb_y == 134) ? (1 << log2_blocksize) : - s->frame->linesize[j] << ((!is_field_mode[mb_index]) * log2_blocksize); - linesize = s->frame->linesize[j] << is_field_mode[mb_index]; - (mb++)->idct_put(c_ptr, linesize, block); - block += 64; - if (s->sys->bpm == 8) { - (mb++)->idct_put(c_ptr + y_stride, linesize, block); + s->frame->linesize[j] << ((!is_field_mode[mb_index]) * log2_blocksize); + if (mb_y == 134 && is_field_mode[mb_index]) { + dv100_idct_put_last_row_field_chroma(s, c_ptr, s->frame->linesize[j], block); + mb += 2; + block += 2*64; + } else { + linesize = s->frame->linesize[j] << is_field_mode[mb_index]; + (mb++)->idct_put(c_ptr, linesize, block); block += 64; + if (s->sys->bpm == 8) { + (mb++)->idct_put(c_ptr + y_stride, linesize, block); + block += 64; + } } } } @@ -542,12 +591,19 @@ static int dvvideo_decode_frame(AVCodecContext *avctx, void *data, if ((ret = ff_thread_get_buffer(avctx, &frame, 0)) < 0) return ret; - frame.f->interlaced_frame = 1; - frame.f->top_field_first = 0; /* Determine the codec's field order from the packet */ if ( *vsc_pack == dv_video_control ) { - frame.f->top_field_first = !(vsc_pack[3] & 0x40); + if (avctx->height == 720) { + frame.f->interlaced_frame = 0; + frame.f->top_field_first = 0; + } else if (avctx->height == 1080) { + frame.f->interlaced_frame = 1; + frame.f->top_field_first = (vsc_pack[3] & 0x40) == 0x40; + } else { + frame.f->interlaced_frame = (vsc_pack[3] & 0x10) == 0x10; + frame.f->top_field_first = !(vsc_pack[3] & 0x40); + } } s->buf = buf; diff --git a/libavcodec/dvdsub.c b/libavcodec/dvdsub.c new file mode 100644 index 00000000000..87215d2bd12 --- /dev/null +++ b/libavcodec/dvdsub.c @@ -0,0 +1,33 @@ +/* + * DVD subtitle decoding/encoding + * Copyright (c) 2005 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 "internal.h" +#include "libavutil/avstring.h" +#include + +void ff_dvdsub_parse_palette(uint32_t *palette, const char *p) +{ + for (int i = 0; i < 16; i++) { + palette[i] = strtoul(p, (char **)&p, 16); + while (*p == ',' || av_isspace(*p)) + p++; + } +} diff --git a/libavcodec/dvdsubdec.c b/libavcodec/dvdsubdec.c index 741ea9fd1e7..bf49788e1b7 100644 --- a/libavcodec/dvdsubdec.c +++ b/libavcodec/dvdsubdec.c @@ -27,7 +27,6 @@ #include "libavutil/colorspace.h" #include "libavutil/opt.h" #include "libavutil/imgutils.h" -#include "libavutil/avstring.h" #include "libavutil/bswap.h" typedef struct DVDSubContext @@ -626,18 +625,6 @@ static int dvdsub_decode(AVCodecContext *avctx, return buf_size; } -static void parse_palette(DVDSubContext *ctx, char *p) -{ - int i; - - ctx->has_palette = 1; - for(i=0;i<16;i++) { - ctx->palette[i] = strtoul(p, &p, 16); - while(*p == ',' || av_isspace(*p)) - p++; - } -} - static int parse_ifo_palette(DVDSubContext *ctx, char *p) { FILE *ifo; @@ -719,7 +706,8 @@ static int dvdsub_parse_extradata(AVCodecContext *avctx) break; if (strncmp("palette:", data, 8) == 0) { - parse_palette(ctx, data + 8); + ctx->has_palette = 1; + ff_dvdsub_parse_palette(ctx->palette, data + 8); } else if (strncmp("size:", data, 5) == 0) { int w, h; if (sscanf(data + 5, "%dx%d", &w, &h) == 2) { @@ -748,8 +736,10 @@ static av_cold int dvdsub_init(AVCodecContext *avctx) if (ctx->ifo_str) parse_ifo_palette(ctx, ctx->ifo_str); - if (ctx->palette_str) - parse_palette(ctx, ctx->palette_str); + if (ctx->palette_str) { + ctx->has_palette = 1; + ff_dvdsub_parse_palette(ctx->palette, ctx->palette_str); + } if (ctx->has_palette) { int i; av_log(avctx, AV_LOG_DEBUG, "palette:"); diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c index ff95ed20026..e54b5f0d7bf 100644 --- a/libavcodec/dvdsubenc.c +++ b/libavcodec/dvdsubenc.c @@ -29,6 +29,7 @@ typedef struct { AVClass *class; uint32_t global_palette[16]; + char *palette_str; int even_rows_fix; } DVDSubtitleContext; @@ -436,7 +437,11 @@ static int dvdsub_init(AVCodecContext *avctx) int i, ret; av_assert0(sizeof(dvdc->global_palette) == sizeof(default_palette)); - memcpy(dvdc->global_palette, default_palette, sizeof(dvdc->global_palette)); + if (dvdc->palette_str) { + ff_dvdsub_parse_palette(dvdc->global_palette, dvdc->palette_str); + } else { + memcpy(dvdc->global_palette, default_palette, sizeof(dvdc->global_palette)); + } av_bprint_init(&extradata, 0, AV_BPRINT_SIZE_AUTOMATIC); if (avctx->width && avctx->height) @@ -467,6 +472,7 @@ static int dvdsub_encode(AVCodecContext *avctx, #define OFFSET(x) offsetof(DVDSubtitleContext, x) #define SE AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { + {"palette", "set the global palette", OFFSET(palette_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SE }, {"even_rows_fix", "Make number of rows even (workaround for some players)", OFFSET(even_rows_fix), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, SE}, { NULL }, }; diff --git a/libavcodec/dvenc.c b/libavcodec/dvenc.c index ce2fc75daa1..3e98d1b38cb 100644 --- a/libavcodec/dvenc.c +++ b/libavcodec/dvenc.c @@ -60,10 +60,7 @@ static av_cold int dvvideo_encode_init(AVCodecContext *avctx) ff_dv_print_profiles(avctx, AV_LOG_ERROR); return AVERROR(EINVAL); } - if (avctx->height > 576) { - av_log(avctx, AV_LOG_ERROR, "DVCPRO HD encoding is not supported.\n"); - return AVERROR_PATCHWELCOME; - } + ret = ff_dv_init_dynamic_tables(s, s->sys); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "Error initializing work tables.\n"); @@ -90,6 +87,7 @@ static av_cold int dvvideo_encode_init(AVCodecContext *avctx) } /* bit budget for AC only in 5 MBs */ +static const int vs_total_ac_bits_hd = (68 * 6 + 52*2) * 5; static const int vs_total_ac_bits = (100 * 4 + 68 * 2) * 5; static const int mb_area_start[5] = { 1, 6, 21, 43, 64 }; @@ -158,6 +156,11 @@ typedef struct EncBlockInfo { uint8_t sign[64]; uint8_t partial_bit_count; uint32_t partial_bit_buffer; /* we can't use uint16_t here */ + /* used by DV100 only: a copy of the weighted and classified but + not-yet-quantized AC coefficients. This is necessary for + re-quantizing at different steps. */ + int16_t save[64]; + int min_qlevel; /* DV100 only: minimum qlevel (for AC coefficients >255) */ } EncBlockInfo; static av_always_inline PutBitContext *dv_encode_ac(EncBlockInfo *bi, @@ -243,13 +246,123 @@ static const int dv_weight_248[64] = { 170627, 170627, 153560, 153560, 165371, 165371, 144651, 144651, }; -static av_always_inline int dv_init_enc_block(EncBlockInfo *bi, uint8_t *data, - ptrdiff_t linesize, - DVVideoContext *s, int bias) +/* setting this to 1 results in a faster codec but + * somewhat lower image quality */ +#define DV100_SACRIFICE_QUALITY_FOR_SPEED 1 +#define DV100_ENABLE_FINER 1 + +/* pack combination of QNO and CNO into a single 8-bit value */ +#define DV100_MAKE_QLEVEL(qno,cno) ((qno<<2) | (cno)) +#define DV100_QLEVEL_QNO(qlevel) (qlevel>>2) +#define DV100_QLEVEL_CNO(qlevel) (qlevel&0x3) + +#define DV100_NUM_QLEVELS 31 + +/* The quantization step is determined by a combination of QNO and + CNO. We refer to these combinations as "qlevels" (this term is our + own, it's not mentioned in the spec). We use CNO, a multiplier on + the quantization step, to "fill in the gaps" between quantization + steps associated with successive values of QNO. e.g. there is no + QNO for a quantization step of 10, but we can use QNO=5 CNO=1 to + get the same result. The table below encodes combinations of QNO + and CNO in order of increasing quantization coarseness. */ +static const uint8_t dv100_qlevels[DV100_NUM_QLEVELS] = { + DV100_MAKE_QLEVEL( 1,0), // 1*1= 1 + DV100_MAKE_QLEVEL( 1,0), // 1*1= 1 + DV100_MAKE_QLEVEL( 2,0), // 2*1= 2 + DV100_MAKE_QLEVEL( 3,0), // 3*1= 3 + DV100_MAKE_QLEVEL( 4,0), // 4*1= 4 + DV100_MAKE_QLEVEL( 5,0), // 5*1= 5 + DV100_MAKE_QLEVEL( 6,0), // 6*1= 6 + DV100_MAKE_QLEVEL( 7,0), // 7*1= 7 + DV100_MAKE_QLEVEL( 8,0), // 8*1= 8 + DV100_MAKE_QLEVEL( 5,1), // 5*2=10 + DV100_MAKE_QLEVEL( 6,1), // 6*2=12 + DV100_MAKE_QLEVEL( 7,1), // 7*2=14 + DV100_MAKE_QLEVEL( 9,0), // 16*1=16 + DV100_MAKE_QLEVEL(10,0), // 18*1=18 + DV100_MAKE_QLEVEL(11,0), // 20*1=20 + DV100_MAKE_QLEVEL(12,0), // 22*1=22 + DV100_MAKE_QLEVEL(13,0), // 24*1=24 + DV100_MAKE_QLEVEL(14,0), // 28*1=28 + DV100_MAKE_QLEVEL( 9,1), // 16*2=32 + DV100_MAKE_QLEVEL(10,1), // 18*2=36 + DV100_MAKE_QLEVEL(11,1), // 20*2=40 + DV100_MAKE_QLEVEL(12,1), // 22*2=44 + DV100_MAKE_QLEVEL(13,1), // 24*2=48 + DV100_MAKE_QLEVEL(15,0), // 52*1=52 + DV100_MAKE_QLEVEL(14,1), // 28*2=56 + DV100_MAKE_QLEVEL( 9,2), // 16*4=64 + DV100_MAKE_QLEVEL(10,2), // 18*4=72 + DV100_MAKE_QLEVEL(11,2), // 20*4=80 + DV100_MAKE_QLEVEL(12,2), // 22*4=88 + DV100_MAKE_QLEVEL(13,2), // 24*4=96 + // ... + DV100_MAKE_QLEVEL(15,3), // 52*8=416 +}; + +static const int dv100_min_bias = 0; +static const int dv100_chroma_bias = 0; +static const int dv100_starting_qno = 1; + +#if DV100_SACRIFICE_QUALITY_FOR_SPEED +static const int dv100_qlevel_inc = 4; +#else +static const int dv100_qlevel_inc = 1; +#endif + +// 1/qstep, shifted up by 16 bits +static const int dv100_qstep_bits = 16; +static const int dv100_qstep_inv[16] = { + 65536, 65536, 32768, 21845, 16384, 13107, 10923, 9362, 8192, 4096, 3641, 3277, 2979, 2731, 2341, 1260, +}; + +/* DV100 weights are pre-zigzagged, inverted and multiplied by 2^(dv100_weight_shift) + (in DV100 the AC components are divided by the spec weights) */ +static const int dv100_weight_shift = 16; +static const int dv_weight_1080[2][64] = { + { 8192, 65536, 65536, 61681, 61681, 61681, 58254, 58254, + 58254, 58254, 58254, 58254, 55188, 58254, 58254, 55188, + 55188, 55188, 55188, 55188, 55188, 24966, 27594, 26214, + 26214, 26214, 27594, 24966, 23831, 24385, 25575, 25575, + 25575, 25575, 24385, 23831, 23302, 23302, 24966, 24966, + 24966, 23302, 23302, 21845, 22795, 24385, 24385, 22795, + 21845, 21400, 21845, 23831, 21845, 21400, 10382, 10700, + 10700, 10382, 10082, 9620, 10082, 9039, 9039, 8525, }, + { 8192, 65536, 65536, 61681, 61681, 61681, 41943, 41943, + 41943, 41943, 40330, 41943, 40330, 41943, 40330, 40330, + 40330, 38836, 38836, 40330, 40330, 24966, 27594, 26214, + 26214, 26214, 27594, 24966, 23831, 24385, 25575, 25575, + 25575, 25575, 24385, 23831, 11523, 11523, 12483, 12483, + 12483, 11523, 11523, 10923, 11275, 12193, 12193, 11275, + 10923, 5323, 5490, 5924, 5490, 5323, 5165, 5323, + 5323, 5165, 5017, 4788, 5017, 4520, 4520, 4263, } +}; + +static const int dv_weight_720[2][64] = { + { 8192, 65536, 65536, 61681, 61681, 61681, 58254, 58254, + 58254, 58254, 58254, 58254, 55188, 58254, 58254, 55188, + 55188, 55188, 55188, 55188, 55188, 24966, 27594, 26214, + 26214, 26214, 27594, 24966, 23831, 24385, 25575, 25575, + 25575, 25575, 24385, 23831, 15420, 15420, 16644, 16644, + 16644, 15420, 15420, 10923, 11398, 12193, 12193, 11398, + 10923, 10700, 10923, 11916, 10923, 10700, 5191, 5350, + 5350, 5191, 5041, 4810, 5041, 4520, 4520, 4263, }, + { 8192, 43691, 43691, 40330, 40330, 40330, 29127, 29127, + 29127, 29127, 29127, 29127, 27594, 29127, 29127, 27594, + 27594, 27594, 27594, 27594, 27594, 12483, 13797, 13107, + 13107, 13107, 13797, 12483, 11916, 12193, 12788, 12788, + 12788, 12788, 12193, 11916, 5761, 5761, 6242, 6242, + 6242, 5761, 5761, 5461, 5638, 5461, 6096, 5638, + 5461, 2661, 2745, 2962, 2745, 2661, 2583, 2661, + 2661, 2583, 2509, 2394, 2509, 2260, 2260, 2131, } +}; + +static av_always_inline int dv_set_class_number_sd(DVVideoContext *s, + int16_t *blk, EncBlockInfo *bi, + const uint8_t *zigzag_scan, + const int *weight, int bias) { - const int *weight; - const uint8_t *zigzag_scan; - LOCAL_ALIGNED_16(int16_t, blk, [64]); int i, area; /* We offer two different methods for class number assignment: the * method suggested in SMPTE 314M Table 22, and an improved @@ -271,31 +384,8 @@ static av_always_inline int dv_init_enc_block(EncBlockInfo *bi, uint8_t *data, const unsigned deadzone = s->quant_deadzone; const unsigned threshold = 2 * deadzone; - av_assert2((((int) blk) & 15) == 0); - - bi->area_q[0] = - bi->area_q[1] = - bi->area_q[2] = - bi->area_q[3] = 0; - bi->partial_bit_count = 0; - bi->partial_bit_buffer = 0; - bi->cur_ac = 0; - if (data) { - bi->dct_mode = dv_guess_dct_mode(s, data, linesize); - s->get_pixels(blk, data, linesize); - s->fdct[bi->dct_mode](blk); - } else { - /* We rely on the fact that encoding all zeros leads to an immediate - * EOB, which is precisely what the spec calls for in the "dummy" - * blocks. */ - memset(blk, 0, 64 * sizeof(*blk)); - bi->dct_mode = 0; - } bi->mb[0] = blk[0]; - zigzag_scan = bi->dct_mode ? ff_dv_zigzag248_direct : ff_zigzag_direct; - weight = bi->dct_mode ? dv_weight_248 : dv_weight_88; - for (area = 0; area < 4; area++) { bi->prev[area] = prev; bi->bit_size[area] = 1; // 4 areas 4 bits for EOB :) @@ -350,6 +440,309 @@ static av_always_inline int dv_init_enc_block(EncBlockInfo *bi, uint8_t *data, bi->bit_size[2] + bi->bit_size[3]; } +/* this function just copies the DCT coefficients and performs + the initial (non-)quantization. */ +static inline void dv_set_class_number_hd(DVVideoContext *s, + int16_t *blk, EncBlockInfo *bi, + const uint8_t *zigzag_scan, + const int *weight, int bias) +{ + int i, max = 0; + + /* the first quantization (none at all) */ + bi->area_q[0] = 1; + + /* weigh AC components and store to save[] */ + /* (i=0 is the DC component; we only include it to make the + number of loop iterations even, for future possible SIMD optimization) */ + for (i = 0; i < 64; i += 2) { + int level0, level1; + + /* get the AC component (in zig-zag order) */ + level0 = blk[zigzag_scan[i+0]]; + level1 = blk[zigzag_scan[i+1]]; + + /* extract sign and make it the lowest bit */ + bi->sign[i+0] = (level0>>31)&1; + bi->sign[i+1] = (level1>>31)&1; + + /* take absolute value of the level */ + level0 = FFABS(level0); + level1 = FFABS(level1); + + /* weigh it */ + level0 = (level0*weight[i+0] + 4096 + (1<<17)) >> 18; + level1 = (level1*weight[i+1] + 4096 + (1<<17)) >> 18; + + /* save unquantized value */ + bi->save[i+0] = level0; + bi->save[i+1] = level1; + + /* find max component */ + if (bi->save[i+0] > max) + max = bi->save[i+0]; + if (bi->save[i+1] > max) + max = bi->save[i+1]; + } + + /* copy DC component */ + bi->mb[0] = blk[0]; + + /* the EOB code is 4 bits */ + bi->bit_size[0] = 4; + bi->bit_size[1] = bi->bit_size[2] = bi->bit_size[3] = 0; + + /* ensure that no AC coefficients are cut off */ + bi->min_qlevel = ((max+256) >> 8); + + bi->area_q[0] = 25; /* set to an "impossible" value */ + bi->cno = 0; +} + +static av_always_inline int dv_init_enc_block(EncBlockInfo* bi, uint8_t *data, int linesize, + DVVideoContext *s, int chroma) +{ + LOCAL_ALIGNED_16(int16_t, blk, [64]); + + bi->area_q[0] = bi->area_q[1] = bi->area_q[2] = bi->area_q[3] = 0; + bi->partial_bit_count = 0; + bi->partial_bit_buffer = 0; + bi->cur_ac = 0; + + if (data) { + if (DV_PROFILE_IS_HD(s->sys)) { + s->get_pixels(blk, data, linesize << bi->dct_mode); + s->fdct[0](blk); + } else { + bi->dct_mode = dv_guess_dct_mode(s, data, linesize); + s->get_pixels(blk, data, linesize); + s->fdct[bi->dct_mode](blk); + } + } else { + /* We rely on the fact that encoding all zeros leads to an immediate EOB, + which is precisely what the spec calls for in the "dummy" blocks. */ + memset(blk, 0, 64*sizeof(*blk)); + bi->dct_mode = 0; + } + + if (DV_PROFILE_IS_HD(s->sys)) { + const int *weights; + if (s->sys->height == 1080) { + weights = dv_weight_1080[chroma]; + } else { /* 720p */ + weights = dv_weight_720[chroma]; + } + dv_set_class_number_hd(s, blk, bi, + ff_zigzag_direct, + weights, + dv100_min_bias+chroma*dv100_chroma_bias); + } else { + dv_set_class_number_sd(s, blk, bi, + bi->dct_mode ? ff_dv_zigzag248_direct : ff_zigzag_direct, + bi->dct_mode ? dv_weight_248 : dv_weight_88, + chroma); + } + + return bi->bit_size[0] + bi->bit_size[1] + bi->bit_size[2] + bi->bit_size[3]; +} + +/* DV100 quantize + Perform quantization by divinding the AC component by the qstep. + As an optimization we use a fixed-point integer multiply instead + of a divide. */ +static av_always_inline int dv100_quantize(int level, int qsinv) +{ + /* this code is equivalent to */ + /* return (level + qs/2) / qs; */ + + return (level * qsinv + 1024 + (1<<(dv100_qstep_bits-1))) >> dv100_qstep_bits; + + /* the extra +1024 is needed to make the rounding come out right. */ + + /* I (DJM) have verified that the results are exactly the same as + division for level 0-2048 at all QNOs. */ +} + +static int dv100_actual_quantize(EncBlockInfo *b, int qlevel) +{ + int prev, k, qsinv; + + int qno = DV100_QLEVEL_QNO(dv100_qlevels[qlevel]); + int cno = DV100_QLEVEL_CNO(dv100_qlevels[qlevel]); + + if (b->area_q[0] == qno && b->cno == cno) + return b->bit_size[0]; + + qsinv = dv100_qstep_inv[qno]; + + /* record the new qstep */ + b->area_q[0] = qno; + b->cno = cno; + + /* reset encoded size (EOB = 4 bits) */ + b->bit_size[0] = 4; + + /* visit nonzero components and quantize */ + prev = 0; + for (k = 1; k < 64; k++) { + /* quantize */ + int ac = dv100_quantize(b->save[k], qsinv) >> cno; + if (ac) { + if (ac > 255) + ac = 255; + b->mb[k] = ac; + b->bit_size[0] += dv_rl2vlc_size(k - prev - 1, ac); + b->next[prev] = k; + prev = k; + } + } + b->next[prev] = k; + + return b->bit_size[0]; +} + +static inline void dv_guess_qnos_hd(EncBlockInfo *blks, int *qnos) +{ + EncBlockInfo *b; + int min_qlevel[5]; + int qlevels[5]; + int size[5]; + int i, j; + /* cache block sizes at hypothetical qlevels */ + uint16_t size_cache[5*8][DV100_NUM_QLEVELS] = {{0}}; + + /* get minimum qlevels */ + for (i = 0; i < 5; i++) { + min_qlevel[i] = 1; + for (j = 0; j < 8; j++) { + if (blks[8*i+j].min_qlevel > min_qlevel[i]) + min_qlevel[i] = blks[8*i+j].min_qlevel; + } + } + + /* initialize sizes */ + for (i = 0; i < 5; i++) { + qlevels[i] = dv100_starting_qno; + if (qlevels[i] < min_qlevel[i]) + qlevels[i] = min_qlevel[i]; + + qnos[i] = DV100_QLEVEL_QNO(dv100_qlevels[qlevels[i]]); + size[i] = 0; + for (j = 0; j < 8; j++) { + size_cache[8*i+j][qlevels[i]] = dv100_actual_quantize(&blks[8*i+j], qlevels[i]); + size[i] += size_cache[8*i+j][qlevels[i]]; + } + } + + /* must we go coarser? */ + if (size[0]+size[1]+size[2]+size[3]+size[4] > vs_total_ac_bits_hd) { + int largest = size[0] % 5; /* 'random' number */ + int qlevels_done = 0; + + do { + /* find the macroblock with the lowest qlevel */ + for (i = 0; i < 5; i++) { + if (qlevels[i] < qlevels[largest]) + largest = i; + } + + i = largest; + /* ensure that we don't enter infinite loop */ + largest = (largest+1) % 5; + + /* quantize a little bit more */ + qlevels[i] += dv100_qlevel_inc; + if (qlevels[i] > DV100_NUM_QLEVELS-1) { + qlevels[i] = DV100_NUM_QLEVELS-1; + qlevels_done++; + } + + qnos[i] = DV100_QLEVEL_QNO(dv100_qlevels[qlevels[i]]); + size[i] = 0; + + /* for each block */ + b = &blks[8*i]; + for (j = 0; j < 8; j++, b++) { + /* accumulate block size into macroblock */ + if(size_cache[8*i+j][qlevels[i]] == 0) { + /* it is safe to use actual_quantize() here because we only go from finer to coarser, + and it saves the final actual_quantize() down below */ + size_cache[8*i+j][qlevels[i]] = dv100_actual_quantize(b, qlevels[i]); + } + size[i] += size_cache[8*i+j][qlevels[i]]; + } /* for each block */ + + } while (vs_total_ac_bits_hd < size[0] + size[1] + size[2] + size[3] + size[4] && qlevels_done < 5); + + // can we go finer? + } else if (DV100_ENABLE_FINER && + size[0]+size[1]+size[2]+size[3]+size[4] < vs_total_ac_bits_hd) { + int save_qlevel; + int largest = size[0] % 5; /* 'random' number */ + + while (qlevels[0] > min_qlevel[0] || + qlevels[1] > min_qlevel[1] || + qlevels[2] > min_qlevel[2] || + qlevels[3] > min_qlevel[3] || + qlevels[4] > min_qlevel[4]) { + + /* find the macroblock with the highest qlevel */ + for (i = 0; i < 5; i++) { + if (qlevels[i] > min_qlevel[i] && qlevels[i] > qlevels[largest]) + largest = i; + } + + i = largest; + + /* ensure that we don't enter infinite loop */ + largest = (largest+1) % 5; + + if (qlevels[i] <= min_qlevel[i]) { + /* can't unquantize any more */ + continue; + } + /* quantize a little bit less */ + save_qlevel = qlevels[i]; + qlevels[i] -= dv100_qlevel_inc; + if (qlevels[i] < min_qlevel[i]) + qlevels[i] = min_qlevel[i]; + + qnos[i] = DV100_QLEVEL_QNO(dv100_qlevels[qlevels[i]]); + + size[i] = 0; + + /* for each block */ + b = &blks[8*i]; + for (j = 0; j < 8; j++, b++) { + /* accumulate block size into macroblock */ + if(size_cache[8*i+j][qlevels[i]] == 0) { + size_cache[8*i+j][qlevels[i]] = dv100_actual_quantize(b, qlevels[i]); + } + size[i] += size_cache[8*i+j][qlevels[i]]; + } /* for each block */ + + /* did we bust the limit? */ + if (vs_total_ac_bits_hd < size[0] + size[1] + size[2] + size[3] + size[4]) { + /* go back down and exit */ + qlevels[i] = save_qlevel; + qnos[i] = DV100_QLEVEL_QNO(dv100_qlevels[qlevels[i]]); + break; + } + } + } + + /* now do the actual quantization */ + for (i = 0; i < 5; i++) { + /* for each block */ + b = &blks[8*i]; + size[i] = 0; + for (j = 0; j < 8; j++, b++) { + /* accumulate block size into macroblock */ + size[i] += dv100_actual_quantize(b, qlevels[i]); + } /* for each block */ + } +} + static inline void dv_guess_qnos(EncBlockInfo *blks, int *qnos) { int size[5]; @@ -422,6 +815,26 @@ static inline void dv_guess_qnos(EncBlockInfo *blks, int *qnos) } } +/* update all cno values into the blocks, over-writing the old values without + touching anything else. (only used for DV100) */ +static inline void dv_revise_cnos(uint8_t *dif, EncBlockInfo *blk, const AVDVProfile *profile) +{ + uint8_t *data; + int mb_index, i; + + for (mb_index = 0; mb_index < 5; mb_index++) { + data = dif + mb_index*80 + 4; + for (i = 0; i < profile->bpm; i++) { + /* zero out the class number */ + data[1] &= 0xCF; + /* add the new one */ + data[1] |= blk[profile->bpm*mb_index+i].cno << 4; + + data += profile->block_sizes[i] >> 3; + } + } +} + static int dv_encode_video_segment(AVCodecContext *avctx, void *arg) { DVVideoContext *s = avctx->priv_data; @@ -430,26 +843,38 @@ static int dv_encode_video_segment(AVCodecContext *avctx, void *arg) int mb_x, mb_y, c_offset; ptrdiff_t linesize, y_stride; uint8_t *y_ptr; - uint8_t *dif; + uint8_t *dif, *p; LOCAL_ALIGNED_8(uint8_t, scratch, [128]); EncBlockInfo enc_blks[5 * DV_MAX_BPM]; PutBitContext pbs[5 * DV_MAX_BPM]; PutBitContext *pb; EncBlockInfo *enc_blk; int vs_bit_size = 0; - int qnos[5] = { 15, 15, 15, 15, 15 }; /* No quantization */ + int qnos[5]; int *qnosp = &qnos[0]; - dif = &s->buf[work_chunk->buf_offset * 80]; + p = dif = &s->buf[work_chunk->buf_offset * 80]; enc_blk = &enc_blks[0]; for (mb_index = 0; mb_index < 5; mb_index++) { dv_calculate_mb_xy(s, work_chunk, mb_index, &mb_x, &mb_y); + qnos[mb_index] = DV_PROFILE_IS_HD(s->sys) ? 1 : 15; + + y_ptr = s->frame->data[0] + ((mb_y * s->frame->linesize[0] + mb_x) << 3); + linesize = s->frame->linesize[0]; + + if (s->sys->height == 1080 && mb_y < 134) + enc_blk->dct_mode = dv_guess_dct_mode(s, y_ptr, linesize); + else + enc_blk->dct_mode = 0; + for (i = 1; i < 8; i++) + enc_blk[i].dct_mode = enc_blk->dct_mode; + /* initializing luminance blocks */ if ((s->sys->pix_fmt == AV_PIX_FMT_YUV420P) || (s->sys->pix_fmt == AV_PIX_FMT_YUV411P && mb_x >= (704 / 8)) || (s->sys->height >= 720 && mb_y != 134)) { - y_stride = s->frame->linesize[0] << 3; + y_stride = s->frame->linesize[0] << (3*!enc_blk->dct_mode); } else { y_stride = 16; } @@ -478,7 +903,7 @@ static int dv_encode_video_segment(AVCodecContext *avctx, void *arg) for (j = 2; j; j--) { uint8_t *c_ptr = s->frame->data[j] + c_offset; linesize = s->frame->linesize[j]; - y_stride = (mb_y == 134) ? 8 : (s->frame->linesize[j] << 3); + y_stride = (mb_y == 134) ? 8 : (s->frame->linesize[j] << (3*!enc_blk->dct_mode)); if (s->sys->pix_fmt == AV_PIX_FMT_YUV411P && mb_x >= (704 / 8)) { uint8_t *d; uint8_t *b = scratch; @@ -506,27 +931,31 @@ static int dv_encode_video_segment(AVCodecContext *avctx, void *arg) } } - if (vs_total_ac_bits < vs_bit_size) + if (DV_PROFILE_IS_HD(s->sys)) { + /* unconditional */ + dv_guess_qnos_hd(&enc_blks[0], qnosp); + } else if (vs_total_ac_bits < vs_bit_size) { dv_guess_qnos(&enc_blks[0], qnosp); + } /* DIF encoding process */ for (j = 0; j < 5 * s->sys->bpm;) { int start_mb = j; - dif[3] = *qnosp++; - dif += 4; + p[3] = *qnosp++; + p += 4; /* First pass over individual cells only */ for (i = 0; i < s->sys->bpm; i++, j++) { int sz = s->sys->block_sizes[i] >> 3; - init_put_bits(&pbs[j], dif, sz); + init_put_bits(&pbs[j], p, sz); put_sbits(&pbs[j], 9, ((enc_blks[j].mb[0] >> 3) - 1024 + 2) >> 2); - put_bits(&pbs[j], 1, enc_blks[j].dct_mode); + put_bits(&pbs[j], 1, DV_PROFILE_IS_HD(s->sys) && i ? 1 : enc_blks[j].dct_mode); put_bits(&pbs[j], 2, enc_blks[j].cno); dv_encode_ac(&enc_blks[j], &pbs[j], &pbs[j + 1]); - dif += sz; + p += sz; } /* Second pass over each MB space */ @@ -559,6 +988,9 @@ static int dv_encode_video_segment(AVCodecContext *avctx, void *arg) memset(pbs[j].buf + pos, 0xff, size - pos); } + if (DV_PROFILE_IS_HD(s->sys)) + dv_revise_cnos(dif, enc_blks, s->sys); + return 0; } @@ -583,12 +1015,19 @@ static inline int dv_write_pack(enum dv_pack_type pack_id, DVVideoContext *c, * 2. It is not at all clear what STYPE is used for 4:2:0 PAL * compression scheme (if any). */ + uint8_t aspect = 0; int apt = (c->sys->pix_fmt == AV_PIX_FMT_YUV420P ? 0 : 1); - int fs = c->frame->top_field_first ? 0x00 : 0x40; + int fs; - uint8_t aspect = 0; - if ((int) (av_q2d(c->avctx->sample_aspect_ratio) * - c->avctx->width / c->avctx->height * 10) >= 17) /* 16:9 */ + if (c->avctx->height >= 720) + fs = c->avctx->height == 720 || c->frame->top_field_first ? 0x40 : 0x00; + else + fs = c->frame->top_field_first ? 0x00 : 0x40; + + if (DV_PROFILE_IS_HD(c->sys) || + (int)(av_q2d(c->avctx->sample_aspect_ratio) * + c->avctx->width / c->avctx->height * 10) >= 17) + /* HD formats are always 16:9 */ aspect = 0x02; buf[0] = (uint8_t) pack_id; @@ -643,10 +1082,14 @@ static inline int dv_write_dif_id(enum dv_section_type t, uint8_t chan_num, uint8_t seq_num, uint8_t dif_num, uint8_t *buf) { + int fsc = chan_num & 1; + int fsp = 1 - (chan_num >> 1); + buf[0] = (uint8_t) t; /* Section type */ buf[1] = (seq_num << 4) | /* DIF seq number 0-9 for 525/60; 0-11 for 625/50 */ - (chan_num << 3) | /* FSC: for 50Mb/s 0 - first channel; 1 - second */ - 7; /* reserved -- always 1 */ + (fsc << 3) | /* FSC: for 50 and 100Mb/s 0 - first channel; 1 - second */ + (fsp << 2) | /* FSP: for 100Mb/s 1 - channels 0-1; 0 - channels 2-3 */ + 3; /* reserved -- always 1 */ buf[2] = dif_num; /* DIF block number Video: 0-134, Audio: 0-8 */ return 3; } @@ -674,20 +1117,22 @@ static inline int dv_write_ssyb_id(uint8_t syb_num, uint8_t fr, uint8_t *buf) static void dv_format_frame(DVVideoContext *c, uint8_t *buf) { int chan, i, j, k; + /* We work with 720p frames split in half. The odd half-frame is chan 2,3 */ + int chan_offset = 2*(c->sys->height == 720 && c->avctx->frame_number & 1); for (chan = 0; chan < c->sys->n_difchan; chan++) { for (i = 0; i < c->sys->difseg_size; i++) { memset(buf, 0xff, 80 * 6); /* first 6 DIF blocks are for control data */ /* DV header: 1DIF */ - buf += dv_write_dif_id(dv_sect_header, chan, i, 0, buf); + buf += dv_write_dif_id(dv_sect_header, chan+chan_offset, i, 0, buf); buf += dv_write_pack((c->sys->dsf ? dv_header625 : dv_header525), c, buf); buf += 72; /* unused bytes */ /* DV subcode: 2DIFs */ for (j = 0; j < 2; j++) { - buf += dv_write_dif_id(dv_sect_subcode, chan, i, j, buf); + buf += dv_write_dif_id(dv_sect_subcode, chan+chan_offset, i, j, buf); for (k = 0; k < 6; k++) buf += dv_write_ssyb_id(k, (i < c->sys->difseg_size / 2), buf) + 5; buf += 29; /* unused bytes */ @@ -695,7 +1140,7 @@ static void dv_format_frame(DVVideoContext *c, uint8_t *buf) /* DV VAUX: 3DIFS */ for (j = 0; j < 3; j++) { - buf += dv_write_dif_id(dv_sect_vaux, chan, i, j, buf); + buf += dv_write_dif_id(dv_sect_vaux, chan+chan_offset, i, j, buf); buf += dv_write_pack(dv_video_source, c, buf); buf += dv_write_pack(dv_video_control, c, buf); buf += 7 * 5; @@ -708,10 +1153,10 @@ static void dv_format_frame(DVVideoContext *c, uint8_t *buf) for (j = 0; j < 135; j++) { if (j % 15 == 0) { memset(buf, 0xff, 80); - buf += dv_write_dif_id(dv_sect_audio, chan, i, j / 15, buf); + buf += dv_write_dif_id(dv_sect_audio, chan+chan_offset, i, j/15, buf); buf += 77; /* audio control & shuffled PCM audio */ } - buf += dv_write_dif_id(dv_sect_video, chan, i, j, buf); + buf += dv_write_dif_id(dv_sect_video, chan+chan_offset, i, j, buf); buf += 77; /* 1 video macroblock: 1 bytes control * 4 * 14 bytes Y 8x8 data * 10 bytes Cr 8x8 data @@ -738,15 +1183,15 @@ FF_DISABLE_DEPRECATION_WARNINGS c->coded_frame->pict_type = AV_PICTURE_TYPE_I; FF_ENABLE_DEPRECATION_WARNINGS #endif - s->buf = pkt->data; + + dv_format_frame(s, pkt->data); + c->execute(c, dv_encode_video_segment, s->work_chunks, NULL, dv_work_pool_size(s->sys), sizeof(DVwork_chunk)); emms_c(); - dv_format_frame(s, pkt->data); - pkt->flags |= AV_PKT_FLAG_KEY; *got_packet = 1; @@ -775,7 +1220,7 @@ AVCodec ff_dvvideo_encoder = { .priv_data_size = sizeof(DVVideoContext), .init = dvvideo_encode_init, .encode2 = dvvideo_encode_frame, - .capabilities = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE diff --git a/libavcodec/dxv.c b/libavcodec/dxv.c index ae79de981f7..71d85208d86 100644 --- a/libavcodec/dxv.c +++ b/libavcodec/dxv.c @@ -745,7 +745,7 @@ static int dxv_decompress_cocg(DXVContext *ctx, GetByteContext *gb, int skip0, skip1, oi0 = 0, oi1 = 0; int ret, state0 = 0, state1 = 0; - if (op_offset < 12) + if (op_offset < 12 || op_offset - 12 > bytestream2_get_bytes_left(gb)) return AVERROR_INVALIDDATA; dst = tex_data; @@ -755,7 +755,6 @@ static int dxv_decompress_cocg(DXVContext *ctx, GetByteContext *gb, skip0 = dxv_decompress_opcodes(gb, op_data0, op_size0); if (skip0 < 0) return skip0; - bytestream2_seek(gb, data_start + op_offset + skip0 - 12, SEEK_SET); if (op_size1 > max_op_size1) return AVERROR_INVALIDDATA; skip1 = dxv_decompress_opcodes(gb, op_data1, op_size1); @@ -784,7 +783,7 @@ static int dxv_decompress_cocg(DXVContext *ctx, GetByteContext *gb, return ret; } - bytestream2_seek(gb, data_start + op_offset + skip0 + skip1 - 12, SEEK_SET); + bytestream2_seek(gb, data_start - 12 + op_offset + skip0 + skip1, SEEK_SET); return 0; } @@ -799,6 +798,9 @@ static int dxv_decompress_yo(DXVContext *ctx, GetByteContext *gb, uint8_t *dst, *table0[256] = { 0 }, *table1[256] = { 0 }; int ret, state = 0, skip, oi = 0, v, vv; + if (op_offset < 8 || op_offset - 8 > bytestream2_get_bytes_left(gb)) + return AVERROR_INVALIDDATA; + dst = tex_data; bytestream2_skip(gb, op_offset - 8); if (op_size > max_op_size) @@ -865,8 +867,8 @@ static int dxv_decompress_dxt5(AVCodecContext *avctx) { DXVContext *ctx = avctx->priv_data; GetByteContext *gbc = &ctx->gbc; - uint32_t value, op; - int idx, prev, state = 0; + uint32_t value, op, prev; + int idx, state = 0; int pos = 4; int run = 0; int probe, check; diff --git a/libavcodec/eac3_core_bsf.c b/libavcodec/eac3_core_bsf.c index 3e4dc2e2a23..49c3389ee13 100644 --- a/libavcodec/eac3_core_bsf.c +++ b/libavcodec/eac3_core_bsf.c @@ -18,8 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "get_bits.h" #include "ac3_parser_internal.h" diff --git a/libavcodec/eac3enc.c b/libavcodec/eac3enc.c index e1d61f68bff..6a90571e564 100644 --- a/libavcodec/eac3enc.c +++ b/libavcodec/eac3enc.c @@ -263,6 +263,7 @@ AVCodec ff_eac3_encoder = { .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .priv_class = &eac3enc_class, + .supported_samplerates = ff_ac3_sample_rate_tab, .channel_layouts = ff_ac3_channel_layouts, .defaults = ac3_defaults, }; diff --git a/libavcodec/encode.c b/libavcodec/encode.c index d12c42526be..b1784ed0508 100644 --- a/libavcodec/encode.c +++ b/libavcodec/encode.c @@ -70,11 +70,6 @@ int ff_alloc_packet2(AVCodecContext *avctx, AVPacket *avpkt, int64_t size, int64 } } -int ff_alloc_packet(AVPacket *avpkt, int size) -{ - return ff_alloc_packet2(NULL, avpkt, size, 0); -} - /** * Pad last frame with silence. */ @@ -90,7 +85,7 @@ static int pad_last_frame(AVCodecContext *s, AVFrame **dst, const AVFrame *src) frame->channel_layout = src->channel_layout; frame->channels = src->channels; frame->nb_samples = s->frame_size; - ret = av_frame_get_buffer(frame, 32); + ret = av_frame_get_buffer(frame, 0); if (ret < 0) goto fail; @@ -174,8 +169,14 @@ int attribute_align_arg avcodec_encode_audio2(AVCodecContext *avctx, goto end; } } else if (!(avctx->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) { - if (frame->nb_samples < avctx->frame_size && - !avctx->internal->last_audio_frame) { + /* if we already got an undersized frame, that must have been the last */ + if (avctx->internal->last_audio_frame) { + av_log(avctx, AV_LOG_ERROR, "frame_size (%d) was not respected for a non-last frame (avcodec_encode_audio2)\n", avctx->frame_size); + ret = AVERROR(EINVAL); + goto end; + } + + if (frame->nb_samples < avctx->frame_size) { ret = pad_last_frame(avctx, &padded_frame, frame); if (ret < 0) goto end; @@ -271,14 +272,12 @@ int attribute_align_arg avcodec_encode_video2(AVCodecContext *avctx, return AVERROR(ENOSYS); } - if(CONFIG_FRAME_THREAD_ENCODER && - avctx->internal->frame_thread_encoder && (avctx->active_thread_type&FF_THREAD_FRAME)) - return ff_thread_video_encode_frame(avctx, avpkt, frame, got_packet_ptr); - if ((avctx->flags&AV_CODEC_FLAG_PASS1) && avctx->stats_out) avctx->stats_out[0] = '\0'; - if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY) && !frame) { + if (!frame && + !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY || + (avctx->internal->frame_thread_encoder && avctx->active_thread_type & FF_THREAD_FRAME))) { av_packet_unref(avpkt); return 0; } @@ -293,7 +292,15 @@ int attribute_align_arg avcodec_encode_video2(AVCodecContext *avctx, av_assert0(avctx->codec->encode2); - ret = avctx->codec->encode2(avctx, avpkt, frame, got_packet_ptr); + + if (CONFIG_FRAME_THREAD_ENCODER && + avctx->internal->frame_thread_encoder && (avctx->active_thread_type & FF_THREAD_FRAME)) + ret = ff_thread_video_encode_frame(avctx, avpkt, frame, got_packet_ptr); + else { + ret = avctx->codec->encode2(avctx, avpkt, frame, got_packet_ptr); + if (*got_packet_ptr && !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY)) + avpkt->pts = avpkt->dts = frame->pts; + } av_assert0(ret <= 0); emms_c(); @@ -320,8 +327,6 @@ int attribute_align_arg avcodec_encode_video2(AVCodecContext *avctx, if (!ret) { if (!*got_packet_ptr) avpkt->size = 0; - else if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY)) - avpkt->pts = avpkt->dts = frame->pts; if (needs_realloc && avpkt->data) { ret = av_buffer_realloc(&avpkt->buf, avpkt->size + AV_INPUT_BUFFER_PADDING_SIZE); @@ -422,9 +427,15 @@ int attribute_align_arg avcodec_receive_packet(AVCodecContext *avctx, AVPacket * return AVERROR(EINVAL); if (avctx->codec->receive_packet) { + int ret; if (avctx->internal->draining && !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY)) return AVERROR_EOF; - return avctx->codec->receive_packet(avctx, avpkt); + ret = avctx->codec->receive_packet(avctx, avpkt); + if (!ret) + // Encoders must always return ref-counted buffers. + // Side-data only packets have no data and can be not ref-counted. + av_assert0(!avpkt->data || avpkt->buf); + return ret; } // Emulation via old API. diff --git a/libavcodec/escape124.c b/libavcodec/escape124.c index cffd3e12b1a..94c2a961e6a 100644 --- a/libavcodec/escape124.c +++ b/libavcodec/escape124.c @@ -252,7 +252,7 @@ static int escape124_decode_frame(AVCodecContext *avctx, if (i == 2) { // This codebook can be cut off at places other than // powers of 2, leaving some of the entries undefined. - cb_size = get_bits_long(&gb, 20); + cb_size = get_bits(&gb, 20); if (!cb_size) { av_log(avctx, AV_LOG_ERROR, "Invalid codebook size 0.\n"); return AVERROR_INVALIDDATA; diff --git a/libavcodec/exr.c b/libavcodec/exr.c index 0f8b0fda9f2..68d5befa400 100644 --- a/libavcodec/exr.c +++ b/libavcodec/exr.c @@ -30,7 +30,6 @@ * For more information on the OpenEXR format, visit: * http://openexr.com/ * - * exr_flt2uint() and exr_halflt2uint() is credited to Reimar Döffinger. * exr_half2float() is credited to Aaftab Munshi, Dan Ginsburg, Dave Shreiner. */ @@ -41,6 +40,7 @@ #include "libavutil/common.h" #include "libavutil/imgutils.h" #include "libavutil/intfloat.h" +#include "libavutil/avstring.h" #include "libavutil/opt.h" #include "libavutil/color_utils.h" @@ -159,7 +159,7 @@ typedef struct EXRContext { enum AVColorTransferCharacteristic apply_trc_type; float gamma; - uint16_t gamma_table[65536]; + union av_intfloat32 gamma_table[65536]; } EXRContext; /* -15 stored using a single precision bias of 127 */ @@ -224,47 +224,6 @@ static union av_intfloat32 exr_half2float(uint16_t hf) return f; } - -/** - * Convert from 32-bit float as uint32_t to uint16_t. - * - * @param v 32-bit float - * - * @return normalized 16-bit unsigned int - */ -static inline uint16_t exr_flt2uint(int32_t v) -{ - int32_t exp = v >> 23; - // "HACK": negative values result in exp< 0, so clipping them to 0 - // is also handled by this condition, avoids explicit check for sign bit. - if (exp <= 127 + 7 - 24) // we would shift out all bits anyway - return 0; - if (exp >= 127) - return 0xffff; - v &= 0x007fffff; - return (v + (1 << 23)) >> (127 + 7 - exp); -} - -/** - * Convert from 16-bit float as uint16_t to uint16_t. - * - * @param v 16-bit float - * - * @return normalized 16-bit unsigned int - */ -static inline uint16_t exr_halflt2uint(uint16_t v) -{ - unsigned exp = 14 - (v >> 10); - if (exp >= 14) { - if (exp == 14) - return (v >> 9) & 1; - else - return (v & 0x8000) ? 0 : 0xffff; - } - v <<= 6; - return (v + (1 << 16)) >> (exp + 1); -} - static int zip_uncompress(EXRContext *s, const uint8_t *src, int compressed_size, int uncompressed_size, EXRThreadData *td) { @@ -881,7 +840,7 @@ static int pxr24_uncompress(EXRContext *s, const uint8_t *src, in = ptr[3] + s->xdelta; for (j = 0; j < s->xdelta; ++j) { - uint32_t diff = (*(ptr[0]++) << 24) | + uint32_t diff = ((uint32_t)*(ptr[0]++) << 24) | (*(ptr[1]++) << 16) | (*(ptr[2]++) << 8 ) | (*(ptr[3]++)); @@ -1034,14 +993,14 @@ static int decode_block(AVCodecContext *avctx, void *tdata, const uint8_t *channel_buffer[4] = { 0 }; const uint8_t *buf = s->buf; uint64_t line_offset, uncompressed_size; - uint16_t *ptr_x; uint8_t *ptr; uint32_t data_size; uint64_t line, col = 0; uint64_t tile_x, tile_y, tile_level_x, tile_level_y; const uint8_t *src; - int axmax = (avctx->width - (s->xmax + 1)) * 2 * s->desc->nb_components; /* nb pixel to add at the right of the datawindow */ - int bxmin = s->xmin * 2 * s->desc->nb_components; /* nb pixel to add at the left of the datawindow */ + int step = s->desc->flags & AV_PIX_FMT_FLAG_FLOAT ? 4 : 2 * s->desc->nb_components; + int axmax = (avctx->width - (s->xmax + 1)) * step; /* nb pixel to add at the right of the datawindow */ + int bxmin = s->xmin * step; /* nb pixel to add at the left of the datawindow */ int i, x, buf_size = s->buf_size; int c, rgb_channel_count; float one_gamma = 1.0f / s->gamma; @@ -1174,69 +1133,89 @@ static int decode_block(AVCodecContext *avctx, void *tdata, if (s->channel_offsets[3] >= 0) channel_buffer[3] = src + td->xsize * s->channel_offsets[3]; - ptr = p->data[0] + line * p->linesize[0] + (col * s->desc->nb_components * 2); - - for (i = 0; - i < td->ysize; i++, ptr += p->linesize[0]) { + if (s->desc->flags & AV_PIX_FMT_FLAG_FLOAT) { - const uint8_t * a; - const uint8_t *rgb[3]; - - for (c = 0; c < rgb_channel_count; c++){ - rgb[c] = channel_buffer[c]; + /* todo: change this when a floating point pixel format with luma with alpha is implemented */ + int channel_count = s->channel_offsets[3] >= 0 ? 4 : rgb_channel_count; + if (s->is_luma) { + channel_buffer[1] = channel_buffer[0]; + channel_buffer[2] = channel_buffer[0]; } - if (channel_buffer[3]) - a = channel_buffer[3]; + for (c = 0; c < channel_count; c++) { + int plane = s->desc->comp[c].plane; + ptr = p->data[plane] + line * p->linesize[plane] + (col * 4); - ptr_x = (uint16_t *) ptr; + for (i = 0; i < td->ysize; i++, ptr += p->linesize[plane]) { + const uint8_t *src; + union av_intfloat32 *ptr_x; - // Zero out the start if xmin is not 0 - memset(ptr_x, 0, bxmin); - ptr_x += s->xmin * s->desc->nb_components; + src = channel_buffer[c]; + ptr_x = (union av_intfloat32 *)ptr; - if (s->pixel_type == EXR_FLOAT) { - // 32-bit - if (trc_func) { - for (x = 0; x < td->xsize; x++) { - union av_intfloat32 t; + // Zero out the start if xmin is not 0 + memset(ptr_x, 0, bxmin); + ptr_x += s->xmin; - for (c = 0; c < rgb_channel_count; c++) { - t.i = bytestream_get_le32(&rgb[c]); - t.f = trc_func(t.f); - *ptr_x++ = exr_flt2uint(t.i); - } - if (channel_buffer[3]) - *ptr_x++ = exr_flt2uint(bytestream_get_le32(&a)); - } - } else { - for (x = 0; x < td->xsize; x++) { + if (s->pixel_type == EXR_FLOAT) { + // 32-bit union av_intfloat32 t; - int c; - - for (c = 0; c < rgb_channel_count; c++) { - t.i = bytestream_get_le32(&rgb[c]); - if (t.f > 0.0f) /* avoid negative values */ - t.f = powf(t.f, one_gamma); - *ptr_x++ = exr_flt2uint(t.i); + if (trc_func && c < 3) { + for (x = 0; x < td->xsize; x++) { + t.i = bytestream_get_le32(&src); + t.f = trc_func(t.f); + *ptr_x++ = t; + } + } else { + for (x = 0; x < td->xsize; x++) { + t.i = bytestream_get_le32(&src); + if (t.f > 0.0f && c < 3) /* avoid negative values */ + t.f = powf(t.f, one_gamma); + *ptr_x++ = t; + } + } + } else if (s->pixel_type == EXR_HALF) { + // 16-bit + if (c < 3) { + for (x = 0; x < td->xsize; x++) { + *ptr_x++ = s->gamma_table[bytestream_get_le16(&src)]; + } + } else { + for (x = 0; x < td->xsize; x++) { + *ptr_x++ = exr_half2float(bytestream_get_le16(&src));; + } } - - if (channel_buffer[3]) - *ptr_x++ = exr_flt2uint(bytestream_get_le32(&a)); } + + // Zero out the end if xmax+1 is not w + memset(ptr_x, 0, axmax); + channel_buffer[c] += td->channel_line_size; } - } else if (s->pixel_type == EXR_HALF) { - // 16-bit - for (x = 0; x < td->xsize; x++) { - int c; - for (c = 0; c < rgb_channel_count; c++) { - *ptr_x++ = s->gamma_table[bytestream_get_le16(&rgb[c])]; - } + } + } else { - if (channel_buffer[3]) - *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&a)); + av_assert1(s->pixel_type == EXR_UINT); + ptr = p->data[0] + line * p->linesize[0] + (col * s->desc->nb_components * 2); + + for (i = 0; i < td->ysize; i++, ptr += p->linesize[0]) { + + const uint8_t * a; + const uint8_t *rgb[3]; + uint16_t *ptr_x; + + for (c = 0; c < rgb_channel_count; c++) { + rgb[c] = channel_buffer[c]; } - } else if (s->pixel_type == EXR_UINT) { + + if (channel_buffer[3]) + a = channel_buffer[3]; + + ptr_x = (uint16_t *) ptr; + + // Zero out the start if xmin is not 0 + memset(ptr_x, 0, bxmin); + ptr_x += s->xmin * s->desc->nb_components; + for (x = 0; x < td->xsize; x++) { for (c = 0; c < rgb_channel_count; c++) { *ptr_x++ = bytestream_get_le32(&rgb[c]) >> 16; @@ -1245,16 +1224,16 @@ static int decode_block(AVCodecContext *avctx, void *tdata, if (channel_buffer[3]) *ptr_x++ = bytestream_get_le32(&a) >> 16; } - } - // Zero out the end if xmax+1 is not w - memset(ptr_x, 0, axmax); + // Zero out the end if xmax+1 is not w + memset(ptr_x, 0, axmax); - channel_buffer[0] += td->channel_line_size; - channel_buffer[1] += td->channel_line_size; - channel_buffer[2] += td->channel_line_size; - if (channel_buffer[3]) - channel_buffer[3] += td->channel_line_size; + channel_buffer[0] += td->channel_line_size; + channel_buffer[1] += td->channel_line_size; + channel_buffer[2] += td->channel_line_size; + if (channel_buffer[3]) + channel_buffer[3] += td->channel_line_size; + } } return 0; @@ -1307,6 +1286,7 @@ static int decode_header(EXRContext *s, AVFrame *frame) int magic_number, version, i, flags, sar = 0; int layer_match = 0; int ret; + int dup_channels = 0; s->current_channel_offset = 0; s->xmin = ~0; @@ -1398,24 +1378,24 @@ static int decode_header(EXRContext *s, AVFrame *frame) } if (layer_match) { /* only search channel if the layer match is valid */ - if (!strcmp(ch_gb.buffer, "R") || - !strcmp(ch_gb.buffer, "X") || - !strcmp(ch_gb.buffer, "U")) { + if (!av_strcasecmp(ch_gb.buffer, "R") || + !av_strcasecmp(ch_gb.buffer, "X") || + !av_strcasecmp(ch_gb.buffer, "U")) { channel_index = 0; s->is_luma = 0; - } else if (!strcmp(ch_gb.buffer, "G") || - !strcmp(ch_gb.buffer, "V")) { + } else if (!av_strcasecmp(ch_gb.buffer, "G") || + !av_strcasecmp(ch_gb.buffer, "V")) { channel_index = 1; s->is_luma = 0; - } else if (!strcmp(ch_gb.buffer, "Y")) { + } else if (!av_strcasecmp(ch_gb.buffer, "Y")) { channel_index = 1; s->is_luma = 1; - } else if (!strcmp(ch_gb.buffer, "B") || - !strcmp(ch_gb.buffer, "Z") || - !strcmp(ch_gb.buffer, "W")){ - channel_index = 2; + } else if (!av_strcasecmp(ch_gb.buffer, "B") || + !av_strcasecmp(ch_gb.buffer, "Z") || + !av_strcasecmp(ch_gb.buffer, "W")) { + channel_index = 2; s->is_luma = 0; - } else if (!strcmp(ch_gb.buffer, "A")) { + } else if (!av_strcasecmp(ch_gb.buffer, "A")) { channel_index = 3; } else { av_log(s->avctx, AV_LOG_WARNING, @@ -1465,10 +1445,12 @@ static int decode_header(EXRContext *s, AVFrame *frame) s->pixel_type = current_pixel_type; s->channel_offsets[channel_index] = s->current_channel_offset; } else if (channel_index >= 0) { - av_log(s->avctx, AV_LOG_ERROR, + av_log(s->avctx, AV_LOG_WARNING, "Multiple channels with index %d.\n", channel_index); - ret = AVERROR_INVALIDDATA; - goto fail; + if (++dup_channels > 10) { + ret = AVERROR_INVALIDDATA; + goto fail; + } } s->channels = av_realloc(s->channels, @@ -1491,7 +1473,7 @@ static int decode_header(EXRContext *s, AVFrame *frame) /* Check if all channels are set with an offset or if the channels * are causing an overflow */ - if (!s->is_luma){/* if we expected to have at least 3 channels */ + if (!s->is_luma) {/* if we expected to have at least 3 channels */ if (FFMIN3(s->channel_offsets[0], s->channel_offsets[1], s->channel_offsets[2]) < 0) { @@ -1592,7 +1574,7 @@ static int decode_header(EXRContext *s, AVFrame *frame) s->tile_attr.level_mode = tileLevel & 0x0f; s->tile_attr.level_round = (tileLevel >> 4) & 0x0f; - if (s->tile_attr.level_mode >= EXR_TILE_LEVEL_UNKNOWN){ + if (s->tile_attr.level_mode >= EXR_TILE_LEVEL_UNKNOWN) { avpriv_report_missing_feature(s->avctx, "Tile level mode %d", s->tile_attr.level_mode); ret = AVERROR_PATCHWELCOME; @@ -1672,7 +1654,8 @@ static int decode_frame(AVCodecContext *avctx, void *data, AVFrame *picture = data; uint8_t *ptr; - int y, ret; + int i, y, ret; + int planes; int out_line_size; int nb_blocks; /* nb scanline or nb tile */ uint64_t start_offset_table; @@ -1687,6 +1670,21 @@ static int decode_frame(AVCodecContext *avctx, void *data, switch (s->pixel_type) { case EXR_FLOAT: case EXR_HALF: + if (s->channel_offsets[3] >= 0) { + if (!s->is_luma) { + avctx->pix_fmt = AV_PIX_FMT_GBRAPF32; + } else { + /* todo: change this when a floating point pixel format with luma with alpha is implemented */ + avctx->pix_fmt = AV_PIX_FMT_GBRAPF32; + } + } else { + if (!s->is_luma) { + avctx->pix_fmt = AV_PIX_FMT_GBRPF32; + } else { + avctx->pix_fmt = AV_PIX_FMT_GRAYF32; + } + } + break; case EXR_UINT: if (s->channel_offsets[3] >= 0) { if (!s->is_luma) { @@ -1747,7 +1745,14 @@ static int decode_frame(AVCodecContext *avctx, void *data, s->desc = av_pix_fmt_desc_get(avctx->pix_fmt); if (!s->desc) return AVERROR_INVALIDDATA; - out_line_size = avctx->width * 2 * s->desc->nb_components; + + if (s->desc->flags & AV_PIX_FMT_FLAG_FLOAT) { + planes = s->desc->nb_components; + out_line_size = avctx->width * 4; + } else { + planes = 1; + out_line_size = avctx->width * 2 * s->desc->nb_components; + } if (s->is_tile) { nb_blocks = ((s->xdelta + s->tile_attr.xSize - 1) / s->tile_attr.xSize) * @@ -1785,12 +1790,14 @@ static int decode_frame(AVCodecContext *avctx, void *data, // save pointer we are going to use in decode_block s->buf = avpkt->data; s->buf_size = avpkt->size; - ptr = picture->data[0]; // Zero out the start if ymin is not 0 - for (y = 0; y < s->ymin; y++) { - memset(ptr, 0, out_line_size); - ptr += picture->linesize[0]; + for (i = 0; i < planes; i++) { + ptr = picture->data[i]; + for (y = 0; y < s->ymin; y++) { + memset(ptr, 0, out_line_size); + ptr += picture->linesize[i]; + } } s->picture = picture; @@ -1798,10 +1805,12 @@ static int decode_frame(AVCodecContext *avctx, void *data, avctx->execute2(avctx, decode_block, s->thread_data, NULL, nb_blocks); // Zero out the end if ymax+1 is not h - ptr = picture->data[0] + ((s->ymax+1) * picture->linesize[0]); - for (y = s->ymax + 1; y < avctx->height; y++) { - memset(ptr, 0, out_line_size); - ptr += picture->linesize[0]; + for (i = 0; i < planes; i++) { + ptr = picture->data[i] + ((s->ymax+1) * picture->linesize[i]); + for (y = s->ymax + 1; y < avctx->height; y++) { + memset(ptr, 0, out_line_size); + ptr += picture->linesize[i]; + } } picture->pict_type = AV_PICTURE_TYPE_I; @@ -1831,21 +1840,22 @@ static av_cold int decode_init(AVCodecContext *avctx) for (i = 0; i < 65536; ++i) { t = exr_half2float(i); t.f = trc_func(t.f); - s->gamma_table[i] = exr_flt2uint(t.i); + s->gamma_table[i] = t; } } else { if (one_gamma > 0.9999f && one_gamma < 1.0001f) { - for (i = 0; i < 65536; ++i) - s->gamma_table[i] = exr_halflt2uint(i); + for (i = 0; i < 65536; ++i) { + s->gamma_table[i] = exr_half2float(i); + } } else { for (i = 0; i < 65536; ++i) { t = exr_half2float(i); /* If negative value we reuse half value */ if (t.f <= 0.0f) { - s->gamma_table[i] = exr_halflt2uint(i); + s->gamma_table[i] = t; } else { t.f = powf(t.f, one_gamma); - s->gamma_table[i] = exr_flt2uint(t.i); + s->gamma_table[i] = t; } } } @@ -1859,19 +1869,6 @@ static av_cold int decode_init(AVCodecContext *avctx) return 0; } -#if HAVE_THREADS -static int decode_init_thread_copy(AVCodecContext *avctx) -{ EXRContext *s = avctx->priv_data; - - // allocate thread data, used for non EXR_RAW compression types - s->thread_data = av_mallocz_array(avctx->thread_count, sizeof(EXRThreadData)); - if (!s->thread_data) - return AVERROR_INVALIDDATA; - - return 0; -} -#endif - static av_cold int decode_end(AVCodecContext *avctx) { EXRContext *s = avctx->priv_data; @@ -1951,7 +1948,6 @@ AVCodec ff_exr_decoder = { .id = AV_CODEC_ID_EXR, .priv_data_size = sizeof(EXRContext), .init = decode_init, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy), .close = decode_end, .decode = decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | diff --git a/libavcodec/extract_extradata_bsf.c b/libavcodec/extract_extradata_bsf.c index 17e5deb96b6..1fead74b57d 100644 --- a/libavcodec/extract_extradata_bsf.c +++ b/libavcodec/extract_extradata_bsf.c @@ -23,10 +23,11 @@ #include "libavutil/log.h" #include "libavutil/opt.h" -#include "avcodec.h" #include "av1.h" #include "av1_parse.h" #include "bsf.h" +#include "bsf_internal.h" +#include "bytestream.h" #include "h2645_parse.h" #include "h264.h" #include "hevc.h" @@ -38,10 +39,10 @@ typedef struct ExtractExtradataContext { int (*extract)(AVBSFContext *ctx, AVPacket *pkt, uint8_t **data, int *size); - /* AV1 specifc fields */ + /* AV1 specific fields */ AV1Packet av1_pkt; - /* H264/HEVC specifc fields */ + /* H264/HEVC specific fields */ H2645Packet h2645_pkt; /* AVOptions */ @@ -85,8 +86,9 @@ static int extract_extradata_av1(AVBSFContext *ctx, AVPacket *pkt, } if (extradata_size && has_seq) { - AVBufferRef *filtered_buf; - uint8_t *extradata, *filtered_data; + AVBufferRef *filtered_buf = NULL; + PutByteContext pb_filtered_data, pb_extradata; + uint8_t *extradata; if (s->remove) { filtered_buf = av_buffer_alloc(filtered_size + AV_INPUT_BUFFER_PADDING_SIZE); @@ -94,8 +96,6 @@ static int extract_extradata_av1(AVBSFContext *ctx, AVPacket *pkt, return AVERROR(ENOMEM); } memset(filtered_buf->data + filtered_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); - - filtered_data = filtered_buf->data; } extradata = av_malloc(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); @@ -108,15 +108,17 @@ static int extract_extradata_av1(AVBSFContext *ctx, AVPacket *pkt, *data = extradata; *size = extradata_size; + bytestream2_init_writer(&pb_extradata, extradata, extradata_size); + if (s->remove) + bytestream2_init_writer(&pb_filtered_data, filtered_buf->data, filtered_size); + 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)) { - memcpy(extradata, obu->raw_data, obu->raw_size); - extradata += obu->raw_size; + bytestream2_put_bufferu(&pb_extradata, obu->raw_data, obu->raw_size); } else if (s->remove) { - memcpy(filtered_data, obu->raw_data, obu->raw_size); - filtered_data += obu->raw_size; + bytestream2_put_bufferu(&pb_filtered_data, obu->raw_data, obu->raw_size); } } @@ -179,8 +181,9 @@ 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_H264 && has_sps))) { - AVBufferRef *filtered_buf; - uint8_t *extradata, *filtered_data; + AVBufferRef *filtered_buf = NULL; + PutByteContext pb_filtered_data, pb_extradata; + uint8_t *extradata; if (s->remove) { filtered_buf = av_buffer_alloc(filtered_size + AV_INPUT_BUFFER_PADDING_SIZE); @@ -188,8 +191,6 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt, return AVERROR(ENOMEM); } memset(filtered_buf->data + filtered_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); - - filtered_data = filtered_buf->data; } extradata = av_malloc(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); @@ -202,17 +203,19 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt, *data = extradata; *size = extradata_size; + bytestream2_init_writer(&pb_extradata, extradata, extradata_size); + if (s->remove) + bytestream2_init_writer(&pb_filtered_data, filtered_buf->data, filtered_size); + for (i = 0; i < s->h2645_pkt.nb_nals; i++) { H2645NAL *nal = &s->h2645_pkt.nals[i]; if (val_in_array(extradata_nal_types, nb_extradata_nal_types, nal->type)) { - AV_WB24(extradata, 1); // startcode - memcpy(extradata + 3, nal->raw_data, nal->raw_size); - extradata += 3 + nal->raw_size; + bytestream2_put_be24u(&pb_extradata, 1); //startcode + bytestream2_put_bufferu(&pb_extradata, nal->raw_data, nal->raw_size); } else if (s->remove) { - AV_WB24(filtered_data, 1); // startcode - memcpy(filtered_data + 3, nal->raw_data, nal->raw_size); - filtered_data += 3 + nal->raw_size; + bytestream2_put_be24u(&pb_filtered_data, 1); // startcode + bytestream2_put_bufferu(&pb_filtered_data, nal->raw_data, nal->raw_size); } } diff --git a/libavcodec/ffv1.c b/libavcodec/ffv1.c index a14dd2aab24..93cec14244b 100644 --- a/libavcodec/ffv1.c +++ b/libavcodec/ffv1.c @@ -31,7 +31,6 @@ #include "libavutil/opt.h" #include "libavutil/imgutils.h" #include "libavutil/pixdesc.h" -#include "libavutil/timer.h" #include "avcodec.h" #include "internal.h" diff --git a/libavcodec/ffv1.h b/libavcodec/ffv1.h index f0bb19350a8..147fe7ae165 100644 --- a/libavcodec/ffv1.h +++ b/libavcodec/ffv1.h @@ -33,7 +33,6 @@ #include "libavutil/opt.h" #include "libavutil/imgutils.h" #include "libavutil/pixdesc.h" -#include "libavutil/timer.h" #include "avcodec.h" #include "get_bits.h" #include "internal.h" diff --git a/libavcodec/ffv1_template.c b/libavcodec/ffv1_template.c index f2ab93313a6..c5f61b01823 100644 --- a/libavcodec/ffv1_template.c +++ b/libavcodec/ffv1_template.c @@ -37,7 +37,7 @@ static inline int RENAME(get_context)(PlaneContext *p, TYPE *src, const int RT = last[1]; const int L = src[-1]; - if (p->quant_table[3][127]) { + if (p->quant_table[3][127] || p->quant_table[4][127]) { const int TT = last2[0]; const int LL = src[-2]; return p->quant_table[0][(L - LT) & 0xFF] + diff --git a/libavcodec/ffv1dec.c b/libavcodec/ffv1dec.c index 261e0cf70c2..c704373cfe3 100644 --- a/libavcodec/ffv1dec.c +++ b/libavcodec/ffv1dec.c @@ -30,7 +30,6 @@ #include "libavutil/opt.h" #include "libavutil/imgutils.h" #include "libavutil/pixdesc.h" -#include "libavutil/timer.h" #include "avcodec.h" #include "internal.h" #include "get_bits.h" @@ -138,7 +137,6 @@ static int decode_plane(FFV1Context *s, uint8_t *src, sample[1][-1] = sample[0][0]; sample[0][w] = sample[0][w - 1]; -// { START_TIMER if (s->avctx->bits_per_raw_sample <= 8) { int ret = decode_line(s, w, sample, plane_index, 8); if (ret < 0) @@ -159,7 +157,6 @@ static int decode_plane(FFV1Context *s, uint8_t *src, } } } -// STOP_TIMER("decode-line") } } return 0; } @@ -829,8 +826,6 @@ static av_cold int decode_init(AVCodecContext *avctx) if ((ret = ff_ffv1_init_slice_contexts(f)) < 0) return ret; - avctx->internal->allocate_progress = 1; - return 0; } @@ -906,7 +901,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac unsigned crc = av_crc(av_crc_get_table(AV_CRC_32_IEEE), 0, buf_p, v); if (crc) { int64_t ts = avpkt->pts != AV_NOPTS_VALUE ? avpkt->pts : avpkt->dts; - av_log(f->avctx, AV_LOG_ERROR, "CRC mismatch %X!", crc); + av_log(f->avctx, AV_LOG_ERROR, "slice CRC mismatch %X!", crc); if (ts != AV_NOPTS_VALUE && avctx->pkt_timebase.num) { av_log(f->avctx, AV_LOG_ERROR, "at %f seconds\n", ts*av_q2d(avctx->pkt_timebase)); } else if (ts != AV_NOPTS_VALUE) { @@ -982,34 +977,6 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac return buf_size; } -#if HAVE_THREADS -static int init_thread_copy(AVCodecContext *avctx) -{ - FFV1Context *f = avctx->priv_data; - int i, ret; - - f->picture.f = NULL; - f->last_picture.f = NULL; - f->sample_buffer = NULL; - f->max_slice_count = 0; - f->slice_count = 0; - - for (i = 0; i < f->quant_table_count; i++) { - av_assert0(f->version > 1); - f->initial_states[i] = av_memdup(f->initial_states[i], - f->context_count[i] * sizeof(*f->initial_states[i])); - } - - f->picture.f = av_frame_alloc(); - f->last_picture.f = av_frame_alloc(); - - if ((ret = ff_ffv1_init_slice_contexts(f)) < 0) - return ret; - - return 0; -} -#endif - static void copy_fields(FFV1Context *fsdst, FFV1Context *fssrc, FFV1Context *fsrc) { fsdst->version = fsrc->version; @@ -1093,9 +1060,8 @@ AVCodec ff_ffv1_decoder = { .init = decode_init, .close = ff_ffv1_close, .decode = decode_frame, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(init_thread_copy), .update_thread_context = ONLY_IF_THREADS_ENABLED(update_thread_context), .capabilities = AV_CODEC_CAP_DR1 /*| AV_CODEC_CAP_DRAW_HORIZ_BAND*/ | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS, - .caps_internal = FF_CODEC_CAP_INIT_CLEANUP + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_ALLOCATE_PROGRESS, }; diff --git a/libavcodec/ffv1enc.c b/libavcodec/ffv1enc.c index 796d81f7c65..97dc15eac91 100644 --- a/libavcodec/ffv1enc.c +++ b/libavcodec/ffv1enc.c @@ -31,7 +31,6 @@ #include "libavutil/opt.h" #include "libavutil/imgutils.h" #include "libavutil/pixdesc.h" -#include "libavutil/timer.h" #include "avcodec.h" #include "internal.h" @@ -287,7 +286,6 @@ static int encode_plane(FFV1Context *s, uint8_t *src, int w, int h, sample[0][-1]= sample[1][0 ]; sample[1][ w]= sample[1][w-1]; -// { START_TIMER if (s->bits_per_raw_sample <= 8) { for (x = 0; x < w; x++) sample[0][x] = src[x * pixel_stride + stride * y]; @@ -306,7 +304,6 @@ static int encode_plane(FFV1Context *s, uint8_t *src, int w, int h, if((ret = encode_line(s, w, sample, plane_index, s->bits_per_raw_sample)) < 0) return ret; } -// STOP_TIMER("encode line") } } return 0; } @@ -334,6 +331,18 @@ static void write_quant_tables(RangeCoder *c, write_quant_table(c, quant_table[i]); } +static int contains_non_128(uint8_t (*initial_state)[CONTEXT_SIZE], + int nb_contexts) +{ + if (!initial_state) + return 0; + for (int i = 0; i < nb_contexts; i++) + for (int j = 0; j < CONTEXT_SIZE; j++) + if (initial_state[i][j] != 128) + return 1; + return 0; +} + static void write_header(FFV1Context *f) { uint8_t state[CONTEXT_SIZE]; @@ -428,10 +437,7 @@ static int write_extradata(FFV1Context *f) write_quant_tables(c, f->quant_tables[i]); for (i = 0; i < f->quant_table_count; i++) { - for (j = 0; j < f->context_count[i] * CONTEXT_SIZE; j++) - if (f->initial_states[i] && f->initial_states[i][0][j] != 128) - break; - if (j < f->context_count[i] * CONTEXT_SIZE) { + if (contains_non_128(f->initial_states[i], f->context_count[i])) { put_rac(c, state, 1); for (j = 0; j < f->context_count[i]; j++) for (k = 0; k < CONTEXT_SIZE; k++) { diff --git a/libavcodec/ffwavesynth.c b/libavcodec/ffwavesynth.c index cfd0951d8f3..8d3ac81aefb 100644 --- a/libavcodec/ffwavesynth.c +++ b/libavcodec/ffwavesynth.c @@ -217,10 +217,10 @@ static void wavesynth_seek(struct wavesynth_context *ws, int64_t ts) *last = -1; lcg_seek(&ws->dither_state, (uint32_t)ts - (uint32_t)ws->cur_ts); if (ws->pink_need) { - int64_t pink_ts_cur = (ws->cur_ts + PINK_UNIT - 1) & ~(PINK_UNIT - 1); - int64_t pink_ts_next = ts & ~(PINK_UNIT - 1); + uint64_t pink_ts_cur = (ws->cur_ts + (uint64_t)PINK_UNIT - 1) & ~(PINK_UNIT - 1); + uint64_t pink_ts_next = ts & ~(PINK_UNIT - 1); int pos = ts & (PINK_UNIT - 1); - lcg_seek(&ws->pink_state, (pink_ts_next - pink_ts_cur) * 2); + lcg_seek(&ws->pink_state, (uint32_t)(pink_ts_next - pink_ts_cur) * 2); if (pos) { pink_fill(ws); ws->pink_pos = pos; @@ -281,7 +281,7 @@ static int wavesynth_parse_extradata(AVCodecContext *avc) dphi1 = frac64(f1, (int64_t)avc->sample_rate << 16); dphi2 = frac64(f2, (int64_t)avc->sample_rate << 16); in->dphi0 = dphi1; - in->ddphi = (dphi2 - dphi1) / dt; + in->ddphi = (int64_t)(dphi2 - (uint64_t)dphi1) / dt; if (phi & 0x80000000) { phi &= ~0x80000000; if (phi >= i) @@ -350,7 +350,8 @@ static av_cold int wavesynth_init(AVCodecContext *avc) static void wavesynth_synth_sample(struct wavesynth_context *ws, int64_t ts, int32_t *channels) { - int32_t amp, val, *cv; + int32_t amp, *cv; + unsigned val; struct ws_interval *in; int i, *last, pink; uint32_t c, all_ch = 0; @@ -377,7 +378,7 @@ static void wavesynth_synth_sample(struct wavesynth_context *ws, int64_t ts, in->dphi += in->ddphi; break; case WS_NOISE: - val = amp * pink; + val = amp * (unsigned)pink; break; default: val = 0; @@ -385,7 +386,7 @@ static void wavesynth_synth_sample(struct wavesynth_context *ws, int64_t ts, all_ch |= in->channels; for (c = in->channels, cv = channels; c; c >>= 1, cv++) if (c & 1) - *cv += val; + *cv += (unsigned)val; } val = (int32_t)lcg_next(&ws->dither_state) >> 16; for (c = all_ch, cv = channels; c; c >>= 1, cv++) @@ -443,7 +444,7 @@ static int wavesynth_decode(AVCodecContext *avc, void *rframe, int *rgot_frame, if (r < 0) return r; pcm = (int16_t *)frame->data[0]; - for (s = 0; s < duration; s++, ts++) { + for (s = 0; s < duration; s++, ts+=(uint64_t)1) { memset(channels, 0, avc->channels * sizeof(*channels)); if (ts >= ws->next_ts) wavesynth_enter_intervals(ws, ts); @@ -451,7 +452,7 @@ static int wavesynth_decode(AVCodecContext *avc, void *rframe, int *rgot_frame, for (c = 0; c < avc->channels; c++) *(pcm++) = channels[c] >> 16; } - ws->cur_ts += duration; + ws->cur_ts += (uint64_t)duration; *rgot_frame = 1; return packet->size; } diff --git a/libavcodec/fic.c b/libavcodec/fic.c index 540078eda12..95baaedaa5e 100644 --- a/libavcodec/fic.c +++ b/libavcodec/fic.c @@ -278,7 +278,7 @@ static int fic_decode_frame(AVCodecContext *avctx, void *data, int skip_cursor = ctx->skip_cursor; uint8_t *sdata; - if ((ret = ff_reget_buffer(avctx, ctx->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, ctx->frame, 0)) < 0) return ret; /* Header + at least one slice (4) */ @@ -421,7 +421,7 @@ static int fic_decode_frame(AVCodecContext *avctx, void *data, } /* Make sure we use a user-supplied buffer. */ - if ((ret = ff_reget_buffer(avctx, ctx->final_frame)) < 0) { + if ((ret = ff_reget_buffer(avctx, ctx->final_frame, 0)) < 0) { av_log(avctx, AV_LOG_ERROR, "Could not make frame writable.\n"); return ret; } diff --git a/libavcodec/filter_units_bsf.c b/libavcodec/filter_units_bsf.c index 380f23e5a75..700dc06ef6a 100644 --- a/libavcodec/filter_units_bsf.c +++ b/libavcodec/filter_units_bsf.c @@ -22,6 +22,7 @@ #include "libavutil/opt.h" #include "bsf.h" +#include "bsf_internal.h" #include "cbs.h" diff --git a/libavcodec/fitsdec.c b/libavcodec/fitsdec.c index 4f452422efd..32a79cdd0d7 100644 --- a/libavcodec/fitsdec.c +++ b/libavcodec/fitsdec.c @@ -143,7 +143,7 @@ static int fits_read_header(AVCodecContext *avctx, const uint8_t **ptr, FITSHead size = abs(header->bitpix) >> 3; for (i = 0; i < header->naxis; i++) { - if (size && header->naxisn[i] > SIZE_MAX / size) { + if (size == 0 || header->naxisn[i] > SIZE_MAX / size) { av_log(avctx, AV_LOG_ERROR, "unsupported size of FITS image"); return AVERROR_INVALIDDATA; } @@ -264,6 +264,13 @@ static int fits_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, CASE_RGB(16, dst16, uint16_t, AV_RB16); } } else { + double scale = header.data_max - header.data_min; + + if (scale <= 0 || !isfinite(scale)) { + scale = 1; + } + scale = 1/scale; + switch (header.bitpix) { #define CASE_GRAY(cas, dst, type, t, rd) \ case cas: \ @@ -272,7 +279,7 @@ static int fits_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, for (j = 0; j < avctx->width; j++) { \ t = rd; \ if (!header.blank_found || t != header.blank) { \ - *dst++ = ((t - header.data_min) * ((1 << (sizeof(type) * 8)) - 1)) / (header.data_max - header.data_min); \ + *dst++ = lrint(((t - header.data_min) * ((1 << (sizeof(type) * 8)) - 1)) * scale); \ } else { \ *dst++ = fitsctx->blank_val; \ } \ diff --git a/libavcodec/flac.c b/libavcodec/flac.c index 5ffbf93190e..7b075d4bd3e 100644 --- a/libavcodec/flac.c +++ b/libavcodec/flac.c @@ -217,9 +217,9 @@ int ff_flac_parse_streaminfo(AVCodecContext *avctx, struct FLACStreaminfo *s, } skip_bits(&gb, 24); /* skip min frame size */ - s->max_framesize = get_bits_long(&gb, 24); + s->max_framesize = get_bits(&gb, 24); - s->samplerate = get_bits_long(&gb, 20); + s->samplerate = get_bits(&gb, 20); s->channels = get_bits(&gb, 3) + 1; s->bps = get_bits(&gb, 5) + 1; diff --git a/libavcodec/flac_parser.c b/libavcodec/flac_parser.c index 2721286464d..3424583c495 100644 --- a/libavcodec/flac_parser.c +++ b/libavcodec/flac_parser.c @@ -58,8 +58,9 @@ typedef struct FLACHeaderMarker { int offset; /**< byte offset from start of FLACParseContext->buffer */ - int *link_penalty; /**< pointer to array of local scores between this header - and the one at a distance equal array position */ + int link_penalty[FLAC_MAX_SEQUENTIAL_HEADERS]; /**< array of local scores + between this header and the one at a distance equal + array position */ int max_score; /**< maximum score found after checking each child that has a valid CRC */ FLACFrameInfo fi; /**< decoded frame header info */ @@ -112,8 +113,8 @@ static int frame_header_is_valid(AVCodecContext *avctx, const uint8_t *buf, * This function is based on av_fifo_generic_read, which is why there is a comment * about a memory barrier for SMP. */ -static uint8_t* flac_fifo_read_wrap(FLACParseContext *fpc, int offset, int len, - uint8_t** wrap_buf, int* allocated_size) +static uint8_t *flac_fifo_read_wrap(FLACParseContext *fpc, int offset, int len, + uint8_t **wrap_buf, int *allocated_size) { AVFifoBuffer *f = fpc->fifo_buf; uint8_t *start = f->rptr + offset; @@ -152,7 +153,7 @@ static uint8_t* flac_fifo_read_wrap(FLACParseContext *fpc, int offset, int len, * A second call to flac_fifo_read (with new offset and len) should be called * to get the post-wrap buf if the returned len is less than the requested. **/ -static uint8_t* flac_fifo_read(FLACParseContext *fpc, int offset, int *len) +static uint8_t *flac_fifo_read(FLACParseContext *fpc, int offset, int *len) { AVFifoBuffer *f = fpc->fifo_buf; uint8_t *start = f->rptr + offset; @@ -188,16 +189,8 @@ static int find_headers_search_validate(FLACParseContext *fpc, int offset) "couldn't allocate FLACHeaderMarker\n"); return AVERROR(ENOMEM); } - (*end_handle)->fi = fi; - (*end_handle)->offset = offset; - (*end_handle)->link_penalty = av_malloc(sizeof(int) * - FLAC_MAX_SEQUENTIAL_HEADERS); - if (!(*end_handle)->link_penalty) { - av_freep(end_handle); - av_log(fpc->avctx, AV_LOG_ERROR, - "couldn't allocate link_penalty\n"); - return AVERROR(ENOMEM); - } + (*end_handle)->fi = fi; + (*end_handle)->offset = offset; for (i = 0; i < FLAC_MAX_SEQUENTIAL_HEADERS; i++) (*end_handle)->link_penalty[i] = FLAC_HEADER_NOT_PENALIZED_YET; @@ -208,24 +201,27 @@ static int find_headers_search_validate(FLACParseContext *fpc, int offset) return size; } -static int find_headers_search(FLACParseContext *fpc, uint8_t *buf, int buf_size, - int search_start) - +static int find_headers_search(FLACParseContext *fpc, uint8_t *buf, + int buf_size, int search_start) { int size = 0, mod_offset = (buf_size - 1) % 4, i, j; uint32_t x; for (i = 0; i < mod_offset; i++) { - if ((AV_RB16(buf + i) & 0xFFFE) == 0xFFF8) - size = find_headers_search_validate(fpc, search_start + i); + if ((AV_RB16(buf + i) & 0xFFFE) == 0xFFF8) { + int ret = find_headers_search_validate(fpc, search_start + i); + size = FFMAX(size, ret); + } } for (; i < buf_size - 1; i += 4) { - x = AV_RB32(buf + i); + x = AV_RN32(buf + i); if (((x & ~(x + 0x01010101)) & 0x80808080)) { for (j = 0; j < 4; j++) { - if ((AV_RB16(buf + i + j) & 0xFFFE) == 0xFFF8) - size = find_headers_search_validate(fpc, search_start + i + j); + if ((AV_RB16(buf + i + j) & 0xFFFE) == 0xFFF8) { + int ret = find_headers_search_validate(fpc, search_start + i + j); + size = FFMAX(size, ret); + } } } } @@ -251,9 +247,9 @@ static int find_new_headers(FLACParseContext *fpc, int search_start) uint8_t wrap[2]; wrap[0] = buf[read_len - 1]; - read_len = search_end - search_start + 1; - /* search_start + 1 is the post-wrap offset in the fifo. */ + read_len = search_end - (search_start + 1) + 1; + buf = flac_fifo_read(fpc, search_start + 1, &read_len); wrap[1] = buf[0]; @@ -321,7 +317,7 @@ static int check_header_mismatch(FLACParseContext *fpc, (child_fi->frame_or_sample_num != header_fi->frame_or_sample_num + 1)) { FLACHeaderMarker *curr; - int expected_frame_num, expected_sample_num; + int64_t expected_frame_num, expected_sample_num; /* If there are frames in the middle we expect this deduction, as they are probably valid and this one follows it */ @@ -471,7 +467,7 @@ static void score_sequences(FLACParseContext *fpc) } } -static int get_best_header(FLACParseContext* fpc, const uint8_t **poutbuf, +static int get_best_header(FLACParseContext *fpc, const uint8_t **poutbuf, int *poutbuf_size) { FLACHeaderMarker *header = fpc->best_header; @@ -497,7 +493,7 @@ static int get_best_header(FLACParseContext* fpc, const uint8_t **poutbuf, &fpc->wrap_buf_allocated_size); - if (fpc->pc->flags & PARSER_FLAG_USE_CODEC_TS){ + if (fpc->pc->flags & PARSER_FLAG_USE_CODEC_TS) { if (header->fi.is_var_size) fpc->pc->pts = header->fi.frame_or_sample_num; else if (header->best_child) @@ -531,7 +527,7 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx, s->duration = fi.blocksize; if (!avctx->sample_rate) avctx->sample_rate = fi.samplerate; - if (fpc->pc->flags & PARSER_FLAG_USE_CODEC_TS){ + if (fpc->pc->flags & PARSER_FLAG_USE_CODEC_TS) { fpc->pc->pts = fi.frame_or_sample_num; if (!fi.is_var_size) fpc->pc->pts *= fi.blocksize; @@ -559,7 +555,6 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx, curr->max_score, curr->offset, curr->next->offset); } temp = curr->next; - av_freep(&curr->link_penalty); av_free(curr); fpc->nb_headers_buffered--; } @@ -570,7 +565,6 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx, for (curr = best_child->next; curr; curr = curr->next) curr->offset -= best_child->offset; - fpc->nb_headers_buffered--; best_child->offset = 0; fpc->headers = best_child; if (fpc->nb_headers_buffered >= FLAC_MIN_HEADERS) { @@ -584,30 +578,26 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx, for (curr = fpc->headers; curr != fpc->best_header; curr = temp) { temp = curr->next; - av_freep(&curr->link_penalty); av_free(curr); fpc->nb_headers_buffered--; } fpc->headers = fpc->best_header->next; - av_freep(&fpc->best_header->link_penalty); av_freep(&fpc->best_header); fpc->nb_headers_buffered--; } /* Find and score new headers. */ - /* buf_size is to zero when padding, so check for this since we do */ + /* buf_size is zero when flushing, so check for this since we do */ /* not want to try to read more input once we have found the end. */ - /* Note that as (non-modified) parameters, buf can be non-NULL, */ - /* while buf_size is 0. */ - while ((buf && buf_size && read_end < buf + buf_size && + /* Also note that buf can't be NULL. */ + while ((buf_size && read_end < buf + buf_size && fpc->nb_headers_buffered < FLAC_MIN_HEADERS) - || ((!buf || !buf_size) && !fpc->end_padded)) { + || (!buf_size && !fpc->end_padded)) { int start_offset; /* Pad the end once if EOF, to check the final region for headers. */ - if (!buf || !buf_size) { - fpc->end_padded = 1; - buf_size = MAX_FRAME_HEADER_SIZE; + if (!buf_size) { + fpc->end_padded = 1; read_end = read_start + MAX_FRAME_HEADER_SIZE; } else { /* The maximum read size is the upper-bound of what the parser @@ -635,7 +625,7 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx, goto handle_error; } - if (buf && buf_size) { + if (buf_size) { av_fifo_generic_write(fpc->fifo_buf, (void*) read_start, read_end - read_start, NULL); } else { @@ -658,7 +648,7 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx, fpc->nb_headers_buffered = nb_headers; /* Wait till FLAC_MIN_HEADERS to output a valid frame. */ if (!fpc->end_padded && fpc->nb_headers_buffered < FLAC_MIN_HEADERS) { - if (buf && read_end < buf + buf_size) { + if (read_end < buf + buf_size) { read_start = read_end; continue; } else { @@ -680,7 +670,6 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx, fpc->fifo_buf->wptr += fpc->fifo_buf->end - fpc->fifo_buf->buffer; } - buf_size = 0; read_start = read_end = NULL; } } @@ -693,7 +682,7 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx, if (fpc->best_header && fpc->best_header->max_score <= 0) { // Only accept a bad header if there is no other option to continue - if (!buf_size || !buf || read_end != buf || fpc->nb_headers_buffered < FLAC_MIN_HEADERS) + if (!buf_size || read_end != buf || fpc->nb_headers_buffered < FLAC_MIN_HEADERS) fpc->best_header = NULL; } @@ -705,13 +694,13 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx, fpc->best_header->offset); /* Set duration to 0. It is unknown or invalid in a junk frame. */ - s->duration = 0; - *poutbuf_size = fpc->best_header->offset; - *poutbuf = flac_fifo_read_wrap(fpc, 0, *poutbuf_size, - &fpc->wrap_buf, - &fpc->wrap_buf_allocated_size); + s->duration = 0; + *poutbuf_size = fpc->best_header->offset; + *poutbuf = flac_fifo_read_wrap(fpc, 0, *poutbuf_size, + &fpc->wrap_buf, + &fpc->wrap_buf_allocated_size); return buf_size ? (read_end - buf) : (fpc->best_header->offset - - av_fifo_size(fpc->fifo_buf)); + av_fifo_size(fpc->fifo_buf)); } if (!buf_size) return get_best_header(fpc, poutbuf, poutbuf_size); @@ -745,10 +734,10 @@ static void flac_parse_close(AVCodecParserContext *c) while (curr) { temp = curr->next; - av_freep(&curr->link_penalty); av_free(curr); curr = temp; } + fpc->headers = NULL; av_fifo_freep(&fpc->fifo_buf); av_freep(&fpc->wrap_buf); } diff --git a/libavcodec/flacdec.c b/libavcodec/flacdec.c index c8eb456049d..fb27e8e6d45 100644 --- a/libavcodec/flacdec.c +++ b/libavcodec/flacdec.c @@ -639,19 +639,6 @@ static int flac_decode_frame(AVCodecContext *avctx, void *data, return bytes_read; } -#if HAVE_THREADS -static int init_thread_copy(AVCodecContext *avctx) -{ - FLACContext *s = avctx->priv_data; - s->decoded_buffer = NULL; - s->decoded_buffer_size = 0; - s->avctx = avctx; - if (s->flac_stream_info.max_blocksize) - return allocate_buffers(s); - return 0; -} -#endif - static av_cold int flac_decode_close(AVCodecContext *avctx) { FLACContext *s = avctx->priv_data; @@ -682,7 +669,6 @@ AVCodec ff_flac_decoder = { .init = flac_decode_init, .close = flac_decode_close, .decode = flac_decode_frame, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(init_thread_copy), .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P, diff --git a/libavcodec/flacdsp_template.c b/libavcodec/flacdsp_template.c index 776c78da719..892418cddcf 100644 --- a/libavcodec/flacdsp_template.c +++ b/libavcodec/flacdsp_template.c @@ -66,8 +66,8 @@ static void FUNC(flac_decorrelate_ls_c)(uint8_t **out, int32_t **in, int i; for (i = 0; i < len; i++) { - int a = in[0][i]; - int b = in[1][i]; + unsigned a = in[0][i]; + unsigned b = in[1][i]; S(samples, 0, i) = a << shift; S(samples, 1, i) = (a - b) << shift; } @@ -80,8 +80,8 @@ static void FUNC(flac_decorrelate_rs_c)(uint8_t **out, int32_t **in, int i; for (i = 0; i < len; i++) { - int a = in[0][i]; - int b = in[1][i]; + unsigned a = in[0][i]; + unsigned b = in[1][i]; S(samples, 0, i) = (a + b) << shift; S(samples, 1, i) = b << shift; } @@ -94,7 +94,7 @@ static void FUNC(flac_decorrelate_ms_c)(uint8_t **out, int32_t **in, int i; for (i = 0; i < len; i++) { - int a = in[0][i]; + unsigned a = in[0][i]; int b = in[1][i]; a -= b >> 1; S(samples, 0, i) = (a + b) << shift; diff --git a/libavcodec/flacenc.c b/libavcodec/flacenc.c index 170c3caf484..8e7dbc52b41 100644 --- a/libavcodec/flacenc.c +++ b/libavcodec/flacenc.c @@ -1508,7 +1508,7 @@ AVCodec ff_flac_encoder = { .init = flac_encode_init, .encode2 = flac_encode_frame, .close = flac_encode_close, - .capabilities = AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_LOSSLESS, + .capabilities = AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_DELAY, .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_NONE }, diff --git a/libavcodec/flashsv.c b/libavcodec/flashsv.c index 92d1af9fcfb..f55cb0feebd 100644 --- a/libavcodec/flashsv.c +++ b/libavcodec/flashsv.c @@ -368,7 +368,7 @@ static int flashsv_decode_frame(AVCodecContext *avctx, void *data, s->image_width, s->image_height, s->block_width, s->block_height, h_blocks, v_blocks, h_part, v_part); - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; /* loop over all block columns */ diff --git a/libavcodec/flicvideo.c b/libavcodec/flicvideo.c index bf8ffeba4fa..276c2ff2a62 100644 --- a/libavcodec/flicvideo.c +++ b/libavcodec/flicvideo.c @@ -185,7 +185,7 @@ static int flic_decode_frame_8BPP(AVCodecContext *avctx, bytestream2_init(&g2, buf, buf_size); - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; pixels = s->frame->data[0]; @@ -519,7 +519,7 @@ static int flic_decode_frame_15_16BPP(AVCodecContext *avctx, bytestream2_init(&g2, buf, buf_size); - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; pixels = s->frame->data[0]; @@ -817,7 +817,7 @@ static int flic_decode_frame_24BPP(AVCodecContext *avctx, bytestream2_init(&g2, buf, buf_size); - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; pixels = s->frame->data[0]; diff --git a/libavcodec/flvdec.c b/libavcodec/flvdec.c index f9beb40afa6..c19f07fe056 100644 --- a/libavcodec/flvdec.c +++ b/libavcodec/flvdec.c @@ -30,7 +30,7 @@ int ff_flv_decode_picture_header(MpegEncContext *s) int format, width, height; /* picture header */ - if (get_bits_long(&s->gb, 17) != 1) { + if (get_bits(&s->gb, 17) != 1) { av_log(s->avctx, AV_LOG_ERROR, "Bad picture start code\n"); return AVERROR_INVALIDDATA; } diff --git a/libavcodec/flvenc.c b/libavcodec/flvenc.c index 15f794e75e2..c1227277b3d 100644 --- a/libavcodec/flvenc.c +++ b/libavcodec/flvenc.c @@ -107,6 +107,7 @@ AVCodec ff_flv_encoder = { .init = ff_mpv_encode_init, .encode2 = ff_mpv_encode_picture, .close = ff_mpv_encode_end, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE}, .priv_class = &flv_class, diff --git a/libavcodec/frame_thread_encoder.c b/libavcodec/frame_thread_encoder.c index 55756c4c540..83229f620ac 100644 --- a/libavcodec/frame_thread_encoder.c +++ b/libavcodec/frame_thread_encoder.c @@ -66,7 +66,7 @@ static void * attribute_align_arg worker(void *v){ AVPacket *pkt = NULL; while (!atomic_load(&c->exit)) { - int got_packet, ret; + int got_packet = 0, ret; AVFrame *frame; Task task; @@ -86,19 +86,20 @@ static void * attribute_align_arg worker(void *v){ pthread_mutex_unlock(&c->task_fifo_mutex); frame = task.indata; - ret = avcodec_encode_video2(avctx, pkt, frame, &got_packet); - pthread_mutex_lock(&c->buffer_mutex); - av_frame_unref(frame); - pthread_mutex_unlock(&c->buffer_mutex); - av_frame_free(&frame); + ret = avctx->codec->encode2(avctx, pkt, frame, &got_packet); if(got_packet) { int ret2 = av_packet_make_refcounted(pkt); if (ret >= 0 && ret2 < 0) ret = ret2; + pkt->pts = pkt->dts = frame->pts; } else { pkt->data = NULL; pkt->size = 0; } + pthread_mutex_lock(&c->buffer_mutex); + av_frame_unref(frame); + pthread_mutex_unlock(&c->buffer_mutex); + av_frame_free(&frame); pthread_mutex_lock(&c->finished_task_mutex); c->finished_tasks[task.index].outdata = pkt; pkt = NULL; c->finished_tasks[task.index].return_code = ret; @@ -120,7 +121,7 @@ int ff_frame_thread_encoder_init(AVCodecContext *avctx, AVDictionary *options){ if( !(avctx->thread_type & FF_THREAD_FRAME) - || !(avctx->codec->capabilities & AV_CODEC_CAP_INTRA_ONLY)) + || !(avctx->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS)) return 0; if( !avctx->thread_count @@ -209,8 +210,9 @@ int ff_frame_thread_encoder_init(AVCodecContext *avctx, AVDictionary *options){ int ret = av_opt_copy(thread_avctx->priv_data, avctx->priv_data); if (ret < 0) goto fail; - } else + } else if (avctx->codec->priv_data_size) { memcpy(thread_avctx->priv_data, avctx->priv_data, avctx->codec->priv_data_size); + } thread_avctx->thread_count = 1; thread_avctx->active_thread_type &= ~FF_THREAD_FRAME; diff --git a/libavcodec/g2meet.c b/libavcodec/g2meet.c index a1dec8d823d..7ef275c9fec 100644 --- a/libavcodec/g2meet.c +++ b/libavcodec/g2meet.c @@ -244,6 +244,9 @@ static int jpg_decode_block(JPGContext *c, GetBitContext *gb, const int is_chroma = !!plane; const uint8_t *qmat = is_chroma ? chroma_quant : luma_quant; + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; + c->bdsp.clear_block(block); dc = get_vlc2(gb, c->dc_vlc[is_chroma].table, 9, 3); if (dc < 0) @@ -854,6 +857,9 @@ static int epic_decode_tile(ePICContext *dc, uint8_t *out, int tile_height, uint32_t ref_pix = curr_row[x - 1]; if (!x || !epic_decode_from_cache(dc, ref_pix, &pix)) { pix = epic_decode_pixel_pred(dc, x, y, curr_row, above_row); + if (is_pixel_on_stack(dc, pix)) + return AVERROR_INVALIDDATA; + if (x) { int ret = epic_add_pixel_to_cache(&dc->hash, ref_pix, @@ -911,6 +917,11 @@ static int epic_jb_decode_tile(G2MContext *c, int tile_x, int tile_y, awidth = FFALIGN(tile_width, 16); aheight = FFALIGN(tile_height, 16); + if (tile_width > (1 << FF_ARRAY_ELEMS(c->ec.prev_row_rung))) { + avpriv_request_sample(avctx, "large tile width"); + return AVERROR_INVALIDDATA; + } + if (els_dsize) { int ret, i, j, k; uint8_t tr_r, tr_g, tr_b, *buf; diff --git a/libavcodec/g723_1dec.c b/libavcodec/g723_1dec.c index d8bc3f97acf..f601d31d002 100644 --- a/libavcodec/g723_1dec.c +++ b/libavcodec/g723_1dec.c @@ -677,7 +677,9 @@ static int estimate_sid_gain(G723_1_ChannelContext *p) if (p->sid_gain < 0) t = INT32_MIN; else t = INT32_MAX; } else - t = p->sid_gain << shift; + t = p->sid_gain * (1 << shift); + } else if(shift < -31) { + t = (p->sid_gain < 0) ? -1 : 0; }else t = p->sid_gain >> -shift; x = av_clipl_int32(t * (int64_t)cng_filt[0] >> 16); @@ -1010,7 +1012,7 @@ static int g723_1_decode_frame(AVCodecContext *avctx, void *data, formant_postfilter(p, lpc, p->audio, out); } else { // if output is not postfiltered it should be scaled by 2 for (i = 0; i < FRAME_LEN; i++) - out[i] = av_clip_int16(p->audio[LPC_ORDER + i] << 1); + out[i] = av_clip_int16(2 * p->audio[LPC_ORDER + i]); } } diff --git a/libavcodec/g729_parser.c b/libavcodec/g729_parser.c index 9982dbfffcc..010f688104e 100644 --- a/libavcodec/g729_parser.c +++ b/libavcodec/g729_parser.c @@ -45,13 +45,20 @@ static int g729_parse(AVCodecParserContext *s1, AVCodecContext *avctx, int next; if (!s->block_size) { - av_assert1(avctx->codec_id == AV_CODEC_ID_G729); /* FIXME: replace this heuristic block_size with more precise estimate */ s->block_size = (avctx->bit_rate < 8000) ? G729D_6K4_BLOCK_SIZE : G729_8K_BLOCK_SIZE; + if (avctx->codec_id == AV_CODEC_ID_ACELP_KELVIN) + s->block_size++; s->block_size *= avctx->channels; s->duration = avctx->frame_size; } + if (!s->block_size) { + *poutbuf = buf; + *poutbuf_size = buf_size; + return buf_size; + } + if (!s->remaining) s->remaining = s->block_size; if (s->remaining <= buf_size) { @@ -76,7 +83,7 @@ static int g729_parse(AVCodecParserContext *s1, AVCodecContext *avctx, } AVCodecParser ff_g729_parser = { - .codec_ids = { AV_CODEC_ID_G729 }, + .codec_ids = { AV_CODEC_ID_G729, AV_CODEC_ID_ACELP_KELVIN }, .priv_data_size = sizeof(G729ParseContext), .parser_parse = g729_parse, .parser_close = ff_parse_close, diff --git a/libavcodec/g729dec.c b/libavcodec/g729dec.c index 2e4756b8057..c181f234527 100644 --- a/libavcodec/g729dec.c +++ b/libavcodec/g729dec.c @@ -97,6 +97,7 @@ typedef struct { uint8_t gc_2nd_index_bits; ///< gain codebook (second stage) index (size in bits) uint8_t fc_signs_bits; ///< number of pulses in fixed-codebook vector uint8_t fc_indexes_bits; ///< size (in bits) of fixed-codebook index entry + uint8_t block_size; } G729FormatDescription; typedef struct { @@ -165,6 +166,7 @@ static const G729FormatDescription format_g729_8k = { .gc_2nd_index_bits = GC_2ND_IDX_BITS_8K, .fc_signs_bits = 4, .fc_indexes_bits = 13, + .block_size = G729_8K_BLOCK_SIZE, }; static const G729FormatDescription format_g729d_6k4 = { @@ -174,6 +176,7 @@ static const G729FormatDescription format_g729d_6k4 = { .gc_2nd_index_bits = GC_2ND_IDX_BITS_6K4, .fc_signs_bits = 2, .fc_indexes_bits = 9, + .block_size = G729D_6K4_BLOCK_SIZE, }; /** @@ -332,11 +335,14 @@ static int16_t g729d_voice_decision(int onset, int prev_voice_decision, const in static int32_t scalarproduct_int16_c(const int16_t * v1, const int16_t * v2, int order) { - int res = 0; + int64_t res = 0; while (order--) res += *v1++ * *v2++; + if (res > INT32_MAX) return INT32_MAX; + else if (res < INT32_MIN) return INT32_MIN; + return res; } @@ -424,14 +430,14 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame_ptr, if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) return ret; - if (buf_size % (G729_8K_BLOCK_SIZE * avctx->channels) == 0) { + if (buf_size && buf_size % ((G729_8K_BLOCK_SIZE + (avctx->codec_id == AV_CODEC_ID_ACELP_KELVIN)) * avctx->channels) == 0) { packet_type = FORMAT_G729_8K; format = &format_g729_8k; //Reset voice decision ctx->onset = 0; ctx->voice_decision = DECISION_VOICE; av_log(avctx, AV_LOG_DEBUG, "Packet type: %s\n", "G.729 @ 8kbit/s"); - } else if (buf_size == G729D_6K4_BLOCK_SIZE * avctx->channels) { + } else if (buf_size == G729D_6K4_BLOCK_SIZE * avctx->channels && avctx->codec_id != AV_CODEC_ID_ACELP_KELVIN) { packet_type = FORMAT_G729D_6K4; format = &format_g729d_6k4; av_log(avctx, AV_LOG_DEBUG, "Packet type: %s\n", "G.729D @ 6.4kbit/s"); @@ -445,12 +451,17 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame_ptr, int bad_pitch = 0; ///< parity check failed int is_periodic = 0; ///< whether one of the subframes is declared as periodic or not out_frame = (int16_t*)frame->data[c]; + if (avctx->codec_id == AV_CODEC_ID_ACELP_KELVIN) { + if (*buf != ((avctx->channels - 1 - c) * 0x80 | 2)) + avpriv_request_sample(avctx, "First byte value %x for channel %d", *buf, c); + buf++; + } - for (i = 0; i < buf_size; i++) + for (i = 0; i < format->block_size; i++) frame_erasure |= buf[i]; frame_erasure = !frame_erasure; - init_get_bits(&gb, buf, 8*buf_size); + init_get_bits8(&gb, buf, format->block_size); ma_predictor = get_bits(&gb, 1); quantizer_1st = get_bits(&gb, VQ_1ST_BITS); @@ -552,12 +563,13 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame_ptr, fc_v[i] = < \ fc_v[i] + gain_pitch * fc_v[i-pitch_delay], i >= pitch_delay */ - ff_acelp_weighted_vector_sum(fc + pitch_delay_int[i], - fc + pitch_delay_int[i], - fc, 1 << 14, - av_clip(ctx->past_gain_pitch[0], SHARP_MIN, SHARP_MAX), - 0, 14, - SUBFRAME_SIZE - pitch_delay_int[i]); + if (SUBFRAME_SIZE > pitch_delay_int[i]) + ff_acelp_weighted_vector_sum(fc + pitch_delay_int[i], + fc + pitch_delay_int[i], + fc, 1 << 14, + av_clip(ctx->past_gain_pitch[0], SHARP_MIN, SHARP_MAX), + 0, 14, + SUBFRAME_SIZE - pitch_delay_int[i]); memmove(ctx->past_gain_pitch+1, ctx->past_gain_pitch, 5 * sizeof(int16_t)); ctx->past_gain_code[1] = ctx->past_gain_code[0]; @@ -722,12 +734,12 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame_ptr, /* Save signal for use in next frame. */ memmove(ctx->exc_base, ctx->exc_base + 2 * SUBFRAME_SIZE, (PITCH_DELAY_MAX+INTERPOL_LEN)*sizeof(int16_t)); - buf += packet_type == FORMAT_G729_8K ? G729_8K_BLOCK_SIZE : G729D_6K4_BLOCK_SIZE; + buf += format->block_size; ctx++; } *got_frame_ptr = 1; - return packet_type == FORMAT_G729_8K ? G729_8K_BLOCK_SIZE * avctx->channels : G729D_6K4_BLOCK_SIZE * avctx->channels; + return (format->block_size + (avctx->codec_id == AV_CODEC_ID_ACELP_KELVIN)) * avctx->channels; } static av_cold int decode_close(AVCodecContext *avctx) @@ -749,3 +761,15 @@ AVCodec ff_g729_decoder = { .close = decode_close, .capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, }; + +AVCodec ff_acelp_kelvin_decoder = { + .name = "acelp.kelvin", + .long_name = NULL_IF_CONFIG_SMALL("Sipro ACELP.KELVIN"), + .type = AVMEDIA_TYPE_AUDIO, + .id = AV_CODEC_ID_ACELP_KELVIN, + .priv_data_size = sizeof(G729Context), + .init = decoder_init, + .decode = decode_frame, + .close = decode_close, + .capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, +}; diff --git a/libavcodec/g729postfilter.c b/libavcodec/g729postfilter.c index d9076ec7357..617744ec8eb 100644 --- a/libavcodec/g729postfilter.c +++ b/libavcodec/g729postfilter.c @@ -156,7 +156,7 @@ static int16_t long_term_filter(AudioDSPContext *adsp, int pitch_delay_int, sig_scaled[i] = residual[i] >> shift; else for (i = 0; i < subframe_size + RES_PREV_DATA_SIZE; i++) - sig_scaled[i] = residual[i] << -shift; + sig_scaled[i] = (unsigned)residual[i] << -shift; /* Start of best delay searching code */ gain_num = 0; @@ -201,8 +201,8 @@ static int16_t long_term_filter(AudioDSPContext *adsp, int pitch_delay_int, } if (corr_int_num) { /* Compute denominator of pseudo-normalized correlation R'(0). */ - corr_int_den = adsp->scalarproduct_int16(sig_scaled - best_delay_int + RES_PREV_DATA_SIZE, - sig_scaled - best_delay_int + RES_PREV_DATA_SIZE, + corr_int_den = adsp->scalarproduct_int16(sig_scaled + RES_PREV_DATA_SIZE - best_delay_int, + sig_scaled + RES_PREV_DATA_SIZE - best_delay_int, subframe_size); /* Compute signals with non-integer delay k (with 1/8 precision), @@ -346,7 +346,7 @@ static int16_t long_term_filter(AudioDSPContext *adsp, int pitch_delay_int, L_temp1 = gain_long_num * gain_long_num; L_temp1 = MULL(L_temp1, gain_den, FRAC_BITS); - tmp = ((sh_gain_long_num - sh_gain_num) << 1) - (sh_gain_long_den - sh_gain_den); + tmp = ((sh_gain_long_num - sh_gain_num) * 2) - (sh_gain_long_den - sh_gain_den); if (tmp > 0) L_temp0 >>= tmp; else @@ -367,7 +367,7 @@ static int16_t long_term_filter(AudioDSPContext *adsp, int pitch_delay_int, /* Rescale selected signal to original value. */ if (shift > 0) for (i = 0; i < subframe_size; i++) - selected_signal[i] <<= shift; + selected_signal[i] *= 1 << shift; else for (i = 0; i < subframe_size; i++) selected_signal[i] >>= -shift; @@ -464,7 +464,7 @@ static int16_t get_tilt_comp(AudioDSPContext *adsp, int16_t *lp_gn, speech[i] = (speech[i] * temp + 0x4000) >> 15; } - return -(rh1 << 15) / rh0; + return -(rh1 * (1 << 15)) / rh0; } /** @@ -486,29 +486,29 @@ static int16_t apply_tilt_comp(int16_t* out, int16_t* res_pst, int refl_coeff, if (refl_coeff > 0) { gt = (refl_coeff * G729_TILT_FACTOR_PLUS + 0x4000) >> 15; - fact = 0x4000; // 0.5 in (0.15) - sh_fact = 15; + fact = 0x2000; // 0.5 in (0.15) + sh_fact = 14; } else { gt = (refl_coeff * G729_TILT_FACTOR_MINUS + 0x4000) >> 15; - fact = 0x800; // 0.5 in (3.12) - sh_fact = 12; + fact = 0x400; // 0.5 in (3.12) + sh_fact = 11; } - ga = (fact << 15) / av_clip_int16(32768 - FFABS(gt)); + ga = (fact << 16) / av_clip_int16(32768 - FFABS(gt)); gt >>= 1; /* Apply tilt compensation filter to signal. */ tmp = res_pst[subframe_size - 1]; for (i = subframe_size - 1; i >= 1; i--) { - tmp2 = (res_pst[i] << 15) + ((gt * res_pst[i-1]) << 1); - tmp2 = (tmp2 + 0x4000) >> 15; + tmp2 = (gt * res_pst[i-1]) * 2 + 0x4000; + tmp2 = res_pst[i] + (tmp2 >> 15); - tmp2 = (tmp2 * ga * 2 + fact) >> sh_fact; + tmp2 = (tmp2 * ga + fact) >> sh_fact; out[i] = tmp2; } - tmp2 = (res_pst[0] << 15) + ((gt * ht_prev_data) << 1); - tmp2 = (tmp2 + 0x4000) >> 15; - tmp2 = (tmp2 * ga * 2 + fact) >> sh_fact; + tmp2 = (gt * ht_prev_data) * 2 + 0x4000; + tmp2 = res_pst[0] + (tmp2 >> 15); + tmp2 = (tmp2 * ga + fact) >> sh_fact; out[0] = tmp2; return tmp; @@ -600,6 +600,7 @@ int16_t ff_g729_adaptive_gain_control(int gain_before, int gain_after, int16_t * gain = ((gain_before - gain_after) << 14) / gain_after + 0x4000; gain = bidir_sal(gain, exp_after - exp_before); } + gain = av_clip_int16(gain); gain = (gain * G729_AGC_FAC1 + 0x4000) >> 15; // gain * (1-0.9875) } else gain = 0; diff --git a/libavcodec/gdv.c b/libavcodec/gdv.c index a5ce6b799e0..f00f3ac1458 100644 --- a/libavcodec/gdv.c +++ b/libavcodec/gdv.c @@ -358,7 +358,8 @@ static int decompress_68(AVCodecContext *avctx, unsigned skip, unsigned use8) if (val != ((1 << lbits) - 1)) { break; } - assert(lbits < 16); + if (lbits >= 16) + return AVERROR_INVALIDDATA; } for (i = 0; i < len; i++) { bytestream2_put_byte(pb, bytestream2_get_byte(gb)); diff --git a/libavcodec/get_bits.h b/libavcodec/get_bits.h index c4ab607744e..66fb8775994 100644 --- a/libavcodec/get_bits.h +++ b/libavcodec/get_bits.h @@ -234,9 +234,9 @@ static inline void refill_32(GetBitContext *s, int is_le) #endif if (is_le) - s->cache = (uint64_t)AV_RL32(s->buffer + (s->index >> 3)) << s->bits_left | s->cache; + s->cache = (uint64_t)AV_RL32(s->buffer + (s->index >> 3)) << s->bits_left | s->cache; else - s->cache = s->cache | (uint64_t)AV_RB32(s->buffer + (s->index >> 3)) << (32 - s->bits_left); + s->cache = s->cache | (uint64_t)AV_RB32(s->buffer + (s->index >> 3)) << (32 - s->bits_left); s->index += 32; s->bits_left += 32; } @@ -249,9 +249,9 @@ static inline void refill_64(GetBitContext *s, int is_le) #endif if (is_le) - s->cache = AV_RL64(s->buffer + (s->index >> 3)); + s->cache = AV_RL64(s->buffer + (s->index >> 3)); else - s->cache = AV_RB64(s->buffer + (s->index >> 3)); + s->cache = AV_RB64(s->buffer + (s->index >> 3)); s->index += 64; s->bits_left = 64; } diff --git a/libavcodec/gifdec.c b/libavcodec/gifdec.c index 2115da163f6..1906a4c738c 100644 --- a/libavcodec/gifdec.c +++ b/libavcodec/gifdec.c @@ -513,7 +513,7 @@ FF_ENABLE_DEPRECATION_WARNINGS return AVERROR_INVALIDDATA; } - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; s->frame->pict_type = AV_PICTURE_TYPE_P; diff --git a/libavcodec/golomb.h b/libavcodec/golomb.h index 5cdfa0945d3..7fd46a91bd9 100644 --- a/libavcodec/golomb.h +++ b/libavcodec/golomb.h @@ -313,7 +313,7 @@ static inline int get_interleaved_se_golomb(GetBitContext *gb) } else { int log; skip_bits(gb, 8); - buf |= 1 | show_bits_long(gb, 24); + buf |= 1 | show_bits(gb, 24); if ((buf & 0xAAAAAAAA) == 0) return INVALID_VLC; diff --git a/libavcodec/h261enc.c b/libavcodec/h261enc.c index 315762c10c0..196c37b5434 100644 --- a/libavcodec/h261enc.c +++ b/libavcodec/h261enc.c @@ -394,6 +394,7 @@ AVCodec ff_h261_encoder = { .init = ff_mpv_encode_init, .encode2 = ff_mpv_encode_picture, .close = ff_mpv_encode_end, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, .priv_class = &h261_class, diff --git a/libavcodec/h263dec.c b/libavcodec/h263dec.c index 8ee844e298e..31ac563f434 100644 --- a/libavcodec/h263dec.c +++ b/libavcodec/h263dec.c @@ -33,7 +33,7 @@ #include "flv.h" #include "h263.h" #include "h263_parser.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "internal.h" #include "mpeg_er.h" #include "mpeg4video.h" diff --git a/libavcodec/h2645_parse.c b/libavcodec/h2645_parse.c index 307e8643e6a..2e03871640c 100644 --- a/libavcodec/h2645_parse.c +++ b/libavcodec/h2645_parse.c @@ -169,8 +169,8 @@ static const char *hevc_nal_type_name[64] = { "IDR_W_RADL", // HEVC_NAL_IDR_W_RADL "IDR_N_LP", // HEVC_NAL_IDR_N_LP "CRA_NUT", // HEVC_NAL_CRA_NUT - "IRAP_IRAP_VCL22", // HEVC_NAL_IRAP_VCL22 - "IRAP_IRAP_VCL23", // HEVC_NAL_IRAP_VCL23 + "RSV_IRAP_VCL22", // HEVC_NAL_RSV_IRAP_VCL22 + "RSV_IRAP_VCL23", // HEVC_NAL_RSV_IRAP_VCL23 "RSV_VCL24", // HEVC_NAL_RSV_VCL24 "RSV_VCL25", // HEVC_NAL_RSV_VCL25 "RSV_VCL26", // HEVC_NAL_RSV_VCL26 @@ -292,23 +292,22 @@ static int get_bit_length(H2645NAL *nal, int skip_trailing_zeros) static int hevc_parse_nal_header(H2645NAL *nal, void *logctx) { GetBitContext *gb = &nal->gb; - int nuh_layer_id; if (get_bits1(gb) != 0) return AVERROR_INVALIDDATA; nal->type = get_bits(gb, 6); - nuh_layer_id = get_bits(gb, 6); + nal->nuh_layer_id = get_bits(gb, 6); nal->temporal_id = get_bits(gb, 3) - 1; if (nal->temporal_id < 0) return AVERROR_INVALIDDATA; av_log(logctx, AV_LOG_DEBUG, "nal_unit_type: %d(%s), nuh_layer_id: %d, temporal_id: %d\n", - nal->type, hevc_nal_unit_name(nal->type), nuh_layer_id, nal->temporal_id); + nal->type, hevc_nal_unit_name(nal->type), nal->nuh_layer_id, nal->temporal_id); - return nuh_layer_id == 0; + return 1; } static int h264_parse_nal_header(H2645NAL *nal, void *logctx) @@ -455,14 +454,17 @@ int ff_h2645_packet_split(H2645Packet *pkt, const uint8_t *buf, int length, if (pkt->nals_allocated < pkt->nb_nals + 1) { int new_size = pkt->nals_allocated + 1; - void *tmp = av_realloc_array(pkt->nals, new_size, sizeof(*pkt->nals)); + void *tmp; + + if (new_size >= INT_MAX / sizeof(*pkt->nals)) + return AVERROR(ENOMEM); + tmp = av_fast_realloc(pkt->nals, &pkt->nal_buffer_size, new_size * sizeof(*pkt->nals)); if (!tmp) return AVERROR(ENOMEM); pkt->nals = tmp; - memset(pkt->nals + pkt->nals_allocated, 0, - (new_size - pkt->nals_allocated) * sizeof(*pkt->nals)); + memset(pkt->nals + pkt->nals_allocated, 0, sizeof(*pkt->nals)); nal = &pkt->nals[pkt->nb_nals]; nal->skipped_bytes_pos_size = 1024; // initial buffer size @@ -521,7 +523,7 @@ void ff_h2645_packet_uninit(H2645Packet *pkt) av_freep(&pkt->nals[i].skipped_bytes_pos); } av_freep(&pkt->nals); - pkt->nals_allocated = 0; + pkt->nals_allocated = pkt->nal_buffer_size = 0; if (pkt->rbsp.rbsp_buffer_ref) { av_buffer_unref(&pkt->rbsp.rbsp_buffer_ref); pkt->rbsp.rbsp_buffer = NULL; diff --git a/libavcodec/h2645_parse.h b/libavcodec/h2645_parse.h index 2c29ca517c8..3e47f86c53b 100644 --- a/libavcodec/h2645_parse.h +++ b/libavcodec/h2645_parse.h @@ -56,6 +56,11 @@ typedef struct H2645NAL { */ int temporal_id; + /* + * HEVC only, identifier of layer to which nal unit belongs + */ + int nuh_layer_id; + int skipped_bytes; int skipped_bytes_pos_size; int *skipped_bytes_pos; @@ -78,6 +83,7 @@ typedef struct H2645Packet { H2645RBSP rbsp; int nb_nals; int nals_allocated; + unsigned nal_buffer_size; } H2645Packet; /** diff --git a/libavcodec/h264_cabac.c b/libavcodec/h264_cabac.c index 815149a501a..86f0a412faa 100644 --- a/libavcodec/h264_cabac.c +++ b/libavcodec/h264_cabac.c @@ -31,7 +31,6 @@ #include "libavutil/attributes.h" #include "libavutil/avassert.h" -#include "libavutil/timer.h" #include "config.h" #include "cabac.h" #include "cabac_functions.h" @@ -1895,9 +1894,7 @@ static av_always_inline void decode_cabac_luma_residual(const H264Context *h, H2 qmul = h->ps.pps->dequant4_coeff[cqm][qscale]; for( i4x4 = 0; i4x4 < 4; i4x4++ ) { const int index = 16*p + 4*i8x8 + i4x4; -//START_TIMER decode_cabac_residual_nondc(h, sl, sl->mb + (16*index << pixel_shift), ctx_cat[2][p], index, scan, qmul, 16); -//STOP_TIMER("decode_residual") } } } else { diff --git a/libavcodec/h264_metadata_bsf.c b/libavcodec/h264_metadata_bsf.c index 1c1c340d8f7..cef054bd65f 100644 --- a/libavcodec/h264_metadata_bsf.c +++ b/libavcodec/h264_metadata_bsf.c @@ -22,6 +22,7 @@ #include "libavutil/opt.h" #include "bsf.h" +#include "bsf_internal.h" #include "cbs.h" #include "cbs_h264.h" #include "h264.h" @@ -57,6 +58,8 @@ typedef struct H264MetadataContext { AVRational sample_aspect_ratio; + int overscan_appropriate_flag; + int video_format; int video_full_range_flag; int colour_primaries; @@ -122,47 +125,39 @@ static int h264_metadata_update_sps(AVBSFContext *bsf, need_vui = 1; } -#define SET_OR_INFER(field, value, present_flag, infer) do { \ - if (value >= 0) { \ - field = value; \ +#define SET_VUI_FIELD(field) do { \ + if (ctx->field >= 0) { \ + sps->vui.field = ctx->field; \ need_vui = 1; \ - } else if (!present_flag) \ - field = infer; \ + } \ } while (0) + if (ctx->overscan_appropriate_flag >= 0) { + SET_VUI_FIELD(overscan_appropriate_flag); + sps->vui.overscan_info_present_flag = 1; + } + if (ctx->video_format >= 0 || ctx->video_full_range_flag >= 0 || ctx->colour_primaries >= 0 || ctx->transfer_characteristics >= 0 || ctx->matrix_coefficients >= 0) { - SET_OR_INFER(sps->vui.video_format, ctx->video_format, - sps->vui.video_signal_type_present_flag, 5); + SET_VUI_FIELD(video_format); - SET_OR_INFER(sps->vui.video_full_range_flag, - ctx->video_full_range_flag, - sps->vui.video_signal_type_present_flag, 0); + SET_VUI_FIELD(video_full_range_flag); if (ctx->colour_primaries >= 0 || ctx->transfer_characteristics >= 0 || ctx->matrix_coefficients >= 0) { - SET_OR_INFER(sps->vui.colour_primaries, - ctx->colour_primaries, - sps->vui.colour_description_present_flag, 2); - - SET_OR_INFER(sps->vui.transfer_characteristics, - ctx->transfer_characteristics, - sps->vui.colour_description_present_flag, 2); - - SET_OR_INFER(sps->vui.matrix_coefficients, - ctx->matrix_coefficients, - sps->vui.colour_description_present_flag, 2); + SET_VUI_FIELD(colour_primaries); + SET_VUI_FIELD(transfer_characteristics); + SET_VUI_FIELD(matrix_coefficients); sps->vui.colour_description_present_flag = 1; } sps->vui.video_signal_type_present_flag = 1; - need_vui = 1; } if (ctx->chroma_sample_loc_type >= 0) { @@ -186,9 +181,7 @@ static int h264_metadata_update_sps(AVBSFContext *bsf, sps->vui.timing_info_present_flag = 1; need_vui = 1; } - SET_OR_INFER(sps->vui.fixed_frame_rate_flag, - ctx->fixed_frame_rate_flag, - sps->vui.timing_info_present_flag, 0); + SET_VUI_FIELD(fixed_frame_rate_flag); if (sps->separate_colour_plane_flag || sps->chroma_format_idc == 0) { crop_unit_x = 1; @@ -283,6 +276,49 @@ static int h264_metadata_update_sps(AVBSFContext *bsf, return 0; } +static int h264_metadata_update_side_data(AVBSFContext *bsf, AVPacket *pkt) +{ + H264MetadataContext *ctx = bsf->priv_data; + CodedBitstreamFragment *au = &ctx->access_unit; + uint8_t *side_data; + int side_data_size; + int err, i; + + side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, + &side_data_size); + if (!side_data_size) + return 0; + + err = ff_cbs_read(ctx->cbc, au, side_data, side_data_size); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read extradata from packet side data.\n"); + return err; + } + + for (i = 0; i < au->nb_units; i++) { + if (au->units[i].type == H264_NAL_SPS) { + err = h264_metadata_update_sps(bsf, au->units[i].content); + if (err < 0) + return err; + } + } + + err = ff_cbs_write_fragment_data(ctx->cbc, au); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to write extradata into packet side data.\n"); + return err; + } + + side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, au->data_size); + if (!side_data) + return AVERROR(ENOMEM); + memcpy(side_data, au->data, au->data_size); + + ff_cbs_fragment_reset(ctx->cbc, au); + + return 0; +} + static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt) { H264MetadataContext *ctx = bsf->priv_data; @@ -294,6 +330,10 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt) if (err < 0) return err; + err = h264_metadata_update_side_data(bsf, pkt); + if (err < 0) + goto fail; + err = ff_cbs_read_packet(ctx->cbc, au, pkt); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n"); @@ -389,7 +429,7 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt) } else { goto invalid_user_data; } - if (i & 1) + if (j & 1) udu->uuid_iso_iec_11578[j / 2] |= v; else udu->uuid_iso_iec_11578[j / 2] = v << 4; @@ -488,7 +528,7 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt) if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to attach extracted " "displaymatrix side data to packet.\n"); - av_freep(matrix); + av_free(matrix); goto fail; } } @@ -645,6 +685,10 @@ static const AVOption h264_metadata_options[] = { OFFSET(sample_aspect_ratio), AV_OPT_TYPE_RATIONAL, { .dbl = 0.0 }, 0, 65535, FLAGS }, + { "overscan_appropriate_flag", "Set VUI overscan appropriate flag", + OFFSET(overscan_appropriate_flag), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 1, FLAGS }, + { "video_format", "Set video format (table E-2)", OFFSET(video_format), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 7, FLAGS}, diff --git a/libavcodec/h264_mp4toannexb_bsf.c b/libavcodec/h264_mp4toannexb_bsf.c index fb3f24ea406..2822644b105 100644 --- a/libavcodec/h264_mp4toannexb_bsf.c +++ b/libavcodec/h264_mp4toannexb_bsf.c @@ -21,16 +21,21 @@ #include +#include "libavutil/avassert.h" #include "libavutil/intreadwrite.h" #include "libavutil/mem.h" #include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" +#include "bytestream.h" #include "h264.h" typedef struct H264BSFContext { - int32_t sps_offset; - int32_t pps_offset; + uint8_t *sps; + uint8_t *pps; + int sps_size; + int pps_size; uint8_t length_size; uint8_t new_idr; uint8_t idr_sps_seen; @@ -38,98 +43,91 @@ typedef struct H264BSFContext { int extradata_parsed; } H264BSFContext; -static int alloc_and_copy(AVPacket *out, - const uint8_t *sps_pps, uint32_t sps_pps_size, - const uint8_t *in, uint32_t in_size, int ps) +static void count_or_copy(uint8_t **out, uint64_t *out_size, + const uint8_t *in, int in_size, int ps, int copy) { - uint32_t offset = out->size; - uint8_t start_code_size = offset == 0 || ps ? 4 : 3; - int err; - - err = av_grow_packet(out, sps_pps_size + in_size + start_code_size); - if (err < 0) - return err; - - if (sps_pps) - memcpy(out->data + offset, sps_pps, sps_pps_size); - memcpy(out->data + sps_pps_size + start_code_size + offset, in, in_size); - if (start_code_size == 4) { - AV_WB32(out->data + offset + sps_pps_size, 1); - } else { - (out->data + offset + sps_pps_size)[0] = - (out->data + offset + sps_pps_size)[1] = 0; - (out->data + offset + sps_pps_size)[2] = 1; + uint8_t start_code_size = ps < 0 ? 0 : *out_size == 0 || ps ? 4 : 3; + + if (copy) { + memcpy(*out + start_code_size, in, in_size); + if (start_code_size == 4) { + AV_WB32(*out, 1); + } else if (start_code_size) { + (*out)[0] = + (*out)[1] = 0; + (*out)[2] = 1; + } + *out += start_code_size + in_size; } - - return 0; + *out_size += start_code_size + in_size; } static int h264_extradata_to_annexb(AVBSFContext *ctx, const int padding) { H264BSFContext *s = ctx->priv_data; + GetByteContext ogb, *gb = &ogb; uint16_t unit_size; - uint64_t total_size = 0; - uint8_t *out = NULL, unit_nb, sps_done = 0, - sps_seen = 0, pps_seen = 0; - const uint8_t *extradata = ctx->par_in->extradata + 4; + 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 }; - int length_size = (*extradata++ & 0x3) + 1; // retrieve length coded size + int length_size, pps_offset = 0; - s->sps_offset = s->pps_offset = -1; + bytestream2_init(gb, ctx->par_in->extradata, ctx->par_in->extradata_size); + + bytestream2_skipu(gb, 4); + + /* retrieve length coded size */ + length_size = (bytestream2_get_byteu(gb) & 0x3) + 1; /* retrieve sps and pps unit(s) */ - unit_nb = *extradata++ & 0x1f; /* number of sps unit(s) */ + unit_nb = bytestream2_get_byteu(gb) & 0x1f; /* number of sps unit(s) */ if (!unit_nb) { goto pps; - } else { - s->sps_offset = 0; - sps_seen = 1; } while (unit_nb--) { int err; - unit_size = AV_RB16(extradata); + /* possible overread ok due to padding */ + unit_size = bytestream2_get_be16u(gb); total_size += unit_size + 4; - if (total_size > INT_MAX - padding) { - av_log(ctx, AV_LOG_ERROR, - "Too big extradata size, corrupted stream or invalid MP4/AVCC bitstream\n"); - av_free(out); - return AVERROR(EINVAL); - } - if (extradata + 2 + unit_size > ctx->par_in->extradata + ctx->par_in->extradata_size) { - av_log(ctx, AV_LOG_ERROR, "Packet header is not contained in global extradata, " + av_assert1(total_size <= INT_MAX - padding); + if (bytestream2_get_bytes_left(gb) < unit_size + !sps_done) { + av_log(ctx, AV_LOG_ERROR, "Global extradata truncated, " "corrupted stream or invalid MP4/AVCC bitstream\n"); av_free(out); - return AVERROR(EINVAL); + return AVERROR_INVALIDDATA; } if ((err = av_reallocp(&out, total_size + padding)) < 0) return err; memcpy(out + total_size - unit_size - 4, nalu_header, 4); - memcpy(out + total_size - unit_size, extradata + 2, unit_size); - extradata += 2 + unit_size; + bytestream2_get_bufferu(gb, out + total_size - unit_size, unit_size); pps: if (!unit_nb && !sps_done++) { - unit_nb = *extradata++; /* number of pps unit(s) */ - if (unit_nb) { - s->pps_offset = total_size; - pps_seen = 1; - } + unit_nb = bytestream2_get_byteu(gb); /* number of pps unit(s) */ + pps_offset = total_size; } } if (out) memset(out + total_size, 0, padding); - if (!sps_seen) + if (pps_offset) { + s->sps = out; + s->sps_size = pps_offset; + } else { av_log(ctx, AV_LOG_WARNING, "Warning: SPS NALU missing or invalid. " "The resulting stream may not play.\n"); - - if (!pps_seen) + } + if (pps_offset < total_size) { + s->pps = out + pps_offset; + s->pps_size = total_size - pps_offset; + } else { av_log(ctx, AV_LOG_WARNING, "Warning: PPS NALU missing or invalid. " "The resulting stream may not play.\n"); + } av_freep(&ctx->par_out->extradata); ctx->par_out->extradata = out; @@ -150,7 +148,7 @@ static int h264_mp4toannexb_init(AVBSFContext *ctx) (extra_size >= 4 && AV_RB32(ctx->par_in->extradata) == 1)) { av_log(ctx, AV_LOG_VERBOSE, "The input looks like it is Annex B already\n"); - } else if (extra_size >= 6) { + } else if (extra_size >= 7) { ret = h264_extradata_to_annexb(ctx, AV_INPUT_BUFFER_PADDING_SIZE); if (ret < 0) return ret; @@ -168,18 +166,16 @@ static int h264_mp4toannexb_init(AVBSFContext *ctx) return 0; } -static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out) +static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt) { H264BSFContext *s = ctx->priv_data; - AVPacket *in; - uint8_t unit_type; - int32_t nal_size; - uint32_t cumul_size = 0; + uint8_t unit_type, new_idr, sps_seen, pps_seen; const uint8_t *buf; const uint8_t *buf_end; - int buf_size; - int ret = 0, i; + uint8_t *out; + uint64_t out_size; + int ret; ret = ff_bsf_get_packet(ctx, &in); if (ret < 0) @@ -187,94 +183,117 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out) /* nothing to filter */ if (!s->extradata_parsed) { - av_packet_move_ref(out, in); + av_packet_move_ref(opkt, in); av_packet_free(&in); return 0; } - buf = in->data; - buf_size = in->size; buf_end = in->data + in->size; - do { - ret= AVERROR(EINVAL); - if (buf + s->length_size > buf_end) - goto fail; - - for (nal_size = 0, i = 0; ilength_size; i++) - nal_size = (nal_size << 8) | buf[i]; - - buf += s->length_size; - unit_type = *buf & 0x1f; - - if (nal_size > buf_end - buf || nal_size < 0) - goto fail; - - if (unit_type == H264_NAL_SPS) - s->idr_sps_seen = s->new_idr = 1; - else if (unit_type == H264_NAL_PPS) { - s->idr_pps_seen = s->new_idr = 1; - /* if SPS has not been seen yet, prepend the AVCC one to PPS */ - if (!s->idr_sps_seen) { - if (s->sps_offset == -1) - av_log(ctx, AV_LOG_WARNING, "SPS not present in the stream, nor in AVCC, stream may be unreadable\n"); - else { - if ((ret = alloc_and_copy(out, - ctx->par_out->extradata + s->sps_offset, - s->pps_offset != -1 ? s->pps_offset : ctx->par_out->extradata_size - s->sps_offset, - buf, nal_size, 1)) < 0) - goto fail; - s->idr_sps_seen = 1; - goto next_nal; +#define LOG_ONCE(...) \ + if (j) \ + av_log(__VA_ARGS__) + for (int j = 0; j < 2; j++) { + buf = in->data; + new_idr = s->new_idr; + sps_seen = s->idr_sps_seen; + pps_seen = s->idr_pps_seen; + out_size = 0; + + 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) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + if (!nal_size) + continue; + + unit_type = *buf & 0x1f; + + if (unit_type == H264_NAL_SPS) { + sps_seen = new_idr = 1; + } else if (unit_type == H264_NAL_PPS) { + pps_seen = new_idr = 1; + /* if SPS has not been seen yet, prepend the AVCC one to PPS */ + if (!sps_seen) { + 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); + sps_seen = 1; + } } } - } - /* if this is a new IDR picture following an IDR picture, reset the idr flag. - * Just check first_mb_in_slice to be 0 as this is the simplest solution. - * This could be checking idr_pic_id instead, but would complexify the parsing. */ - if (!s->new_idr && unit_type == H264_NAL_IDR_SLICE && (buf[1] & 0x80)) - s->new_idr = 1; - - /* prepend only to the first type 5 NAL unit of an IDR picture, if no sps/pps are already present */ - if (s->new_idr && unit_type == H264_NAL_IDR_SLICE && !s->idr_sps_seen && !s->idr_pps_seen) { - if ((ret=alloc_and_copy(out, - ctx->par_out->extradata, ctx->par_out->extradata_size, - buf, nal_size, 1)) < 0) - goto fail; - s->new_idr = 0; - /* if only SPS has been seen, also insert PPS */ - } else if (s->new_idr && unit_type == H264_NAL_IDR_SLICE && s->idr_sps_seen && !s->idr_pps_seen) { - if (s->pps_offset == -1) { - av_log(ctx, AV_LOG_WARNING, "PPS not present in the stream, nor in AVCC, stream may be unreadable\n"); - if ((ret = alloc_and_copy(out, NULL, 0, buf, nal_size, 0)) < 0) - goto fail; - } else if ((ret = alloc_and_copy(out, - ctx->par_out->extradata + s->pps_offset, ctx->par_out->extradata_size - s->pps_offset, - buf, nal_size, 1)) < 0) - goto fail; - } else { - if ((ret=alloc_and_copy(out, NULL, 0, buf, nal_size, unit_type == H264_NAL_SPS || unit_type == H264_NAL_PPS)) < 0) + /* If this is a new IDR picture following an IDR picture, reset the idr flag. + * Just check first_mb_in_slice to be 0 as this is the simplest solution. + * This could be checking idr_pic_id instead, but would complexify the parsing. */ + if (!new_idr && unit_type == H264_NAL_IDR_SLICE && (buf[1] & 0x80)) + new_idr = 1; + + /* 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); + 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, buf, nal_size, + unit_type == H264_NAL_SPS || unit_type == H264_NAL_PPS, j); + if (!new_idr && unit_type == H264_NAL_SLICE) { + new_idr = 1; + sps_seen = 0; + pps_seen = 0; + } + + buf += nal_size; + } while (buf < buf_end); + + if (!j) { + if (out_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) { + ret = AVERROR_INVALIDDATA; goto fail; - if (!s->new_idr && unit_type == H264_NAL_SLICE) { - s->new_idr = 1; - s->idr_sps_seen = 0; - s->idr_pps_seen = 0; } + ret = av_new_packet(opkt, out_size); + if (ret < 0) + goto fail; + out = opkt->data; } + } +#undef LOG_ONCE + + av_assert1(out_size == opkt->size); -next_nal: - buf += nal_size; - cumul_size += nal_size + s->length_size; - } while (cumul_size < buf_size); + s->new_idr = new_idr; + s->idr_sps_seen = sps_seen; + s->idr_pps_seen = pps_seen; - ret = av_packet_copy_props(out, in); + ret = av_packet_copy_props(opkt, in); if (ret < 0) goto fail; fail: if (ret < 0) - av_packet_unref(out); + av_packet_unref(opkt); av_packet_free(&in); return ret; diff --git a/libavcodec/h264_parse.c b/libavcodec/h264_parse.c index ac31f54e07e..352ffea9489 100644 --- a/libavcodec/h264_parse.c +++ b/libavcodec/h264_parse.c @@ -287,6 +287,8 @@ int ff_h264_init_poc(int pic_field_poc[2], int *pic_poc, if (sps->poc_type == 0) { const int max_poc_lsb = 1 << sps->log2_max_poc_lsb; + if (pc->prev_poc_lsb < 0) + pc->prev_poc_lsb = pc->poc_lsb; if (pc->poc_lsb < pc->prev_poc_lsb && pc->prev_poc_lsb - pc->poc_lsb >= max_poc_lsb / 2) @@ -374,11 +376,22 @@ static int decode_extradata_ps(const uint8_t *data, int size, H264ParamSets *ps, for (i = 0; i < pkt.nb_nals; i++) { H2645NAL *nal = &pkt.nals[i]; switch (nal->type) { - case H264_NAL_SPS: - ret = ff_h264_decode_seq_parameter_set(&nal->gb, logctx, ps, 0); + case H264_NAL_SPS: { + GetBitContext tmp_gb = nal->gb; + ret = ff_h264_decode_seq_parameter_set(&tmp_gb, logctx, ps, 0); + if (ret >= 0) + break; + av_log(logctx, AV_LOG_DEBUG, + "SPS decoding failure, trying again with the complete NAL\n"); + init_get_bits8(&tmp_gb, nal->raw_data + 1, nal->raw_size - 1); + ret = ff_h264_decode_seq_parameter_set(&tmp_gb, logctx, ps, 0); + if (ret >= 0) + break; + ret = ff_h264_decode_seq_parameter_set(&nal->gb, logctx, ps, 1); if (ret < 0) goto fail; break; + } case H264_NAL_PPS: ret = ff_h264_decode_picture_parameter_set(&nal->gb, logctx, ps, nal->size_bits); diff --git a/libavcodec/h264_parser.c b/libavcodec/h264_parser.c index 5f9a9c46eff..aacd44cf3b5 100644 --- a/libavcodec/h264_parser.c +++ b/libavcodec/h264_parser.c @@ -361,26 +361,14 @@ static inline int parse_nal_units(AVCodecParserContext *s, } av_buffer_unref(&p->ps.pps_ref); - av_buffer_unref(&p->ps.sps_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; - - if (!p->ps.sps_list[p->ps.pps->sps_id]) { - av_log(avctx, AV_LOG_ERROR, - "non-existing SPS %u referenced\n", p->ps.pps->sps_id); - goto fail; - } - - p->ps.sps_ref = av_buffer_ref(p->ps.sps_list[p->ps.pps->sps_id]); - if (!p->ps.sps_ref) - goto fail; - p->ps.sps = (const SPS*)p->ps.sps_ref->data; - - sps = p->ps.sps; + p->ps.sps = p->ps.pps->sps; + sps = p->ps.sps; // heuristic to detect non marked keyframes if (p->ps.sps->ref_frame_count <= 1 && p->ps.pps->ref_count[0] <= 1 && s->pict_type == AV_PICTURE_TYPE_I) @@ -481,6 +469,15 @@ static inline int parse_nal_units(AVCodecParserContext *s, } } + if (p->sei.picture_timing.present) { + ret = ff_h264_sei_process_picture_timing(&p->sei.picture_timing, + sps, avctx); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error processing the picture timing SEI\n"); + p->sei.picture_timing.present = 0; + } + } + if (sps->pic_struct_present_flag && p->sei.picture_timing.present) { switch (p->sei.picture_timing.pic_struct) { case H264_SEI_PIC_STRUCT_TOP_FIELD: diff --git a/libavcodec/h264_picture.c b/libavcodec/h264_picture.c index e833835a77a..eec5e9fb9a0 100644 --- a/libavcodec/h264_picture.c +++ b/libavcodec/h264_picture.c @@ -27,7 +27,6 @@ #include "libavutil/avassert.h" #include "libavutil/imgutils.h" -#include "libavutil/timer.h" #include "internal.h" #include "cabac.h" #include "cabac_functions.h" @@ -55,6 +54,7 @@ void ff_h264_unref_picture(H264Context *h, H264Picture *pic) av_buffer_unref(&pic->qscale_table_buf); av_buffer_unref(&pic->mb_type_buf); + av_buffer_unref(&pic->pps_buf); for (i = 0; i < 2; i++) { av_buffer_unref(&pic->motion_val_buf[i]); av_buffer_unref(&pic->ref_index_buf[i]); @@ -78,12 +78,14 @@ int ff_h264_ref_picture(H264Context *h, H264Picture *dst, H264Picture *src) dst->qscale_table_buf = av_buffer_ref(src->qscale_table_buf); dst->mb_type_buf = av_buffer_ref(src->mb_type_buf); - if (!dst->qscale_table_buf || !dst->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; } dst->qscale_table = src->qscale_table; dst->mb_type = src->mb_type; + dst->pps = src->pps; for (i = 0; i < 2; i++) { dst->motion_val_buf[i] = av_buffer_ref(src->motion_val_buf[i]); @@ -121,6 +123,9 @@ int ff_h264_ref_picture(H264Context *h, H264Picture *dst, H264Picture *src) dst->recovered = src->recovered; dst->invalid_gap = src->invalid_gap; dst->sei_recovery_frame_cnt = src->sei_recovery_frame_cnt; + dst->mb_width = src->mb_width; + dst->mb_height = src->mb_height; + dst->mb_stride = src->mb_stride; return 0; fail: diff --git a/libavcodec/h264_ps.c b/libavcodec/h264_ps.c index e8738d85023..e774929e218 100644 --- a/libavcodec/h264_ps.c +++ b/libavcodec/h264_ps.c @@ -104,14 +104,14 @@ static void remove_sps(H264ParamSets *s, int id) av_buffer_unref(&s->sps_list[id]); } -static inline int decode_hrd_parameters(GetBitContext *gb, AVCodecContext *avctx, +static inline int decode_hrd_parameters(GetBitContext *gb, void *logctx, SPS *sps) { int cpb_count, i; cpb_count = get_ue_golomb_31(gb) + 1; if (cpb_count > 32U) { - av_log(avctx, AV_LOG_ERROR, "cpb_count %d invalid\n", cpb_count); + av_log(logctx, AV_LOG_ERROR, "cpb_count %d invalid\n", cpb_count); return AVERROR_INVALIDDATA; } @@ -130,7 +130,7 @@ static inline int decode_hrd_parameters(GetBitContext *gb, AVCodecContext *avctx return 0; } -static inline int decode_vui_parameters(GetBitContext *gb, AVCodecContext *avctx, +static inline int decode_vui_parameters(GetBitContext *gb, void *logctx, SPS *sps) { int aspect_ratio_info_present_flag; @@ -146,7 +146,7 @@ static inline int decode_vui_parameters(GetBitContext *gb, AVCodecContext *avctx } else if (aspect_ratio_idc < FF_ARRAY_ELEMS(ff_h264_pixel_aspect)) { sps->sar = ff_h264_pixel_aspect[aspect_ratio_idc]; } else { - av_log(avctx, AV_LOG_ERROR, "illegal aspect ratio\n"); + av_log(logctx, AV_LOG_ERROR, "illegal aspect ratio\n"); return AVERROR_INVALIDDATA; } } else { @@ -181,12 +181,13 @@ static inline int decode_vui_parameters(GetBitContext *gb, AVCodecContext *avctx /* chroma_location_info_present_flag */ if (get_bits1(gb)) { /* chroma_sample_location_type_top_field */ - avctx->chroma_sample_location = get_ue_golomb(gb) + 1; + sps->chroma_location = get_ue_golomb(gb) + 1; get_ue_golomb(gb); /* chroma_sample_location_type_bottom_field */ - } + } else + sps->chroma_location = AVCHROMA_LOC_LEFT; if (show_bits1(gb) && get_bits_left(gb) < 10) { - av_log(avctx, AV_LOG_WARNING, "Truncated VUI\n"); + av_log(logctx, AV_LOG_WARNING, "Truncated VUI (%d)\n", get_bits_left(gb)); return 0; } @@ -195,7 +196,7 @@ static inline int decode_vui_parameters(GetBitContext *gb, AVCodecContext *avctx unsigned num_units_in_tick = get_bits_long(gb, 32); unsigned time_scale = get_bits_long(gb, 32); if (!num_units_in_tick || !time_scale) { - av_log(avctx, AV_LOG_ERROR, + av_log(logctx, AV_LOG_ERROR, "time_scale/num_units_in_tick invalid or unsupported (%u/%u)\n", time_scale, num_units_in_tick); sps->timing_info_present_flag = 0; @@ -208,11 +209,11 @@ static inline int decode_vui_parameters(GetBitContext *gb, AVCodecContext *avctx sps->nal_hrd_parameters_present_flag = get_bits1(gb); if (sps->nal_hrd_parameters_present_flag) - if (decode_hrd_parameters(gb, avctx, sps) < 0) + if (decode_hrd_parameters(gb, logctx, sps) < 0) return AVERROR_INVALIDDATA; sps->vcl_hrd_parameters_present_flag = get_bits1(gb); if (sps->vcl_hrd_parameters_present_flag) - if (decode_hrd_parameters(gb, avctx, sps) < 0) + if (decode_hrd_parameters(gb, logctx, sps) < 0) return AVERROR_INVALIDDATA; if (sps->nal_hrd_parameters_present_flag || sps->vcl_hrd_parameters_present_flag) @@ -237,7 +238,7 @@ static inline int decode_vui_parameters(GetBitContext *gb, AVCodecContext *avctx if (sps->num_reorder_frames > 16U /* max_dec_frame_buffering || max_dec_frame_buffering > 16 */) { - av_log(avctx, AV_LOG_ERROR, + av_log(logctx, AV_LOG_ERROR, "Clipping illegal num_reorder_frames %d\n", sps->num_reorder_frames); sps->num_reorder_frames = 16; @@ -323,7 +324,6 @@ void ff_h264_ps_uninit(H264ParamSets *ps) for (i = 0; i < MAX_PPS_COUNT; i++) av_buffer_unref(&ps->pps_list[i]); - av_buffer_unref(&ps->sps_ref); av_buffer_unref(&ps->pps_ref); ps->pps = NULL; @@ -579,7 +579,8 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, } if (get_bits_left(gb) < 0) { - av_log(avctx, ignore_truncation ? AV_LOG_WARNING : AV_LOG_ERROR, + av_log_once(avctx, ignore_truncation ? AV_LOG_WARNING : AV_LOG_ERROR, AV_LOG_DEBUG, + &ps->overread_warning_printed[sps->vui_parameters_present_flag], "Overread %s by %d bits\n", sps->vui_parameters_present_flag ? "VUI" : "SPS", -get_bits_left(gb)); if (!ignore_truncation) goto fail; @@ -736,6 +737,15 @@ static int more_rbsp_data_in_pps(const SPS *sps, void *logctx) return 1; } +static void pps_free(void *opaque, uint8_t *data) +{ + PPS *pps = (PPS*)data; + + av_buffer_unref(&pps->sps_ref); + + av_freep(&data); +} + int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avctx, H264ParamSets *ps, int bit_length) { @@ -752,10 +762,15 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct return AVERROR_INVALIDDATA; } - pps_buf = av_buffer_allocz(sizeof(*pps)); - if (!pps_buf) + pps = av_mallocz(sizeof(*pps)); + if (!pps) return AVERROR(ENOMEM); - pps = (PPS*)pps_buf->data; + 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)) { @@ -773,7 +788,14 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct ret = AVERROR_INVALIDDATA; goto fail; } - sps = (const SPS*)ps->sps_list[pps->sps_id]->data; + 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; + sps = pps->sps; + if (sps->bit_depth_luma > 14) { av_log(avctx, AV_LOG_ERROR, "Invalid luma bit depth=%d\n", @@ -793,7 +815,9 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct pps->slice_group_count = get_ue_golomb(gb) + 1; if (pps->slice_group_count > 1) { pps->mb_slice_group_map_type = get_ue_golomb(gb); - av_log(avctx, AV_LOG_ERROR, "FMO not supported\n"); + avpriv_report_missing_feature(avctx, "FMO"); + ret = AVERROR_PATCHWELCOME; + goto fail; } pps->ref_count[0] = get_ue_golomb(gb) + 1; pps->ref_count[1] = get_ue_golomb(gb) + 1; diff --git a/libavcodec/h264_ps.h b/libavcodec/h264_ps.h index 9014326dfb4..3f1ab72e385 100644 --- a/libavcodec/h264_ps.h +++ b/libavcodec/h264_ps.h @@ -77,6 +77,8 @@ typedef struct SPS { enum AVColorPrimaries color_primaries; enum AVColorTransferCharacteristic color_trc; enum AVColorSpace colorspace; + enum AVChromaLocation chroma_location; + int timing_info_present_flag; uint32_t num_units_in_tick; uint32_t time_scale; @@ -133,6 +135,9 @@ typedef struct PPS { uint32_t dequant8_buffer[6][QP_MAX_NUM + 1][64]; uint32_t(*dequant4_coeff[6])[16]; uint32_t(*dequant8_coeff[6])[64]; + + AVBufferRef *sps_ref; + const SPS *sps; } PPS; typedef struct H264ParamSets { @@ -140,10 +145,11 @@ typedef struct H264ParamSets { AVBufferRef *pps_list[MAX_PPS_COUNT]; AVBufferRef *pps_ref; - AVBufferRef *sps_ref; /* currently active parameters sets */ const PPS *pps; const SPS *sps; + + int overread_warning_printed[2]; } H264ParamSets; /** diff --git a/libavcodec/h264_redundant_pps_bsf.c b/libavcodec/h264_redundant_pps_bsf.c index 8405738c4b2..8f69780951f 100644 --- a/libavcodec/h264_redundant_pps_bsf.c +++ b/libavcodec/h264_redundant_pps_bsf.c @@ -22,6 +22,7 @@ #include "libavutil/mem.h" #include "bsf.h" +#include "bsf_internal.h" #include "cbs.h" #include "cbs_h264.h" #include "h264.h" diff --git a/libavcodec/h264_refs.c b/libavcodec/h264_refs.c index 74087ff2662..dae8bd278ad 100644 --- a/libavcodec/h264_refs.c +++ b/libavcodec/h264_refs.c @@ -732,7 +732,7 @@ int ff_h264_execute_ref_pic_marking(H264Context *h) for (j = 0; j < MAX_DELAYED_PIC_COUNT; j++) h->last_pocs[j] = INT_MIN; break; - default: assert(0); + default: av_assert0(0); } } @@ -868,6 +868,7 @@ int ff_h264_decode_ref_pic_marking(H264SliceContext *sl, GetBitContext *gb, av_log(logctx, AV_LOG_ERROR, "illegal long ref in memory management control " "operation %d\n", opcode); + sl->nb_mmco = i; return -1; } mmco[i].long_arg = long_arg; @@ -877,6 +878,7 @@ int ff_h264_decode_ref_pic_marking(H264SliceContext *sl, GetBitContext *gb, av_log(logctx, AV_LOG_ERROR, "illegal memory management control operation %d\n", opcode); + sl->nb_mmco = i; return -1; } if (opcode == MMCO_END) diff --git a/libavcodec/h264_sei.c b/libavcodec/h264_sei.c index d4eb9c0dab6..870dd90717e 100644 --- a/libavcodec/h264_sei.c +++ b/libavcodec/h264_sei.c @@ -54,30 +54,22 @@ void ff_h264_sei_uninit(H264SEIContext *h) av_buffer_unref(&h->a53_caption.buf_ref); } -static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb, - const H264ParamSets *ps, void *logctx) +int ff_h264_sei_process_picture_timing(H264SEIPictureTiming *h, const SPS *sps, + void *logctx) { - int i; - const SPS *sps = ps->sps; - - for (i = 0; ilog2_max_frame_num) && ps->sps_list[i]) - sps = (const SPS *)ps->sps_list[i]->data; + GetBitContext gb; - if (!sps) { - av_log(logctx, AV_LOG_ERROR, "SPS unavailable in decode_picture_timing\n"); - return AVERROR_PS_NOT_FOUND; - } + init_get_bits(&gb, h->payload, h->payload_size_bits); if (sps->nal_hrd_parameters_present_flag || sps->vcl_hrd_parameters_present_flag) { - h->cpb_removal_delay = get_bits_long(gb, sps->cpb_removal_delay_length); - h->dpb_output_delay = get_bits_long(gb, sps->dpb_output_delay_length); + h->cpb_removal_delay = get_bits_long(&gb, sps->cpb_removal_delay_length); + h->dpb_output_delay = get_bits_long(&gb, sps->dpb_output_delay_length); } if (sps->pic_struct_present_flag) { unsigned int i, num_clock_ts; - h->pic_struct = get_bits(gb, 4); + h->pic_struct = get_bits(&gb, 4); h->ct_type = 0; if (h->pic_struct > H264_SEI_PIC_STRUCT_FRAME_TRIPLING) @@ -86,38 +78,38 @@ static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb, num_clock_ts = sei_num_clock_ts_table[h->pic_struct]; h->timecode_cnt = 0; for (i = 0; i < num_clock_ts; i++) { - if (get_bits(gb, 1)) { /* clock_timestamp_flag */ + if (get_bits(&gb, 1)) { /* clock_timestamp_flag */ H264SEITimeCode *tc = &h->timecode[h->timecode_cnt++]; unsigned int full_timestamp_flag; unsigned int counting_type, cnt_dropped_flag; - h->ct_type |= 1 << get_bits(gb, 2); - skip_bits(gb, 1); /* nuit_field_based_flag */ - counting_type = get_bits(gb, 5); /* counting_type */ - full_timestamp_flag = get_bits(gb, 1); - skip_bits(gb, 1); /* discontinuity_flag */ - cnt_dropped_flag = get_bits(gb, 1); /* cnt_dropped_flag */ + h->ct_type |= 1 << get_bits(&gb, 2); + skip_bits(&gb, 1); /* nuit_field_based_flag */ + counting_type = get_bits(&gb, 5); /* counting_type */ + full_timestamp_flag = get_bits(&gb, 1); + skip_bits(&gb, 1); /* discontinuity_flag */ + cnt_dropped_flag = get_bits(&gb, 1); /* cnt_dropped_flag */ if (cnt_dropped_flag && counting_type > 1 && counting_type < 7) tc->dropframe = 1; - tc->frame = get_bits(gb, 8); /* n_frames */ + tc->frame = get_bits(&gb, 8); /* n_frames */ if (full_timestamp_flag) { tc->full = 1; - tc->seconds = get_bits(gb, 6); /* seconds_value 0..59 */ - tc->minutes = get_bits(gb, 6); /* minutes_value 0..59 */ - tc->hours = get_bits(gb, 5); /* hours_value 0..23 */ + tc->seconds = get_bits(&gb, 6); /* seconds_value 0..59 */ + tc->minutes = get_bits(&gb, 6); /* minutes_value 0..59 */ + tc->hours = get_bits(&gb, 5); /* hours_value 0..23 */ } else { tc->seconds = tc->minutes = tc->hours = tc->full = 0; - if (get_bits(gb, 1)) { /* seconds_flag */ - tc->seconds = get_bits(gb, 6); - if (get_bits(gb, 1)) { /* minutes_flag */ - tc->minutes = get_bits(gb, 6); - if (get_bits(gb, 1)) /* hours_flag */ - tc->hours = get_bits(gb, 5); + if (get_bits(&gb, 1)) { /* seconds_flag */ + tc->seconds = get_bits(&gb, 6); + if (get_bits(&gb, 1)) { /* minutes_flag */ + tc->minutes = get_bits(&gb, 6); + if (get_bits(&gb, 1)) /* hours_flag */ + tc->hours = get_bits(&gb, 5); } } } if (sps->time_offset_length > 0) - skip_bits(gb, + skip_bits(&gb, sps->time_offset_length); /* time_offset */ } } @@ -126,6 +118,28 @@ static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb, h->ct_type, h->pic_struct); } + return 0; +} + +static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb, + void *logctx) +{ + int index = get_bits_count(gb); + int size_bits = get_bits_left(gb); + int size = (size_bits + 7) / 8; + + if (index & 7) { + av_log(logctx, AV_LOG_ERROR, "Unaligned SEI payload\n"); + return AVERROR_INVALIDDATA; + } + if (size > sizeof(h->payload)) { + av_log(logctx, AV_LOG_ERROR, "Picture timing SEI payload too large\n"); + return AVERROR_INVALIDDATA; + } + memcpy(h->payload, gb->buffer + index / 8, size); + + h->payload_size_bits = size_bits; + h->present = 1; return 0; } @@ -247,14 +261,14 @@ static int decode_unregistered_user_data(H264SEIUnregistered *h, GetBitContext * uint8_t *user_data; int e, build, i; - if (size < 16 || size >= INT_MAX - 16) + if (size < 16 || size >= INT_MAX - 1) return AVERROR_INVALIDDATA; - user_data = av_malloc(16 + size + 1); + user_data = av_malloc(size + 1); if (!user_data) return AVERROR(ENOMEM); - for (i = 0; i < size + 16; i++) + for (i = 0; i < size; i++) user_data[i] = get_bits(gb, 8); user_data[i] = 0; @@ -407,9 +421,9 @@ int ff_h264_sei_decode(H264SEIContext *h, GetBitContext *gb, int master_ret = 0; while (get_bits_left(gb) > 16 && show_bits(gb, 16)) { + GetBitContext gb_payload; int type = 0; unsigned size = 0; - unsigned next; int ret = 0; do { @@ -429,35 +443,38 @@ int ff_h264_sei_decode(H264SEIContext *h, GetBitContext *gb, type, 8*size, get_bits_left(gb)); return AVERROR_INVALIDDATA; } - next = get_bits_count(gb) + 8 * size; + + ret = init_get_bits8(&gb_payload, gb->buffer + get_bits_count(gb) / 8, size); + if (ret < 0) + return ret; switch (type) { case H264_SEI_TYPE_PIC_TIMING: // Picture timing SEI - ret = decode_picture_timing(&h->picture_timing, gb, ps, logctx); + ret = decode_picture_timing(&h->picture_timing, &gb_payload, logctx); break; case H264_SEI_TYPE_USER_DATA_REGISTERED: - ret = decode_registered_user_data(h, gb, logctx, size); + ret = decode_registered_user_data(h, &gb_payload, logctx, size); break; case H264_SEI_TYPE_USER_DATA_UNREGISTERED: - ret = decode_unregistered_user_data(&h->unregistered, gb, logctx, size); + ret = decode_unregistered_user_data(&h->unregistered, &gb_payload, logctx, size); break; case H264_SEI_TYPE_RECOVERY_POINT: - ret = decode_recovery_point(&h->recovery_point, gb, logctx); + ret = decode_recovery_point(&h->recovery_point, &gb_payload, logctx); break; case H264_SEI_TYPE_BUFFERING_PERIOD: - ret = decode_buffering_period(&h->buffering_period, gb, ps, logctx); + ret = decode_buffering_period(&h->buffering_period, &gb_payload, ps, logctx); break; case H264_SEI_TYPE_FRAME_PACKING: - ret = decode_frame_packing_arrangement(&h->frame_packing, gb); + ret = decode_frame_packing_arrangement(&h->frame_packing, &gb_payload); break; case H264_SEI_TYPE_DISPLAY_ORIENTATION: - ret = decode_display_orientation(&h->display_orientation, gb); + ret = decode_display_orientation(&h->display_orientation, &gb_payload); break; case H264_SEI_TYPE_GREEN_METADATA: - ret = decode_green_metadata(&h->green_metadata, gb); + ret = decode_green_metadata(&h->green_metadata, &gb_payload); break; case H264_SEI_TYPE_ALTERNATIVE_TRANSFER: - ret = decode_alternative_transfer(&h->alternative_transfer, gb); + ret = decode_alternative_transfer(&h->alternative_transfer, &gb_payload); break; default: av_log(logctx, AV_LOG_DEBUG, "unknown SEI type %d\n", type); @@ -467,10 +484,12 @@ int ff_h264_sei_decode(H264SEIContext *h, GetBitContext *gb, if (ret < 0) master_ret = ret; - skip_bits_long(gb, next - get_bits_count(gb)); + if (get_bits_left(&gb_payload) < 0) { + av_log(logctx, AV_LOG_WARNING, "SEI type %d overread by %d bits\n", + type, -get_bits_left(&gb_payload)); + } - // FIXME check bits here - align_get_bits(gb); + skip_bits_long(gb, 8 * size); } return master_ret; diff --git a/libavcodec/h264_sei.h b/libavcodec/h264_sei.h index a75c3aa1753..f07a5055c31 100644 --- a/libavcodec/h264_sei.h +++ b/libavcodec/h264_sei.h @@ -20,6 +20,7 @@ #define AVCODEC_H264_SEI_H #include "get_bits.h" +#include "h264_ps.h" /** * SEI message types @@ -79,6 +80,10 @@ typedef struct H264SEITimeCode { } H264SEITimeCode; typedef struct H264SEIPictureTiming { + // maximum size of pic_timing according to the spec should be 274 bits + uint8_t payload[40]; + int payload_size_bits; + int present; H264_SEI_PicStructType pic_struct; @@ -202,4 +207,10 @@ void ff_h264_sei_uninit(H264SEIContext *h); */ const char *ff_h264_sei_stereo_mode(const H264SEIFramePacking *h); +/** + * Parse the contents of a picture timing message given an active SPS. + */ +int ff_h264_sei_process_picture_timing(H264SEIPictureTiming *h, const SPS *sps, + void *logctx); + #endif /* AVCODEC_H264_SEI_H */ diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c index 5ceee107a04..713953778ae 100644 --- a/libavcodec/h264_slice.c +++ b/libavcodec/h264_slice.c @@ -29,7 +29,6 @@ #include "libavutil/display.h" #include "libavutil/imgutils.h" #include "libavutil/stereo3d.h" -#include "libavutil/timer.h" #include "internal.h" #include "cabac.h" #include "cabac_functions.h" @@ -244,6 +243,15 @@ static int alloc_picture(H264Context *h, H264Picture *pic) pic->ref_index[i] = pic->ref_index_buf[i]->data; } + 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->mb_width = h->mb_width; + pic->mb_height = h->mb_height; + pic->mb_stride = h->mb_stride; + return 0; fail: ff_h264_unref_picture(h, pic); @@ -334,7 +342,6 @@ int ff_h264_update_thread_context(AVCodecContext *dst, } av_buffer_unref(&h->ps.pps_ref); - av_buffer_unref(&h->ps.sps_ref); h->ps.pps = NULL; h->ps.sps = NULL; if (h1->ps.pps_ref) { @@ -342,12 +349,7 @@ int ff_h264_update_thread_context(AVCodecContext *dst, if (!h->ps.pps_ref) return AVERROR(ENOMEM); h->ps.pps = (const PPS*)h->ps.pps_ref->data; - } - if (h1->ps.sps_ref) { - h->ps.sps_ref = av_buffer_ref(h1->ps.sps_ref); - if (!h->ps.sps_ref) - return AVERROR(ENOMEM); - h->ps.sps = (const SPS*)h->ps.sps_ref->data; + h->ps.sps = h->ps.pps->sps; } if (need_reinit || !inited) { @@ -458,12 +460,6 @@ static int h264_frame_start(H264Context *h) H264Picture *pic; int i, ret; const int pixel_shift = h->pixel_shift; - int c[4] = { - 1<<(h->ps.sps->bit_depth_luma-1), - 1<<(h->ps.sps->bit_depth_chroma-1), - 1<<(h->ps.sps->bit_depth_chroma-1), - -1 - }; if (!ff_thread_can_start_frame(h->avctx)) { av_log(h->avctx, AV_LOG_ERROR, "Attempt to start a frame outside SETUP state\n"); @@ -504,8 +500,6 @@ static int h264_frame_start(H264Context *h) if ((ret = alloc_picture(h, pic)) < 0) return ret; - if(!h->frame_recovered && !h->avctx->hwaccel) - ff_color_frame(pic->f, c); h->cur_pic_ptr = pic; ff_h264_unref_picture(h, &h->cur_pic); @@ -873,7 +867,7 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) } /* export coded and cropped frame dimensions to AVCodecContext */ -static int init_dimensions(H264Context *h) +static void init_dimensions(H264Context *h) { const SPS *sps = (const SPS*)h->ps.sps; int cr = sps->crop_right; @@ -911,8 +905,6 @@ static int init_dimensions(H264Context *h) h->crop_left = cl; h->crop_top = ct; h->crop_bottom = cb; - - return 0; } static int h264_slice_header_init(H264Context *h) @@ -1024,13 +1016,8 @@ static int h264_init_ps(H264Context *h, const H264SliceContext *sl, int first_sl h->ps.pps = (const PPS*)h->ps.pps_ref->data; } - if (h->ps.sps != (const SPS*)h->ps.sps_list[h->ps.pps->sps_id]->data) { - av_buffer_unref(&h->ps.sps_ref); - h->ps.sps = NULL; - h->ps.sps_ref = av_buffer_ref(h->ps.sps_list[h->ps.pps->sps_id]); - if (!h->ps.sps_ref) - return AVERROR(ENOMEM); - h->ps.sps = (const SPS*)h->ps.sps_ref->data; + if (h->ps.sps != h->ps.pps->sps) { + h->ps.sps = (const SPS*)h->ps.pps->sps; if (h->mb_width != h->ps.sps->mb_width || h->mb_height != h->ps.sps->mb_height || @@ -1077,9 +1064,7 @@ static int h264_init_ps(H264Context *h, const H264SliceContext *sl, int first_sl h->width = 16 * h->mb_width; h->height = 16 * h->mb_height; - ret = init_dimensions(h); - if (ret < 0) - return ret; + init_dimensions(h); if (sps->video_signal_type_present_flag) { h->avctx->color_range = sps->full_range > 0 ? AVCOL_RANGE_JPEG @@ -1099,6 +1084,7 @@ static int h264_init_ps(H264Context *h, const H264SliceContext *sl, int first_sl h->avctx->color_trc = h->sei.alternative_transfer.preferred_transfer_characteristics; } } + h->avctx->chroma_sample_location = sps->chroma_location; if (!h->context_initialized || must_reinit || needs_reinit) { int flush_changes = h->context_initialized; @@ -1146,6 +1132,16 @@ static int h264_export_frame_props(H264Context *h) /* Signal interlacing information externally. */ /* Prioritize picture timing SEI information over used * decoding process if it exists. */ + if (h->sei.picture_timing.present) { + int ret = ff_h264_sei_process_picture_timing(&h->sei.picture_timing, sps, + h->avctx); + if (ret < 0) { + av_log(h->avctx, AV_LOG_ERROR, "Error processing a picture timing SEI\n"); + if (h->avctx->err_recognition & AV_EF_EXPLODE) + return ret; + h->sei.picture_timing.present = 0; + } + } if (sps->pic_struct_present_flag && h->sei.picture_timing.present) { H264SEIPictureTiming *pt = &h->sei.picture_timing; @@ -1582,6 +1578,13 @@ static int h264_field_start(H264Context *h, const H264SliceContext *sl, * vectors. Given we are concealing a lost frame, this probably * is not noticeable by comparison, but it should be fixed. */ if (h->short_ref_count) { + int c[4] = { + 1<<(h->ps.sps->bit_depth_luma-1), + 1<<(h->ps.sps->bit_depth_chroma-1), + 1<<(h->ps.sps->bit_depth_chroma-1), + -1 + }; + if (prev && h->short_ref[0]->f->width == prev->f->width && h->short_ref[0]->f->height == prev->f->height && @@ -1597,7 +1600,8 @@ static int h264_field_start(H264Context *h, const H264SliceContext *sl, prev->f->width, prev->f->height); h->short_ref[0]->poc = prev->poc + 2; - } + } else if (!h->frame_recovered && !h->avctx->hwaccel) + ff_color_frame(h->short_ref[0]->f, c); h->short_ref[0]->frame_num = h->poc.prev_frame_num; } } @@ -1773,13 +1777,7 @@ static int h264_slice_header_parse(const H264Context *h, H264SliceContext *sl, return AVERROR_INVALIDDATA; } pps = (const PPS*)h->ps.pps_list[sl->pps_id]->data; - - if (!h->ps.sps_list[pps->sps_id]) { - av_log(h->avctx, AV_LOG_ERROR, - "non-existing SPS %u referenced\n", pps->sps_id); - return AVERROR_INVALIDDATA; - } - sps = (const SPS*)h->ps.sps_list[pps->sps_id]->data; + sps = pps->sps; sl->frame_num = get_bits(&sl->gb, sps->log2_max_frame_num); if (!first_slice) { @@ -2165,7 +2163,7 @@ int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal) av_log(h->avctx, AV_LOG_ERROR, "PPS changed between slices\n"); return AVERROR_INVALIDDATA; } - if (h->ps.sps != (const SPS*)h->ps.sps_list[h->ps.pps->sps_id]->data) { + if (h->ps.sps != pps->sps) { av_log(h->avctx, AV_LOG_ERROR, "SPS changed in the middle of the frame\n"); return AVERROR_INVALIDDATA; @@ -2621,7 +2619,6 @@ static int decode_slice(struct AVCodecContext *avctx, void *arg) ff_h264_init_cabac_states(h, sl); for (;;) { - // START_TIMER int ret, eos; if (sl->mb_x + sl->mb_y * h->mb_width >= sl->next_slice_idx) { av_log(h->avctx, AV_LOG_ERROR, "Slice overlaps with next at %d\n", @@ -2632,7 +2629,6 @@ static int decode_slice(struct AVCodecContext *avctx, void *arg) } ret = ff_h264_decode_mb_cabac(h, sl); - // STOP_TIMER("decode_mb_cabac") if (ret >= 0) ff_h264_hl_decode_mb(h, sl); diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c index 8d1bd16a8e7..6270ea80dff 100644 --- a/libavcodec/h264dec.c +++ b/libavcodec/h264dec.c @@ -32,7 +32,8 @@ #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/stereo3d.h" -#include "libavutil/timer.h" +#include "libavutil/video_enc_params.h" + #include "internal.h" #include "bytestream.h" #include "cabac.h" @@ -47,7 +48,7 @@ #include "h264_mvpred.h" #include "h264_ps.h" #include "golomb.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "mathops.h" #include "me_cmp.h" #include "mpegutils.h" @@ -227,7 +228,6 @@ int ff_h264_alloc_tables(H264Context *h) return 0; fail: - ff_h264_free_tables(h); return AVERROR(ENOMEM); } @@ -310,7 +310,6 @@ static int h264_init_context(AVCodecContext *avctx, H264Context *h) h->width_from_caller = avctx->width; h->height_from_caller = avctx->height; - h->picture_structure = PICT_FRAME; h->workaround_bugs = avctx->workaround_bugs; h->flags = avctx->flags; h->poc.prev_poc_msb = 1 << 16; @@ -326,8 +325,6 @@ static int h264_init_context(AVCodecContext *avctx, H264Context *h) ff_h264_sei_uninit(&h->sei); - avctx->chroma_sample_location = AVCHROMA_LOC_LEFT; - h->nb_slice_ctx = (avctx->active_thread_type & FF_THREAD_SLICE) ? avctx->thread_count : 1; h->slice_ctx = av_mallocz_array(h->nb_slice_ctx, sizeof(*h->slice_ctx)); if (!h->slice_ctx) { @@ -412,13 +409,20 @@ static av_cold int h264_decode_init(AVCodecContext *avctx) } avctx->ticks_per_frame = 2; - if (avctx->extradata_size > 0 && avctx->extradata) { - ret = ff_h264_decode_extradata(avctx->extradata, avctx->extradata_size, - &h->ps, &h->is_avc, &h->nal_length_size, - avctx->err_recognition, avctx); - if (ret < 0) { - h264_decode_end(avctx); - return ret; + if (!avctx->internal->is_copy) { + if (avctx->extradata_size > 0 && avctx->extradata) { + ret = ff_h264_decode_extradata(avctx->extradata, avctx->extradata_size, + &h->ps, &h->is_avc, &h->nal_length_size, + avctx->err_recognition, avctx); + if (ret < 0) { + int explode = avctx->err_recognition & AV_EF_EXPLODE; + av_log(avctx, explode ? AV_LOG_ERROR: AV_LOG_WARNING, + "Error decoding the extradata\n"); + if (explode) { + return ret; + } + ret = 0; + } } } @@ -427,8 +431,6 @@ static av_cold int h264_decode_init(AVCodecContext *avctx) h->avctx->has_b_frames = h->ps.sps->num_reorder_frames; } - avctx->internal->allocate_progress = 1; - ff_h264_flush_change(h); if (h->enable_er < 0 && (avctx->active_thread_type & FF_THREAD_SLICE)) @@ -443,27 +445,6 @@ static av_cold int h264_decode_init(AVCodecContext *avctx) return 0; } -#if HAVE_THREADS -static int decode_init_thread_copy(AVCodecContext *avctx) -{ - H264Context *h = avctx->priv_data; - int ret; - - if (!avctx->internal->is_copy) - return 0; - - memset(h, 0, sizeof(*h)); - - ret = h264_init_context(avctx, h); - if (ret < 0) - return ret; - - h->context_initialized = 0; - - return 0; -} -#endif - /** * instantaneous decoder refresh. */ @@ -474,7 +455,7 @@ static void idr(H264Context *h) h->poc.prev_frame_num = h->poc.prev_frame_num_offset = 0; h->poc.prev_poc_msb = 1<<16; - h->poc.prev_poc_lsb = 0; + h->poc.prev_poc_lsb = -1; for (i = 0; i < MAX_DELAYED_PIC_COUNT; i++) h->last_pocs[i] = INT_MIN; } @@ -505,8 +486,7 @@ void ff_h264_flush_change(H264Context *h) h->mmco_reset = 1; } -/* forget old pics after a seek */ -static void flush_dpb(AVCodecContext *avctx) +static void h264_decode_flush(AVCodecContext *avctx) { H264Context *h = avctx->priv_data; int i; @@ -623,7 +603,7 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size) } ret = ff_h2645_packet_split(&h->pkt, buf, buf_size, avctx, h->is_avc, h->nal_length_size, - avctx->codec_id, avctx->flags2 & AV_CODEC_FLAG2_FAST, 0); + avctx->codec_id, 0, 0); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "Error splitting the input into NAL units.\n"); @@ -782,9 +762,7 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size) * past end by one (callers fault) and resync_mb_y != 0 * causes problems for the first MB line, too. */ - if (!FIELD_PICTURE(h) && h->current_slice && - h->ps.sps == (const SPS*)h->ps.sps_list[h->ps.pps->sps_id]->data && - h->enable_er) { + if (!FIELD_PICTURE(h) && h->current_slice && h->enable_er) { H264SliceContext *sl = h->slice_ctx; int use_last_pic = h->last_pic_for_ec.f->buf[0] && !sl->ref_count[0]; @@ -834,6 +812,40 @@ static int get_consumed_bytes(int pos, int buf_size) return pos; } +static int h264_export_enc_params(AVFrame *f, H264Picture *p) +{ + AVVideoEncParams *par; + unsigned int nb_mb = p->mb_height * p->mb_width; + unsigned int x, y; + + par = av_video_enc_params_create_side_data(f, AV_VIDEO_ENC_PARAMS_H264, nb_mb); + if (!par) + return AVERROR(ENOMEM); + + par->qp = p->pps->init_qp; + + par->delta_qp[1][0] = p->pps->chroma_qp_index_offset[0]; + par->delta_qp[1][1] = p->pps->chroma_qp_index_offset[0]; + par->delta_qp[2][0] = p->pps->chroma_qp_index_offset[1]; + par->delta_qp[2][1] = p->pps->chroma_qp_index_offset[1]; + + for (y = 0; y < p->mb_height; y++) + for (x = 0; x < p->mb_width; x++) { + const unsigned int block_idx = y * p->mb_width + x; + const unsigned int mb_xy = y * p->mb_stride + x; + AVVideoBlockParams *b = av_video_enc_params_block(par, block_idx); + + b->src_x = x * 16; + b->src_y = y * 16; + b->w = 16; + b->h = 16; + + b->delta_qp = p->qscale_table[mb_xy] - par->qp; + } + + return 0; +} + static int output_frame(H264Context *h, AVFrame *dst, H264Picture *srcp) { AVFrame *src = srcp->f; @@ -848,7 +860,16 @@ static int output_frame(H264Context *h, AVFrame *dst, H264Picture *srcp) if (srcp->sei_recovery_frame_cnt == 0) dst->key_frame = 1; + if (h->avctx->export_side_data & AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS) { + ret = h264_export_enc_params(dst, srcp); + if (ret < 0) + goto fail; + } + return 0; +fail: + av_frame_unref(dst); + return ret; } static int is_extra(const uint8_t *buf, int buf_size) @@ -1083,9 +1104,9 @@ AVCodec ff_h264_decoder = { #endif NULL }, - .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_EXPORTS_CROPPING, - .flush = flush_dpb, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy), + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_EXPORTS_CROPPING | + FF_CODEC_CAP_ALLOCATE_PROGRESS | FF_CODEC_CAP_INIT_CLEANUP, + .flush = h264_decode_flush, .update_thread_context = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context), .profiles = NULL_IF_CONFIG_SMALL(ff_h264_profiles), .priv_class = &h264_class, diff --git a/libavcodec/h264dec.h b/libavcodec/h264dec.h index 1d9723260dd..29c4d4e42c1 100644 --- a/libavcodec/h264dec.h +++ b/libavcodec/h264dec.h @@ -161,6 +161,12 @@ typedef struct H264Picture { int recovered; ///< picture at IDR or recovery point + recovery count int invalid_gap; int sei_recovery_frame_cnt; + + AVBufferRef *pps_buf; + const PPS *pps; + + int mb_width, mb_height; + int mb_stride; } H264Picture; typedef struct H264Ref { @@ -832,8 +838,6 @@ int ff_h264_slice_context_init(H264Context *h, H264SliceContext *sl); void ff_h264_draw_horiz_band(const H264Context *h, H264SliceContext *sl, int y, int height); -int ff_h264_decode_slice_header(H264Context *h, H264SliceContext *sl, - const H2645NAL *nal); /** * Submit a slice for decoding. * diff --git a/libavcodec/h265_metadata_bsf.c b/libavcodec/h265_metadata_bsf.c index b3a1fda144e..749456157b9 100644 --- a/libavcodec/h265_metadata_bsf.c +++ b/libavcodec/h265_metadata_bsf.c @@ -20,6 +20,7 @@ #include "libavutil/opt.h" #include "bsf.h" +#include "bsf_internal.h" #include "cbs.h" #include "cbs_h265.h" #include "hevc.h" @@ -131,7 +132,7 @@ static void h265_metadata_guess_level(AVBSFContext *bsf, } desc = ff_h265_guess_level(ptl, bit_rate, width, height, - 0, tile_cols, tile_rows, + 0, tile_rows, tile_cols, max_dec_pic_buffering); if (desc) { av_log(bsf, AV_LOG_DEBUG, "Stream appears to conform to " @@ -336,6 +337,57 @@ static int h265_metadata_update_sps(AVBSFContext *bsf, return 0; } +static int h265_metadata_update_side_data(AVBSFContext *bsf, AVPacket *pkt) +{ + H265MetadataContext *ctx = bsf->priv_data; + CodedBitstreamFragment *au = &ctx->access_unit; + uint8_t *side_data; + int side_data_size; + int err, i; + + side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, + &side_data_size); + if (!side_data_size) + return 0; + + err = ff_cbs_read(ctx->cbc, au, side_data, side_data_size); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read extradata from packet side data.\n"); + return err; + } + + if (ctx->level == LEVEL_AUTO && !ctx->level_guess) + h265_metadata_guess_level(bsf, au); + + for (i = 0; i < au->nb_units; i++) { + if (au->units[i].type == HEVC_NAL_VPS) { + err = h265_metadata_update_vps(bsf, au->units[i].content); + if (err < 0) + return err; + } + if (au->units[i].type == HEVC_NAL_SPS) { + err = h265_metadata_update_sps(bsf, au->units[i].content); + if (err < 0) + return err; + } + } + + err = ff_cbs_write_fragment_data(ctx->cbc, au); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to write extradata into packet side data.\n"); + return err; + } + + side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, au->data_size); + if (!side_data) + return AVERROR(ENOMEM); + memcpy(side_data, au->data, au->data_size); + + ff_cbs_fragment_reset(ctx->cbc, au); + + return 0; +} + static int h265_metadata_filter(AVBSFContext *bsf, AVPacket *pkt) { H265MetadataContext *ctx = bsf->priv_data; @@ -346,6 +398,10 @@ static int h265_metadata_filter(AVBSFContext *bsf, AVPacket *pkt) if (err < 0) return err; + err = h265_metadata_update_side_data(bsf, pkt); + if (err < 0) + goto fail; + err = ff_cbs_read_packet(ctx->cbc, au, pkt); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n"); diff --git a/libavcodec/h265_profile_level.c b/libavcodec/h265_profile_level.c index 70db1a52f6e..d79c1ab2046 100644 --- a/libavcodec/h265_profile_level.c +++ b/libavcodec/h265_profile_level.c @@ -188,7 +188,7 @@ const H265LevelDescriptor *ff_h265_guess_level(const H265RawProfileTierLevel *pt profile = NULL; if (!profile) { // Default to using multiplication factors for Main profile. - profile = &h265_profiles[3]; + profile = &h265_profiles[4]; } pic_size = width * height; diff --git a/libavcodec/hapdec.c b/libavcodec/hapdec.c index 8c845770cf3..ab364aa7906 100644 --- a/libavcodec/hapdec.c +++ b/libavcodec/hapdec.c @@ -305,7 +305,6 @@ static int hap_decode(AVCodecContext *avctx, void *data, HapContext *ctx = avctx->priv_data; ThreadFrame tframe; int ret, i, t; - int tex_size; int section_size; enum HapSectionType section_type; int start_texture_section = 0; @@ -342,6 +341,13 @@ static int hap_decode(AVCodecContext *avctx, void *data, if (ret < 0) return ret; + if (ctx->tex_size != (avctx->coded_width / TEXTURE_BLOCK_W) + *(avctx->coded_height / TEXTURE_BLOCK_H) + *tex_rat[t]) { + av_log(avctx, AV_LOG_ERROR, "uncompressed size mismatches\n"); + return AVERROR_INVALIDDATA; + } + start_texture_section += ctx->texture_section_size + 4; if (avctx->codec->update_thread_context) @@ -349,9 +355,16 @@ static int hap_decode(AVCodecContext *avctx, void *data, /* Unpack the DXT texture */ if (hap_can_use_tex_in_place(ctx)) { + int tex_size; /* Only DXTC texture compression in a contiguous block */ ctx->tex_data = ctx->gbc.buffer; tex_size = FFMIN(ctx->texture_section_size, bytestream2_get_bytes_left(&ctx->gbc)); + if (tex_size < (avctx->coded_width / TEXTURE_BLOCK_W) + *(avctx->coded_height / TEXTURE_BLOCK_H) + *tex_rat[t]) { + av_log(avctx, AV_LOG_ERROR, "Insufficient data\n"); + return AVERROR_INVALIDDATA; + } } else { /* Perform the second-stage decompression */ ret = av_reallocp(&ctx->tex_buf, ctx->tex_size); @@ -367,14 +380,6 @@ static int hap_decode(AVCodecContext *avctx, void *data, } ctx->tex_data = ctx->tex_buf; - tex_size = ctx->tex_size; - } - - if (tex_size < (avctx->coded_width / TEXTURE_BLOCK_W) - *(avctx->coded_height / TEXTURE_BLOCK_H) - *tex_rat[t]) { - av_log(avctx, AV_LOG_ERROR, "Insufficient data\n"); - return AVERROR_INVALIDDATA; } /* Use the decompress function on the texture, one block per thread */ @@ -484,4 +489,12 @@ AVCodec ff_hap_decoder = { AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, + .codec_tags = (const uint32_t []){ + MKTAG('H','a','p','1'), + MKTAG('H','a','p','5'), + MKTAG('H','a','p','Y'), + MKTAG('H','a','p','A'), + MKTAG('H','a','p','M'), + FF_CODEC_TAGS_END, + }, }; diff --git a/libavcodec/hapqa_extract_bsf.c b/libavcodec/hapqa_extract_bsf.c index 5c22184813d..1c8b0669b78 100644 --- a/libavcodec/hapqa_extract_bsf.c +++ b/libavcodec/hapqa_extract_bsf.c @@ -25,8 +25,8 @@ * extract one of the two textures of the HAQA */ -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "bytestream.h" #include "hap.h" diff --git a/libavcodec/hca_data.h b/libavcodec/hca_data.h new file mode 100644 index 00000000000..80b4a794dcb --- /dev/null +++ b/libavcodec/hca_data.h @@ -0,0 +1,174 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_HCA_DATA_H +#define AVCODEC_HCA_DATA_H + +#include + +static const uint8_t max_bits_table[] = { + 0, 2, 3, 3, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, +}; + +static const uint8_t quant_spectrum_bits[] = +{ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,3,3,0,0,0,0,0,0,0,0, + 2,2,3,3,3,3,3,3,0,0,0,0,0,0,0,0, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4, + 3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4, + 3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4, + 3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +}; + +static const int8_t quant_spectrum_value[] = +{ + +0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0, + +0,+0,+1,-1,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0, + +0,+0,+1,+1,-1,-1,+2,-2,+0,+0,+0,+0,+0,+0,+0,+0, + +0,+0,+1,-1,+2,-2,+3,-3,+0,+0,+0,+0,+0,+0,+0,+0, + +0,+0,+1,+1,-1,-1,+2,+2,-2,-2,+3,+3,-3,-3,+4,-4, + +0,+0,+1,+1,-1,-1,+2,+2,-2,-2,+3,-3,+4,-4,+5,-5, + +0,+0,+1,+1,-1,-1,+2,-2,+3,-3,+4,-4,+5,-5,+6,-6, + +0,+0,+1,-1,+2,-2,+3,-3,+4,-4,+5,-5,+6,-6,+7,-7, +}; + +static const uint8_t scale_table[] = +{ + 15, 14, 14, 14, 14, 14, 14, 13, 13, + 13, 13, 13, 13, 12, 12, 12, 12, + 12, 12, 11, 11, 11, 11, 11, 11, + 10, 10, 10, 10, 10, 10, 10, 9, + 9, 9, 9, 9, 9, 8, 8, 8, + 8, 8, 8, 7, 6, 6, 5, 4, + 4, 4, 3, 3, 3, 2, 2, 2, + 2, 1, +}; + +static const float window[128] = +{ + 0.000690534, 0.00197623, 0.00367386, 0.00572424, 0.0080967, 0.0107732, 0.0137425, 0.0169979, + 0.0205353, 0.0243529, 0.0284505, 0.0328291, 0.0374906, 0.0424379, 0.0476744, 0.0532043, + 0.0590321, 0.0651629, 0.071602, 0.0783552, 0.0854285, 0.092828, 0.10056, 0.108631, + 0.117048, 0.125817, 0.134944, 0.144437, 0.1543, 0.164539, 0.175161, 0.186169, + 0.197569, 0.209363, 0.221555, 0.234145, 0.247136, 0.260526, 0.274313, 0.288493, + 0.303062, 0.318012, 0.333333, 0.349015, 0.365044, 0.381403, 0.398073, 0.415034, + 0.43226, 0.449725, 0.4674, 0.485251, 0.503245, 0.521344, 0.539509, 0.557698, + 0.575869, 0.593978, 0.611981, 0.629831, 0.647486, 0.6649, 0.682031, 0.698838, + 0.71528, 0.731323, 0.746932, 0.762077, 0.776732, 0.790873, 0.804481, 0.817542, + 0.830044, 0.84198, 0.853347, 0.864144, 0.874375, 0.884046, 0.893167, 0.901749, + 0.909806, 0.917354, 0.924409, 0.93099, 0.937117, 0.942809, 0.948087, 0.952971, + 0.957482, 0.961641, 0.965467, 0.968981, 0.972202, 0.975148, 0.977838, 0.980289, + 0.982518, 0.98454, 0.986371, 0.988024, 0.989514, 0.990853, 0.992053, 0.993126, + 0.994082, 0.994931, 0.995682, 0.996344, 0.996926, 0.997433, 0.997875, 0.998256, + 0.998584, 0.998863, 0.999099, 0.999297, 0.999461, 0.999595, 0.999703, 0.999789, + 0.999856, 0.999906, 0.999942, 0.999967, 0.999984, 0.999993, 0.999998, 1.0, +}; + +static const float intensity_ratio_table[] = +{ + 2.0, 1.85714, 1.71429, 1.57143, 1.42857, 1.28571, 1.14286, 1.0, + 0.857143, 0.714286, 0.571429, 0.428571, 0.285714, 0.142857, 0.0, 0.0, + 0, 1.87066e-08, 2.49253e-08, 3.32113e-08, 4.42518e-08, 5.89626e-08, 7.85637e-08, 1.04681e-07, + 1.3948e-07, 1.85848e-07, 2.4763e-07, 3.2995e-07, 4.39636e-07, 5.85785e-07, 7.80519e-07, 1.03999e-06, + 1.38572e-06, 1.84637e-06, 2.46017e-06, 3.27801e-06, 4.36772e-06, 5.8197e-06, 7.75435e-06, 1.03321e-05, + 1.37669e-05, 1.83435e-05, 2.44414e-05, 3.25665e-05, 4.33927e-05, 5.78179e-05, 7.70384e-05, 0.000102648, + 0.000136772, 0.00018224, 0.000242822, 0.000323544, 0.000431101, 0.000574413, 0.000765366, 0.0010198, + 0.00135881, 0.00181053, 0.0024124, 0.00321437, 0.00428293, 0.00570671, 0.00760381, 0.0101316, + 0.0134996, 0.0179873, 0.0239669, 0.0319343, 0.0425503, 0.0566954, 0.0755428, 0.100656, + 0.134117, 0.178702, 0.238108, 0.317263, 0.422731, 0.563261, 0.750507, 0.0, +}; + +static const float scale_conversion_table[] = +{ + 1.0, 1.33243, 1.77538, 2.36557, 3.15196, 4.19978, 5.59592, 7.45618, + 9.93486, 13.2375, 17.6381, 23.5016, 31.3143, 41.7242, 55.5947, 74.0762, + 98.7015, 131.513, 175.232, 233.485, 311.103, 414.524, 552.326, 735.937, + 980.586, 1306.56, 1740.91, 2319.64, 3090.77, 4118.24, 5487.28, 7311.43, + 9741.98, 12980.5, 17295.7, 23045.3, 30706.4, 40914.2, 54515.4, 72638, + 96785.3, 128960, 171830, 228952, 305064, 406477, 541603, 721649, + 961548, 1.2812e+06, 1.70711e+06, 2.27461e+06, 3.03076e+06, 4.03829e+06, 5.38075e+06, 7.16948e+06, + 9.55285e+06, 1.27285e+07, 1.69599e+07, 2.25979e+07, 3.01102e+07, 4.01198e+07, 5.3457e+07, 0, +}; + +static const float dequantizer_scaling_table[] = +{ + 1.58838e-07, 2.11641e-07, 2.81998e-07, 3.75743e-07, 5.00652e-07, 6.67085e-07, 8.88846e-07, 1.18433e-06, + 1.57804e-06, 2.10263e-06, 2.80161e-06, 3.73296e-06, 4.97391e-06, 6.6274e-06, 8.83057e-06, 1.17661e-05, + 1.56776e-05, 2.08893e-05, 2.78336e-05, 3.70864e-05, 4.94151e-05, 6.58423e-05, 8.77305e-05, 0.000116895, + 0.000155755, 0.000207533, 0.000276523, 0.000368448, 0.000490933, 0.000654135, 0.00087159, 0.00116134, + 0.0015474, 0.00206181, 0.00274722, 0.00366048, 0.00487735, 0.00649874, 0.00865913, 0.0115377, + 0.0153732, 0.0204838, 0.0272932, 0.0363664, 0.0484558, 0.0645641, 0.0860272, 0.114626, + 0.152731, 0.203503, 0.271155, 0.361295, 0.481401, 0.641435, 0.854669, 1.13879, + 1.51736, 2.02178, 2.69388, 3.58942, 4.78266, 6.37257, 8.49102, 11.3137, +}; + +static const float quant_step_size[] = +{ + 0.0, 0.666667, 0.4, 0.285714, 0.222222, 0.181818, 0.153846, 0.133333, 0.0645161, + 0.031746, 0.015748, 0.00784314, 0.00391389, 0.00195503, 0.00097704, 0.000488401, +}; + +static const uint8_t ath_base_curve[656] = +{ + 0x78,0x5F,0x56,0x51,0x4E,0x4C,0x4B,0x49,0x48,0x48,0x47,0x46,0x46,0x45,0x45,0x45, + 0x44,0x44,0x44,0x44,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x42,0x42,0x42,0x42,0x42, + 0x42,0x42,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x40,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, + 0x3F,0x3F,0x3F,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, + 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, + 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, + 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, + 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3F, + 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, + 0x3F,0x3F,0x3F,0x3F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x41,0x41,0x41,0x41,0x41,0x41,0x41, + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42, + 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x43,0x43,0x43, + 0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x44,0x44, + 0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x45,0x45,0x45,0x45, + 0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46, + 0x46,0x46,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x48,0x48,0x48,0x48, + 0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x4A,0x4A,0x4A,0x4A, + 0x4A,0x4A,0x4A,0x4A,0x4B,0x4B,0x4B,0x4B,0x4B,0x4B,0x4B,0x4C,0x4C,0x4C,0x4C,0x4C, + 0x4C,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4E,0x4E,0x4E,0x4E,0x4E,0x4E,0x4F,0x4F,0x4F, + 0x4F,0x4F,0x4F,0x50,0x50,0x50,0x50,0x50,0x51,0x51,0x51,0x51,0x51,0x52,0x52,0x52, + 0x52,0x52,0x53,0x53,0x53,0x53,0x54,0x54,0x54,0x54,0x54,0x55,0x55,0x55,0x55,0x56, + 0x56,0x56,0x56,0x57,0x57,0x57,0x57,0x57,0x58,0x58,0x58,0x59,0x59,0x59,0x59,0x5A, + 0x5A,0x5A,0x5A,0x5B,0x5B,0x5B,0x5B,0x5C,0x5C,0x5C,0x5D,0x5D,0x5D,0x5D,0x5E,0x5E, + 0x5E,0x5F,0x5F,0x5F,0x60,0x60,0x60,0x61,0x61,0x61,0x61,0x62,0x62,0x62,0x63,0x63, + 0x63,0x64,0x64,0x64,0x65,0x65,0x66,0x66,0x66,0x67,0x67,0x67,0x68,0x68,0x68,0x69, + 0x69,0x6A,0x6A,0x6A,0x6B,0x6B,0x6B,0x6C,0x6C,0x6D,0x6D,0x6D,0x6E,0x6E,0x6F,0x6F, + 0x70,0x70,0x70,0x71,0x71,0x72,0x72,0x73,0x73,0x73,0x74,0x74,0x75,0x75,0x76,0x76, + 0x77,0x77,0x78,0x78,0x78,0x79,0x79,0x7A,0x7A,0x7B,0x7B,0x7C,0x7C,0x7D,0x7D,0x7E, + 0x7E,0x7F,0x7F,0x80,0x80,0x81,0x81,0x82,0x83,0x83,0x84,0x84,0x85,0x85,0x86,0x86, + 0x87,0x88,0x88,0x89,0x89,0x8A,0x8A,0x8B,0x8C,0x8C,0x8D,0x8D,0x8E,0x8F,0x8F,0x90, + 0x90,0x91,0x92,0x92,0x93,0x94,0x94,0x95,0x95,0x96,0x97,0x97,0x98,0x99,0x99,0x9A, + 0x9B,0x9B,0x9C,0x9D,0x9D,0x9E,0x9F,0xA0,0xA0,0xA1,0xA2,0xA2,0xA3,0xA4,0xA5,0xA5, + 0xA6,0xA7,0xA7,0xA8,0xA9,0xAA,0xAA,0xAB,0xAC,0xAD,0xAE,0xAE,0xAF,0xB0,0xB1,0xB1, + 0xB2,0xB3,0xB4,0xB5,0xB6,0xB6,0xB7,0xB8,0xB9,0xBA,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xC9,0xCA,0xCB,0xCC,0xCD, + 0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD, + 0xDE,0xDF,0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xED,0xEE, + 0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFF,0xFF, +}; + +#endif /* AVCODEC_HCA_DATA_H */ diff --git a/libavcodec/hcadec.c b/libavcodec/hcadec.c new file mode 100644 index 00000000000..f46ed699d12 --- /dev/null +++ b/libavcodec/hcadec.c @@ -0,0 +1,458 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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/crc.h" +#include "libavutil/float_dsp.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/tx.h" + +#include "avcodec.h" +#include "get_bits.h" +#include "internal.h" +#include "hca_data.h" + +typedef struct ChannelContext { + float base[128]; + DECLARE_ALIGNED(32, float, imdct_in)[128]; + DECLARE_ALIGNED(32, float, imdct_out)[128]; + DECLARE_ALIGNED(32, float, imdct_prev)[128]; + int8_t scale_factors[128]; + uint8_t scale[128]; + int8_t intensity[8]; + int8_t *hfr_scale; + unsigned count; + int chan_type; +} ChannelContext; + +typedef struct HCAContext { + GetBitContext gb; + + const AVCRC *crc_table; + + ChannelContext ch[16]; + + uint8_t ath[128]; + + int ath_type; + unsigned hfr_group_count; + uint8_t track_count; + uint8_t channel_config; + uint8_t total_band_count; + uint8_t base_band_count; + uint8_t stereo_band_count; + uint8_t bands_per_hfr_group; + + av_tx_fn tx_fn; + AVTXContext *tx_ctx; + AVFloatDSPContext *fdsp; +} HCAContext; + +static void ath_init1(uint8_t *ath, int sample_rate) +{ + unsigned int index; + unsigned int acc = 0; + + for (int i = 0; i < 128; i++) { + acc += sample_rate; + index = acc >> 13; + + if (index >= 654) { + memset(ath+i, 0xFF, (128 - i)); + break; + } + + ath[i] = ath_base_curve[index]; + } +} + +static int ath_init(uint8_t *ath, int type, int sample_rate) +{ + switch (type) { + case 0: + /* nothing to do */ + break; + case 1: + ath_init1(ath, sample_rate); + break; + default: + return AVERROR_INVALIDDATA; + } + + return 0; +} + +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) +{ + HCAContext *c = avctx->priv_data; + GetBitContext *gb = &c->gb; + int8_t r[16] = { 0 }; + float scale = 1.f / 8.f; + unsigned b, chunk; + int version, ret; + + avctx->sample_fmt = AV_SAMPLE_FMT_FLTP; + c->crc_table = av_crc_get_table(AV_CRC_16_ANSI); + + if (avctx->channels <= 0 || avctx->channels > 16) + return AVERROR(EINVAL); + + ret = init_get_bits8(gb, avctx->extradata, avctx->extradata_size); + if (ret < 0) + return ret; + skip_bits_long(gb, 32); + version = get_bits(gb, 16); + skip_bits_long(gb, 16); + + c->ath_type = version >= 0x200 ? 0 : 1; + + if (get_bits_long(gb, 32) != MKBETAG('f', 'm', 't', 0)) + return AVERROR_INVALIDDATA; + skip_bits_long(gb, 32); + skip_bits_long(gb, 32); + skip_bits_long(gb, 32); + + chunk = get_bits_long(gb, 32); + if (chunk == MKBETAG('c', 'o', 'm', 'p')) { + skip_bits_long(gb, 16); + skip_bits_long(gb, 8); + skip_bits_long(gb, 8); + c->track_count = get_bits(gb, 8); + c->channel_config = get_bits(gb, 8); + c->total_band_count = get_bits(gb, 8); + c->base_band_count = get_bits(gb, 8); + c->stereo_band_count = get_bits(gb, 8); + c->bands_per_hfr_group = get_bits(gb, 8); + } else if (chunk == MKBETAG('d', 'e', 'c', 0)) { + skip_bits_long(gb, 16); + skip_bits_long(gb, 8); + skip_bits_long(gb, 8); + c->total_band_count = get_bits(gb, 8) + 1; + c->base_band_count = get_bits(gb, 8) + 1; + c->track_count = get_bits(gb, 4); + c->channel_config = get_bits(gb, 4); + if (!get_bits(gb, 8)) + c->base_band_count = c->total_band_count; + c->stereo_band_count = c->total_band_count - c->base_band_count; + c->bands_per_hfr_group = 0; + } else + return AVERROR_INVALIDDATA; + + if (c->total_band_count > FF_ARRAY_ELEMS(c->ch->imdct_in)) + return AVERROR_INVALIDDATA; + + + while (get_bits_left(gb) >= 32) { + chunk = get_bits_long(gb, 32); + if (chunk == MKBETAG('v', 'b', 'r', 0)) { + skip_bits_long(gb, 16); + skip_bits_long(gb, 16); + } else if (chunk == MKBETAG('a', 't', 'h', 0)) { + c->ath_type = get_bits(gb, 16); + } else if (chunk == MKBETAG('r', 'v', 'a', 0)) { + skip_bits_long(gb, 32); + } else if (chunk == MKBETAG('c', 'o', 'm', 'm')) { + skip_bits_long(gb, get_bits(gb, 8) * 8); + } else if (chunk == MKBETAG('c', 'i', 'p', 'h')) { + skip_bits_long(gb, 16); + } else if (chunk == MKBETAG('l', 'o', 'o', 'p')) { + skip_bits_long(gb, 32); + skip_bits_long(gb, 32); + skip_bits_long(gb, 16); + skip_bits_long(gb, 16); + } else if (chunk == MKBETAG('p', 'a', 'd', 0)) { + break; + } else { + break; + } + } + + ret = ath_init(c->ath, c->ath_type, avctx->sample_rate); + if (ret < 0) + return ret; + + if (!c->track_count) + c->track_count = 1; + + b = avctx->channels / c->track_count; + if (c->stereo_band_count && b > 1) { + int8_t *x = r; + + for (int i = 0; i < c->track_count; i++, x+=b) { + switch (b) { + case 2: + case 3: + x[0] = 1; + x[1] = 2; + break; + case 4: + x[0]=1; x[1] = 2; + if (c->channel_config == 0) { + x[2]=1; + x[3]=2; + } + break; + case 5: + x[0]=1; x[1] = 2; + if (c->channel_config <= 2) { + x[3]=1; + x[4]=2; + } + break; + case 6: + case 7: + x[0] = 1; x[1] = 2; x[4] = 1; x[5] = 2; + break; + case 8: + x[0] = 1; x[1] = 2; x[4] = 1; x[5] = 2; x[6] = 1; x[7] = 2; + break; + } + } + } + + 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), + c->bands_per_hfr_group); + + if (c->base_band_count + c->stereo_band_count + (unsigned long)c->hfr_group_count > 128ULL) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < avctx->channels; i++) { + c->ch[i].chan_type = r[i]; + c->ch[i].count = c->base_band_count + ((r[i] != 2) ? c->stereo_band_count : 0); + c->ch[i].hfr_scale = &c->ch[i].scale_factors[c->base_band_count + c->stereo_band_count]; + if (c->ch[i].count > 128) + return AVERROR_INVALIDDATA; + } + + 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); +} + +static void run_imdct(HCAContext *c, ChannelContext *ch, int index, float *out) +{ + c->tx_fn(c->tx_ctx, ch->imdct_out, ch->imdct_in, sizeof(float)); + + c->fdsp->vector_fmul_window(out, ch->imdct_prev + (128 >> 1), + ch->imdct_out, window, 128 >> 1); + + memcpy(ch->imdct_prev, ch->imdct_out, 128 * sizeof(float)); +} + +static void apply_intensity_stereo(HCAContext *s, ChannelContext *ch1, ChannelContext *ch2, + int index, unsigned band_count, unsigned base_band_count, + unsigned stereo_band_count) +{ + float ratio_l = intensity_ratio_table[ch1->intensity[index]]; + float ratio_r = ratio_l - 2.0f; + float *c1 = &ch1->imdct_in[base_band_count]; + float *c2 = &ch2->imdct_in[base_band_count]; + + if (ch1->chan_type != 1 || !stereo_band_count) + return; + + for (int i = 0; i < band_count; i++) { + *(c2++) = *c1 * ratio_r; + *(c1++) *= ratio_l; + } +} + +static void reconstruct_hfr(HCAContext *s, ChannelContext *ch, + unsigned hfr_group_count, + unsigned bands_per_hfr_group, + unsigned start_band, unsigned total_band_count) +{ + if (ch->chan_type == 2 || !bands_per_hfr_group) + return; + + for (int i = 0, k = start_band, l = start_band - 1; i < hfr_group_count; i++){ + for (int j = 0; j < bands_per_hfr_group && k < total_band_count && l >= 0; j++, k++, l--){ + ch->imdct_in[k] = scale_conversion_table[ (ch->hfr_scale[i] - ch->scale_factors[l]) & 63 ] * ch->imdct_in[l]; + } + } + + ch->imdct_in[127] = 0; +} + +static void dequantize_coefficients(HCAContext *c, ChannelContext *ch) +{ + GetBitContext *gb = &c->gb; + + for (int i = 0; i < ch->count; i++) { + unsigned scale = ch->scale[i]; + int nb_bits = max_bits_table[scale]; + int value = get_bitsz(gb, nb_bits); + float factor; + + if (scale > 7) { + value = (1 - ((value & 1) << 1)) * (value >> 1); + if (!value) + skip_bits_long(gb, -1); + factor = value; + } else { + value += scale << 4; + skip_bits_long(gb, quant_spectrum_bits[value] - nb_bits); + factor = quant_spectrum_value[value]; + } + ch->imdct_in[i] = factor * ch->base[i]; + } + + memset(ch->imdct_in + ch->count, 0, sizeof(ch->imdct_in) - ch->count * sizeof(ch->imdct_in[0])); +} + +static void unpack(HCAContext *c, ChannelContext *ch, + unsigned hfr_group_count, + int packed_noise_level, + const uint8_t *ath) +{ + GetBitContext *gb = &c->gb; + int delta_bits = get_bits(gb, 3); + + if (delta_bits > 5) { + for (int i = 0; i < ch->count; i++) + ch->scale_factors[i] = get_bits(gb, 6); + } else if (delta_bits) { + int factor = get_bits(gb, 6); + int max_value = (1 << delta_bits) - 1; + int half_max = max_value >> 1; + + ch->scale_factors[0] = factor; + for (int i = 1; i < ch->count; i++){ + int delta = get_bits(gb, delta_bits); + + if (delta == max_value) { + factor = get_bits(gb, 6); + } else { + factor += delta - half_max; + } + factor = av_clip_uintp2(factor, 6); + + ch->scale_factors[i] = factor; + } + } else { + memset(ch->scale_factors, 0, 128); + } + + if (ch->chan_type == 2){ + ch->intensity[0] = get_bits(gb, 4); + if (ch->intensity[0] < 15) { + for (int i = 1; i < 8; i++) + ch->intensity[i] = get_bits(gb, 4); + } + } else { + for (int i = 0; i < hfr_group_count; i++) + ch->hfr_scale[i] = get_bits(gb, 6); + } + + for (int i = 0; i < ch->count; i++) { + int scale = ch->scale_factors[i]; + + if (scale) { + scale = c->ath[i] + ((packed_noise_level + i) >> 8) - ((scale * 5) >> 1) + 2; + scale = scale_table[av_clip(scale, 0, 58)]; + } + ch->scale[i] = scale; + } + + memset(ch->scale + ch->count, 0, sizeof(ch->scale) - ch->count); + + for (int i = 0; i < ch->count; i++) + ch->base[i] = dequantizer_scaling_table[ch->scale_factors[i]] * quant_step_size[ch->scale[i]]; +} + +static int decode_frame(AVCodecContext *avctx, void *data, + int *got_frame_ptr, AVPacket *avpkt) +{ + AVFrame *frame = data; + HCAContext *c = avctx->priv_data; + int ch, ret, packed_noise_level; + GetBitContext *gb = &c->gb; + float **samples; + + if (avctx->err_recognition & AV_EF_CRCCHECK) { + if (av_crc(c->crc_table, 0, avpkt->data, avpkt->size)) + return AVERROR_INVALIDDATA; + } + + if ((ret = init_get_bits8(gb, avpkt->data, avpkt->size)) < 0) + return ret; + + if (get_bits(gb, 16) != 0xFFFF) + return AVERROR_INVALIDDATA; + + frame->nb_samples = 1024; + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + return ret; + samples = (float **)frame->extended_data; + + packed_noise_level = (get_bits(gb, 9) << 8) - get_bits(gb, 7); + + for (ch = 0; ch < avctx->channels; ch++) + unpack(c, &c->ch[ch], c->hfr_group_count, packed_noise_level, c->ath); + + for (int i = 0; i < 8; i++) { + for (ch = 0; ch < avctx->channels; ch++) + dequantize_coefficients(c, &c->ch[ch]); + for (ch = 0; ch < avctx->channels; ch++) + reconstruct_hfr(c, &c->ch[ch], c->hfr_group_count, c->bands_per_hfr_group, + c->stereo_band_count + c->base_band_count, c->total_band_count); + for (ch = 0; ch < avctx->channels - 1; ch++) + apply_intensity_stereo(c, &c->ch[ch], &c->ch[ch+1], i, + c->total_band_count - c->base_band_count, + c->base_band_count, c->stereo_band_count); + for (ch = 0; ch < avctx->channels; ch++) + run_imdct(c, &c->ch[ch], i, samples[ch] + i * 128); + } + + *got_frame_ptr = 1; + + return avpkt->size; +} + +static av_cold int decode_close(AVCodecContext *avctx) +{ + HCAContext *c = avctx->priv_data; + + av_freep(&c->fdsp); + av_tx_uninit(&c->tx_ctx); + + return 0; +} + +AVCodec ff_hca_decoder = { + .name = "hca", + .long_name = NULL_IF_CONFIG_SMALL("CRI HCA"), + .type = AVMEDIA_TYPE_AUDIO, + .id = AV_CODEC_ID_HCA, + .priv_data_size = sizeof(HCAContext), + .init = decode_init, + .decode = decode_frame, + .close = decode_close, + .capabilities = AV_CODEC_CAP_DR1, + .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, + AV_SAMPLE_FMT_NONE }, +}; diff --git a/libavcodec/hcom.c b/libavcodec/hcom.c index bce9e80aa52..8300676f98a 100644 --- a/libavcodec/hcom.c +++ b/libavcodec/hcom.c @@ -52,7 +52,8 @@ static av_cold int hcom_init(AVCodecContext *avctx) if (avctx->extradata_size <= 7) return AVERROR_INVALIDDATA; s->dict_entries = AV_RB16(avctx->extradata); - if (avctx->extradata_size < s->dict_entries * 4 + 7) + if (avctx->extradata_size < s->dict_entries * 4 + 7 || + s->dict_entries == 0) return AVERROR_INVALIDDATA; s->delta_compression = AV_RB32(avctx->extradata + 2); s->sample = s->first_sample = avctx->extradata[avctx->extradata_size - 1]; @@ -65,8 +66,15 @@ static av_cold int hcom_init(AVCodecContext *avctx) s->dict[i].r = AV_RB16(avctx->extradata + 6 + 4 * i + 2); if (s->dict[i].l >= 0 && (s->dict[i].l >= s->dict_entries || - s->dict[i].r >= s->dict_entries)) + s->dict[i].r >= s->dict_entries || + s->dict[i].r < 0 )) { + av_freep(&s->dict); return AVERROR_INVALIDDATA; + } + } + if (s->dict[0].l < 0) { + av_freep(&s->dict); + return AVERROR_INVALIDDATA; } avctx->sample_fmt = AV_SAMPLE_FMT_U8; diff --git a/libavcodec/hevc.h b/libavcodec/hevc.h index 56b5541d90a..1804755327e 100644 --- a/libavcodec/hevc.h +++ b/libavcodec/hevc.h @@ -26,70 +26,70 @@ * T-REC-H.265-201802 */ enum HEVCNALUnitType { - HEVC_NAL_TRAIL_N = 0, - HEVC_NAL_TRAIL_R = 1, - HEVC_NAL_TSA_N = 2, - HEVC_NAL_TSA_R = 3, - HEVC_NAL_STSA_N = 4, - HEVC_NAL_STSA_R = 5, - HEVC_NAL_RADL_N = 6, - HEVC_NAL_RADL_R = 7, - HEVC_NAL_RASL_N = 8, - HEVC_NAL_RASL_R = 9, - HEVC_NAL_VCL_N10 = 10, - HEVC_NAL_VCL_R11 = 11, - HEVC_NAL_VCL_N12 = 12, - HEVC_NAL_VCL_R13 = 13, - HEVC_NAL_VCL_N14 = 14, - HEVC_NAL_VCL_R15 = 15, - HEVC_NAL_BLA_W_LP = 16, - HEVC_NAL_BLA_W_RADL = 17, - HEVC_NAL_BLA_N_LP = 18, - HEVC_NAL_IDR_W_RADL = 19, - HEVC_NAL_IDR_N_LP = 20, - HEVC_NAL_CRA_NUT = 21, - HEVC_NAL_IRAP_VCL22 = 22, - HEVC_NAL_IRAP_VCL23 = 23, - HEVC_NAL_RSV_VCL24 = 24, - HEVC_NAL_RSV_VCL25 = 25, - HEVC_NAL_RSV_VCL26 = 26, - HEVC_NAL_RSV_VCL27 = 27, - HEVC_NAL_RSV_VCL28 = 28, - HEVC_NAL_RSV_VCL29 = 29, - HEVC_NAL_RSV_VCL30 = 30, - HEVC_NAL_RSV_VCL31 = 31, - HEVC_NAL_VPS = 32, - HEVC_NAL_SPS = 33, - HEVC_NAL_PPS = 34, - HEVC_NAL_AUD = 35, - HEVC_NAL_EOS_NUT = 36, - HEVC_NAL_EOB_NUT = 37, - HEVC_NAL_FD_NUT = 38, - HEVC_NAL_SEI_PREFIX = 39, - HEVC_NAL_SEI_SUFFIX = 40, - HEVC_NAL_RSV_NVCL41 = 41, - HEVC_NAL_RSV_NVCL42 = 42, - HEVC_NAL_RSV_NVCL43 = 43, - HEVC_NAL_RSV_NVCL44 = 44, - HEVC_NAL_RSV_NVCL45 = 45, - HEVC_NAL_RSV_NVCL46 = 46, - HEVC_NAL_RSV_NVCL47 = 47, - HEVC_NAL_UNSPEC48 = 48, - HEVC_NAL_UNSPEC49 = 49, - HEVC_NAL_UNSPEC50 = 50, - HEVC_NAL_UNSPEC51 = 51, - HEVC_NAL_UNSPEC52 = 52, - HEVC_NAL_UNSPEC53 = 53, - HEVC_NAL_UNSPEC54 = 54, - HEVC_NAL_UNSPEC55 = 55, - HEVC_NAL_UNSPEC56 = 56, - HEVC_NAL_UNSPEC57 = 57, - HEVC_NAL_UNSPEC58 = 58, - HEVC_NAL_UNSPEC59 = 59, - HEVC_NAL_UNSPEC60 = 60, - HEVC_NAL_UNSPEC61 = 61, - HEVC_NAL_UNSPEC62 = 62, - HEVC_NAL_UNSPEC63 = 63, + HEVC_NAL_TRAIL_N = 0, + HEVC_NAL_TRAIL_R = 1, + HEVC_NAL_TSA_N = 2, + HEVC_NAL_TSA_R = 3, + HEVC_NAL_STSA_N = 4, + HEVC_NAL_STSA_R = 5, + HEVC_NAL_RADL_N = 6, + HEVC_NAL_RADL_R = 7, + HEVC_NAL_RASL_N = 8, + HEVC_NAL_RASL_R = 9, + HEVC_NAL_VCL_N10 = 10, + HEVC_NAL_VCL_R11 = 11, + HEVC_NAL_VCL_N12 = 12, + HEVC_NAL_VCL_R13 = 13, + HEVC_NAL_VCL_N14 = 14, + HEVC_NAL_VCL_R15 = 15, + HEVC_NAL_BLA_W_LP = 16, + HEVC_NAL_BLA_W_RADL = 17, + HEVC_NAL_BLA_N_LP = 18, + HEVC_NAL_IDR_W_RADL = 19, + HEVC_NAL_IDR_N_LP = 20, + HEVC_NAL_CRA_NUT = 21, + HEVC_NAL_RSV_IRAP_VCL22 = 22, + HEVC_NAL_RSV_IRAP_VCL23 = 23, + HEVC_NAL_RSV_VCL24 = 24, + HEVC_NAL_RSV_VCL25 = 25, + HEVC_NAL_RSV_VCL26 = 26, + HEVC_NAL_RSV_VCL27 = 27, + HEVC_NAL_RSV_VCL28 = 28, + HEVC_NAL_RSV_VCL29 = 29, + HEVC_NAL_RSV_VCL30 = 30, + HEVC_NAL_RSV_VCL31 = 31, + HEVC_NAL_VPS = 32, + HEVC_NAL_SPS = 33, + HEVC_NAL_PPS = 34, + HEVC_NAL_AUD = 35, + HEVC_NAL_EOS_NUT = 36, + HEVC_NAL_EOB_NUT = 37, + HEVC_NAL_FD_NUT = 38, + HEVC_NAL_SEI_PREFIX = 39, + HEVC_NAL_SEI_SUFFIX = 40, + HEVC_NAL_RSV_NVCL41 = 41, + HEVC_NAL_RSV_NVCL42 = 42, + HEVC_NAL_RSV_NVCL43 = 43, + HEVC_NAL_RSV_NVCL44 = 44, + HEVC_NAL_RSV_NVCL45 = 45, + HEVC_NAL_RSV_NVCL46 = 46, + HEVC_NAL_RSV_NVCL47 = 47, + HEVC_NAL_UNSPEC48 = 48, + HEVC_NAL_UNSPEC49 = 49, + HEVC_NAL_UNSPEC50 = 50, + HEVC_NAL_UNSPEC51 = 51, + HEVC_NAL_UNSPEC52 = 52, + HEVC_NAL_UNSPEC53 = 53, + HEVC_NAL_UNSPEC54 = 54, + HEVC_NAL_UNSPEC55 = 55, + HEVC_NAL_UNSPEC56 = 56, + HEVC_NAL_UNSPEC57 = 57, + HEVC_NAL_UNSPEC58 = 58, + HEVC_NAL_UNSPEC59 = 59, + HEVC_NAL_UNSPEC60 = 60, + HEVC_NAL_UNSPEC61 = 61, + HEVC_NAL_UNSPEC62 = 62, + HEVC_NAL_UNSPEC63 = 63, }; enum HEVCSliceType { diff --git a/libavcodec/hevc_cabac.c b/libavcodec/hevc_cabac.c index faa36d54593..3dc0987dad5 100644 --- a/libavcodec/hevc_cabac.c +++ b/libavcodec/hevc_cabac.c @@ -66,7 +66,7 @@ static const int8_t num_bins_in_se[] = { 1, // no_residual_data_flag 3, // split_transform_flag 2, // cbf_luma - 4, // cbf_cb, cbf_cr + 5, // cbf_cb, cbf_cr 2, // transform_skip_flag[][] 2, // explicit_rdpcm_flag[][] 2, // explicit_rdpcm_dir_flag[][] @@ -122,23 +122,23 @@ static const int elem_offset[sizeof(num_bins_in_se)] = { 37, // split_transform_flag 40, // cbf_luma 42, // cbf_cb, cbf_cr - 46, // transform_skip_flag[][] - 48, // explicit_rdpcm_flag[][] - 50, // explicit_rdpcm_dir_flag[][] - 52, // last_significant_coeff_x_prefix - 70, // last_significant_coeff_y_prefix - 88, // last_significant_coeff_x_suffix - 88, // last_significant_coeff_y_suffix - 88, // significant_coeff_group_flag - 92, // significant_coeff_flag - 136, // coeff_abs_level_greater1_flag - 160, // coeff_abs_level_greater2_flag - 166, // coeff_abs_level_remaining - 166, // coeff_sign_flag - 166, // log2_res_scale_abs - 174, // res_scale_sign_flag - 176, // cu_chroma_qp_offset_flag - 177, // cu_chroma_qp_offset_idx + 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 }; #define CNU 154 @@ -189,7 +189,7 @@ static const uint8_t init_values[3][HEVC_CONTEXTS] = { // cbf_luma 111, 141, // cbf_cb, cbf_cr - 94, 138, 182, 154, + 94, 138, 182, 154, 154, // transform_skip_flag 139, 139, // explicit_rdpcm_flag @@ -266,7 +266,7 @@ static const uint8_t init_values[3][HEVC_CONTEXTS] = { // cbf_luma 153, 111, // cbf_cb, cbf_cr - 149, 107, 167, 154, + 149, 107, 167, 154, 154, // transform_skip_flag 139, 139, // explicit_rdpcm_flag @@ -343,7 +343,7 @@ static const uint8_t init_values[3][HEVC_CONTEXTS] = { // cbf_luma 153, 111, // cbf_cb, cbf_cr - 149, 92, 167, 154, + 149, 92, 167, 154, 154, // transform_skip_flag 139, 139, // explicit_rdpcm_flag @@ -642,11 +642,11 @@ int ff_hevc_cu_qp_delta_abs(HEVCContext *s) } if (prefix_val >= 5) { int k = 0; - while (k < CABAC_MAX_BIN && get_cabac_bypass(&s->HEVClc->cc)) { + while (k < 7 && get_cabac_bypass(&s->HEVClc->cc)) { suffix_val += 1 << k; k++; } - if (k == CABAC_MAX_BIN) { + if (k == 7) { av_log(s->avctx, AV_LOG_ERROR, "CABAC_MAX_BIN : %d\n", k); return AVERROR_INVALIDDATA; } diff --git a/libavcodec/hevc_mp4toannexb_bsf.c b/libavcodec/hevc_mp4toannexb_bsf.c index 09bce5b34c2..ba1deb28489 100644 --- a/libavcodec/hevc_mp4toannexb_bsf.c +++ b/libavcodec/hevc_mp4toannexb_bsf.c @@ -26,6 +26,7 @@ #include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "bytestream.h" #include "hevc.h" @@ -141,9 +142,18 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out) int nalu_type; int is_irap, 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_byte(&gb) >> 1) & 0x3f; /* prepend extradata to IRAP frames */ @@ -152,8 +162,7 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out) extra_size = add_extradata * ctx->par_out->extradata_size; got_irap |= is_irap; - if (SIZE_MAX - nalu_size < 4 || - SIZE_MAX - 4 - nalu_size < extra_size) { + if (FFMIN(INT_MAX, SIZE_MAX) < 4ULL + nalu_size + extra_size) { ret = AVERROR_INVALIDDATA; goto fail; } @@ -164,7 +173,7 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out) if (ret < 0) goto fail; - if (add_extradata) + 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); diff --git a/libavcodec/hevc_parse.c b/libavcodec/hevc_parse.c index dddb293df64..29dfd479f38 100644 --- a/libavcodec/hevc_parse.c +++ b/libavcodec/hevc_parse.c @@ -37,6 +37,8 @@ static int hevc_decode_nal_units(const uint8_t *buf, int buf_size, HEVCParamSets for (i = 0; i < pkt.nb_nals; i++) { H2645NAL *nal = &pkt.nals[i]; + if (nal->nuh_layer_id > 0) + continue; /* ignore everything except parameter sets and VCL NALUs */ switch (nal->type) { diff --git a/libavcodec/hevc_parser.c b/libavcodec/hevc_parser.c index b444b999550..5af4b788d50 100644 --- a/libavcodec/hevc_parser.c +++ b/libavcodec/hevc_parser.c @@ -42,7 +42,6 @@ typedef struct HEVCParserContext { H2645Packet pkt; HEVCParamSets ps; HEVCSEI sei; - SliceHeader sh; int is_avc; int nal_length_size; @@ -58,26 +57,28 @@ static int hevc_parse_slice_header(AVCodecParserContext *s, H2645NAL *nal, HEVCParserContext *ctx = s->priv_data; HEVCParamSets *ps = &ctx->ps; HEVCSEI *sei = &ctx->sei; - SliceHeader *sh = &ctx->sh; GetBitContext *gb = &nal->gb; const HEVCWindow *ow; int i, num = 0, den = 0; - sh->first_slice_in_pic_flag = get_bits1(gb); + unsigned int pps_id, first_slice_in_pic_flag, dependent_slice_segment_flag; + enum HEVCSliceType slice_type; + + first_slice_in_pic_flag = get_bits1(gb); s->picture_structure = sei->picture_timing.picture_struct; s->field_order = sei->picture_timing.picture_struct; if (IS_IRAP_NAL(nal)) { s->key_frame = 1; - sh->no_output_of_prior_pics_flag = get_bits1(gb); + skip_bits1(gb); // no_output_of_prior_pics_flag } - sh->pps_id = get_ue_golomb(gb); - if (sh->pps_id >= HEVC_MAX_PPS_COUNT || !ps->pps_list[sh->pps_id]) { - av_log(avctx, AV_LOG_ERROR, "PPS id out of range: %d\n", sh->pps_id); + pps_id = get_ue_golomb(gb); + if (pps_id >= HEVC_MAX_PPS_COUNT || !ps->pps_list[pps_id]) { + av_log(avctx, AV_LOG_ERROR, "PPS id out of range: %d\n", pps_id); return AVERROR_INVALIDDATA; } - ps->pps = (HEVCPPS*)ps->pps_list[sh->pps_id]->data; + ps->pps = (HEVCPPS*)ps->pps_list[pps_id]->data; 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); @@ -109,51 +110,53 @@ static int hevc_parse_slice_header(AVCodecParserContext *s, H2645NAL *nal, av_reduce(&avctx->framerate.den, &avctx->framerate.num, num, den, 1 << 30); - if (!sh->first_slice_in_pic_flag) { + if (!first_slice_in_pic_flag) { + unsigned int slice_segment_addr; int slice_address_length; if (ps->pps->dependent_slice_segments_enabled_flag) - sh->dependent_slice_segment_flag = get_bits1(gb); + dependent_slice_segment_flag = get_bits1(gb); else - sh->dependent_slice_segment_flag = 0; + dependent_slice_segment_flag = 0; slice_address_length = av_ceil_log2_c(ps->sps->ctb_width * ps->sps->ctb_height); - sh->slice_segment_addr = get_bitsz(gb, slice_address_length); - if (sh->slice_segment_addr >= ps->sps->ctb_width * ps->sps->ctb_height) { + slice_segment_addr = get_bitsz(gb, slice_address_length); + if (slice_segment_addr >= ps->sps->ctb_width * ps->sps->ctb_height) { av_log(avctx, AV_LOG_ERROR, "Invalid slice segment address: %u.\n", - sh->slice_segment_addr); + slice_segment_addr); return AVERROR_INVALIDDATA; } } else - sh->dependent_slice_segment_flag = 0; + dependent_slice_segment_flag = 0; - if (sh->dependent_slice_segment_flag) + if (dependent_slice_segment_flag) return 0; /* break; */ for (i = 0; i < ps->pps->num_extra_slice_header_bits; i++) skip_bits(gb, 1); // slice_reserved_undetermined_flag[] - sh->slice_type = get_ue_golomb(gb); - if (!(sh->slice_type == HEVC_SLICE_I || sh->slice_type == HEVC_SLICE_P || - sh->slice_type == HEVC_SLICE_B)) { + slice_type = get_ue_golomb(gb); + if (!(slice_type == HEVC_SLICE_I || slice_type == HEVC_SLICE_P || + slice_type == HEVC_SLICE_B)) { av_log(avctx, AV_LOG_ERROR, "Unknown slice type: %d.\n", - sh->slice_type); + slice_type); return AVERROR_INVALIDDATA; } - s->pict_type = sh->slice_type == HEVC_SLICE_B ? AV_PICTURE_TYPE_B : - sh->slice_type == HEVC_SLICE_P ? AV_PICTURE_TYPE_P : - AV_PICTURE_TYPE_I; + s->pict_type = slice_type == HEVC_SLICE_B ? AV_PICTURE_TYPE_B : + slice_type == HEVC_SLICE_P ? AV_PICTURE_TYPE_P : + AV_PICTURE_TYPE_I; if (ps->pps->output_flag_present_flag) - sh->pic_output_flag = get_bits1(gb); + skip_bits1(gb); // pic_output_flag if (ps->sps->separate_colour_plane_flag) - sh->colour_plane_id = get_bits(gb, 2); + skip_bits(gb, 2); // colour_plane_id if (!IS_IDR_NAL(nal)) { - sh->pic_order_cnt_lsb = get_bits(gb, ps->sps->log2_max_poc_lsb); - s->output_picture_number = ctx->poc = ff_hevc_compute_poc(ps->sps, ctx->pocTid0, sh->pic_order_cnt_lsb, nal->type); + int pic_order_cnt_lsb = get_bits(gb, ps->sps->log2_max_poc_lsb); + s->output_picture_number = ctx->poc = + ff_hevc_compute_poc(ps->sps, ctx->pocTid0, pic_order_cnt_lsb, nal->type); } else s->output_picture_number = ctx->poc = 0; @@ -202,6 +205,9 @@ static int parse_nal_units(AVCodecParserContext *s, const uint8_t *buf, H2645NAL *nal = &ctx->pkt.nals[i]; GetBitContext *gb = &nal->gb; + if (nal->nuh_layer_id > 0) + continue; + switch (nal->type) { case HEVC_NAL_VPS: ff_hevc_decode_nal_vps(gb, avctx, ps); @@ -232,6 +238,11 @@ static int parse_nal_units(AVCodecParserContext *s, const uint8_t *buf, case HEVC_NAL_RADL_R: case HEVC_NAL_RASL_N: case HEVC_NAL_RASL_R: + if (ctx->sei.picture_timing.picture_struct == HEVC_SEI_PIC_STRUCT_FRAME_DOUBLING) { + s->repeat_pict = 1; + } else if (ctx->sei.picture_timing.picture_struct == HEVC_SEI_PIC_STRUCT_FRAME_TRIPLING) { + s->repeat_pict = 2; + } ret = hevc_parse_slice_header(s, nal, avctx); if (ret) return ret; diff --git a/libavcodec/hevc_ps.c b/libavcodec/hevc_ps.c index abf08b919b2..ea6fd536c6b 100644 --- a/libavcodec/hevc_ps.c +++ b/libavcodec/hevc_ps.c @@ -267,7 +267,7 @@ static int decode_profile_tier_level(GetBitContext *gb, AVCodecContext *avctx, { int i; - if (get_bits_left(gb) < 2+1+5 + 32 + 4 + 16 + 16 + 12) + if (get_bits_left(gb) < 2+1+5 + 32 + 4 + 43 + 1) return -1; ptl->profile_space = get_bits(gb, 2); @@ -295,9 +295,43 @@ static int decode_profile_tier_level(GetBitContext *gb, AVCodecContext *avctx, ptl->non_packed_constraint_flag = get_bits1(gb); ptl->frame_only_constraint_flag = get_bits1(gb); - skip_bits(gb, 16); // XXX_reserved_zero_44bits[0..15] - skip_bits(gb, 16); // XXX_reserved_zero_44bits[16..31] - skip_bits(gb, 12); // XXX_reserved_zero_44bits[32..43] +#define check_profile_idc(idc) \ + ptl->profile_idc == idc || ptl->profile_compatibility_flag[idc] + + if (check_profile_idc(4) || check_profile_idc(5) || check_profile_idc(6) || + check_profile_idc(7) || check_profile_idc(8) || check_profile_idc(9) || + check_profile_idc(10)) { + + ptl->max_12bit_constraint_flag = get_bits1(gb); + ptl->max_10bit_constraint_flag = get_bits1(gb); + ptl->max_8bit_constraint_flag = get_bits1(gb); + ptl->max_422chroma_constraint_flag = get_bits1(gb); + ptl->max_420chroma_constraint_flag = get_bits1(gb); + ptl->max_monochrome_constraint_flag = get_bits1(gb); + ptl->intra_constraint_flag = get_bits1(gb); + ptl->one_picture_only_constraint_flag = get_bits1(gb); + ptl->lower_bit_rate_constraint_flag = get_bits1(gb); + + if (check_profile_idc(5) || check_profile_idc(9) || check_profile_idc(10)) { + ptl->max_14bit_constraint_flag = get_bits1(gb); + skip_bits_long(gb, 33); // XXX_reserved_zero_33bits[0..32] + } else { + skip_bits_long(gb, 34); // XXX_reserved_zero_34bits[0..33] + } + } else if (check_profile_idc(2)) { + skip_bits(gb, 7); + ptl->one_picture_only_constraint_flag = get_bits1(gb); + skip_bits_long(gb, 35); // XXX_reserved_zero_35bits[0..34] + } else { + skip_bits_long(gb, 43); // XXX_reserved_zero_43bits[0..42] + } + + if (check_profile_idc(1) || check_profile_idc(2) || check_profile_idc(3) || + check_profile_idc(4) || check_profile_idc(5) || check_profile_idc(9)) + ptl->inbld_flag = get_bits1(gb); + else + skip_bits1(gb); +#undef check_profile_idc return 0; } @@ -448,10 +482,6 @@ int ff_hevc_decode_nal_vps(GetBitContext *gb, AVCodecContext *avctx, memcpy(vps->data, gb->buffer, vps->data_size); vps_id = get_bits(gb, 4); - if (vps_id >= HEVC_MAX_VPS_COUNT) { - av_log(avctx, AV_LOG_ERROR, "VPS id out of range: %d\n", vps_id); - goto err; - } if (get_bits(gb, 2) != 3) { // vps_reserved_three_2bits av_log(avctx, AV_LOG_ERROR, "vps_reserved_three_2bits is not three\n"); @@ -883,10 +913,6 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, // Coded parameters sps->vps_id = get_bits(gb, 4); - if (sps->vps_id >= HEVC_MAX_VPS_COUNT) { - av_log(avctx, AV_LOG_ERROR, "VPS id out of range: %d\n", sps->vps_id); - return AVERROR_INVALIDDATA; - } if (vps_list && !vps_list[sps->vps_id]) { av_log(avctx, AV_LOG_ERROR, "VPS %d does not exist\n", diff --git a/libavcodec/hevc_ps.h b/libavcodec/hevc_ps.h index 2840dc416f3..2a1bbf64899 100644 --- a/libavcodec/hevc_ps.h +++ b/libavcodec/hevc_ps.h @@ -39,89 +39,6 @@ typedef struct ShortTermRPS { uint8_t used[32]; } ShortTermRPS; -typedef struct LongTermRPS { - int poc[32]; - uint8_t used[32]; - uint8_t nb_refs; -} LongTermRPS; - -typedef struct SliceHeader { - unsigned int pps_id; - - ///< address (in raster order) of the first block in the current slice segment - unsigned int slice_segment_addr; - ///< address (in raster order) of the first block in the current slice - unsigned int slice_addr; - - enum HEVCSliceType slice_type; - - int pic_order_cnt_lsb; - - uint8_t first_slice_in_pic_flag; - uint8_t dependent_slice_segment_flag; - uint8_t pic_output_flag; - uint8_t colour_plane_id; - - ///< RPS coded in the slice header itself is stored here - int short_term_ref_pic_set_sps_flag; - int short_term_ref_pic_set_size; - ShortTermRPS slice_rps; - const ShortTermRPS *short_term_rps; - int long_term_ref_pic_set_size; - LongTermRPS long_term_rps; - unsigned int list_entry_lx[2][32]; - - uint8_t rpl_modification_flag[2]; - uint8_t no_output_of_prior_pics_flag; - uint8_t slice_temporal_mvp_enabled_flag; - - unsigned int nb_refs[2]; - - uint8_t slice_sample_adaptive_offset_flag[3]; - uint8_t mvd_l1_zero_flag; - - uint8_t cabac_init_flag; - uint8_t disable_deblocking_filter_flag; ///< slice_header_disable_deblocking_filter_flag - uint8_t slice_loop_filter_across_slices_enabled_flag; - uint8_t collocated_list; - - unsigned int collocated_ref_idx; - - int slice_qp_delta; - int slice_cb_qp_offset; - int slice_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 - - unsigned *entry_point_offset; - int * offset; - int * size; - int num_entry_point_offsets; - - int8_t slice_qp; - - uint8_t luma_log2_weight_denom; - int16_t chroma_log2_weight_denom; - - int16_t luma_weight_l0[16]; - int16_t chroma_weight_l0[16][2]; - int16_t chroma_weight_l1[16][2]; - int16_t luma_weight_l1[16]; - - int16_t luma_offset_l0[16]; - int16_t chroma_offset_l0[16][2]; - - int16_t luma_offset_l1[16]; - int16_t chroma_offset_l1[16][2]; - - int slice_ctb_addr_rs; -} SliceHeader; - typedef struct HEVCWindow { unsigned int left_offset; unsigned int right_offset; @@ -177,11 +94,22 @@ typedef struct PTLCommon { uint8_t tier_flag; uint8_t profile_idc; uint8_t profile_compatibility_flag[32]; - uint8_t level_idc; uint8_t progressive_source_flag; uint8_t interlaced_source_flag; uint8_t non_packed_constraint_flag; uint8_t frame_only_constraint_flag; + uint8_t max_12bit_constraint_flag; + uint8_t max_10bit_constraint_flag; + uint8_t max_8bit_constraint_flag; + uint8_t max_422chroma_constraint_flag; + uint8_t max_420chroma_constraint_flag; + uint8_t max_monochrome_constraint_flag; + uint8_t intra_constraint_flag; + uint8_t one_picture_only_constraint_flag; + uint8_t lower_bit_rate_constraint_flag; + uint8_t max_14bit_constraint_flag; + uint8_t inbld_flag; + uint8_t level_idc; } PTLCommon; typedef struct PTL { diff --git a/libavcodec/hevc_refs.c b/libavcodec/hevc_refs.c index 7870a72fd6d..4f6d985ae60 100644 --- a/libavcodec/hevc_refs.c +++ b/libavcodec/hevc_refs.c @@ -358,23 +358,15 @@ int ff_hevc_slice_rpl(HEVCContext *s) return 0; } -static HEVCFrame *find_ref_idx(HEVCContext *s, int poc) +static HEVCFrame *find_ref_idx(HEVCContext *s, int poc, uint8_t use_msb) { + int mask = use_msb ? ~0 : (1 << s->ps.sps->log2_max_poc_lsb) - 1; int i; - int LtMask = (1 << s->ps.sps->log2_max_poc_lsb) - 1; - - for (i = 0; i < FF_ARRAY_ELEMS(s->DPB); i++) { - HEVCFrame *ref = &s->DPB[i]; - if (ref->frame->buf[0] && (ref->sequence == s->seq_decode)) { - if ((ref->poc & LtMask) == poc) - return ref; - } - } for (i = 0; i < FF_ARRAY_ELEMS(s->DPB); i++) { HEVCFrame *ref = &s->DPB[i]; if (ref->frame->buf[0] && ref->sequence == s->seq_decode) { - if (ref->poc == poc || (ref->poc & LtMask) == poc) + if ((ref->poc & mask) == poc) return ref; } } @@ -427,9 +419,9 @@ static HEVCFrame *generate_missing_ref(HEVCContext *s, int poc) /* add a reference with the given poc to the list and mark it as used in DPB */ static int add_candidate_ref(HEVCContext *s, RefPicList *list, - int poc, int ref_flag) + int poc, int ref_flag, uint8_t use_msb) { - HEVCFrame *ref = find_ref_idx(s, poc); + HEVCFrame *ref = find_ref_idx(s, poc, use_msb); if (ref == s->ref || list->nb_refs >= HEVC_MAX_REFS) return AVERROR_INVALIDDATA; @@ -485,7 +477,7 @@ int ff_hevc_frame_rps(HEVCContext *s) else list = ST_CURR_AFT; - ret = add_candidate_ref(s, &rps[list], poc, HEVC_FRAME_FLAG_SHORT_REF); + ret = add_candidate_ref(s, &rps[list], poc, HEVC_FRAME_FLAG_SHORT_REF, 1); if (ret < 0) goto fail; } @@ -495,7 +487,7 @@ int ff_hevc_frame_rps(HEVCContext *s) int poc = long_rps->poc[i]; int list = long_rps->used[i] ? LT_CURR : LT_FOLL; - ret = add_candidate_ref(s, &rps[list], poc, HEVC_FRAME_FLAG_LONG_REF); + ret = add_candidate_ref(s, &rps[list], poc, HEVC_FRAME_FLAG_LONG_REF, long_rps->poc_msb_present[i]); if (ret < 0) goto fail; } diff --git a/libavcodec/hevc_sei.c b/libavcodec/hevc_sei.c index c59bd4321e2..60570690cff 100644 --- a/libavcodec/hevc_sei.c +++ b/libavcodec/hevc_sei.c @@ -76,8 +76,8 @@ static int decode_nal_sei_mastering_display_info(HEVCSEIMasteringDisplay *s, Get static int decode_nal_sei_content_light_info(HEVCSEIContentLight *s, GetBitContext *gb) { // Max and average light levels - s->max_content_light_level = get_bits_long(gb, 16); - s->max_pic_average_light_level = get_bits_long(gb, 16); + s->max_content_light_level = get_bits(gb, 16); + s->max_pic_average_light_level = get_bits(gb, 16); // 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) @@ -144,6 +144,12 @@ static int decode_nal_sei_pic_timing(HEVCSEI *s, GetBitContext *gb, const HEVCPa } else if (pic_struct == 1 || pic_struct == 9 || pic_struct == 11) { av_log(logctx, AV_LOG_DEBUG, "TOP Field\n"); h->picture_struct = AV_PICTURE_STRUCTURE_TOP_FIELD; + } else if (pic_struct == 7) { + av_log(logctx, AV_LOG_DEBUG, "Frame/Field Doubling\n"); + h->picture_struct = HEVC_SEI_PIC_STRUCT_FRAME_DOUBLING; + } else if (pic_struct == 8) { + av_log(logctx, AV_LOG_DEBUG, "Frame/Field Tripling\n"); + h->picture_struct = HEVC_SEI_PIC_STRUCT_FRAME_TRIPLING; } get_bits(gb, 2); // source_scan_type get_bits(gb, 1); // duplicate_flag @@ -177,7 +183,8 @@ static int decode_registered_user_data_closed_caption(HEVCSEIA53Caption *s, GetB size -= 2; if (cc_count && size >= cc_count * 3) { - const uint64_t new_size = (s->a53_caption_size + cc_count + int old_size = s->buf_ref ? s->buf_ref->size : 0; + const uint64_t new_size = (old_size + cc_count * UINT64_C(3)); int i, ret; @@ -185,14 +192,14 @@ static int decode_registered_user_data_closed_caption(HEVCSEIA53Caption *s, GetB return AVERROR(EINVAL); /* Allow merging of the cc data from two fields. */ - ret = av_reallocp(&s->a53_caption, new_size); + ret = av_buffer_realloc(&s->buf_ref, new_size); if (ret < 0) return ret; for (i = 0; i < cc_count; i++) { - s->a53_caption[s->a53_caption_size++] = get_bits(gb, 8); - s->a53_caption[s->a53_caption_size++] = get_bits(gb, 8); - s->a53_caption[s->a53_caption_size++] = get_bits(gb, 8); + s->buf_ref->data[old_size++] = get_bits(gb, 8); + s->buf_ref->data[old_size++] = get_bits(gb, 8); + s->buf_ref->data[old_size++] = get_bits(gb, 8); } skip_bits(gb, 8); // marker_bits } @@ -363,6 +370,5 @@ int ff_hevc_decode_nal_sei(GetBitContext *gb, void *logctx, HEVCSEI *s, void ff_hevc_reset_sei(HEVCSEI *s) { - s->a53_caption.a53_caption_size = 0; - av_freep(&s->a53_caption.a53_caption); + av_buffer_unref(&s->a53_caption.buf_ref); } diff --git a/libavcodec/hevc_sei.h b/libavcodec/hevc_sei.h index f6516ae9827..a44ccca7f8e 100644 --- a/libavcodec/hevc_sei.h +++ b/libavcodec/hevc_sei.h @@ -59,6 +59,11 @@ typedef enum { HEVC_SEI_TYPE_ALPHA_CHANNEL_INFO = 165, } HEVC_SEI_Type; +typedef enum { + HEVC_SEI_PIC_STRUCT_FRAME_DOUBLING = 7, + HEVC_SEI_PIC_STRUCT_FRAME_TRIPLING = 8 +} HEVC_SEI_PicStructType; + typedef struct HEVCSEIPictureHash { uint8_t md5[3][16]; uint8_t is_md5; @@ -83,8 +88,7 @@ typedef struct HEVCSEIPictureTiming { } HEVCSEIPictureTiming; typedef struct HEVCSEIA53Caption { - int a53_caption_size; - uint8_t *a53_caption; + AVBufferRef *buf_ref; } HEVCSEIA53Caption; typedef struct HEVCSEIMasteringDisplay { diff --git a/libavcodec/hevcdec.c b/libavcodec/hevcdec.c index eed031913a1..0772608a30e 100644 --- a/libavcodec/hevcdec.c +++ b/libavcodec/hevcdec.c @@ -41,7 +41,7 @@ #include "hevc_data.h" #include "hevc_parse.h" #include "hevcdec.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "profiles.h" const uint8_t ff_hevc_pel_weight[65] = { [2] = 0, [4] = 1, [6] = 2, [8] = 3, [12] = 4, [16] = 5, [24] = 6, [32] = 7, [48] = 8, [64] = 9 }; @@ -280,7 +280,6 @@ static int decode_lt_rps(HEVCContext *s, LongTermRPS *rps, GetBitContext *gb) rps->nb_refs = nb_sh + nb_sps; for (i = 0; i < rps->nb_refs; i++) { - uint8_t delta_poc_msb_present; if (i < nb_sps) { uint8_t lt_idx_sps = 0; @@ -295,8 +294,8 @@ static int decode_lt_rps(HEVCContext *s, LongTermRPS *rps, GetBitContext *gb) rps->used[i] = get_bits1(gb); } - delta_poc_msb_present = get_bits1(gb); - if (delta_poc_msb_present) { + rps->poc_msb_present[i] = get_bits1(gb); + if (rps->poc_msb_present[i]) { int64_t delta = get_ue_golomb_long(gb); int64_t poc; @@ -425,6 +424,12 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) #endif #if CONFIG_HEVC_NVDEC_HWACCEL *fmt++ = AV_PIX_FMT_CUDA; +#endif + break; + case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUV422P10LE: +#if CONFIG_HEVC_VAAPI_HWACCEL + *fmt++ = AV_PIX_FMT_VAAPI; #endif break; case AV_PIX_FMT_YUV420P12: @@ -508,7 +513,7 @@ static int hls_slice_header(HEVCContext *s) sh->first_slice_in_pic_flag = get_bits1(gb); if (s->ref && sh->first_slice_in_pic_flag) { av_log(s->avctx, AV_LOG_ERROR, "Two slices reporting being the first in the same frame.\n"); - return 1; // This slice will be skiped later, do not corrupt state + return 1; // This slice will be skipped later, do not corrupt state } if ((IS_IDR(s) || IS_BLA(s)) && sh->first_slice_in_pic_flag) { @@ -2778,14 +2783,14 @@ static int set_side_data(HEVCContext *s) metadata->MaxCLL, metadata->MaxFALL); } - if (s->sei.a53_caption.a53_caption) { - AVFrameSideData* sd = av_frame_new_side_data(out, - AV_FRAME_DATA_A53_CC, - s->sei.a53_caption.a53_caption_size); - if (sd) - memcpy(sd->data, s->sei.a53_caption.a53_caption, s->sei.a53_caption.a53_caption_size); - av_freep(&s->sei.a53_caption.a53_caption); - s->sei.a53_caption.a53_caption_size = 0; + if (s->sei.a53_caption.buf_ref) { + HEVCSEIA53Caption *a53 = &s->sei.a53_caption; + + AVFrameSideData *sd = av_frame_new_side_data_from_buf(out, AV_FRAME_DATA_A53_CC, a53->buf_ref); + if (!sd) + av_buffer_unref(&a53->buf_ref); + a53->buf_ref = NULL; + s->avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; } @@ -3077,7 +3082,7 @@ static int decode_nal_units(HEVCContext *s, const uint8_t *buf, int length) if (s->avctx->skip_frame >= AVDISCARD_ALL || (s->avctx->skip_frame >= AVDISCARD_NONREF - && ff_hevc_nal_is_nonref(nal->type))) + && ff_hevc_nal_is_nonref(nal->type)) || nal->nuh_layer_id > 0) continue; ret = decode_nal_unit(s, nal); @@ -3336,6 +3341,8 @@ static av_cold int hevc_decode_free(AVCodecContext *avctx) ff_h2645_packet_uninit(&s->pkt); + ff_hevc_reset_sei(&s->sei); + return 0; } @@ -3461,6 +3468,13 @@ static int hevc_update_thread_context(AVCodecContext *dst, s->max_ra = INT_MAX; } + av_buffer_unref(&s->sei.a53_caption.buf_ref); + if (s0->sei.a53_caption.buf_ref) { + s->sei.a53_caption.buf_ref = av_buffer_ref(s0->sei.a53_caption.buf_ref); + if (!s->sei.a53_caption.buf_ref) + return AVERROR(ENOMEM); + } + s->sei.frame_packing = s0->sei.frame_packing; s->sei.display_orientation = s0->sei.display_orientation; s->sei.mastering_display = s0->sei.mastering_display; @@ -3476,8 +3490,6 @@ static av_cold int hevc_decode_init(AVCodecContext *avctx) HEVCContext *s = avctx->priv_data; int ret; - avctx->internal->allocate_progress = 1; - ret = hevc_init_context(avctx); if (ret < 0) return ret; @@ -3493,11 +3505,13 @@ static av_cold int hevc_decode_init(AVCodecContext *avctx) else s->threads_number = 1; - if (avctx->extradata_size > 0 && avctx->extradata) { - ret = hevc_decode_extradata(s, avctx->extradata, avctx->extradata_size, 1); - if (ret < 0) { - hevc_decode_free(avctx); - return ret; + if (!avctx->internal->is_copy) { + if (avctx->extradata_size > 0 && avctx->extradata) { + ret = hevc_decode_extradata(s, avctx->extradata, avctx->extradata_size, 1); + if (ret < 0) { + hevc_decode_free(avctx); + return ret; + } } } @@ -3509,26 +3523,11 @@ static av_cold int hevc_decode_init(AVCodecContext *avctx) return 0; } -#if HAVE_THREADS -static av_cold int hevc_init_thread_copy(AVCodecContext *avctx) -{ - HEVCContext *s = avctx->priv_data; - int ret; - - memset(s, 0, sizeof(*s)); - - ret = hevc_init_context(avctx); - if (ret < 0) - return ret; - - return 0; -} -#endif - static void hevc_decode_flush(AVCodecContext *avctx) { HEVCContext *s = avctx->priv_data; ff_hevc_flush_dpb(s); + ff_hevc_reset_sei(&s->sei); s->max_ra = INT_MAX; s->eos = 1; } @@ -3563,10 +3562,10 @@ AVCodec ff_hevc_decoder = { .decode = hevc_decode_frame, .flush = hevc_decode_flush, .update_thread_context = ONLY_IF_THREADS_ENABLED(hevc_update_thread_context), - .init_thread_copy = ONLY_IF_THREADS_ENABLED(hevc_init_thread_copy), .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS, - .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_EXPORTS_CROPPING, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_EXPORTS_CROPPING | + FF_CODEC_CAP_ALLOCATE_PROGRESS, .profiles = NULL_IF_CONFIG_SMALL(ff_hevc_profiles), .hw_configs = (const AVCodecHWConfigInternal*[]) { #if CONFIG_HEVC_DXVA2_HWACCEL diff --git a/libavcodec/hevcdec.h b/libavcodec/hevcdec.h index b45969b7e23..39c5c7f89ff 100644 --- a/libavcodec/hevcdec.h +++ b/libavcodec/hevcdec.h @@ -228,6 +228,13 @@ enum ScanType { SCAN_VERT, }; +typedef struct LongTermRPS { + int poc[32]; + uint8_t poc_msb_present[32]; + uint8_t used[32]; + uint8_t nb_refs; +} LongTermRPS; + typedef struct RefPicList { struct HEVCFrame *ref[HEVC_MAX_REFS]; int list[HEVC_MAX_REFS]; @@ -239,6 +246,83 @@ typedef struct RefPicListTab { RefPicList refPicList[2]; } RefPicListTab; +typedef struct SliceHeader { + unsigned int pps_id; + + ///< address (in raster order) of the first block in the current slice segment + unsigned int slice_segment_addr; + ///< address (in raster order) of the first block in the current slice + unsigned int slice_addr; + + enum HEVCSliceType slice_type; + + int pic_order_cnt_lsb; + + uint8_t first_slice_in_pic_flag; + uint8_t dependent_slice_segment_flag; + uint8_t pic_output_flag; + uint8_t colour_plane_id; + + ///< RPS coded in the slice header itself is stored here + int short_term_ref_pic_set_sps_flag; + int short_term_ref_pic_set_size; + ShortTermRPS slice_rps; + const ShortTermRPS *short_term_rps; + int long_term_ref_pic_set_size; + LongTermRPS long_term_rps; + unsigned int list_entry_lx[2][32]; + + uint8_t rpl_modification_flag[2]; + uint8_t no_output_of_prior_pics_flag; + uint8_t slice_temporal_mvp_enabled_flag; + + unsigned int nb_refs[2]; + + uint8_t slice_sample_adaptive_offset_flag[3]; + uint8_t mvd_l1_zero_flag; + + uint8_t cabac_init_flag; + uint8_t disable_deblocking_filter_flag; ///< slice_header_disable_deblocking_filter_flag + uint8_t slice_loop_filter_across_slices_enabled_flag; + uint8_t collocated_list; + + unsigned int collocated_ref_idx; + + int slice_qp_delta; + int slice_cb_qp_offset; + int slice_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 + + unsigned *entry_point_offset; + int * offset; + int * size; + int num_entry_point_offsets; + + int8_t slice_qp; + + uint8_t luma_log2_weight_denom; + int16_t chroma_log2_weight_denom; + + int16_t luma_weight_l0[16]; + int16_t chroma_weight_l0[16][2]; + int16_t chroma_weight_l1[16][2]; + int16_t luma_weight_l1[16]; + + int16_t luma_offset_l0[16]; + int16_t chroma_offset_l0[16][2]; + + int16_t luma_offset_l1[16]; + int16_t chroma_offset_l1[16][2]; + + int slice_ctb_addr_rs; +} SliceHeader; + typedef struct CodingUnit { int x; int y; @@ -561,7 +645,6 @@ static av_always_inline int ff_hevc_nal_is_nonref(enum HEVCNALUnitType type) case HEVC_NAL_VCL_N12: case HEVC_NAL_VCL_N14: return 1; - break; default: break; } return 0; diff --git a/libavcodec/hq_hqa.c b/libavcodec/hq_hqa.c index eec2e980b3a..8404e80ec8e 100644 --- a/libavcodec/hq_hqa.c +++ b/libavcodec/hq_hqa.c @@ -321,7 +321,7 @@ static int hq_hqa_decode_frame(AVCodecContext *avctx, void *data, int info_size; bytestream2_skip(&ctx->gbc, 4); info_size = bytestream2_get_le32(&ctx->gbc); - if (bytestream2_get_bytes_left(&ctx->gbc) < info_size) { + if (info_size < 0 || bytestream2_get_bytes_left(&ctx->gbc) < info_size) { av_log(avctx, AV_LOG_ERROR, "Invalid INFO size (%d).\n", info_size); return AVERROR_INVALIDDATA; } diff --git a/libavcodec/hqx.c b/libavcodec/hqx.c index bc24ba91d12..e2b895ac40b 100644 --- a/libavcodec/hqx.c +++ b/libavcodec/hqx.c @@ -471,6 +471,14 @@ static int hqx_decode_frame(AVCodecContext *avctx, void *data, avctx->height = ctx->height; avctx->bits_per_raw_sample = 10; + //The minimum size is 2bit per macroblock + // hqx_decode_422 & hqx_decode_444 have a unconditionally stored 4bits hqx_quants index + // hqx_decode_422a & hqx_decode_444a use cbp_vlc which has a minimum length of 2 bits for its VLCs + // The code rejects slices overlapping in their input data + if (avctx->coded_width / 16 * (avctx->coded_height / 16) * + (100 - avctx->discard_damaged_percentage) / 100 > 4LL * avpkt->size) + return AVERROR_INVALIDDATA; + switch (ctx->format) { case HQX_422: avctx->pix_fmt = AV_PIX_FMT_YUV422P16; @@ -512,9 +520,6 @@ static av_cold int hqx_decode_close(AVCodecContext *avctx) int i; HQXContext *ctx = avctx->priv_data; - if (avctx->internal->is_copy) - return 0; - ff_free_vlc(&ctx->cbp_vlc); for (i = 0; i < 3; i++) { ff_free_vlc(&ctx->dc_vlc[i]); diff --git a/libavcodec/htmlsubtitles.c b/libavcodec/htmlsubtitles.c index d9221ba16b0..8ce66e0b27e 100644 --- a/libavcodec/htmlsubtitles.c +++ b/libavcodec/htmlsubtitles.c @@ -55,7 +55,7 @@ static int scanbraces(const char* in) { if (strncmp(in, "{\\an", 4) != 0) { return 0; } - if (!isdigit(in[4])) { + if (!av_isdigit(in[4])) { return 0; } if (in[5] != '}') { diff --git a/libavcodec/huffyuvdec.c b/libavcodec/huffyuvdec.c index 46dcfa82354..e713b91e4dd 100644 --- a/libavcodec/huffyuvdec.c +++ b/libavcodec/huffyuvdec.c @@ -570,35 +570,6 @@ static av_cold int decode_init(AVCodecContext *avctx) return ret; } -#if HAVE_THREADS -static av_cold int decode_init_thread_copy(AVCodecContext *avctx) -{ - HYuvContext *s = avctx->priv_data; - int i, ret; - - s->avctx = avctx; - - if ((ret = ff_huffyuv_alloc_temp(s)) < 0) { - ff_huffyuv_common_end(s); - return ret; - } - - for (i = 0; i < 8; i++) - s->vlc[i].table = NULL; - - if (s->version >= 2) { - if ((ret = read_huffman_tables(s, avctx->extradata + 4, - avctx->extradata_size)) < 0) - return ret; - } else { - if ((ret = read_old_huffman_tables(s)) < 0) - return ret; - } - - return 0; -} -#endif - /** Subset of GET_VLC for use in hand-roller VLC code */ #define VLC_INTERN(dst, table, gb, name, bits, max_depth) \ code = table[index][0]; \ @@ -957,12 +928,16 @@ static int decode_slice(AVCodecContext *avctx, AVFrame *p, int height, left= left_prediction(s, p->data[plane], s->temp[0], w, 0); y = 1; + if (y >= h) + break; /* second line is left predicted for interlaced case */ if (s->interlaced) { decode_plane_bitstream(s, w, plane); left = left_prediction(s, p->data[plane] + p->linesize[plane], s->temp[0], w, left); y++; + if (y >= h) + break; } lefttop = p->data[plane][0]; @@ -1074,6 +1049,8 @@ static int decode_slice(AVCodecContext *avctx, AVFrame *p, int height, } cy = y = 1; + if (y >= height) + break; /* second line is left predicted for interlaced case */ if (s->interlaced) { @@ -1086,6 +1063,8 @@ static int decode_slice(AVCodecContext *avctx, AVFrame *p, int height, } y++; cy++; + if (y >= height) + break; } /* next 4 pixels are left predicted too */ @@ -1302,7 +1281,6 @@ AVCodec ff_huffyuv_decoder = { .decode = decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_FRAME_THREADS, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy), }; #if CONFIG_FFVHUFF_DECODER @@ -1317,7 +1295,6 @@ AVCodec ff_ffvhuff_decoder = { .decode = decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_FRAME_THREADS, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy), }; #endif /* CONFIG_FFVHUFF_DECODER */ @@ -1333,6 +1310,5 @@ AVCodec ff_hymt_decoder = { .decode = decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_FRAME_THREADS, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy), }; #endif /* CONFIG_HYMT_DECODER */ diff --git a/libavcodec/huffyuvenc.c b/libavcodec/huffyuvenc.c index a6f0d064453..28a55345350 100644 --- a/libavcodec/huffyuvenc.c +++ b/libavcodec/huffyuvenc.c @@ -1091,7 +1091,7 @@ AVCodec ff_huffyuv_encoder = { .init = encode_init, .encode2 = encode_frame, .close = encode_end, - .capabilities = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_FRAME_THREADS, .priv_class = &normal_class, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV422P, AV_PIX_FMT_RGB24, @@ -1111,7 +1111,7 @@ AVCodec ff_ffvhuff_encoder = { .init = encode_init, .encode2 = encode_frame, .close = encode_end, - .capabilities = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_FRAME_THREADS, .priv_class = &ff_class, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV411P, diff --git a/libavcodec/hwaccels.h b/libavcodec/hwaccels.h index 7d73da86767..6109c89bd63 100644 --- a/libavcodec/hwaccels.h +++ b/libavcodec/hwaccels.h @@ -68,6 +68,7 @@ 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_wmv3_d3d11va_hwaccel; extern const AVHWAccel ff_wmv3_d3d11va2_hwaccel; extern const AVHWAccel ff_wmv3_dxva2_hwaccel; diff --git a/libavcodec/hwaccel.h b/libavcodec/hwconfig.h similarity index 78% rename from libavcodec/hwaccel.h rename to libavcodec/hwconfig.h index 3aaa92571c4..f421dc909f4 100644 --- a/libavcodec/hwaccel.h +++ b/libavcodec/hwconfig.h @@ -16,8 +16,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVCODEC_HWACCEL_H -#define AVCODEC_HWACCEL_H +#ifndef AVCODEC_HWCONFIG_H +#define AVCODEC_HWCONFIG_H #include "avcodec.h" #include "hwaccels.h" @@ -81,4 +81,22 @@ typedef struct AVCodecHWConfigInternal { #define HWACCEL_XVMC(codec) \ HW_CONFIG_HWACCEL(0, 0, 1, XVMC, NONE, ff_ ## codec ## _xvmc_hwaccel) -#endif /* AVCODEC_HWACCEL_H */ +#define HW_CONFIG_ENCODER(device, frames, ad_hoc, format, device_type_) \ + &(const AVCodecHWConfigInternal) { \ + .public = { \ + .pix_fmt = AV_PIX_FMT_ ## format, \ + .methods = (device ? AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX : 0) | \ + (frames ? AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX : 0) | \ + (ad_hoc ? AV_CODEC_HW_CONFIG_METHOD_AD_HOC : 0), \ + .device_type = AV_HWDEVICE_TYPE_ ## device_type_, \ + }, \ + .hwaccel = NULL, \ + } + +#define HW_CONFIG_ENCODER_DEVICE(format, device_type_) \ + HW_CONFIG_ENCODER(1, 0, 0, format, device_type_) + +#define HW_CONFIG_ENCODER_FRAMES(format, device_type_) \ + HW_CONFIG_ENCODER(0, 1, 0, format, device_type_) + +#endif /* AVCODEC_HWCONFIG_H */ diff --git a/libavcodec/iff.c b/libavcodec/iff.c index c18bac1ea18..79f6215c770 100644 --- a/libavcodec/iff.c +++ b/libavcodec/iff.c @@ -322,6 +322,8 @@ static int extract_header(AVCodecContext *const avctx, av_log(avctx, AV_LOG_ERROR, "Invalid number of bitplanes: %u\n", s->bpp); return AVERROR_INVALIDDATA; } + if (s->video_size && s->planesize * s->bpp * avctx->height > s->video_size) + return AVERROR_INVALIDDATA; av_freep(&s->ham_buf); av_freep(&s->ham_palbuf); @@ -330,13 +332,17 @@ static int extract_header(AVCodecContext *const avctx, int i, count = FFMIN(palette_size / 3, 1 << s->ham); int ham_count; const uint8_t *const palette = avctx->extradata + AV_RB16(avctx->extradata); + int extra_space = 1; + + if (avctx->codec_tag == MKTAG('P', 'B', 'M', ' ') && s->ham == 4) + extra_space = 4; s->ham_buf = av_malloc((s->planesize * 8) + AV_INPUT_BUFFER_PADDING_SIZE); if (!s->ham_buf) return AVERROR(ENOMEM); ham_count = 8 * (1 << s->ham); - s->ham_palbuf = av_malloc((ham_count << !!(s->masking == MASK_HAS_MASK)) * sizeof (uint32_t) + AV_INPUT_BUFFER_PADDING_SIZE); + s->ham_palbuf = av_malloc(extra_space * (ham_count << !!(s->masking == MASK_HAS_MASK)) * sizeof (uint32_t) + AV_INPUT_BUFFER_PADDING_SIZE); if (!s->ham_palbuf) { av_freep(&s->ham_buf); return AVERROR(ENOMEM); @@ -434,6 +440,8 @@ static av_cold int decode_init(AVCodecContext *avctx) if (avctx->codec_tag == MKTAG('A', 'N', 'I', 'M')) { s->video_size = FFALIGN(avctx->width, 2) * avctx->height * s->bpp; + if (!s->video_size) + return AVERROR_INVALIDDATA; s->video[0] = av_calloc(FFALIGN(avctx->width, 2) * avctx->height, s->bpp); s->video[1] = av_calloc(FFALIGN(avctx->width, 2) * avctx->height, s->bpp); s->pal = av_calloc(256, sizeof(*s->pal)); @@ -456,11 +464,12 @@ static av_cold int decode_init(AVCodecContext *avctx) */ static void decodeplane8(uint8_t *dst, const uint8_t *buf, int buf_size, int plane) { - const uint64_t *lut = plane8_lut[plane]; + const uint64_t *lut; if (plane >= 8) { av_log(NULL, AV_LOG_WARNING, "Ignoring extra planes beyond 8\n"); return; } + lut = plane8_lut[plane]; do { uint64_t v = AV_RN64A(dst) | lut[*buf++]; AV_WN64A(dst, v); @@ -708,13 +717,15 @@ static void decode_deep_rle32(uint8_t *dst, const uint8_t *src, int src_size, in { const uint8_t *src_end = src + src_size; int x = 0, y = 0, i; - while (src + 5 <= src_end) { + while (src_end - src >= 5) { int opcode; opcode = *(int8_t *)src++; if (opcode >= 0) { int size = opcode + 1; for (i = 0; i < size; i++) { - int length = FFMIN(size - i, width); + int length = FFMIN(size - i, width - x); + if (src_end - src < length * 4) + return; memcpy(dst + y*linesize + x * 4, src, length * 4); src += length * 4; x += length; @@ -1143,6 +1154,9 @@ static void decode_long_vertical_delta(uint8_t *dst, x = bytestream2_get_be32(&dgb); } + if (ofsdst + (opcode - 1LL) * dstpitch > bytestream2_size_p(&pb)) + return; + while (opcode) { bytestream2_seek_p(&pb, ofsdst, SEEK_SET); if (h && (j == (ncolumns - 1))) { @@ -1283,6 +1297,9 @@ static void decode_long_vertical_delta2(uint8_t *dst, x = bytestream2_get_be32(&gb); } + if (ofsdst + (opcode - 1LL) * dstpitch > bytestream2_size_p(&pb)) + return; + while (opcode && bytestream2_get_bytes_left_p(&pb) > 1) { bytestream2_seek_p(&pb, ofsdst, SEEK_SET); if (h && (j == ncolumns - 1)) @@ -1345,6 +1362,9 @@ static void decode_delta_d(uint8_t *dst, bytestream2_init(&gb, buf + ofssrc, buf_end - (buf + ofssrc)); entries = bytestream2_get_be32(&gb); + if (entries * 8LL > bytestream2_get_bytes_left(&gb)) + return; + while (entries && bytestream2_get_bytes_left(&gb) >= 8) { int32_t opcode = bytestream2_get_be32(&gb); unsigned offset = bytestream2_get_be32(&gb); @@ -1352,17 +1372,18 @@ static void decode_delta_d(uint8_t *dst, bytestream2_seek_p(&pb, (offset / planepitch_byte) * pitch + (offset % planepitch_byte) + k * planepitch, SEEK_SET); if (opcode >= 0) { uint32_t x = bytestream2_get_be32(&gb); + if (opcode && 4 + (opcode - 1LL) * pitch > bytestream2_get_bytes_left_p(&pb)) + continue; while (opcode && bytestream2_get_bytes_left_p(&pb) > 0) { bytestream2_put_be32(&pb, x); bytestream2_skip_p(&pb, pitch - 4); opcode--; } } else { - opcode = -opcode; while (opcode && bytestream2_get_bytes_left(&gb) > 0) { bytestream2_put_be32(&pb, bytestream2_get_be32(&gb)); bytestream2_skip_p(&pb, pitch - 4); - opcode--; + opcode++; } } entries--; diff --git a/libavcodec/imm5.c b/libavcodec/imm5.c new file mode 100644 index 00000000000..917b414e661 --- /dev/null +++ b/libavcodec/imm5.c @@ -0,0 +1,193 @@ +/* + * 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/intreadwrite.h" + +#include "avcodec.h" +#include "internal.h" + +typedef struct IMM5Context { + AVCodecContext *h264_avctx; // wrapper context for H264 + AVCodecContext *hevc_avctx; // wrapper context for HEVC +} IMM5Context; + +static const struct IMM5_unit { + uint8_t bits[14]; + uint8_t len; +} IMM5_units[14] = { + { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x1E, 0xF4, 0x0B, 0x0F, 0x88 }, 12 }, + { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x1E, 0xF4, 0x05, 0x83, 0xE2 }, 12 }, + { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x1E, 0xF4, 0x05, 0x81, 0xE8, 0x80 }, 13 }, + { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x1E, 0xF4, 0x0B, 0x04, 0xA2 }, 12 }, + { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x1E, 0xF4, 0x05, 0x81, 0x28, 0x80 }, 13 }, + { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x80, 0x1E, 0xF4, 0x05, 0x80, 0x92, 0x20 }, 13 }, + { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1E, 0x9A, 0x74, 0x0B, 0x0F, 0xC8 }, 13 }, + { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1E, 0x9A, 0x74, 0x05, 0x83, 0xF2 }, 13 }, + { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1E, 0x9A, 0x74, 0x05, 0x81, 0xEC, 0x80 }, 14 }, + { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1E, 0x9A, 0x74, 0x0B, 0x04, 0xB2 }, 13 }, + { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1E, 0x9A, 0x74, 0x05, 0x81, 0x2C, 0x80 }, 14 }, + { { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1E, 0x9A, 0x74, 0x05, 0x80, 0x93, 0x20 }, 14 }, + { { 0x00, 0x00, 0x00, 0x01, 0x68, 0xDE, 0x3C, 0x80 }, 8 }, + { { 0x00, 0x00, 0x00, 0x01, 0x68, 0xCE, 0x32, 0x28 }, 8 }, +}; + +static av_cold int imm5_init(AVCodecContext *avctx) +{ + IMM5Context *ctx = avctx->priv_data; + const AVCodec *codec; + int ret; + + codec = avcodec_find_decoder(AV_CODEC_ID_H264); + if (!codec) + return AVERROR_BUG; + ctx->h264_avctx = avcodec_alloc_context3(codec); + if (!ctx->h264_avctx) + return AVERROR(ENOMEM); + ctx->h264_avctx->thread_count = 1; + ctx->h264_avctx->flags = avctx->flags; + ctx->h264_avctx->flags2 = avctx->flags2; + ret = ff_codec_open2_recursive(ctx->h264_avctx, codec, NULL); + if (ret < 0) + return ret; + + codec = avcodec_find_decoder(AV_CODEC_ID_HEVC); + if (!codec) + return AVERROR_BUG; + ctx->hevc_avctx = avcodec_alloc_context3(codec); + if (!ctx->hevc_avctx) + return AVERROR(ENOMEM); + ctx->hevc_avctx->thread_count = 1; + ctx->hevc_avctx->flags = avctx->flags; + ctx->hevc_avctx->flags2 = avctx->flags2; + ret = ff_codec_open2_recursive(ctx->hevc_avctx, codec, NULL); + if (ret < 0) + return ret; + + return 0; +} + +static int imm5_decode_frame(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *avpkt) +{ + IMM5Context *ctx = avctx->priv_data; + AVFrame *frame = data; + AVCodecContext *codec_avctx = ctx->h264_avctx; + int ret; + + if (avpkt->size > 24 && avpkt->data[8] <= 1 && AV_RL32(avpkt->data + 4) + 24ULL <= avpkt->size) { + int codec_type = avpkt->data[1]; + int index = avpkt->data[10]; + int new_size = AV_RL32(avpkt->data + 4); + int offset, off; + + if (codec_type == 0xA) { + codec_avctx = ctx->hevc_avctx; + } else if (index == 17) { + index = 4; + } else if (index == 18) { + index = 5; + } + + if (index >= 1 && index <= 12) { + ret = av_packet_make_writable(avpkt); + if (ret < 0) + return ret; + + index -= 1; + off = offset = IMM5_units[index].len; + if (codec_type == 2) { + offset += IMM5_units[12].len; + } else { + offset += IMM5_units[13].len; + } + + avpkt->data += 24 - offset; + avpkt->size = new_size + offset; + + memcpy(avpkt->data, IMM5_units[index].bits, IMM5_units[index].len); + if (codec_type == 2) { + memcpy(avpkt->data + off, IMM5_units[12].bits, IMM5_units[12].len); + } else { + memcpy(avpkt->data + off, IMM5_units[13].bits, IMM5_units[13].len); + } + } else { + avpkt->data += 24; + avpkt->size -= 24; + } + } + + ret = avcodec_send_packet(codec_avctx, avpkt); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error submitting a packet for decoding\n"); + return ret; + } + + ret = avcodec_receive_frame(codec_avctx, frame); + if (ret < 0) + return ret; + + avctx->pix_fmt = codec_avctx->pix_fmt; + avctx->coded_width = codec_avctx->coded_width; + avctx->coded_height = codec_avctx->coded_height; + avctx->width = codec_avctx->width; + avctx->height = codec_avctx->height; + avctx->bit_rate = codec_avctx->bit_rate; + avctx->colorspace = codec_avctx->colorspace; + avctx->color_range = codec_avctx->color_range; + avctx->color_trc = codec_avctx->color_trc; + avctx->color_primaries = codec_avctx->color_primaries; + avctx->chroma_sample_location = codec_avctx->chroma_sample_location; + + *got_frame = 1; + + return avpkt->size; +} + +static void imm5_flush(AVCodecContext *avctx) +{ + IMM5Context *ctx = avctx->priv_data; + + avcodec_flush_buffers(ctx->h264_avctx); + avcodec_flush_buffers(ctx->hevc_avctx); +} + +static av_cold int imm5_close(AVCodecContext *avctx) +{ + IMM5Context *ctx = avctx->priv_data; + + avcodec_free_context(&ctx->h264_avctx); + avcodec_free_context(&ctx->hevc_avctx); + + return 0; +} + +AVCodec ff_imm5_decoder = { + .name = "imm5", + .long_name = NULL_IF_CONFIG_SMALL("Infinity IMM5"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_IMM5, + .init = imm5_init, + .decode = imm5_decode_frame, + .close = imm5_close, + .flush = imm5_flush, + .priv_data_size = sizeof(IMM5Context), + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | + FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/libavcodec/imx_dump_header_bsf.c b/libavcodec/imx_dump_header_bsf.c index 9a9de05f056..e2b6a155912 100644 --- a/libavcodec/imx_dump_header_bsf.c +++ b/libavcodec/imx_dump_header_bsf.c @@ -25,8 +25,8 @@ * modifies bitstream to fit in mov and be decoded by final cut pro decoder */ -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "bytestream.h" diff --git a/libavcodec/indeo2.c b/libavcodec/indeo2.c index f367682e618..ccf6cd84cb6 100644 --- a/libavcodec/indeo2.c +++ b/libavcodec/indeo2.c @@ -161,7 +161,7 @@ static int ir2_decode_frame(AVCodecContext *avctx, int start, ret; int ltab, ctab; - if ((ret = ff_reget_buffer(avctx, p)) < 0) + if ((ret = ff_reget_buffer(avctx, p, 0)) < 0) return ret; start = 48; /* hardcoded for now */ diff --git a/libavcodec/indeo5.c b/libavcodec/indeo5.c index 7b9da53df48..4ccdbcaf0ab 100644 --- a/libavcodec/indeo5.c +++ b/libavcodec/indeo5.c @@ -264,7 +264,7 @@ static int decode_gop_header(IVI45DecContext *ctx, AVCodecContext *avctx) } if (get_bits1(&ctx->gb)) - skip_bits_long(&ctx->gb, 24); /* skip transparency fill color */ + skip_bits(&ctx->gb, 24); /* skip transparency fill color */ } align_get_bits(&ctx->gb); @@ -348,7 +348,7 @@ static int decode_pic_hdr(IVI45DecContext *ctx, AVCodecContext *avctx) if (ctx->frame_type != FRAMETYPE_NULL) { ctx->frame_flags = get_bits(&ctx->gb, 8); - ctx->pic_hdr_size = (ctx->frame_flags & 1) ? get_bits_long(&ctx->gb, 24) : 0; + ctx->pic_hdr_size = (ctx->frame_flags & 1) ? get_bits(&ctx->gb, 24) : 0; ctx->checksum = (ctx->frame_flags & 0x10) ? get_bits(&ctx->gb, 16) : 0; @@ -392,7 +392,7 @@ static int decode_band_hdr(IVI45DecContext *ctx, IVIBandDesc *band, return 0; } - band->data_size = (ctx->frame_flags & 0x80) ? get_bits_long(&ctx->gb, 24) : 0; + band->data_size = (ctx->frame_flags & 0x80) ? get_bits(&ctx->gb, 24) : 0; band->inherit_mv = band_flags & 2; band->inherit_qdelta = band_flags & 8; diff --git a/libavcodec/intelh263dec.c b/libavcodec/intelh263dec.c index d321dd4dba5..283fb1cb0a8 100644 --- a/libavcodec/intelh263dec.c +++ b/libavcodec/intelh263dec.c @@ -33,7 +33,7 @@ int ff_intel_h263_decode_picture_header(MpegEncContext *s) } /* picture header */ - if (get_bits_long(&s->gb, 22) != 0x20) { + if (get_bits(&s->gb, 22) != 0x20) { av_log(s->avctx, AV_LOG_ERROR, "Bad picture start code\n"); return -1; } diff --git a/libavcodec/internal.h b/libavcodec/internal.h index 5096ffa1d90..0a72a0e3723 100644 --- a/libavcodec/internal.h +++ b/libavcodec/internal.h @@ -68,6 +68,17 @@ * Codec initializes slice-based threading with a main function */ #define FF_CODEC_CAP_SLICE_THREAD_HAS_MF (1 << 5) +/* + * The codec supports frame threading and has inter-frame dependencies, so it + * uses ff_thread_report/await_progress(). + */ +#define FF_CODEC_CAP_ALLOCATE_PROGRESS (1 << 6) + +/** + * AVCodec.codec_tags termination value + */ +#define FF_CODEC_TAGS_END -1 + #ifdef TRACE # define ff_tlog(ctx, ...) av_log(ctx, AV_LOG_TRACE, __VA_ARGS__) @@ -83,7 +94,7 @@ #define FF_QSCALE_TYPE_H264 2 #define FF_QSCALE_TYPE_VP56 3 -#define FF_SANE_NB_CHANNELS 256U +#define FF_SANE_NB_CHANNELS 512U #define FF_SIGNBIT(x) ((x) >> CHAR_BIT * sizeof(x) - 1) @@ -97,35 +108,11 @@ # define STRIDE_ALIGN 8 #endif -typedef struct FramePool { - /** - * Pools for each data plane. For audio all the planes have the same size, - * so only pools[0] is used. - */ - AVBufferPool *pools[4]; - - /* - * Pool parameters - */ - int format; - int width, height; - int stride_align[AV_NUM_DATA_POINTERS]; - int linesize[4]; - int planes; - int channels; - int samples; -} FramePool; - typedef struct DecodeSimpleContext { AVPacket *in_pkt; AVFrame *out_frame; } DecodeSimpleContext; -typedef struct DecodeFilterContext { - AVBSFContext **bsfs; - int nb_bsfs; -} DecodeFilterContext; - typedef struct AVCodecInternal { /** * Whether the parent AVCodecContext is a copy of the context which had @@ -135,21 +122,6 @@ typedef struct AVCodecInternal { */ int is_copy; - /** - * Whether to allocate progress for frame threading. - * - * The codec must set it to 1 if it uses ff_thread_await/report_progress(), - * then progress will be allocated in ff_thread_get_buffer(). The frames - * then MUST be freed with ff_thread_release_buffer(). - * - * If the codec does not need to call the progress functions (there are no - * dependencies between the frames), it should leave this at 0. Then it can - * decode straight to the user-provided frames (which the user will then - * free with av_frame_unref()), there is no need to call - * ff_thread_release_buffer(). - */ - int allocate_progress; - /** * An audio frame with less than required samples has been submitted and * padded with silence. Reject all subsequent frames. @@ -158,12 +130,12 @@ typedef struct AVCodecInternal { AVFrame *to_free; - FramePool *pool; + AVBufferRef *pool; void *thread_ctx; DecodeSimpleContext ds; - DecodeFilterContext filter; + AVBSFContext *bsf; /** * Properties (timestamps+side data) extracted from the last packet passed @@ -201,8 +173,6 @@ typedef struct AVCodecInternal { int buffer_pkt_valid; // encoding: packet without data can be valid AVFrame *buffer_frame; int draining_done; - /* set to 1 when the caller is using the old decoding API */ - int compat_decode; int compat_decode_warned; /* this variable is set by the decoder internals to signal to the old * API compat wrappers the amount of data consumed from the last packet */ @@ -280,8 +250,6 @@ void ff_color_frame(AVFrame *frame, const int color[4]); */ int ff_alloc_packet2(AVCodecContext *avctx, AVPacket *avpkt, int64_t size, int64_t min_size); -attribute_deprecated int ff_alloc_packet(AVPacket *avpkt, int size); - /** * Rescale from sample rate to AVCodecContext.time_base. */ @@ -320,11 +288,12 @@ static av_always_inline float ff_exp2fi(int x) { */ int ff_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags); +#define FF_REGET_BUFFER_FLAG_READONLY 1 ///< the returned buffer does not need to be writable /** - * Identical in function to av_frame_make_writable(), except it uses - * ff_get_buffer() to allocate the buffer when needed. + * Identical in function to ff_get_buffer(), except it reuses the existing buffer + * if available. */ -int ff_reget_buffer(AVCodecContext *avctx, AVFrame *frame); +int ff_reget_buffer(AVCodecContext *avctx, AVFrame *frame, int flags); int ff_thread_can_start_frame(AVCodecContext *avctx); @@ -389,8 +358,6 @@ int ff_decode_frame_props(AVCodecContext *avctx, AVFrame *frame); */ AVCPBProperties *ff_add_cpb_side_data(AVCodecContext *avctx); -int ff_side_data_set_encoder_stats(AVPacket *pkt, int quality, int64_t *error, int error_count, int pict_type); - /** * Check AVFrame for A53 side data and allocate and fill SEI message with A53 info * @@ -424,6 +391,8 @@ int64_t ff_guess_coded_bitrate(AVCodecContext *avctx); int ff_int_from_list_or_default(void *ctx, const char * val_name, int val, const int * array_valid_values, int default_value); +void ff_dvdsub_parse_palette(uint32_t *palette, const char *p); + #if defined(_WIN32) && CONFIG_SHARED && !defined(BUILDING_avcodec) # define av_export_avcodec __declspec(dllimport) #else diff --git a/libavcodec/interplayacm.c b/libavcodec/interplayacm.c index 5639d8de82c..3704d1a2f2d 100644 --- a/libavcodec/interplayacm.c +++ b/libavcodec/interplayacm.c @@ -435,7 +435,8 @@ static int fill_block(InterplayACMContext *s) static void juggle(int *wrap_p, int *block_p, unsigned sub_len, unsigned sub_count) { unsigned i, j; - int *p, r0, r1, r2, r3; + int *p; + unsigned int r0, r1, r2, r3; for (i = 0; i < sub_len; i++) { p = block_p; @@ -528,7 +529,7 @@ static int decode_block(InterplayACMContext *s) for (i = 1, x = -val; i <= count; i++) { s->midbuf[-i] = x; - x -= val; + x -= (unsigned)val; } ret = fill_block(s); diff --git a/libavcodec/interplayvideo.c b/libavcodec/interplayvideo.c index 4313fdf7ac1..274641c3d1a 100644 --- a/libavcodec/interplayvideo.c +++ b/libavcodec/interplayvideo.c @@ -77,9 +77,14 @@ typedef struct IpvideoContext { static int copy_from(IpvideoContext *s, AVFrame *src, AVFrame *dst, int delta_x, int delta_y) { + int width = dst->width; int current_offset = s->pixel_ptr - dst->data[0]; - int motion_offset = current_offset + delta_y * dst->linesize[0] - + delta_x * (1 + s->is_16bpp); + int x = (current_offset % dst->linesize[0]) / (1 + s->is_16bpp); + int y = current_offset / dst->linesize[0]; + int dx = delta_x + x - ((delta_x + x >= width) - (delta_x + x < 0)) * width; + int dy = delta_y + y + (delta_x + x >= width) - (delta_x + x < 0); + int motion_offset = dy * src->linesize[0] + dx * (1 + s->is_16bpp); + if (motion_offset < 0) { av_log(s->avctx, AV_LOG_ERROR, "motion offset < 0 (%d)\n", motion_offset); return AVERROR_INVALIDDATA; @@ -931,12 +936,12 @@ static void ipvideo_format_06_secondpass(IpvideoContext *s, AVFrame *frame, int1 int off_x, off_y; if (opcode < 0) { - off_x = ((uint16_t)opcode - 0xC000) % frame->linesize[0]; - off_y = ((uint16_t)opcode - 0xC000) / frame->linesize[0]; + off_x = ((uint16_t)opcode - 0xC000) % frame->width; + off_y = ((uint16_t)opcode - 0xC000) / frame->width; copy_from(s, s->last_frame, frame, off_x, off_y); } else if (opcode > 0) { - off_x = ((uint16_t)opcode - 0x4000) % frame->linesize[0]; - off_y = ((uint16_t)opcode - 0x4000) / frame->linesize[0]; + off_x = ((uint16_t)opcode - 0x4000) % frame->width; + off_y = ((uint16_t)opcode - 0x4000) / frame->width; copy_from(s, frame, frame, off_x, off_y); } } @@ -1001,12 +1006,12 @@ static void ipvideo_format_10_secondpass(IpvideoContext *s, AVFrame *frame, int1 int off_x, off_y; if (opcode < 0) { - off_x = ((uint16_t)opcode - 0xC000) % s->cur_decode_frame->linesize[0]; - off_y = ((uint16_t)opcode - 0xC000) / s->cur_decode_frame->linesize[0]; + off_x = ((uint16_t)opcode - 0xC000) % s->cur_decode_frame->width; + off_y = ((uint16_t)opcode - 0xC000) / s->cur_decode_frame->width; copy_from(s, s->prev_decode_frame, s->cur_decode_frame, off_x, off_y); } else if (opcode > 0) { - off_x = ((uint16_t)opcode - 0x4000) % s->cur_decode_frame->linesize[0]; - off_y = ((uint16_t)opcode - 0x4000) / s->cur_decode_frame->linesize[0]; + off_x = ((uint16_t)opcode - 0x4000) % s->cur_decode_frame->width; + off_y = ((uint16_t)opcode - 0x4000) / s->cur_decode_frame->width; copy_from(s, s->cur_decode_frame, s->cur_decode_frame, off_x, off_y); } } @@ -1181,14 +1186,6 @@ static av_cold int ipvideo_decode_init(AVCodecContext *avctx) s->cur_decode_frame->format = avctx->pix_fmt; s->prev_decode_frame->format = avctx->pix_fmt; - ret = ff_get_buffer(avctx, s->cur_decode_frame, 0); - if (ret < 0) - goto error; - - ret = ff_get_buffer(avctx, s->prev_decode_frame, 0); - if (ret < 0) - goto error; - return 0; error: av_frame_free(&s->last_frame); @@ -1239,83 +1236,83 @@ static int ipvideo_decode_frame(AVCodecContext *avctx, s->decoding_map_size = AV_RL16(buf + 4); s->skip_map_size = AV_RL16(buf + 6); - switch(frame_format) { - case 0x06: - if (s->decoding_map_size) { - av_log(avctx, AV_LOG_ERROR, "Decoding map for format 0x06\n"); - return AVERROR_INVALIDDATA; - } + switch (frame_format) { + case 0x06: + if (s->decoding_map_size) { + av_log(avctx, AV_LOG_ERROR, "Decoding map for format 0x06\n"); + return AVERROR_INVALIDDATA; + } - if (s->skip_map_size) { - av_log(avctx, AV_LOG_ERROR, "Skip map for format 0x06\n"); - return AVERROR_INVALIDDATA; - } + if (s->skip_map_size) { + av_log(avctx, AV_LOG_ERROR, "Skip map for format 0x06\n"); + return AVERROR_INVALIDDATA; + } - if (s->is_16bpp) { - av_log(avctx, AV_LOG_ERROR, "Video format 0x06 does not support 16bpp movies\n"); - return AVERROR_INVALIDDATA; - } + if (s->is_16bpp) { + av_log(avctx, AV_LOG_ERROR, "Video format 0x06 does not support 16bpp movies\n"); + return AVERROR_INVALIDDATA; + } - /* Decoding map for 0x06 frame format is at the top of pixeldata */ - s->decoding_map_size = ((s->avctx->width / 8) * (s->avctx->height / 8)) * 2; - s->decoding_map = buf + 8 + 14; /* 14 bits of op data */ - video_data_size -= s->decoding_map_size + 14; - if (video_data_size <= 0 || s->decoding_map_size == 0) - return AVERROR_INVALIDDATA; + /* Decoding map for 0x06 frame format is at the top of pixeldata */ + s->decoding_map_size = ((s->avctx->width / 8) * (s->avctx->height / 8)) * 2; + s->decoding_map = buf + 8 + 14; /* 14 bits of op data */ + video_data_size -= s->decoding_map_size + 14; + if (video_data_size <= 0 || s->decoding_map_size == 0) + return AVERROR_INVALIDDATA; - if (buf_size < 8 + s->decoding_map_size + 14 + video_data_size) - return AVERROR_INVALIDDATA; + if (buf_size < 8 + s->decoding_map_size + 14 + video_data_size) + return AVERROR_INVALIDDATA; - bytestream2_init(&s->stream_ptr, buf + 8 + s->decoding_map_size + 14, video_data_size); + bytestream2_init(&s->stream_ptr, buf + 8 + s->decoding_map_size + 14, video_data_size); - break; + break; - case 0x10: - if (! s->decoding_map_size) { - av_log(avctx, AV_LOG_ERROR, "Empty decoding map for format 0x10\n"); - return AVERROR_INVALIDDATA; - } + case 0x10: + if (! s->decoding_map_size) { + av_log(avctx, AV_LOG_ERROR, "Empty decoding map for format 0x10\n"); + return AVERROR_INVALIDDATA; + } - if (! s->skip_map_size) { - av_log(avctx, AV_LOG_ERROR, "Empty skip map for format 0x10\n"); - return AVERROR_INVALIDDATA; - } + if (! s->skip_map_size) { + av_log(avctx, AV_LOG_ERROR, "Empty skip map for format 0x10\n"); + return AVERROR_INVALIDDATA; + } - if (s->is_16bpp) { - av_log(avctx, AV_LOG_ERROR, "Video format 0x10 does not support 16bpp movies\n"); - return AVERROR_INVALIDDATA; - } + if (s->is_16bpp) { + av_log(avctx, AV_LOG_ERROR, "Video format 0x10 does not support 16bpp movies\n"); + return AVERROR_INVALIDDATA; + } - if (buf_size < 8 + video_data_size + s->decoding_map_size + s->skip_map_size) - return AVERROR_INVALIDDATA; + if (buf_size < 8 + video_data_size + s->decoding_map_size + s->skip_map_size) + return AVERROR_INVALIDDATA; - bytestream2_init(&s->stream_ptr, buf + 8, video_data_size); - s->decoding_map = buf + 8 + video_data_size; - s->skip_map = buf + 8 + video_data_size + s->decoding_map_size; + bytestream2_init(&s->stream_ptr, buf + 8, video_data_size); + s->decoding_map = buf + 8 + video_data_size; + s->skip_map = buf + 8 + video_data_size + s->decoding_map_size; - break; + break; - case 0x11: - if (! s->decoding_map_size) { - av_log(avctx, AV_LOG_ERROR, "Empty decoding map for format 0x11\n"); - return AVERROR_INVALIDDATA; - } + case 0x11: + if (! s->decoding_map_size) { + av_log(avctx, AV_LOG_ERROR, "Empty decoding map for format 0x11\n"); + return AVERROR_INVALIDDATA; + } - if (s->skip_map_size) { - av_log(avctx, AV_LOG_ERROR, "Skip map for format 0x11\n"); - return AVERROR_INVALIDDATA; - } + if (s->skip_map_size) { + av_log(avctx, AV_LOG_ERROR, "Skip map for format 0x11\n"); + return AVERROR_INVALIDDATA; + } - if (buf_size < 8 + video_data_size + s->decoding_map_size) - return AVERROR_INVALIDDATA; + if (buf_size < 8 + video_data_size + s->decoding_map_size) + return AVERROR_INVALIDDATA; - bytestream2_init(&s->stream_ptr, buf + 8, video_data_size); - s->decoding_map = buf + 8 + video_data_size; + bytestream2_init(&s->stream_ptr, buf + 8, video_data_size); + s->decoding_map = buf + 8 + video_data_size; - break; + break; - default: - av_log(avctx, AV_LOG_ERROR, "Frame type 0x%02X unsupported\n", frame_format); + default: + av_log(avctx, AV_LOG_ERROR, "Frame type 0x%02X unsupported\n", frame_format); } /* ensure we can't overread the packet */ @@ -1338,16 +1335,16 @@ static int ipvideo_decode_frame(AVCodecContext *avctx, } } - switch(frame_format) { - case 0x06: - ipvideo_decode_format_06_opcodes(s, frame); - break; - case 0x10: - ipvideo_decode_format_10_opcodes(s, frame); - break; - case 0x11: - ipvideo_decode_format_11_opcodes(s, frame); - break; + switch (frame_format) { + case 0x06: + ipvideo_decode_format_06_opcodes(s, frame); + break; + case 0x10: + ipvideo_decode_format_10_opcodes(s, frame); + break; + case 0x11: + ipvideo_decode_format_11_opcodes(s, frame); + break; } *got_frame = send_buffer; diff --git a/libavcodec/intrax8.c b/libavcodec/intrax8.c index d46f97c7a4b..f385423dc1f 100644 --- a/libavcodec/intrax8.c +++ b/libavcodec/intrax8.c @@ -801,6 +801,8 @@ int ff_intrax8_decode_picture(IntraX8Context *w, Picture *pict, for (w->mb_y = 0; w->mb_y < w->mb_height * 2; w->mb_y++) { x8_init_block_index(w, w->frame); mb_xy = (w->mb_y >> 1) * (w->mb_width + 1); + if (get_bits_left(gb) < 1) + goto error; for (w->mb_x = 0; w->mb_x < w->mb_width * 2; w->mb_x++) { x8_get_prediction(w); if (x8_setup_spatial_predictor(w, 0)) diff --git a/libavcodec/ituh263dec.c b/libavcodec/ituh263dec.c index 1b57e53cad9..c1005b0994f 100644 --- a/libavcodec/ituh263dec.c +++ b/libavcodec/ituh263dec.c @@ -222,7 +222,7 @@ int ff_h263_resync(MpegEncContext *s){ get_bits(&s->gb, 8); } - if (show_bits_long(&s->gb, 32) == SLICE_START_CODE) + if (get_bits_left(&s->gb) >= 32 && show_bits_long(&s->gb, 32) == SLICE_START_CODE) return get_bits_count(&s->gb); else return -1; @@ -1218,6 +1218,11 @@ int ff_h263_decode_picture_header(MpegEncContext *s) if ((ret = av_image_check_size(s->width, s->height, 0, s)) < 0) return ret; + if (!(s->avctx->flags2 & AV_CODEC_FLAG2_CHUNKS)) { + if ((s->width * s->height / 256 / 8) > get_bits_left(&s->gb)) + return AVERROR_INVALIDDATA; + } + s->mb_width = (s->width + 15) / 16; s->mb_height = (s->height + 15) / 16; s->mb_num = s->mb_width * s->mb_height; @@ -1281,7 +1286,7 @@ int ff_h263_decode_picture_header(MpegEncContext *s) for(i=0; i<13; i++){ for(j=0; j<3; j++){ int v= get_bits(&s->gb, 8); - v |= get_sbits(&s->gb, 8)<<8; + v |= get_sbits(&s->gb, 8) * (1 << 8); av_log(s->avctx, AV_LOG_DEBUG, " %5d", v); } av_log(s->avctx, AV_LOG_DEBUG, "\n"); diff --git a/libavcodec/ivi.c b/libavcodec/ivi.c index 73fcf51b7bb..c5c50fb5c12 100644 --- a/libavcodec/ivi.c +++ b/libavcodec/ivi.c @@ -30,7 +30,6 @@ #include "libavutil/attributes.h" #include "libavutil/imgutils.h" -#include "libavutil/timer.h" #define BITSTREAM_READER_LE #include "avcodec.h" @@ -354,23 +353,11 @@ av_cold int ff_ivi_init_planes(AVCodecContext *avctx, IVIPlaneDesc *planes, cons band->height = b_height; band->pitch = width_aligned; band->aheight = height_aligned; - band->bufs[0] = av_mallocz(buf_size); - band->bufs[1] = av_mallocz(buf_size); + av_assert0(!band->bufs[0] && !band->bufs[1] && + !band->bufs[2] && !band->bufs[3]); band->bufsize = buf_size/2; - if (!band->bufs[0] || !band->bufs[1]) - return AVERROR(ENOMEM); + av_assert0(buf_size % 2 == 0); - /* allocate the 3rd band buffer for scalability mode */ - if (cfg->luma_bands > 1) { - band->bufs[2] = av_mallocz(buf_size); - if (!band->bufs[2]) - return AVERROR(ENOMEM); - } - if (is_indeo4) { - band->bufs[3] = av_mallocz(buf_size); - if (!band->bufs[3]) - return AVERROR(ENOMEM); - } /* reset custom vlc */ planes[p].bands[0].blk_vlc.cust_desc.num_rows = 0; } @@ -488,7 +475,7 @@ static int ivi_dec_tile_data_size(GetBitContext *gb) if (get_bits1(gb)) { len = get_bits(gb, 8); if (len == 255) - len = get_bits_long(gb, 24); + len = get_bits(gb, 24); } /* align the bitstream reader on the byte boundary */ @@ -945,6 +932,15 @@ static void ivi_output_plane(IVIPlaneDesc *plane, uint8_t *dst, ptrdiff_t dst_pi } } +static void *prepare_buf(IVI45DecContext *ctx, IVIBandDesc *band, int i) +{ + if (ctx->pic_conf.luma_bands <= 1 && i == 2) + return NULL; + if (!band->bufs[i]) + band->bufs[i] = av_mallocz(2 * band->bufsize); + return band->bufs[i]; +} + /** * Decode an Indeo 4 or 5 band. * @@ -959,18 +955,22 @@ static int decode_band(IVI45DecContext *ctx, int result, i, t, idx1, idx2, pos; IVITile *tile; - band->buf = band->bufs[ctx->dst_buf]; + band->buf = prepare_buf(ctx, band, ctx->dst_buf); if (!band->buf) { av_log(avctx, AV_LOG_ERROR, "Band buffer points to no data!\n"); return AVERROR_INVALIDDATA; } if (ctx->is_indeo4 && ctx->frame_type == IVI4_FRAMETYPE_BIDIR) { - band->ref_buf = band->bufs[ctx->b_ref_buf]; - band->b_ref_buf = band->bufs[ctx->ref_buf]; + band->ref_buf = prepare_buf(ctx, band, ctx->b_ref_buf); + band->b_ref_buf = prepare_buf(ctx, band, ctx->ref_buf); + if (!band->b_ref_buf) + return AVERROR(ENOMEM); } else { - band->ref_buf = band->bufs[ctx->ref_buf]; + band->ref_buf = prepare_buf(ctx, band, ctx->ref_buf); band->b_ref_buf = 0; } + if (!band->ref_buf) + return AVERROR(ENOMEM); band->data_ptr = ctx->frame_data + (get_bits_count(&ctx->gb) >> 3); result = ctx->decode_band_hdr(ctx, band, avctx); @@ -1123,8 +1123,6 @@ int ff_ivi_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, ctx->switch_buffers(ctx); - //{ START_TIMER; - if (ctx->is_nonnull_frame(ctx)) { ctx->buf_invalid[ctx->dst_buf] = 1; for (p = 0; p < 3; p++) { @@ -1150,8 +1148,6 @@ int ff_ivi_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, if (ctx->buf_invalid[ctx->dst_buf]) return -1; - //STOP_TIMER("decode_planes"); } - if (!ctx->is_nonnull_frame(ctx)) return buf_size; @@ -1192,10 +1188,12 @@ int ff_ivi_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, left = get_bits_count(&ctx->gb) & 0x18; skip_bits_long(&ctx->gb, 64 - left); if (get_bits_left(&ctx->gb) > 18 && - show_bits_long(&ctx->gb, 21) == 0xBFFF8) { // syncheader + inter type + show_bits(&ctx->gb, 21) == 0xBFFF8) { // syncheader + inter type AVPacket pkt; pkt.data = avpkt->data + (get_bits_count(&ctx->gb) >> 3); pkt.size = get_bits_left(&ctx->gb) >> 3; + ctx->got_p_frame = 0; + av_frame_unref(ctx->p_frame); ff_ivi_decode_frame(avctx, ctx->p_frame, &ctx->got_p_frame, &pkt); } } diff --git a/libavcodec/j2kenc.c b/libavcodec/j2kenc.c index e91d932bb7f..38643c9a28c 100644 --- a/libavcodec/j2kenc.c +++ b/libavcodec/j2kenc.c @@ -521,13 +521,13 @@ static void init_luts(void) mask = ~((1<> (NMSEDEC_BITS-2)&2) + 1; - lut_nmsedec_ref[i] = FFMAX((-2*i + (1<i_data[(comp->coord[0][1] - comp->coord[0][0]) * y + x] << NMSEDEC_FRACBITS; + *ptr++ = comp->i_data[(comp->coord[0][1] - comp->coord[0][0]) * y + x] * (1 << NMSEDEC_FRACBITS); } } } else{ diff --git a/libavcodec/jpeg2000.c b/libavcodec/jpeg2000.c index 3f50bf9fb34..73206d17f31 100644 --- a/libavcodec/jpeg2000.c +++ b/libavcodec/jpeg2000.c @@ -276,11 +276,11 @@ static int init_prec(Jpeg2000Band *band, /* TODO: Verify with previous count of codeblocks per band */ /* Compute P_x0 */ - prec->coord[0][0] = ((band->coord[0][0] >> log2_band_prec_width) + precno % reslevel->num_precincts_x) * + prec->coord[0][0] = ((reslevel->coord[0][0] >> reslevel->log2_prec_width) + precno % reslevel->num_precincts_x) * (1 << log2_band_prec_width); /* Compute P_y0 */ - prec->coord[1][0] = ((band->coord[1][0] >> log2_band_prec_height) + precno / reslevel->num_precincts_x) * + prec->coord[1][0] = ((reslevel->coord[1][0] >> reslevel->log2_prec_height) + precno / reslevel->num_precincts_x) * (1 << log2_band_prec_height); /* Compute P_x1 */ diff --git a/libavcodec/jpeg2000.h b/libavcodec/jpeg2000.h index c429ca59961..0f827169816 100644 --- a/libavcodec/jpeg2000.h +++ b/libavcodec/jpeg2000.h @@ -40,15 +40,15 @@ enum Jpeg2000Markers { JPEG2000_SIZ = 0xff51, // image and tile size JPEG2000_COD, // coding style default JPEG2000_COC, // coding style component - JPEG2000_TLM = 0xff55, // packed packet headers, tile-part header - JPEG2000_PLM = 0xff57, // tile-part lengths - JPEG2000_PLT, // packet length, main header + JPEG2000_TLM = 0xff55, // tile-part length, main header + JPEG2000_PLM = 0xff57, // packet length, main header + JPEG2000_PLT, // packet length, tile-part header JPEG2000_QCD = 0xff5c, // quantization default JPEG2000_QCC, // quantization component JPEG2000_RGN, // region of interest JPEG2000_POC, // progression order change - JPEG2000_PPM, // packet length, tile-part header - JPEG2000_PPT, // packed packet headers, main header + JPEG2000_PPM, // packed packet headers, main header + JPEG2000_PPT, // packed packet headers, tile-part header JPEG2000_CRG = 0xff63, // component registration JPEG2000_COM, // comment JPEG2000_SOT = 0xff90, // start of tile-part @@ -210,6 +210,7 @@ typedef struct Jpeg2000Component { int *i_data; int coord[2][2]; // border coordinates {{x0, x1}, {y0, y1}} -- can be reduced with lowres option int coord_o[2][2]; // border coordinates {{x0, x1}, {y0, y1}} -- original values from jpeg2000 headers + uint8_t roi_shift; // ROI scaling value for the component } Jpeg2000Component; /* misc tools */ diff --git a/libavcodec/jpeg2000_parser.c b/libavcodec/jpeg2000_parser.c new file mode 100644 index 00000000000..5923e8f4336 --- /dev/null +++ b/libavcodec/jpeg2000_parser.c @@ -0,0 +1,190 @@ +/* + * JPEG2000 parser + * Copyright (c) 2020 Gautam Ramakrishnan + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 + * JPEG2000 parser. + */ + +#include "parser.h" + +/* Whether frame is jp2 file or codestream +*/ +enum frame_type { + jp2_file = 1, + j2k_cstream +}; + +typedef struct JPEG2000ParserContext { + ParseContext pc; + uint64_t bytes_read; + uint64_t fheader_state; + uint32_t skip_bytes; // skip bytes inside codestream data + enum frame_type ft; // 1 if file, 2 if codestream + uint8_t fheader_read; // are we reading + uint8_t reading_file_header; + uint8_t skipped_codestream; + uint8_t codestream_frame_end; + uint8_t read_tp; + uint8_t in_codestream; +} JPEG2000ParserContext; + +static inline void reset_context(JPEG2000ParserContext *m) +{ + ParseContext *pc = &m->pc; + + pc->frame_start_found= 0; + pc->state = 0; + m->bytes_read = 0; + m->ft = 0; + m->skipped_codestream = 0; + m->fheader_read = 0; + m->codestream_frame_end = 0; + m->skip_bytes = 0; + m->read_tp = 0; + m->in_codestream = 0; +} + +/* Returns 1 if marker has any data which can be skipped +*/ +static uint8_t info_marker(uint16_t marker) +{ + if (marker == 0xFF92 || marker == 0xFF4F || + marker == 0xFF90 || marker == 0xFF93 || + marker == 0xFFD9) + return 0; + else + if (marker > 0xFF00) return 1; + return 0; +} + +/** + * Find the end of the current frame in the bitstream. + * @return the position of the first byte of the next frame, or -1 + */ +static int find_frame_end(JPEG2000ParserContext *m, const uint8_t *buf, int buf_size) +{ + ParseContext *pc= &m->pc; + int i; + uint32_t state; + uint64_t state64; + state= pc->state; + state64 = pc->state64; + if (buf_size == 0) { + return 0; + } + + for (i = 0; i < buf_size; i++) { + state = state << 8 | buf[i]; + state64 = state64 << 8 | buf[i]; + m->bytes_read++; + if (m->skip_bytes) { + m->skip_bytes--; + continue; + } + if (m->codestream_frame_end) { + reset_context(m); + return i; + } + if (m->read_tp) { // Find out how many bytes inside Tile part codestream to skip. + if (m->read_tp == 1) { + m->skip_bytes = (state64 & 0xFFFFFFFF) - 10 > 0? + (state64 & 0xFFFFFFFF) - 10 : 0; + } + m->read_tp--; + } + if (m->fheader_read) { + if (m->fheader_read == 1) { + if (state64 == 0x6A5020200D0A870A) { // JP2 signature box value. + if (pc->frame_start_found) { + pc->frame_start_found = 0; + reset_context(m); + return i - 11; + } else { + pc->frame_start_found = 1; + m->ft = jp2_file; + } + } + } + m->fheader_read--; + } + if (state == 0x0000000C && m->bytes_read >= 3) { // Indicates start of JP2 file. Check signature next. + m->fheader_read = 8; + } else if ((state & 0xFFFF) == 0xFF4F) { + m->in_codestream = 1; + if (!pc->frame_start_found) { + pc->frame_start_found = 1; + m->ft = j2k_cstream; + } else if (pc->frame_start_found && m->ft == jp2_file && m->skipped_codestream) { + reset_context(m); + return i - 1; + } + } else if ((state & 0xFFFF) == 0xFFD9) { + if (pc->frame_start_found && m->ft == jp2_file) { + m->skipped_codestream = 1; + } else if (pc->frame_start_found && m->ft == j2k_cstream) { + m->codestream_frame_end = 1; + } + m->in_codestream = 0; + } else if (m->in_codestream && (state & 0xFFFF) == 0xFF90) { // Are we in tile part header? + m->read_tp = 8; + } else if (pc->frame_start_found && info_marker((state & 0xFFFF0000)>>16) && m->in_codestream) { + m->skip_bytes = (state & 0xFFFF) - 2; + } + } + + pc->state = state; + pc->state64 = state64; + return END_NOT_FOUND; +} + +static int jpeg2000_parse(AVCodecParserContext *s, + AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) +{ + JPEG2000ParserContext *m = s->priv_data; + ParseContext *pc = &m->pc; + int next; + + if(s->flags & PARSER_FLAG_COMPLETE_FRAMES) { + next= buf_size; + } else { + next= find_frame_end(m, buf, buf_size); + + if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) { + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size; + } + } + + *poutbuf = buf; + *poutbuf_size = buf_size; + return next; +} + +AVCodecParser ff_jpeg2000_parser = { + .codec_ids = { AV_CODEC_ID_JPEG2000 }, + .priv_data_size = sizeof(JPEG2000ParserContext), + .parser_parse = jpeg2000_parse, + .parser_close = ff_parse_close, +}; diff --git a/libavcodec/jpeg2000dec.c b/libavcodec/jpeg2000dec.c index 019dc81f56b..ab36009a2d4 100644 --- a/libavcodec/jpeg2000dec.c +++ b/libavcodec/jpeg2000dec.c @@ -83,6 +83,10 @@ typedef struct Jpeg2000Tile { 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; @@ -113,6 +117,7 @@ typedef struct Jpeg2000DecoderContext { Jpeg2000CodingStyle codsty[4]; Jpeg2000QuantStyle qntsty[4]; Jpeg2000POC poc; + uint8_t roi_shift[4]; int bit_index; @@ -398,12 +403,16 @@ static int get_siz(Jpeg2000DecoderContext *s) break; } } - for (i = 0; i < possible_fmts_nb; ++i) { - if (pix_fmt_match(possible_fmts[i], ncomponents, s->precision, log2_chroma_wh, s->pal8)) { - s->avctx->pix_fmt = possible_fmts[i]; - break; + if ( s->avctx->pix_fmt != AV_PIX_FMT_NONE + && !pix_fmt_match(s->avctx->pix_fmt, ncomponents, s->precision, log2_chroma_wh, s->pal8)) + s->avctx->pix_fmt = AV_PIX_FMT_NONE; + if (s->avctx->pix_fmt == AV_PIX_FMT_NONE) + for (i = 0; i < possible_fmts_nb; ++i) { + if (pix_fmt_match(possible_fmts[i], ncomponents, s->precision, log2_chroma_wh, s->pal8)) { + s->avctx->pix_fmt = possible_fmts[i]; + break; + } } - } if (i == possible_fmts_nb) { if (ncomponents == 4 && @@ -562,6 +571,7 @@ static int get_coc(Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c, uint8_t *properties) { int compno, ret; + uint8_t has_eph; if (bytestream2_get_bytes_left(&s->g) < 2) { av_log(s->avctx, AV_LOG_ERROR, "Insufficient space for COC\n"); @@ -578,7 +588,9 @@ static int get_coc(Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c, } c += compno; + has_eph = c->csty & JPEG2000_CSTY_EPH; c->csty = bytestream2_get_byteu(&s->g); + c->csty |= has_eph; //do not override eph present bits from COD if ((ret = get_cox(s, c)) < 0) return ret; @@ -587,6 +599,38 @@ static int get_coc(Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c, return 0; } +static int get_rgn(Jpeg2000DecoderContext *s, int n) +{ + uint16_t compno; + compno = (s->ncomponents < 257)? bytestream2_get_byte(&s->g): + bytestream2_get_be16u(&s->g); + if (bytestream2_get_byte(&s->g)) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid RGN header.\n"); + return AVERROR_INVALIDDATA; // SRgn field value is 0 + } + // SPrgn field + // Currently compno cannot be greater than 4. + // However, future implementation should support compno up to 65536 + if (compno < s->ncomponents) { + int v; + if (s->curtileno == -1) { + v = bytestream2_get_byte(&s->g); + if (v > 30) + return AVERROR_PATCHWELCOME; + s->roi_shift[compno] = v; + } else { + if (s->tile[s->curtileno].tp_idx != 0) + return AVERROR_INVALIDDATA; // marker occurs only in first tile part of tile + v = bytestream2_get_byte(&s->g); + if (v > 30) + return AVERROR_PATCHWELCOME; + s->tile[s->curtileno].comp[compno].roi_shift = v; + } + return 0; + } + return AVERROR_INVALIDDATA; +} + /* Get common part for QCD and QCC segments. */ static int get_qcx(Jpeg2000DecoderContext *s, int n, Jpeg2000QuantStyle *q) { @@ -787,6 +831,15 @@ static int get_sot(Jpeg2000DecoderContext *s, int n) return 0; } +static int read_crg(Jpeg2000DecoderContext *s, int n) +{ + if (s->ncomponents*4 != n - 2) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid CRG marker.\n"); + return AVERROR_INVALIDDATA; + } + bytestream2_skip(&s->g, n - 2); + return 0; +} /* Tile-part lengths: see ISO 15444-1:2002, section A.7.1 * Used to know the number of tile parts and lengths. * There may be multiple TLMs in the header. @@ -795,7 +848,7 @@ static int get_sot(Jpeg2000DecoderContext *s, int n) * markers. Parsing the TLM header is needed to increment the input header * buffer. * This marker is mandatory for DCI. */ -static uint8_t get_tlm(Jpeg2000DecoderContext *s, int n) +static int get_tlm(Jpeg2000DecoderContext *s, int n) { uint8_t Stlm, ST, SP, tile_tlm, i; bytestream2_get_byte(&s->g); /* Ztlm: skipped */ @@ -803,7 +856,11 @@ static uint8_t get_tlm(Jpeg2000DecoderContext *s, int n) // too complex ? ST = ((Stlm >> 4) & 0x01) + ((Stlm >> 4) & 0x02); ST = (Stlm >> 4) & 0x03; - // TODO: Manage case of ST = 0b11 --> raise error + if (ST == 0x03) { + av_log(s->avctx, AV_LOG_ERROR, "TLM marker contains invalid ST value.\n"); + return AVERROR_INVALIDDATA; + } + SP = (Stlm >> 6) & 0x01; tile_tlm = (n - 4) / ((SP + 1) * 2 + ST); for (i = 0; i < tile_tlm; i++) { @@ -851,6 +908,42 @@ static int get_plt(Jpeg2000DecoderContext *s, int n) return 0; } +static int get_ppt(Jpeg2000DecoderContext *s, int n) +{ + Jpeg2000Tile *tile; + void *new; + + if (n < 3) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid length for PPT data.\n"); + return AVERROR_INVALIDDATA; + } + if (s->curtileno < 0) + return AVERROR_INVALIDDATA; + + tile = &s->tile[s->curtileno]; + if (tile->tp_idx != 0) { + av_log(s->avctx, AV_LOG_ERROR, + "PPT marker can occur only on first tile part of a tile.\n"); + return AVERROR_INVALIDDATA; + } + + tile->has_ppt = 1; // this tile has a ppt marker + bytestream2_get_byte(&s->g); // Zppt is skipped and not used + new = av_realloc(tile->packed_headers, + tile->packed_headers_size + n - 3); + if (new) { + tile->packed_headers = new; + } 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); + tile->packed_headers_size += n - 3; + bytestream2_skip(&s->g, n - 3); + + return 0; +} + static int init_tile(Jpeg2000DecoderContext *s, int tileno) { int compno; @@ -888,6 +981,9 @@ static int init_tile(Jpeg2000DecoderContext *s, int tileno) comp->coord[1][0] = ff_jpeg2000_ceildivpow2(comp->coord_o[1][0], s->reduction_factor); comp->coord[1][1] = ff_jpeg2000_ceildivpow2(comp->coord_o[1][1], s->reduction_factor); + if (!comp->roi_shift) + comp->roi_shift = s->roi_shift[compno]; + if (ret = ff_jpeg2000_init_component(comp, codsty, qntsty, s->cbps[compno], s->cdx[compno], s->cdy[compno], s->avctx)) @@ -923,6 +1019,19 @@ static int getlblockinc(Jpeg2000DecoderContext *s) return res; } +static inline void select_stream(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, + int *tp_index) +{ + s->g = tile->tile_part[*tp_index].tpg; + if (bytestream2_get_bytes_left(&s->g) == 0 && s->bit_index == 8) { + if (*tp_index < FF_ARRAY_ELEMS(tile->tile_part) - 1) { + s->g = tile->tile_part[++(*tp_index)].tpg; + } + } + if (bytestream2_peek_be32(&s->g) == JPEG2000_SOP_FIXED_BYTES) + bytestream2_skip(&s->g, JPEG2000_SOP_BYTE_LENGTH); +} + static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, int *tp_index, Jpeg2000CodingStyle *codsty, Jpeg2000ResLevel *rlevel, int precno, @@ -934,19 +1043,15 @@ static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, if (layno < rlevel->band[0].prec[precno].decoded_layers) return 0; rlevel->band[0].prec[precno].decoded_layers = layno + 1; - - if (bytestream2_get_bytes_left(&s->g) == 0 && s->bit_index == 8) { - if (*tp_index < FF_ARRAY_ELEMS(tile->tile_part) - 1) { - s->g = tile->tile_part[++(*tp_index)].tpg; - } - } - - if (bytestream2_peek_be32(&s->g) == JPEG2000_SOP_FIXED_BYTES) - bytestream2_skip(&s->g, JPEG2000_SOP_BYTE_LENGTH); + // Select stream to read from + if (tile->has_ppt) + s->g = tile->packed_headers_stream; + else + select_stream(s, tile, tp_index); if (!(ret = get_bits(s, 1))) { jpeg2000_flush(s); - return 0; + goto skip_data; } else if (ret < 0) return ret; @@ -1052,6 +1157,11 @@ static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, av_log(s->avctx, AV_LOG_ERROR, "EPH marker not found. instead %X\n", bytestream2_peek_be32(&s->g)); } + // Save state of stream + if (tile->has_ppt) { + tile->packed_headers_stream = s->g; + select_stream(s, tile, tp_index); + } for (bandno = 0; bandno < rlevel->nbands; bandno++) { Jpeg2000Band *band = rlevel->band + bandno; Jpeg2000Prec *prec = band->prec + precno; @@ -1093,6 +1203,15 @@ static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, av_freep(&cblk->lengthinc); } } + // Save state of stream + tile->tile_part[*tp_index].tpg = s->g; + return 0; + +skip_data: + if (tile->has_ppt) + tile->packed_headers_stream = s->g; + else + tile->tile_part[*tp_index].tpg = s->g; return 0; } @@ -1284,14 +1403,14 @@ static int jpeg2000_decode_packets_po_iteration(Jpeg2000DecoderContext *s, Jpeg2 continue; } - for (layno = 0; layno < LYEpoc; layno++) { - if ((ret = jpeg2000_decode_packet(s, tile, tp_index, - codsty, rlevel, - precno, layno, - qntsty->expn + (reslevelno ? 3 * (reslevelno - 1) + 1 : 0), - qntsty->nguardbits)) < 0) - return ret; - } + for (layno = 0; layno < LYEpoc; layno++) { + if ((ret = jpeg2000_decode_packet(s, tile, tp_index, + codsty, rlevel, + precno, layno, + qntsty->expn + (reslevelno ? 3 * (reslevelno - 1) + 1 : 0), + qntsty->nguardbits)) < 0) + return ret; + } } } } @@ -1533,9 +1652,9 @@ static void decode_clnpass(Jpeg2000DecoderContext *s, Jpeg2000T1Context *t1, static int decode_cblk(Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *codsty, Jpeg2000T1Context *t1, Jpeg2000Cblk *cblk, - int width, int height, int bandpos) + int width, int height, int bandpos, uint8_t roi_shift) { - int passno = cblk->npasses, pass_t = 2, bpno = cblk->nonzerobits - 1; + int passno = cblk->npasses, pass_t = 2, bpno = cblk->nonzerobits - 1 + roi_shift; int pass_cnt = 0; int vert_causal_ctx_csty_symbol = codsty->cblk_style & JPEG2000_CBLK_VSC; int term_cnt = 0; @@ -1557,8 +1676,8 @@ static int decode_cblk(Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *codsty, ff_mqc_initdec(&t1->mqc, cblk->data, 0, 1); while (passno--) { - if (bpno < 0) { - av_log(s->avctx, AV_LOG_ERROR, "bpno became negative\n"); + if (bpno < 0 || bpno > 29) { + av_log(s->avctx, AV_LOG_ERROR, "bpno became invalid\n"); return AVERROR_INVALIDDATA; } switch(pass_t) { @@ -1609,6 +1728,19 @@ static int decode_cblk(Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *codsty, return 1; } +static inline int roi_shift_param(Jpeg2000Component *comp, + int quan_parameter) +{ + uint8_t roi_shift; + int val; + roi_shift = comp->roi_shift; + val = (quan_parameter < 0)?-quan_parameter:quan_parameter; + + if (val > (1 << roi_shift)) + return (quan_parameter < 0)?-(val >> roi_shift):(val >> roi_shift); + return quan_parameter; +} + /* TODO: Verify dequantization for lossless case * comp->data can be float or int * band->stepsize can be float or int @@ -1693,6 +1825,19 @@ static inline void mct_decode(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile) s->dsp.mct_decode[tile->codsty[0].transform](src[0], src[1], src[2], csize); } +static inline void roi_scale_cblk(Jpeg2000Cblk *cblk, + Jpeg2000Component *comp, + Jpeg2000T1Context *t1) +{ + int i, j; + int w = cblk->coord[0][1] - cblk->coord[0][0]; + for (j = 0; j < (cblk->coord[1][1] - cblk->coord[1][0]); ++j) { + int *src = t1->data + j*t1->stride; + for (i = 0; i < w; ++i) + src[i] = roi_shift_param(comp, src[i]); + } +} + static inline void tile_codeblocks(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile) { Jpeg2000T1Context t1; @@ -1736,7 +1881,7 @@ static inline void tile_codeblocks(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile 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); + bandpos, comp->roi_shift); if (ret) coded = 1; else @@ -1744,6 +1889,8 @@ static inline void tile_codeblocks(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile x = cblk->coord[0][0] - band->coord[0][0]; y = cblk->coord[1][0] - band->coord[1][0]; + if (comp->roi_shift) + roi_scale_cblk(cblk, comp, &t1); if (codsty->transform == FF_DWT97) dequantization_float(x, y, cblk, comp, &t1, band); else if (codsty->transform == FF_DWT97_INT) @@ -1875,6 +2022,8 @@ static void jpeg2000_dec_cleanup(Jpeg2000DecoderContext *s) ff_jpeg2000_cleanup(comp, codsty); } av_freep(&s->tile[tileno].comp); + av_freep(&s->tile[tileno].packed_headers); + s->tile[tileno].packed_headers_size = 0; } } av_freep(&s->tile); @@ -1925,6 +2074,11 @@ static int jpeg2000_read_main_headers(Jpeg2000DecoderContext *s) av_log(s->avctx, AV_LOG_ERROR, "Invalid tpend\n"); return AVERROR_INVALIDDATA; } + + if (tile->has_ppt && tile->tp_idx == 0) { + bytestream2_init(&tile->packed_headers_stream, tile->packed_headers, tile->packed_headers_size); + } + bytestream2_init(&tp->tpg, s->g.buffer, tp->tp_end - s->g.buffer); bytestream2_skip(&s->g, tp->tp_end - s->g.buffer); @@ -1935,8 +2089,12 @@ static int jpeg2000_read_main_headers(Jpeg2000DecoderContext *s) len = bytestream2_get_be16(&s->g); if (len < 2 || bytestream2_get_bytes_left(&s->g) < len - 2) { - av_log(s->avctx, AV_LOG_ERROR, "Invalid len %d left=%d\n", len, bytestream2_get_bytes_left(&s->g)); - return AVERROR_INVALIDDATA; + if (s->avctx->strict_std_compliance >= FF_COMPLIANCE_STRICT) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid len %d left=%d\n", len, bytestream2_get_bytes_left(&s->g)); + return AVERROR_INVALIDDATA; + } + av_log(s->avctx, AV_LOG_WARNING, "Missing EOC Marker.\n"); + break; } switch (marker) { @@ -1955,6 +2113,9 @@ static int jpeg2000_read_main_headers(Jpeg2000DecoderContext *s) case JPEG2000_COD: ret = get_cod(s, codsty, properties); break; + case JPEG2000_RGN: + ret = get_rgn(s, len); + break; case JPEG2000_QCC: ret = get_qcc(s, len, qntsty, properties); break; @@ -1979,6 +2140,9 @@ static int jpeg2000_read_main_headers(Jpeg2000DecoderContext *s) // the comment is ignored bytestream2_skip(&s->g, len - 2); break; + case JPEG2000_CRG: + ret = read_crg(s, len); + break; case JPEG2000_TLM: // Tile-part lengths ret = get_tlm(s, len); @@ -1987,6 +2151,10 @@ static int jpeg2000_read_main_headers(Jpeg2000DecoderContext *s) // Packet length, tile-part header ret = get_plt(s, len); break; + case JPEG2000_PPT: + // Packed headers, tile-part header + ret = get_ppt(s, len); + break; default: av_log(s->avctx, AV_LOG_ERROR, "unsupported marker 0x%.4"PRIX16" at pos 0x%X\n", @@ -2016,7 +2184,6 @@ static int jpeg2000_read_bitstream_packets(Jpeg2000DecoderContext *s) if ((ret = init_tile(s, tileno)) < 0) return ret; - s->g = tile->tile_part[0].tpg; if ((ret = jpeg2000_decode_packets(s, tile)) < 0) return ret; } diff --git a/libavcodec/jpeg2000dwt.c b/libavcodec/jpeg2000dwt.c index badf0f8cd05..f418454ee90 100644 --- a/libavcodec/jpeg2000dwt.c +++ b/libavcodec/jpeg2000dwt.c @@ -255,7 +255,7 @@ static void dwt_encode97_int(DWTContext *s, int *t) line += 5; for (i = 0; i < w * h; i++) - t[i] <<= I_PRESHIFT; + t[i] *= 1 << I_PRESHIFT; for (lev = s->ndeclevels-1; lev >= 0; lev--){ int lh = s->linelen[lev][0], diff --git a/libavcodec/jpeglsdec.c b/libavcodec/jpeglsdec.c index 5308b744df1..0b1e139048b 100644 --- a/libavcodec/jpeglsdec.c +++ b/libavcodec/jpeglsdec.c @@ -222,7 +222,7 @@ static inline int ls_get_code_runterm(GetBitContext *gb, JLSState *state, /** * Decode one line of image */ -static inline void ls_decode_line(JLSState *state, MJpegDecodeContext *s, +static inline int ls_decode_line(JLSState *state, MJpegDecodeContext *s, void *last, void *dst, int last2, int w, int stride, int comp, int bits) { @@ -234,7 +234,7 @@ static inline void ls_decode_line(JLSState *state, MJpegDecodeContext *s, int err, pred; if (get_bits_left(&s->gb) <= 0) - return; + return AVERROR_INVALIDDATA; /* compute gradients */ Ra = x ? R(dst, x - stride) : R(last, x); @@ -263,11 +263,11 @@ static inline void ls_decode_line(JLSState *state, MJpegDecodeContext *s, } /* if EOL reached, we stop decoding */ if (r != 1 << ff_log2_run[state->run_index[comp]]) - return; + return 0; if (state->run_index[comp] < 31) state->run_index[comp]++; if (x + stride > w) - return; + return 0; } /* decode aborted run */ r = ff_log2_run[state->run_index[comp]]; @@ -284,7 +284,7 @@ static inline void ls_decode_line(JLSState *state, MJpegDecodeContext *s, if (x >= w) { av_log(NULL, AV_LOG_ERROR, "run overflow\n"); av_assert0(x <= w); - return; + return AVERROR_INVALIDDATA; } /* decode run termination value */ @@ -341,6 +341,8 @@ static inline void ls_decode_line(JLSState *state, MJpegDecodeContext *s, W(dst, x, pred); x += stride; } + + return 0; } int ff_jpegls_decode_picture(MJpegDecodeContext *s, int near, @@ -350,6 +352,7 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s, int near, uint8_t *zero, *last, *cur; JLSState *state; int off = 0, stride = 1, width, shift, ret = 0; + int decoded_height = 0; zero = av_mallocz(s->picture_ptr->linesize[0]); if (!zero) @@ -407,13 +410,16 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s, int near, width = s->width * stride; cur += off; for (i = 0; i < s->height; i++) { + int ret; if (s->bits <= 8) { - ls_decode_line(state, s, last, cur, t, width, stride, off, 8); + ret = ls_decode_line(state, s, last, cur, t, width, stride, off, 8); t = last[0]; } else { - ls_decode_line(state, s, last, cur, t, width, stride, off, 16); + ret = ls_decode_line(state, s, last, cur, t, width, stride, off, 16); t = *((uint16_t *)last); } + if (ret < 0) + break; last = cur; cur += s->picture_ptr->linesize[0]; @@ -422,6 +428,7 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s, int near, skip_bits(&s->gb, 16); /* skip RSTn */ } } + decoded_height = i; } else if (ilv == 1) { /* line interleaving */ int j; int Rc[3] = { 0, 0, 0 }; @@ -429,9 +436,12 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s, int near, memset(cur, 0, s->picture_ptr->linesize[0]); width = s->width * stride; for (i = 0; i < s->height; i++) { + int ret; for (j = 0; j < stride; j++) { - ls_decode_line(state, s, last + j, cur + j, + ret = ls_decode_line(state, s, last + j, cur + j, Rc[j], width, stride, j, 8); + if (ret < 0) + break; Rc[j] = last[j]; if (s->restart_interval && !--s->restart_count) { @@ -439,9 +449,12 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s, int near, skip_bits(&s->gb, 16); /* skip RSTn */ } } + if (ret < 0) + break; last = cur; cur += s->picture_ptr->linesize[0]; } + decoded_height = i; } else if (ilv == 2) { /* sample interleaving */ avpriv_report_missing_feature(s->avctx, "Sample interleaved images"); ret = AVERROR_PATCHWELCOME; @@ -507,7 +520,7 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s, int near, if (s->bits <= 8) { uint8_t *src = s->picture_ptr->data[0]; - for (i = 0; i < s->height; i++) { + for (i = 0; i < decoded_height; i++) { for (x = off; x < w; x += stride) src[x] <<= shift; src += s->picture_ptr->linesize[0]; @@ -515,7 +528,7 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s, int near, } else { uint16_t *src = (uint16_t *)s->picture_ptr->data[0]; - for (i = 0; i < s->height; i++) { + for (i = 0; i < decoded_height; i++) { for (x = 0; x < w; x++) src[x] <<= shift; src += s->picture_ptr->linesize[0] / 2; diff --git a/libavcodec/jpeglsenc.c b/libavcodec/jpeglsenc.c index 1208cda396f..5ecd430db75 100644 --- a/libavcodec/jpeglsenc.c +++ b/libavcodec/jpeglsenc.c @@ -471,7 +471,7 @@ AVCodec ff_jpegls_encoder = { .priv_data_size = sizeof(JPEGLSContext), .priv_class = &jpegls_class, .init = encode_init_ls, - .capabilities = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_FRAME_THREADS, .encode2 = encode_picture_ls, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_BGR24, AV_PIX_FMT_RGB24, diff --git a/libavcodec/jpegtables.c b/libavcodec/jpegtables.c index cbe5523cb43..fa5c6f9fc58 100644 --- a/libavcodec/jpegtables.c +++ b/libavcodec/jpegtables.c @@ -130,14 +130,25 @@ void ff_mjpeg_build_huffman_codes(uint8_t *huff_size, uint16_t *huff_code, { int i, j, k,nb, code, sym; - code = 0; + /* Some badly encoded files [1] map 2 different codes to symbol 0. + Only the first one is valid, so we zero-initialize this here and + make sure we only set it once (the first time) in the loop below. + + [1]: Embedded JPEGs in "X7 RAW" and "X7 CinemaDNG" samples here: + https://www.dji.com/gr/zenmuse-x7/info#downloads + */ + huff_size[0] = 0; + k = 0; + code = 0; for(i=1;i<=16;i++) { nb = bits_table[i]; for(j=0;jframe)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; if (avctx->height/8 * (avctx->width/8) > 4 * video_size) { diff --git a/libavcodec/lagarith.c b/libavcodec/lagarith.c index 59169be5dea..d81e55cf4ca 100644 --- a/libavcodec/lagarith.c +++ b/libavcodec/lagarith.c @@ -226,6 +226,9 @@ static int lag_read_prob_header(lag_rac *rac, GetBitContext *gb) } } + if (scale_factor > 23) + return AVERROR_INVALIDDATA; + rac->scale = scale_factor; /* Fill probability array with cumulative probability for each symbol. */ @@ -709,16 +712,6 @@ static av_cold int lag_decode_init(AVCodecContext *avctx) return 0; } -#if HAVE_THREADS -static av_cold int lag_decode_init_thread_copy(AVCodecContext *avctx) -{ - LagarithContext *l = avctx->priv_data; - l->avctx = avctx; - - return 0; -} -#endif - AVCodec ff_lagarith_decoder = { .name = "lagarith", .long_name = NULL_IF_CONFIG_SMALL("Lagarith lossless"), @@ -726,7 +719,6 @@ AVCodec ff_lagarith_decoder = { .id = AV_CODEC_ID_LAGARITH, .priv_data_size = sizeof(LagarithContext), .init = lag_decode_init, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(lag_decode_init_thread_copy), .decode = lag_decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, }; diff --git a/libavcodec/lcldec.c b/libavcodec/lcldec.c index 104defa5f57..2dcd249b65f 100644 --- a/libavcodec/lcldec.c +++ b/libavcodec/lcldec.c @@ -136,7 +136,7 @@ static int zlib_decomp(AVCodecContext *avctx, const uint8_t *src, int src_len, i av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", zret); return AVERROR_UNKNOWN; } - c->zstream.next_in = (uint8_t *)src; + c->zstream.next_in = src; c->zstream.avail_in = src_len; c->zstream.next_out = c->decomp_buf + offset; c->zstream.avail_out = c->decomp_size - offset; @@ -190,11 +190,10 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac ; } else if (c->flags & FLAG_MULTITHREAD) { mthread_inlen = AV_RL32(buf); - if (len < 8) { + if (len < 8 || len - 8 < mthread_inlen) { av_log(avctx, AV_LOG_ERROR, "len %d is too small\n", len); return AVERROR_INVALIDDATA; } - mthread_inlen = FFMIN(mthread_inlen, len - 8); mthread_outlen = AV_RL32(buf + 4); mthread_outlen = FFMIN(mthread_outlen, c->decomp_size); mszh_dlen = mszh_decomp(buf + 8, mthread_inlen, c->decomp_buf, c->decomp_size); @@ -623,13 +622,6 @@ static av_cold int decode_init(AVCodecContext *avctx) return 0; } -#if HAVE_THREADS -static int init_thread_copy(AVCodecContext *avctx) -{ - return decode_init(avctx); -} -#endif - static av_cold int decode_end(AVCodecContext *avctx) { LclDecContext * const c = avctx->priv_data; @@ -651,7 +643,6 @@ AVCodec ff_mszh_decoder = { .id = AV_CODEC_ID_MSZH, .priv_data_size = sizeof(LclDecContext), .init = decode_init, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(init_thread_copy), .close = decode_end, .decode = decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, @@ -667,7 +658,6 @@ AVCodec ff_zlib_decoder = { .id = AV_CODEC_ID_ZLIB, .priv_data_size = sizeof(LclDecContext), .init = decode_init, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(init_thread_copy), .close = decode_end, .decode = decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, diff --git a/libavcodec/lclenc.c b/libavcodec/lclenc.c index 357313df494..4fe52b40da6 100644 --- a/libavcodec/lclenc.c +++ b/libavcodec/lclenc.c @@ -176,7 +176,7 @@ AVCodec ff_zlib_encoder = { .init = encode_init, .encode2 = encode_frame, .close = encode_end, - .capabilities = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_FRAME_THREADS, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_BGR24, AV_PIX_FMT_NONE }, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, diff --git a/libavcodec/libaomenc.c b/libavcodec/libaomenc.c index 9b4fb3b4eba..1c78da719a8 100644 --- a/libavcodec/libaomenc.c +++ b/libavcodec/libaomenc.c @@ -37,6 +37,7 @@ #include "av1.h" #include "avcodec.h" #include "internal.h" +#include "packet_internal.h" #include "profiles.h" /* @@ -92,6 +93,9 @@ typedef struct AOMEncoderContext { int enable_cdef; int enable_global_motion; int enable_intrabc; + int enable_restoration; + int usage; + int tune; } AOMContext; static const char *const ctlidstr[] = { @@ -110,6 +114,7 @@ static const char *const ctlidstr[] = { [AV1E_SET_SUPERBLOCK_SIZE] = "AV1E_SET_SUPERBLOCK_SIZE", [AV1E_SET_TILE_COLUMNS] = "AV1E_SET_TILE_COLUMNS", [AV1E_SET_TILE_ROWS] = "AV1E_SET_TILE_ROWS", + [AV1E_SET_ENABLE_RESTORATION] = "AV1E_SET_ENABLE_RESTORATION", #ifdef AOM_CTRL_AV1E_SET_ROW_MT [AV1E_SET_ROW_MT] = "AV1E_SET_ROW_MT", #endif @@ -129,6 +134,7 @@ static const char *const ctlidstr[] = { [AV1E_SET_ENABLE_INTRABC] = "AV1E_SET_ENABLE_INTRABC", #endif [AV1E_SET_ENABLE_CDEF] = "AV1E_SET_ENABLE_CDEF", + [AOME_SET_TUNING] = "AOME_SET_TUNING", }; static av_cold void log_encoder_error(AVCodecContext *avctx, const char *desc) @@ -549,6 +555,8 @@ static av_cold int aom_init(AVCodecContext *avctx, enccfg.g_threads = FFMIN(avctx->thread_count ? avctx->thread_count : av_cpu_count(), 64); + enccfg.g_usage = ctx->usage; + if (ctx->lag_in_frames >= 0) enccfg.g_lag_in_frames = ctx->lag_in_frames; @@ -572,14 +580,11 @@ static av_cold int aom_init(AVCodecContext *avctx, enccfg.rc_target_bitrate = av_rescale_rnd(avctx->bit_rate, 1, 1000, AV_ROUND_NEAR_INF); } else if (enccfg.rc_end_usage != AOM_Q) { - if (enccfg.rc_end_usage == AOM_CQ) { - enccfg.rc_target_bitrate = 1000000; - } else { - avctx->bit_rate = enccfg.rc_target_bitrate * 1000; - av_log(avctx, AV_LOG_WARNING, - "Neither bitrate nor constrained quality specified, using default bitrate of %dkbit/sec\n", - enccfg.rc_target_bitrate); - } + enccfg.rc_end_usage = AOM_Q; + ctx->crf = 32; + av_log(avctx, AV_LOG_WARNING, + "Neither bitrate nor constrained quality specified, using default CRF of %d\n", + ctx->crf); } if (avctx->qmin >= 0) @@ -691,9 +696,14 @@ static av_cold int aom_init(AVCodecContext *avctx, codecctl_int(avctx, AOME_SET_ARNR_STRENGTH, ctx->arnr_strength); if (ctx->enable_cdef >= 0) codecctl_int(avctx, AV1E_SET_ENABLE_CDEF, ctx->enable_cdef); + if (ctx->enable_restoration >= 0) + codecctl_int(avctx, AV1E_SET_ENABLE_RESTORATION, ctx->enable_restoration); + codecctl_int(avctx, AOME_SET_STATIC_THRESHOLD, ctx->static_thresh); if (ctx->crf >= 0) codecctl_int(avctx, AOME_SET_CQ_LEVEL, ctx->crf); + if (ctx->tune >= 0) + codecctl_int(avctx, AOME_SET_TUNING, ctx->tune); codecctl_int(avctx, AV1E_SET_COLOR_PRIMARIES, avctx->color_primaries); codecctl_int(avctx, AV1E_SET_MATRIX_COEFFICIENTS, avctx->colorspace); @@ -1046,6 +1056,9 @@ static av_cold void av1_init_static(AVCodec *codec) codec->pix_fmts = av1_pix_fmts_highbd; else codec->pix_fmts = av1_pix_fmts; + + if (aom_codec_version_major() < 2) + codec->capabilities |= AV_CODEC_CAP_EXPERIMENTAL; } static av_cold int av1_init(AVCodecContext *avctx) @@ -1087,11 +1100,18 @@ static const AVOption options[] = { { "enable-cdef", "Enable CDEF filtering", OFFSET(enable_cdef), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE}, { "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"}, + { "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"}, { NULL }, }; static const AVCodecDefault defaults[] = { - { "b", "256*1000" }, + { "b", "0" }, { "qmin", "-1" }, { "qmax", "-1" }, { "g", "-1" }, @@ -1115,7 +1135,7 @@ AVCodec ff_libaom_av1_encoder = { .init = av1_init, .encode2 = aom_encode, .close = aom_free, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS | AV_CODEC_CAP_EXPERIMENTAL, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS, .profiles = NULL_IF_CONFIG_SMALL(ff_av1_profiles), .priv_class = &class_aom, .defaults = defaults, diff --git a/libavcodec/libdav1d.c b/libavcodec/libdav1d.c index 12c63245f83..bbb3ec1e6c0 100644 --- a/libavcodec/libdav1d.c +++ b/libavcodec/libdav1d.c @@ -40,6 +40,8 @@ typedef struct Libdav1dContext { int tile_threads; int frame_threads; int apply_grain; + int operating_point; + int all_layers; } Libdav1dContext; static const enum AVPixelFormat pix_fmt[][3] = { @@ -130,7 +132,13 @@ static av_cold int libdav1d_init(AVCodecContext *c) s.allocator.cookie = dav1d; s.allocator.alloc_picture_callback = libdav1d_picture_allocator; s.allocator.release_picture_callback = libdav1d_picture_release; - s.apply_grain = dav1d->apply_grain; + s.frame_size_limit = c->max_pixels; + if (dav1d->apply_grain >= 0) + s.apply_grain = dav1d->apply_grain; + + s.all_layers = dav1d->all_layers; + if (dav1d->operating_point >= 0) + s.operating_point = dav1d->operating_point; s.n_tile_threads = dav1d->tile_threads ? dav1d->tile_threads @@ -162,6 +170,11 @@ static void libdav1d_data_free(const uint8_t *data, void *opaque) { av_buffer_unref(&buf); } +static void libdav1d_user_data_free(const uint8_t *data, void *opaque) { + av_assert0(data == opaque); + av_free(opaque); +} + static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) { Libdav1dContext *dav1d = c->priv_data; @@ -189,6 +202,23 @@ static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) pkt.buf = NULL; av_packet_unref(&pkt); + + if (c->reordered_opaque != AV_NOPTS_VALUE) { + uint8_t *reordered_opaque = av_malloc(sizeof(c->reordered_opaque)); + if (!reordered_opaque) { + dav1d_data_unref(data); + return AVERROR(ENOMEM); + } + + memcpy(reordered_opaque, &c->reordered_opaque, sizeof(c->reordered_opaque)); + res = dav1d_data_wrap_user_data(data, reordered_opaque, + libdav1d_user_data_free, reordered_opaque); + if (res < 0) { + av_free(reordered_opaque); + dav1d_data_unref(data); + return res; + } + } } } @@ -237,6 +267,12 @@ static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) goto fail; } + av_reduce(&frame->sample_aspect_ratio.num, + &frame->sample_aspect_ratio.den, + frame->height * (int64_t)p->frame_hdr->render_width, + frame->width * (int64_t)p->frame_hdr->render_height, + INT_MAX); + switch (p->seq_hdr->chr) { case DAV1D_CHR_VERTICAL: frame->chroma_location = c->chroma_sample_location = AVCHROMA_LOC_LEFT; @@ -258,6 +294,18 @@ static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) else frame->format = c->pix_fmt = pix_fmt[p->p.layout][p->seq_hdr->hbd]; + if (p->m.user_data.data) + memcpy(&frame->reordered_opaque, p->m.user_data.data, sizeof(frame->reordered_opaque)); + else + frame->reordered_opaque = AV_NOPTS_VALUE; + + if (p->seq_hdr->num_units_in_tick && p->seq_hdr->time_scale) { + av_reduce(&c->framerate.den, &c->framerate.num, + p->seq_hdr->num_units_in_tick, p->seq_hdr->time_scale, INT_MAX); + if (p->seq_hdr->equal_picture_interval) + c->ticks_per_frame = p->seq_hdr->num_ticks_per_picture; + } + // match timestamps and packet size frame->pts = frame->best_effort_timestamp = p->m.timestamp; #if FF_API_PKT_PTS @@ -341,7 +389,9 @@ static av_cold int libdav1d_close(AVCodecContext *c) static const AVOption libdav1d_options[] = { { "tilethreads", "Tile threads", OFFSET(tile_threads), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, DAV1D_MAX_TILE_THREADS, VD }, { "framethreads", "Frame threads", OFFSET(frame_threads), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, DAV1D_MAX_FRAME_THREADS, VD }, - { "filmgrain", "Apply Film Grain", OFFSET(apply_grain), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VD }, + { "filmgrain", "Apply Film Grain", OFFSET(apply_grain), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VD }, + { "oppoint", "Select an operating point of the scalable bitstream", OFFSET(operating_point), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 31, VD }, + { "alllayers", "Output all spatial layers", OFFSET(all_layers), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD }, { NULL } }; diff --git a/libavcodec/libfdk-aacdec.c b/libavcodec/libfdk-aacdec.c index 1abe1d8438d..1a86dffe4bd 100644 --- a/libavcodec/libfdk-aacdec.c +++ b/libavcodec/libfdk-aacdec.c @@ -57,6 +57,7 @@ typedef struct FDKAACDecContext { int drc_effect; int drc_cut; int level_limit; + int output_delay; } FDKAACDecContext; @@ -75,12 +76,13 @@ static const AVOption fdk_aac_dec_options[] = { OFFSET(drc_boost), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 127, AD, 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 }, - { "drc_level", "Dynamic Range Control: reference level, quantized to 0.25dB steps where [0] is 0dB and [127] is -31.75dB", - OFFSET(drc_level), AV_OPT_TYPE_INT, { .i64 = -1}, -1, 127, AD, 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 }, { "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 }, #if FDKDEC_VER_AT_LEAST(2, 5) // 2.5.10 - { "level_limit", "Signal level limiting", OFFSET(level_limit), AV_OPT_TYPE_INT, { .i64 = 0 }, -1, 1, AD }, + { "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", @@ -115,6 +117,9 @@ static int get_stream_info(AVCodecContext *avctx) } avctx->sample_rate = info->sampleRate; avctx->frame_size = info->frameSize; +#if FDKDEC_VER_AT_LEAST(2, 5) // 2.5.10 + s->output_delay = info->outputDelay; +#endif for (i = 0; i < info->numChannels; i++) { AUDIO_CHANNEL_TYPE ctype = info->pChannelType[i]; @@ -294,6 +299,12 @@ static av_cold int fdk_aac_decode_init(AVCodecContext *avctx) } if (s->drc_level != -1) { + // This option defaults to -1, i.e. not calling + // aacDecoder_SetParam(AAC_DRC_REFERENCE_LEVEL) at all, which defaults + // to the level from DRC metadata, if available. The user can set + // -drc_level -2, which calls aacDecoder_SetParam( + // AAC_DRC_REFERENCE_LEVEL) with a negative value, which then + // explicitly disables the feature. if (aacDecoder_SetParam(s->handle, AAC_DRC_REFERENCE_LEVEL, s->drc_level) != AAC_DEC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set DRC reference level in the decoder\n"); return AVERROR_UNKNOWN; @@ -308,6 +319,7 @@ static av_cold int fdk_aac_decode_init(AVCodecContext *avctx) } #if FDKDEC_VER_AT_LEAST(2, 5) // 2.5.10 + // Setting this parameter to -1 enables the auto behaviour in the library. if (aacDecoder_SetParam(s->handle, AAC_PCM_LIMITER_ENABLE, s->level_limit) != AAC_DEC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set in signal level limiting in the decoder\n"); return AVERROR_UNKNOWN; @@ -367,6 +379,11 @@ static int fdk_aac_decode_frame(AVCodecContext *avctx, void *data, if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) goto end; + if (frame->pts != AV_NOPTS_VALUE) + frame->pts -= av_rescale_q(s->output_delay, + (AVRational){1, avctx->sample_rate}, + avctx->time_base); + memcpy(frame->extended_data[0], s->decoder_buffer, avctx->channels * avctx->frame_size * av_get_bytes_per_sample(avctx->sample_fmt)); diff --git a/libavcodec/libfdk-aacenc.c b/libavcodec/libfdk-aacenc.c index 5620bb59511..6494c11ddc1 100644 --- a/libavcodec/libfdk-aacenc.c +++ b/libavcodec/libfdk-aacenc.c @@ -25,6 +25,7 @@ #include "avcodec.h" #include "audio_frame_queue.h" #include "internal.h" +#include "profiles.h" #ifdef AACENCODER_LIB_VL0 #define FDKENC_VER_AT_LEAST(vl0, vl1) \ @@ -62,6 +63,7 @@ static const AVOption aac_enc_options[] = { { "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 }, + FF_AAC_PROFILE_OPTS { NULL } }; diff --git a/libavcodec/libkvazaar.c b/libavcodec/libkvazaar.c index a89ca7f7495..02bcae3d5c0 100644 --- a/libavcodec/libkvazaar.c +++ b/libavcodec/libkvazaar.c @@ -110,8 +110,8 @@ static av_cold int libkvazaar_init(AVCodecContext *avctx) entry->key, entry->value); } } - av_dict_free(&dict); } + av_dict_free(&dict); } ctx->encoder = enc = api->encoder_open(cfg); diff --git a/libavcodec/libmp3lame.c b/libavcodec/libmp3lame.c index ecdd2e334c6..2beb28e569e 100644 --- a/libavcodec/libmp3lame.c +++ b/libavcodec/libmp3lame.c @@ -279,7 +279,6 @@ static int mp3lame_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, if ((discard_padding < avctx->frame_size) != (avpkt->duration > 0)) { av_log(avctx, AV_LOG_ERROR, "discard padding overflow\n"); av_packet_unref(avpkt); - av_free(avpkt); return AVERROR(EINVAL); } if ((!s->delay_sent && avctx->initial_padding > 0) || discard_padding > 0) { @@ -288,7 +287,6 @@ static int mp3lame_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, 10); if(!side_data) { av_packet_unref(avpkt); - av_free(avpkt); return AVERROR(ENOMEM); } if (!s->delay_sent) { diff --git a/libavcodec/libopencore-amr.c b/libavcodec/libopencore-amr.c index 516f625720f..614b3a2218b 100644 --- a/libavcodec/libopencore-amr.c +++ b/libavcodec/libopencore-amr.c @@ -29,6 +29,7 @@ #include "audio_frame_queue.h" #include "internal.h" +#if CONFIG_LIBOPENCORE_AMRNB_DECODER || CONFIG_LIBOPENCORE_AMRWB_DECODER static int amr_decode_fix_avctx(AVCodecContext *avctx) { const int is_amr_wb = 1 + (avctx->codec_id == AV_CODEC_ID_AMR_WB); @@ -46,6 +47,7 @@ static int amr_decode_fix_avctx(AVCodecContext *avctx) avctx->sample_fmt = AV_SAMPLE_FMT_S16; return 0; } +#endif #if CONFIG_LIBOPENCORE_AMRNB diff --git a/libavcodec/libopenh264enc.c b/libavcodec/libopenh264enc.c index ae6d17c6d2c..f63aa52348c 100644 --- a/libavcodec/libopenh264enc.c +++ b/libavcodec/libopenh264enc.c @@ -37,25 +37,35 @@ #define SM_SIZELIMITED_SLICE SM_DYN_SLICE #endif +#define TARGET_BITRATE_DEFAULT 2*1000*1000 + typedef struct SVCContext { const AVClass *av_class; ISVCEncoder *encoder; int slice_mode; int loopfilter; - char *profile; + int profile; int max_nal_size; int skip_frames; int skipped; - int cabac; +#if FF_API_OPENH264_CABAC + int cabac; // deprecated +#endif + int coder; + + // rate control mode + int rc_mode; } SVCContext; #define OFFSET(x) offsetof(SVCContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM +#define DEPRECATED AV_OPT_FLAG_DEPRECATED static const AVOption options[] = { +#if FF_API_OPENH264_SLICE_MODE #if OPENH264_VER_AT_LEAST(1, 6) - { "slice_mode", "set slice mode", OFFSET(slice_mode), AV_OPT_TYPE_INT, { .i64 = SM_FIXEDSLCNUM_SLICE }, SM_SINGLE_SLICE, SM_RESERVED, VE, "slice_mode" }, + { "slice_mode", "set slice mode, use slices/max_nal_size", OFFSET(slice_mode), AV_OPT_TYPE_INT, { .i64 = SM_FIXEDSLCNUM_SLICE }, SM_SINGLE_SLICE, SM_RESERVED, VE|DEPRECATED, "slice_mode" }, #else - { "slice_mode", "set slice mode", OFFSET(slice_mode), AV_OPT_TYPE_INT, { .i64 = SM_AUTO_SLICE }, SM_SINGLE_SLICE, SM_RESERVED, VE, "slice_mode" }, + { "slice_mode", "set slice mode, use slices/max_nal_size", OFFSET(slice_mode), AV_OPT_TYPE_INT, { .i64 = SM_AUTO_SLICE }, SM_SINGLE_SLICE, SM_RESERVED, VE|DEPRECATED, "slice_mode" }, #endif { "fixed", "a fixed number of slices", 0, AV_OPT_TYPE_CONST, { .i64 = SM_FIXEDSLCNUM_SLICE }, 0, 0, VE, "slice_mode" }, #if OPENH264_VER_AT_LEAST(1, 6) @@ -65,12 +75,36 @@ static const AVOption options[] = { { "rowmb", "one slice per row of macroblocks", 0, AV_OPT_TYPE_CONST, { .i64 = SM_ROWMB_SLICE }, 0, 0, VE, "slice_mode" }, { "auto", "automatic number of slices according to number of threads", 0, AV_OPT_TYPE_CONST, { .i64 = SM_AUTO_SLICE }, 0, 0, VE, "slice_mode" }, { "dyn", "Dynamic slicing", 0, AV_OPT_TYPE_CONST, { .i64 = SM_DYN_SLICE }, 0, 0, VE, "slice_mode" }, +#endif #endif { "loopfilter", "enable loop filter", OFFSET(loopfilter), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE }, - { "profile", "set profile restrictions", OFFSET(profile), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, 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) }, +#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 }, - { "cabac", "Enable cabac", OFFSET(cabac), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, +#if FF_API_OPENH264_CABAC + { "cabac", "Enable cabac(deprecated, use coder)", OFFSET(cabac), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE|DEPRECATED }, +#endif + { "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" }, +#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" }, +#endif + { NULL } }; @@ -122,19 +156,16 @@ static av_cold int svc_encode_init(AVCodecContext *avctx) (*s->encoder)->GetDefaultParams(s->encoder, ¶m); -#if FF_API_CODER_TYPE -FF_DISABLE_DEPRECATION_WARNINGS - if (!s->cabac) - s->cabac = avctx->coder_type == FF_CODER_TYPE_AC; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - param.fMaxFrameRate = 1/av_q2d(avctx->time_base); param.iPicWidth = avctx->width; param.iPicHeight = avctx->height; - param.iTargetBitrate = avctx->bit_rate; + param.iTargetBitrate = avctx->bit_rate > 0 ? avctx->bit_rate : TARGET_BITRATE_DEFAULT; param.iMaxBitrate = FFMAX(avctx->rc_max_rate, avctx->bit_rate); - param.iRCMode = RC_QUALITY_MODE; + param.iRCMode = s->rc_mode; + if (avctx->qmax >= 0) + param.iMaxQp = av_clip(avctx->qmax, 1, 51); + if (avctx->qmin >= 0) + param.iMinQp = av_clip(avctx->qmin, 1, param.iMaxQp); param.iTemporalLayerNum = 1; param.iSpatialLayerNum = 1; param.bEnableDenoise = 0; @@ -143,7 +174,8 @@ FF_ENABLE_DEPRECATION_WARNINGS param.bEnableFrameSkip = s->skip_frames; param.bEnableLongTermReference = 0; param.iLtrMarkPeriod = 30; - param.uiIntraPeriod = avctx->gop_size; + if (avctx->gop_size >= 0) + param.uiIntraPeriod = avctx->gop_size; #if OPENH264_VER_AT_LEAST(1, 4) param.eSpsPpsIdStrategy = CONSTANT_ID; #else @@ -153,10 +185,66 @@ FF_ENABLE_DEPRECATION_WARNINGS param.iLoopFilterDisableIdc = !s->loopfilter; param.iEntropyCodingModeFlag = 0; param.iMultipleThreadIdc = avctx->thread_count; - if (s->profile && !strcmp(s->profile, "main")) + + /* Allow specifying the libopenh264 profile through AVCodecContext. */ + if (FF_PROFILE_UNKNOWN == s->profile && + FF_PROFILE_UNKNOWN != avctx->profile) + switch (avctx->profile) { + case FF_PROFILE_H264_HIGH: + case FF_PROFILE_H264_MAIN: + case FF_PROFILE_H264_CONSTRAINED_BASELINE: + s->profile = avctx->profile; + break; + default: + av_log(avctx, AV_LOG_WARNING, + "Unsupported avctx->profile: %d.\n", avctx->profile); + break; + } + +#if FF_API_CODER_TYPE && FF_API_OPENH264_CABAC +FF_DISABLE_DEPRECATION_WARNINGS + if (s->coder < 0 && avctx->coder_type == FF_CODER_TYPE_AC) + s->coder = 1; + + if (s->coder < 0) + s->coder = s->cabac; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + if (s->profile == FF_PROFILE_UNKNOWN && s->coder >= 0) + s->profile = s->coder == 0 ? FF_PROFILE_H264_CONSTRAINED_BASELINE : +#if OPENH264_VER_AT_LEAST(1, 8) + FF_PROFILE_H264_HIGH; +#else + FF_PROFILE_H264_MAIN; +#endif + + switch (s->profile) { +#if OPENH264_VER_AT_LEAST(1, 8) + case FF_PROFILE_H264_HIGH: param.iEntropyCodingModeFlag = 1; - else if (!s->profile && s->cabac) + av_log(avctx, AV_LOG_VERBOSE, "Using CABAC, " + "select EProfileIdc PRO_HIGH in libopenh264.\n"); + break; +#else + case FF_PROFILE_H264_MAIN: param.iEntropyCodingModeFlag = 1; + av_log(avctx, AV_LOG_VERBOSE, "Using CABAC, " + "select EProfileIdc PRO_MAIN in libopenh264.\n"); + break; +#endif + case FF_PROFILE_H264_CONSTRAINED_BASELINE: + case FF_PROFILE_UNKNOWN: + param.iEntropyCodingModeFlag = 0; + av_log(avctx, AV_LOG_VERBOSE, "Using CAVLC, " + "select EProfileIdc PRO_BASELINE in libopenh264.\n"); + break; + default: + param.iEntropyCodingModeFlag = 0; + av_log(avctx, AV_LOG_WARNING, "Unsupported profile, " + "select EProfileIdc PRO_BASELINE in libopenh264.\n"); + break; + } param.sSpatialLayers[0].iVideoWidth = param.iPicWidth; param.sSpatialLayers[0].iVideoHeight = param.iPicHeight; @@ -199,8 +287,7 @@ FF_ENABLE_DEPRECATION_WARNINGS param.sSpatialLayers[0].eAspectRatio = asp_idc[i]; } param.sSpatialLayers[0].bAspectRatioPresent = true; - } - else { + } else { param.sSpatialLayers[0].bAspectRatioPresent = false; } #endif @@ -225,9 +312,11 @@ FF_ENABLE_DEPRECATION_WARNINGS param.sSpatialLayers[0].sSliceCfg.uiSliceMode = s->slice_mode; param.sSpatialLayers[0].sSliceCfg.sSliceArgument.uiSliceNum = avctx->slices; #endif + if (avctx->slices == 0 && s->slice_mode == SM_FIXEDSLCNUM_SLICE) + av_log(avctx, AV_LOG_WARNING, "Slice count will be set automatically\n"); if (s->slice_mode == SM_SIZELIMITED_SLICE) { - if (s->max_nal_size){ + if (s->max_nal_size) { param.uiMaxNalSize = s->max_nal_size; #if OPENH264_VER_AT_LEAST(1, 6) param.sSpatialLayers[0].sSliceArgument.uiSliceSizeConstraint = s->max_nal_size; @@ -332,6 +421,14 @@ static int svc_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, return 0; } +static const AVCodecDefault svc_enc_defaults[] = { + { "b", "0" }, + { "g", "-1" }, + { "qmin", "-1" }, + { "qmax", "-1" }, + { NULL }, +}; + AVCodec ff_libopenh264_encoder = { .name = "libopenh264", .long_name = NULL_IF_CONFIG_SMALL("OpenH264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"), @@ -345,6 +442,7 @@ AVCodec ff_libopenh264_encoder = { .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, + .defaults = svc_enc_defaults, .priv_class = &class, .wrapper_name = "libopenh264", }; diff --git a/libavcodec/libopenjpegenc.c b/libavcodec/libopenjpegenc.c index 199800898e4..e4641235f5d 100644 --- a/libavcodec/libopenjpegenc.c +++ b/libavcodec/libopenjpegenc.c @@ -763,7 +763,7 @@ AVCodec ff_libopenjpeg_encoder = { .priv_data_size = sizeof(LibOpenJPEGContext), .init = libopenjpeg_encode_init, .encode2 = libopenjpeg_encode_frame, - .capabilities = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_FRAME_THREADS, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, AV_PIX_FMT_RGB48, AV_PIX_FMT_RGBA64, AV_PIX_FMT_GBR24P, diff --git a/libavcodec/libopusenc.c b/libavcodec/libopusenc.c index 7c025a66d77..13017ac323e 100644 --- a/libavcodec/libopusenc.c +++ b/libavcodec/libopusenc.c @@ -503,7 +503,6 @@ static int libopus_encode(AVCodecContext *avctx, AVPacket *avpkt, // Check if subtraction resulted in an overflow if ((discard_padding < opus->opts.packet_size) != (avpkt->duration > 0)) { av_packet_unref(avpkt); - av_free(avpkt); return AVERROR(EINVAL); } if (discard_padding > 0) { @@ -512,7 +511,6 @@ static int libopus_encode(AVCodecContext *avctx, AVPacket *avpkt, 10); if(!side_data) { av_packet_unref(avpkt); - av_free(avpkt); return AVERROR(ENOMEM); } AV_WL32(side_data + 4, discard_padding); diff --git a/libavcodec/librav1e.c b/libavcodec/librav1e.c new file mode 100644 index 00000000000..6f9b4cce4c7 --- /dev/null +++ b/libavcodec/librav1e.c @@ -0,0 +1,605 @@ +/* + * librav1e encoder + * + * Copyright (c) 2019 Derek Buitenhuis + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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/internal.h" +#include "libavutil/avassert.h" +#include "libavutil/base64.h" +#include "libavutil/common.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avcodec.h" +#include "internal.h" + +typedef struct librav1eContext { + const AVClass *class; + + RaContext *ctx; + AVBSFContext *bsf; + + uint8_t *pass_data; + size_t pass_pos; + int pass_size; + + AVDictionary *rav1e_opts; + int quantizer; + int speed; + int tiles; + int tile_rows; + int tile_cols; +} librav1eContext; + +static inline RaPixelRange range_map(enum AVPixelFormat pix_fmt, enum AVColorRange range) +{ + switch (pix_fmt) { + case AV_PIX_FMT_YUVJ420P: + case AV_PIX_FMT_YUVJ422P: + case AV_PIX_FMT_YUVJ444P: + return RA_PIXEL_RANGE_FULL; + } + + switch (range) { + case AVCOL_RANGE_JPEG: + return RA_PIXEL_RANGE_FULL; + case AVCOL_RANGE_MPEG: + default: + return RA_PIXEL_RANGE_LIMITED; + } +} + +static inline RaChromaSampling pix_fmt_map(enum AVPixelFormat pix_fmt) +{ + switch (pix_fmt) { + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUVJ420P: + case AV_PIX_FMT_YUV420P10: + case AV_PIX_FMT_YUV420P12: + return RA_CHROMA_SAMPLING_CS420; + case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUVJ422P: + case AV_PIX_FMT_YUV422P10: + case AV_PIX_FMT_YUV422P12: + return RA_CHROMA_SAMPLING_CS422; + case AV_PIX_FMT_YUV444P: + case AV_PIX_FMT_YUVJ444P: + case AV_PIX_FMT_YUV444P10: + case AV_PIX_FMT_YUV444P12: + return RA_CHROMA_SAMPLING_CS444; + default: + av_assert0(0); + } +} + +static inline RaChromaSamplePosition chroma_loc_map(enum AVChromaLocation chroma_loc) +{ + switch (chroma_loc) { + case AVCHROMA_LOC_LEFT: + return RA_CHROMA_SAMPLE_POSITION_VERTICAL; + case AVCHROMA_LOC_TOPLEFT: + return RA_CHROMA_SAMPLE_POSITION_COLOCATED; + default: + return RA_CHROMA_SAMPLE_POSITION_UNKNOWN; + } +} + +static int get_stats(AVCodecContext *avctx, int eos) +{ + librav1eContext *ctx = avctx->priv_data; + RaData* buf = rav1e_twopass_out(ctx->ctx); + if (!buf) + return 0; + + if (!eos) { + uint8_t *tmp = av_fast_realloc(ctx->pass_data, &ctx->pass_size, + ctx->pass_pos + buf->len); + if (!tmp) { + rav1e_data_unref(buf); + return AVERROR(ENOMEM); + } + + ctx->pass_data = tmp; + memcpy(ctx->pass_data + ctx->pass_pos, buf->data, buf->len); + ctx->pass_pos += buf->len; + } else { + size_t b64_size = AV_BASE64_SIZE(ctx->pass_pos); + + memcpy(ctx->pass_data, buf->data, buf->len); + + avctx->stats_out = av_malloc(b64_size); + if (!avctx->stats_out) { + rav1e_data_unref(buf); + return AVERROR(ENOMEM); + } + + av_base64_encode(avctx->stats_out, b64_size, ctx->pass_data, ctx->pass_pos); + + av_freep(&ctx->pass_data); + } + + rav1e_data_unref(buf); + + return 0; +} + +static int set_stats(AVCodecContext *avctx) +{ + librav1eContext *ctx = avctx->priv_data; + int ret = 1; + + while (ret > 0 && ctx->pass_size - ctx->pass_pos > 0) { + ret = rav1e_twopass_in(ctx->ctx, ctx->pass_data + ctx->pass_pos, ctx->pass_size); + if (ret < 0) + return AVERROR_EXTERNAL; + ctx->pass_pos += ret; + } + + return 0; +} + +static av_cold int librav1e_encode_close(AVCodecContext *avctx) +{ + librav1eContext *ctx = avctx->priv_data; + + if (ctx->ctx) { + rav1e_context_unref(ctx->ctx); + ctx->ctx = NULL; + } + + av_bsf_free(&ctx->bsf); + av_freep(&ctx->pass_data); + + return 0; +} + +static av_cold int librav1e_encode_init(AVCodecContext *avctx) +{ + librav1eContext *ctx = avctx->priv_data; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt); + RaConfig *cfg = NULL; + int rret; + int ret = 0; + + cfg = rav1e_config_default(); + if (!cfg) { + av_log(avctx, AV_LOG_ERROR, "Could not allocate rav1e config.\n"); + return AVERROR_EXTERNAL; + } + + /* + * Rav1e currently uses the time base given to it only for ratecontrol... where + * the inverse is taken and used as a framerate. So, do what we do in other wrappers + * and use the framerate if we can. + */ + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { + rav1e_config_set_time_base(cfg, (RaRational) { + avctx->framerate.den, avctx->framerate.num + }); + } else { + rav1e_config_set_time_base(cfg, (RaRational) { + avctx->time_base.num * avctx->ticks_per_frame, + avctx->time_base.den + }); + } + + if ((avctx->flags & AV_CODEC_FLAG_PASS1 || avctx->flags & AV_CODEC_FLAG_PASS2) && !avctx->bit_rate) { + av_log(avctx, AV_LOG_ERROR, "A bitrate must be set to use two pass mode.\n"); + ret = AVERROR_INVALIDDATA; + goto end; + } + + if (avctx->flags & AV_CODEC_FLAG_PASS2) { + if (!avctx->stats_in) { + av_log(avctx, AV_LOG_ERROR, "No stats file provided for second pass.\n"); + ret = AVERROR(EINVAL); + goto end; + } + + ctx->pass_size = (strlen(avctx->stats_in) * 3) / 4; + ctx->pass_data = av_malloc(ctx->pass_size); + if (!ctx->pass_data) { + av_log(avctx, AV_LOG_ERROR, "Could not allocate stats buffer.\n"); + ret = AVERROR(ENOMEM); + goto end; + } + + ctx->pass_size = av_base64_decode(ctx->pass_data, avctx->stats_in, ctx->pass_size); + if (ctx->pass_size < 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid pass file.\n"); + ret = AVERROR(EINVAL); + goto end; + } + } + + if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { + const AVBitStreamFilter *filter = av_bsf_get_by_name("extract_extradata"); + int bret; + + if (!filter) { + av_log(avctx, AV_LOG_ERROR, "extract_extradata bitstream filter " + "not found. This is a bug, please report it.\n"); + ret = AVERROR_BUG; + goto end; + } + + bret = av_bsf_alloc(filter, &ctx->bsf); + if (bret < 0) { + ret = bret; + goto end; + } + + bret = avcodec_parameters_from_context(ctx->bsf->par_in, avctx); + if (bret < 0) { + ret = bret; + goto end; + } + + bret = av_bsf_init(ctx->bsf); + if (bret < 0) { + ret = bret; + goto end; + } + } + + { + AVDictionaryEntry *en = NULL; + while ((en = av_dict_get(ctx->rav1e_opts, "", en, AV_DICT_IGNORE_SUFFIX))) { + int parse_ret = rav1e_config_parse(cfg, en->key, en->value); + if (parse_ret < 0) + av_log(avctx, AV_LOG_WARNING, "Invalid value for %s: %s.\n", en->key, en->value); + } + } + + rret = rav1e_config_parse_int(cfg, "width", avctx->width); + if (rret < 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid width passed to rav1e.\n"); + ret = AVERROR_INVALIDDATA; + goto end; + } + + rret = rav1e_config_parse_int(cfg, "height", avctx->height); + if (rret < 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid height passed to rav1e.\n"); + ret = AVERROR_INVALIDDATA; + goto end; + } + + rret = rav1e_config_parse_int(cfg, "threads", avctx->thread_count); + if (rret < 0) + av_log(avctx, AV_LOG_WARNING, "Invalid number of threads, defaulting to auto.\n"); + + if (ctx->speed >= 0) { + rret = rav1e_config_parse_int(cfg, "speed", ctx->speed); + if (rret < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not set speed preset.\n"); + ret = AVERROR_EXTERNAL; + goto end; + } + } + + /* rav1e handles precedence between 'tiles' and cols/rows for us. */ + if (ctx->tiles > 0) { + rret = rav1e_config_parse_int(cfg, "tiles", ctx->tiles); + if (rret < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not set number of tiles to encode with.\n"); + ret = AVERROR_EXTERNAL; + goto end; + } + } + if (ctx->tile_rows > 0) { + rret = rav1e_config_parse_int(cfg, "tile_rows", ctx->tile_rows); + if (rret < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not set number of tile rows to encode with.\n"); + ret = AVERROR_EXTERNAL; + goto end; + } + } + if (ctx->tile_cols > 0) { + rret = rav1e_config_parse_int(cfg, "tile_cols", ctx->tile_cols); + if (rret < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not set number of tile cols to encode with.\n"); + ret = AVERROR_EXTERNAL; + goto end; + } + } + + if (avctx->gop_size > 0) { + rret = rav1e_config_parse_int(cfg, "key_frame_interval", avctx->gop_size); + if (rret < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not set max keyint.\n"); + ret = AVERROR_EXTERNAL; + goto end; + } + } + + if (avctx->keyint_min > 0) { + rret = rav1e_config_parse_int(cfg, "min_key_frame_interval", avctx->keyint_min); + if (rret < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not set min keyint.\n"); + ret = AVERROR_EXTERNAL; + goto end; + } + } + + if (avctx->bit_rate && ctx->quantizer < 0) { + int max_quantizer = avctx->qmax >= 0 ? avctx->qmax : 255; + + rret = rav1e_config_parse_int(cfg, "quantizer", max_quantizer); + if (rret < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not set max quantizer.\n"); + ret = AVERROR_EXTERNAL; + goto end; + } + + if (avctx->qmin >= 0) { + rret = rav1e_config_parse_int(cfg, "min_quantizer", avctx->qmin); + if (rret < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not set min quantizer.\n"); + ret = AVERROR_EXTERNAL; + goto end; + } + } + + rret = rav1e_config_parse_int(cfg, "bitrate", avctx->bit_rate); + if (rret < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not set bitrate.\n"); + ret = AVERROR_INVALIDDATA; + goto end; + } + } else if (ctx->quantizer >= 0) { + if (avctx->bit_rate) + av_log(avctx, AV_LOG_WARNING, "Both bitrate and quantizer specified. Using quantizer mode."); + + rret = rav1e_config_parse_int(cfg, "quantizer", ctx->quantizer); + if (rret < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not set quantizer.\n"); + ret = AVERROR_EXTERNAL; + goto end; + } + } + + rret = rav1e_config_set_pixel_format(cfg, desc->comp[0].depth, + pix_fmt_map(avctx->pix_fmt), + chroma_loc_map(avctx->chroma_sample_location), + range_map(avctx->pix_fmt, avctx->color_range)); + if (rret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to set pixel format properties.\n"); + ret = AVERROR_INVALIDDATA; + goto end; + } + + /* rav1e's colorspace enums match standard values. */ + rret = rav1e_config_set_color_description(cfg, (RaMatrixCoefficients) avctx->colorspace, + (RaColorPrimaries) avctx->color_primaries, + (RaTransferCharacteristics) avctx->color_trc); + if (rret < 0) { + av_log(avctx, AV_LOG_WARNING, "Failed to set color properties.\n"); + if (avctx->err_recognition & AV_EF_EXPLODE) { + ret = AVERROR_INVALIDDATA; + goto end; + } + } + + ctx->ctx = rav1e_context_new(cfg); + if (!ctx->ctx) { + av_log(avctx, AV_LOG_ERROR, "Failed to create rav1e encode context.\n"); + ret = AVERROR_EXTERNAL; + goto end; + } + + ret = 0; + +end: + + rav1e_config_unref(cfg); + + return ret; +} + +static int librav1e_send_frame(AVCodecContext *avctx, const AVFrame *frame) +{ + librav1eContext *ctx = avctx->priv_data; + RaFrame *rframe = NULL; + int ret; + + if (frame) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); + + rframe = rav1e_frame_new(ctx->ctx); + if (!rframe) { + av_log(avctx, AV_LOG_ERROR, "Could not allocate new rav1e frame.\n"); + return AVERROR(ENOMEM); + } + + for (int i = 0; i < desc->nb_components; i++) { + int shift = i ? desc->log2_chroma_h : 0; + int bytes = desc->comp[0].depth == 8 ? 1 : 2; + rav1e_frame_fill_plane(rframe, i, frame->data[i], + (frame->height >> shift) * frame->linesize[i], + frame->linesize[i], bytes); + } + } + + ret = rav1e_send_frame(ctx->ctx, rframe); + if (rframe) + rav1e_frame_unref(rframe); /* No need to unref if flushing. */ + + switch (ret) { + case RA_ENCODER_STATUS_SUCCESS: + break; + case RA_ENCODER_STATUS_ENOUGH_DATA: + return AVERROR(EAGAIN); + case RA_ENCODER_STATUS_FAILURE: + av_log(avctx, AV_LOG_ERROR, "Could not send frame: %s\n", rav1e_status_to_str(ret)); + return AVERROR_EXTERNAL; + default: + av_log(avctx, AV_LOG_ERROR, "Unknown return code %d from rav1e_send_frame: %s\n", ret, rav1e_status_to_str(ret)); + return AVERROR_UNKNOWN; + } + + return 0; +} + +static int librav1e_receive_packet(AVCodecContext *avctx, AVPacket *pkt) +{ + librav1eContext *ctx = avctx->priv_data; + RaPacket *rpkt = NULL; + int ret; + +retry: + + if (avctx->flags & AV_CODEC_FLAG_PASS1) { + int sret = get_stats(avctx, 0); + if (sret < 0) + return sret; + } else if (avctx->flags & AV_CODEC_FLAG_PASS2) { + int sret = set_stats(avctx); + if (sret < 0) + return sret; + } + + ret = rav1e_receive_packet(ctx->ctx, &rpkt); + switch (ret) { + case RA_ENCODER_STATUS_SUCCESS: + break; + case RA_ENCODER_STATUS_LIMIT_REACHED: + if (avctx->flags & AV_CODEC_FLAG_PASS1) { + int sret = get_stats(avctx, 1); + if (sret < 0) + return sret; + } + return AVERROR_EOF; + case RA_ENCODER_STATUS_ENCODED: + if (avctx->internal->draining) + goto retry; + return AVERROR(EAGAIN); + case RA_ENCODER_STATUS_NEED_MORE_DATA: + if (avctx->internal->draining) { + av_log(avctx, AV_LOG_ERROR, "Unexpected error when receiving packet after EOF.\n"); + return AVERROR_EXTERNAL; + } + return AVERROR(EAGAIN); + case RA_ENCODER_STATUS_FAILURE: + av_log(avctx, AV_LOG_ERROR, "Could not encode frame: %s\n", rav1e_status_to_str(ret)); + return AVERROR_EXTERNAL; + default: + av_log(avctx, AV_LOG_ERROR, "Unknown return code %d from rav1e_receive_packet: %s\n", ret, rav1e_status_to_str(ret)); + return AVERROR_UNKNOWN; + } + + ret = av_new_packet(pkt, rpkt->len); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not allocate packet.\n"); + rav1e_packet_unref(rpkt); + return ret; + } + + memcpy(pkt->data, rpkt->data, rpkt->len); + + if (rpkt->frame_type == RA_FRAME_TYPE_KEY) + pkt->flags |= AV_PKT_FLAG_KEY; + + pkt->pts = pkt->dts = rpkt->input_frameno * avctx->ticks_per_frame; + rav1e_packet_unref(rpkt); + + if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { + int ret = av_bsf_send_packet(ctx->bsf, pkt); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "extradata extraction send failed.\n"); + av_packet_unref(pkt); + return ret; + } + + ret = av_bsf_receive_packet(ctx->bsf, pkt); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "extradata extraction receive failed.\n"); + av_packet_unref(pkt); + return ret; + } + } + + return 0; +} + +#define OFFSET(x) offsetof(librav1eContext, x) +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM + +static const AVOption options[] = { + { "qp", "use constant quantizer mode", OFFSET(quantizer), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 255, VE }, + { "speed", "what speed preset to use", OFFSET(speed), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 10, VE }, + { "tiles", "number of tiles encode with", OFFSET(tiles), AV_OPT_TYPE_INT, { .i64 = 0 }, -1, INT64_MAX, VE }, + { "tile-rows", "number of tiles rows to encode with", OFFSET(tile_rows), AV_OPT_TYPE_INT, { .i64 = 0 }, -1, INT64_MAX, VE }, + { "tile-columns", "number of tiles columns to encode with", OFFSET(tile_cols), AV_OPT_TYPE_INT, { .i64 = 0 }, -1, INT64_MAX, VE }, + { "rav1e-params", "set the rav1e configuration using a :-separated list of key=value parameters", OFFSET(rav1e_opts), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE }, + { NULL } +}; + +static const AVCodecDefault librav1e_defaults[] = { + { "b", "0" }, + { "g", "0" }, + { "keyint_min", "0" }, + { "qmax", "-1" }, + { "qmin", "-1" }, + { NULL } +}; + +const enum AVPixelFormat librav1e_pix_fmts[] = { + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUV420P10, + AV_PIX_FMT_YUV420P12, + AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUVJ422P, + AV_PIX_FMT_YUV422P10, + AV_PIX_FMT_YUV422P12, + AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUVJ444P, + AV_PIX_FMT_YUV444P10, + AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_NONE +}; + +static const AVClass class = { + .class_name = "librav1e", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVCodec ff_librav1e_encoder = { + .name = "librav1e", + .long_name = NULL_IF_CONFIG_SMALL("librav1e AV1"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_AV1, + .init = librav1e_encode_init, + .send_frame = librav1e_send_frame, + .receive_packet = librav1e_receive_packet, + .close = librav1e_encode_close, + .priv_data_size = sizeof(librav1eContext), + .priv_class = &class, + .defaults = librav1e_defaults, + .pix_fmts = librav1e_pix_fmts, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, + .wrapper_name = "librav1e", +}; diff --git a/libavcodec/librsvgdec.c b/libavcodec/librsvgdec.c index 66977850261..e9a181d092c 100644 --- a/libavcodec/librsvgdec.c +++ b/libavcodec/librsvgdec.c @@ -125,6 +125,6 @@ AVCodec ff_librsvg_decoder = { .id = AV_CODEC_ID_SVG, .decode = librsvg_decode_frame, .priv_data_size = sizeof(LibRSVGContext), - .capabilities = AV_CODEC_CAP_LOSSLESS | AV_CODEC_CAP_DR1, + .capabilities = AV_CODEC_CAP_DR1, .wrapper_name = "librsvg", }; diff --git a/libavcodec/libtwolame.c b/libavcodec/libtwolame.c index 030f88868fb..5ceb3d9f3f5 100644 --- a/libavcodec/libtwolame.c +++ b/libavcodec/libtwolame.c @@ -78,8 +78,12 @@ static av_cold int twolame_encode_init(AVCodecContext *avctx) twolame_set_in_samplerate(s->glopts, avctx->sample_rate); twolame_set_out_samplerate(s->glopts, avctx->sample_rate); - if (!avctx->bit_rate) - avctx->bit_rate = avctx->sample_rate < 28000 ? 160000 : 384000; + if (!avctx->bit_rate) { + if ((s->mode == TWOLAME_AUTO_MODE && avctx->channels == 1) || s->mode == TWOLAME_MONO) + avctx->bit_rate = avctx->sample_rate < 28000 ? 80000 : 192000; + else + avctx->bit_rate = avctx->sample_rate < 28000 ? 160000 : 384000; + } if (avctx->flags & AV_CODEC_FLAG_QSCALE || !avctx->bit_rate) { twolame_set_VBR(s->glopts, TRUE); diff --git a/libavcodec/libvorbisdec.c b/libavcodec/libvorbisdec.c index 89cbbb41b68..3c53b8fdaf2 100644 --- a/libavcodec/libvorbisdec.c +++ b/libavcodec/libvorbisdec.c @@ -64,22 +64,25 @@ static int oggvorbis_decode_init(AVCodecContext *avccontext) { } } else if(*p == 2) { unsigned int offset = 1; + unsigned int sizesum = 1; p++; for(i=0; i<2; i++) { hsizes[i] = 0; - while((*p == 0xFF) && (offset < avccontext->extradata_size)) { + while((*p == 0xFF) && (sizesum < avccontext->extradata_size)) { hsizes[i] += 0xFF; offset++; + sizesum += 1 + 0xFF; p++; } - if(offset >= avccontext->extradata_size - 1) { + hsizes[i] += *p; + offset++; + sizesum += 1 + *p; + if(sizesum > avccontext->extradata_size) { av_log(avccontext, AV_LOG_ERROR, "vorbis header sizes damaged\n"); ret = AVERROR_INVALIDDATA; goto error; } - hsizes[i] += *p; - offset++; p++; } hsizes[2] = avccontext->extradata_size - hsizes[0]-hsizes[1]-offset; diff --git a/libavcodec/libvpxdec.c b/libavcodec/libvpxdec.c index 164dbda49b0..1063c546b05 100644 --- a/libavcodec/libvpxdec.c +++ b/libavcodec/libvpxdec.c @@ -25,6 +25,7 @@ #define VPX_CODEC_DISABLE_COMPAT 1 #include +#include #include #include "libavutil/common.h" @@ -38,14 +39,50 @@ typedef struct VPxDecoderContext { struct vpx_codec_ctx decoder; struct vpx_codec_ctx decoder_alpha; + AVBufferPool *pool; + size_t pool_size; int has_alpha_channel; } VPxContext; + +static int get_frame_buffer(void *priv, size_t min_size, vpx_codec_frame_buffer_t *fb) +{ + VPxContext *ctx = priv; + AVBufferRef *buf; + + if (min_size > ctx->pool_size) { + av_buffer_pool_uninit(&ctx->pool); + /* According to the libvpx docs the buffer must be zeroed out. */ + ctx->pool = av_buffer_pool_init(min_size, av_buffer_allocz); + if (!ctx->pool) { + ctx->pool_size = 0; + return AVERROR(ENOMEM); + } + ctx->pool_size = min_size; + } + + buf = av_buffer_pool_get(ctx->pool); + if (!buf) + return AVERROR(ENOMEM); + + fb->priv = buf; + fb->size = ctx->pool_size; + fb->data = buf->data; + + return 0; +} + +static int release_frame_buffer(void *priv, vpx_codec_frame_buffer_t *fb) +{ + AVBufferRef *buf = fb->priv; + av_buffer_unref(&buf); + return 0; +} + static av_cold int vpx_init(AVCodecContext *avctx, - const struct vpx_codec_iface *iface, - int is_alpha_decoder) + struct vpx_codec_ctx* decoder, + const struct vpx_codec_iface *iface) { - VPxContext *ctx = avctx->priv_data; struct vpx_codec_dec_cfg deccfg = { .threads = FFMIN(avctx->thread_count ? avctx->thread_count : av_cpu_count(), 16) }; @@ -53,15 +90,16 @@ static av_cold int vpx_init(AVCodecContext *avctx, av_log(avctx, AV_LOG_INFO, "%s\n", vpx_codec_version_str()); av_log(avctx, AV_LOG_VERBOSE, "%s\n", vpx_codec_build_config()); - if (vpx_codec_dec_init( - is_alpha_decoder ? &ctx->decoder_alpha : &ctx->decoder, - iface, &deccfg, 0) != VPX_CODEC_OK) { - const char *error = vpx_codec_error(&ctx->decoder); + if (vpx_codec_dec_init(decoder, iface, &deccfg, 0) != VPX_CODEC_OK) { + const char *error = vpx_codec_error(decoder); av_log(avctx, AV_LOG_ERROR, "Failed to initialize decoder: %s\n", error); return AVERROR(EINVAL); } + if (avctx->codec_id == AV_CODEC_ID_VP9) + vpx_codec_set_frame_buffer_functions(decoder, get_frame_buffer, release_frame_buffer, avctx->priv_data); + return 0; } @@ -191,7 +229,7 @@ static int vpx_decode(AVCodecContext *avctx, side_data = av_packet_get_side_data(avpkt, AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, &side_data_size); - if (side_data_size > 1) { + if (side_data_size >= 8) { const uint64_t additional_id = AV_RB64(side_data); side_data += 8; side_data_size -= 8; @@ -199,15 +237,16 @@ static int vpx_decode(AVCodecContext *avctx, if (!ctx->has_alpha_channel) { ctx->has_alpha_channel = 1; ret = vpx_init(avctx, + &ctx->decoder_alpha, #if CONFIG_LIBVPX_VP8_DECODER && CONFIG_LIBVPX_VP9_DECODER (avctx->codec_id == AV_CODEC_ID_VP8) ? - &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo, + &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo #elif CONFIG_LIBVPX_VP8_DECODER - &vpx_codec_vp8_dx_algo, + &vpx_codec_vp8_dx_algo #else - &vpx_codec_vp9_dx_algo, + &vpx_codec_vp9_dx_algo #endif - 1); + ); if (ret) return ret; } @@ -243,8 +282,17 @@ static int vpx_decode(AVCodecContext *avctx, if (ret < 0) return ret; } - if ((ret = ff_get_buffer(avctx, picture, 0)) < 0) - return ret; + + if (ctx->has_alpha_channel && + (img->d_w != img_alpha->d_w || + img->d_h != img_alpha->d_h || + img->bit_depth != img_alpha->bit_depth)) { + av_log(avctx, AV_LOG_ERROR, + "Video dimensions %dx%d@%dbpc differ from alpha dimensions %dx%d@%dbpc\n", + img->d_w, img->d_h, img->bit_depth, + img_alpha->d_w, img_alpha->d_h, img_alpha->bit_depth); + return AVERROR_INVALIDDATA; + } planes[0] = img->planes[VPX_PLANE_Y]; planes[1] = img->planes[VPX_PLANE_U]; @@ -256,8 +304,31 @@ static int vpx_decode(AVCodecContext *avctx, linesizes[2] = img->stride[VPX_PLANE_V]; linesizes[3] = ctx->has_alpha_channel ? img_alpha->stride[VPX_PLANE_Y] : 0; - av_image_copy(picture->data, picture->linesize, (const uint8_t**)planes, - linesizes, avctx->pix_fmt, img->d_w, img->d_h); + + if (img->fb_priv && (!ctx->has_alpha_channel || img_alpha->fb_priv)) { + ret = ff_decode_frame_props(avctx, picture); + if (ret < 0) + return ret; + picture->buf[0] = av_buffer_ref(img->fb_priv); + if (!picture->buf[0]) + 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); + return AVERROR(ENOMEM); + } + } + for (int i = 0; i < 4; i++) { + picture->data[i] = planes[i]; + picture->linesize[i] = linesizes[i]; + } + } 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); + } *got_frame = 1; } return avpkt->size; @@ -269,13 +340,15 @@ static av_cold int vpx_free(AVCodecContext *avctx) vpx_codec_destroy(&ctx->decoder); if (ctx->has_alpha_channel) vpx_codec_destroy(&ctx->decoder_alpha); + av_buffer_pool_uninit(&ctx->pool); return 0; } #if CONFIG_LIBVPX_VP8_DECODER static av_cold int vp8_init(AVCodecContext *avctx) { - return vpx_init(avctx, &vpx_codec_vp8_dx_algo, 0); + VPxContext *ctx = avctx->priv_data; + return vpx_init(avctx, &ctx->decoder, &vpx_codec_vp8_dx_algo); } AVCodec ff_libvpx_vp8_decoder = { @@ -295,7 +368,8 @@ AVCodec ff_libvpx_vp8_decoder = { #if CONFIG_LIBVPX_VP9_DECODER static av_cold int vp9_init(AVCodecContext *avctx) { - return vpx_init(avctx, &vpx_codec_vp9_dx_algo, 0); + VPxContext *ctx = avctx->priv_data; + return vpx_init(avctx, &ctx->decoder, &vpx_codec_vp9_dx_algo); } AVCodec ff_libvpx_vp9_decoder = { @@ -307,7 +381,7 @@ AVCodec ff_libvpx_vp9_decoder = { .init = vp9_init, .close = vpx_free, .decode = vpx_decode, - .capabilities = AV_CODEC_CAP_AUTO_THREADS | AV_CODEC_CAP_DR1, + .capabilities = AV_CODEC_CAP_AUTO_THREADS, .init_static_data = ff_vp9_init_static, .profiles = NULL_IF_CONFIG_SMALL(ff_vp9_profiles), .wrapper_name = "libvpx", diff --git a/libavcodec/libvpxenc.c b/libavcodec/libvpxenc.c index feb52ea0dd0..8e0ea42375a 100644 --- a/libavcodec/libvpxenc.c +++ b/libavcodec/libvpxenc.c @@ -32,6 +32,7 @@ #include "internal.h" #include "libavutil/avassert.h" #include "libvpx.h" +#include "packet_internal.h" #include "profiles.h" #include "libavutil/avstring.h" #include "libavutil/base64.h" @@ -100,7 +101,9 @@ typedef struct VPxEncoderContext { int rc_undershoot_pct; int rc_overshoot_pct; - char *vp8_ts_parameters; + AVDictionary *vpx_ts_parameters; + int *ts_layer_flags; + int current_temporal_idx; // VP9-only int lossless; @@ -116,6 +119,11 @@ typedef struct VPxEncoderContext { int tune_content; int corpus_complexity; int tpl_model; + /** + * If the driver does not support ROI then warn the first time we + * encounter a frame with ROI side data. + */ + int roi_warned; } VPxContext; /** String mappings for enum vp8e_enc_control_id */ @@ -132,6 +140,7 @@ static const char *const ctlidstr[] = { [VP8E_SET_CQ_LEVEL] = "VP8E_SET_CQ_LEVEL", [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", #if CONFIG_LIBVPX_VP9_ENCODER [VP9E_SET_LOSSLESS] = "VP9E_SET_LOSSLESS", [VP9E_SET_TILE_COLUMNS] = "VP9E_SET_TILE_COLUMNS", @@ -139,6 +148,11 @@ static const char *const ctlidstr[] = { [VP9E_SET_FRAME_PARALLEL_DECODING] = "VP9E_SET_FRAME_PARALLEL_DECODING", [VP9E_SET_AQ_MODE] = "VP9E_SET_AQ_MODE", [VP9E_SET_COLOR_SPACE] = "VP9E_SET_COLOR_SPACE", + [VP9E_SET_SVC_LAYER_ID] = "VP9E_SET_SVC_LAYER_ID", +#if VPX_ENCODER_ABI_VERSION >= 12 + [VP9E_SET_SVC_PARAMETERS] = "VP9E_SET_SVC_PARAMETERS", +#endif + [VP9E_SET_SVC] = "VP9E_SET_SVC", #if VPX_ENCODER_ABI_VERSION >= 11 [VP9E_SET_COLOR_RANGE] = "VP9E_SET_COLOR_RANGE", #endif @@ -216,10 +230,22 @@ static av_cold void dump_enc_cfg(AVCodecContext *avctx, width, "rc_overshoot_pct:", cfg->rc_overshoot_pct); av_log(avctx, level, "temporal layering settings\n" " %*s%u\n", width, "ts_number_layers:", cfg->ts_number_layers); - av_log(avctx, level, - "\n %*s", width, "ts_target_bitrate:"); - for (i = 0; i < VPX_TS_MAX_LAYERS; i++) - av_log(avctx, level, "%u ", cfg->ts_target_bitrate[i]); + if (avctx->codec_id == AV_CODEC_ID_VP8) { + av_log(avctx, level, + "\n %*s", width, "ts_target_bitrate:"); + for (i = 0; i < VPX_TS_MAX_LAYERS; i++) + av_log(avctx, level, + "%u ", cfg->ts_target_bitrate[i]); + } +#if (VPX_ENCODER_ABI_VERSION >= 12) && CONFIG_LIBVPX_VP9_ENCODER + if (avctx->codec_id == AV_CODEC_ID_VP9) { + av_log(avctx, level, + "\n %*s", width, "layer_target_bitrate:"); + for (i = 0; i < VPX_TS_MAX_LAYERS; i++) + av_log(avctx, level, + "%u ", cfg->layer_target_bitrate[i]); + } +#endif av_log(avctx, level, "\n"); av_log(avctx, level, "\n %*s", width, "ts_rate_decimator:"); @@ -341,9 +367,14 @@ static av_cold int vpx_free(AVCodecContext *avctx) } #endif + av_freep(&ctx->ts_layer_flags); + vpx_codec_destroy(&ctx->encoder); - if (ctx->is_alpha) + if (ctx->is_alpha) { vpx_codec_destroy(&ctx->encoder_alpha); + av_freep(&ctx->rawimg_alpha.planes[VPX_PLANE_U]); + av_freep(&ctx->rawimg_alpha.planes[VPX_PLANE_V]); + } av_freep(&ctx->twopass_stats.buf); av_freep(&avctx->stats_out); free_frame_list(ctx->coded_frame_list); @@ -362,23 +393,149 @@ static void vp8_ts_parse_int_array(int *dest, char *value, size_t value_len, int } } -static int vp8_ts_param_parse(struct vpx_codec_enc_cfg *enccfg, char *key, char *value) +static void set_temporal_layer_pattern(int layering_mode, vpx_codec_enc_cfg_t *cfg, + int *layer_flags, int *flag_periodicity) +{ + switch (layering_mode) { + case 2: { + /** + * 2-layers, 2-frame period. + */ + static const int ids[2] = { 0, 1 }; + cfg->ts_periodicity = 2; + *flag_periodicity = 2; + cfg->ts_number_layers = 2; + cfg->ts_rate_decimator[0] = 2; + cfg->ts_rate_decimator[1] = 1; + memcpy(cfg->ts_layer_id, ids, sizeof(ids)); + + layer_flags[0] = + VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF | + VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF; + layer_flags[1] = + VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_GF | + VP8_EFLAG_NO_UPD_LAST | + VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_GF; + break; + } + case 3: { + /** + * 3-layers structure with one reference frame. + * This works same as temporal_layering_mode 3. + * + * 3-layers, 4-frame period. + */ + static const int ids[4] = { 0, 2, 1, 2 }; + cfg->ts_periodicity = 4; + *flag_periodicity = 4; + cfg->ts_number_layers = 3; + cfg->ts_rate_decimator[0] = 4; + cfg->ts_rate_decimator[1] = 2; + cfg->ts_rate_decimator[2] = 1; + memcpy(cfg->ts_layer_id, ids, sizeof(ids)); + + /** + * 0=L, 1=GF, 2=ARF, + * Intra-layer prediction disabled. + */ + layer_flags[0] = + VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF | + VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF; + layer_flags[1] = + VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF | + VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF | + VP8_EFLAG_NO_UPD_ARF; + layer_flags[2] = + VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF | + VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST; + layer_flags[3] = + VP8_EFLAG_NO_REF_LAST | VP8_EFLAG_NO_REF_ARF | + VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF | + VP8_EFLAG_NO_UPD_ARF; + break; + } + case 4: { + /** + * 3-layers structure. + * added dependency between the two TL2 frames (on top of case 3). + * 3-layers, 4-frame period. + */ + static const int ids[4] = { 0, 2, 1, 2 }; + cfg->ts_periodicity = 4; + *flag_periodicity = 4; + cfg->ts_number_layers = 3; + cfg->ts_rate_decimator[0] = 4; + cfg->ts_rate_decimator[1] = 2; + cfg->ts_rate_decimator[2] = 1; + memcpy(cfg->ts_layer_id, ids, sizeof(ids)); + + /** + * 0=L, 1=GF, 2=ARF, Intra-layer prediction disabled. + */ + layer_flags[0] = + VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF | + VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF; + layer_flags[1] = + VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF | + VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF; + layer_flags[2] = + VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF | + VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST; + layer_flags[3] = + VP8_EFLAG_NO_REF_LAST | + VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF | + VP8_EFLAG_NO_UPD_ARF; + break; + } + default: + /** + * do not change the layer_flags or the flag_periodicity in this case; + * it might be that the code is using external flags to be used. + */ + break; + + } +} + +static int vpx_ts_param_parse(VPxContext *ctx, struct vpx_codec_enc_cfg *enccfg, + char *key, char *value, enum AVCodecID codec_id) { size_t value_len = strlen(value); + int ts_layering_mode = 0; if (!value_len) return -1; if (!strcmp(key, "ts_number_layers")) enccfg->ts_number_layers = strtoul(value, &value, 10); - else if (!strcmp(key, "ts_target_bitrate")) - vp8_ts_parse_int_array(enccfg->ts_target_bitrate, value, value_len, VPX_TS_MAX_LAYERS); - else if (!strcmp(key, "ts_rate_decimator")) - vp8_ts_parse_int_array(enccfg->ts_rate_decimator, value, value_len, VPX_TS_MAX_LAYERS); - else if (!strcmp(key, "ts_periodicity")) + else if (!strcmp(key, "ts_target_bitrate")) { + if (codec_id == AV_CODEC_ID_VP8) + vp8_ts_parse_int_array(enccfg->ts_target_bitrate, value, value_len, VPX_TS_MAX_LAYERS); +#if (VPX_ENCODER_ABI_VERSION >= 12) && CONFIG_LIBVPX_VP9_ENCODER + if (codec_id == AV_CODEC_ID_VP9) + vp8_ts_parse_int_array(enccfg->layer_target_bitrate, value, value_len, VPX_TS_MAX_LAYERS); +#endif + } else if (!strcmp(key, "ts_rate_decimator")) { + vp8_ts_parse_int_array(enccfg->ts_rate_decimator, value, value_len, VPX_TS_MAX_LAYERS); + } else if (!strcmp(key, "ts_periodicity")) { enccfg->ts_periodicity = strtoul(value, &value, 10); - else if (!strcmp(key, "ts_layer_id")) + } else if (!strcmp(key, "ts_layer_id")) { vp8_ts_parse_int_array(enccfg->ts_layer_id, value, value_len, VPX_TS_MAX_PERIODICITY); + } else if (!strcmp(key, "ts_layering_mode")) { + /* option for pre-defined temporal structures in function set_temporal_layer_pattern. */ + ts_layering_mode = strtoul(value, &value, 4); + } + +#if (VPX_ENCODER_ABI_VERSION >= 12) && CONFIG_LIBVPX_VP9_ENCODER + enccfg->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_BYPASS; // only bypass mode is supported for now. + enccfg->ss_number_layers = 1; // TODO: add spatial scalability support. +#endif + if (ts_layering_mode) { + // make sure the ts_layering_mode comes at the end of the ts_parameter string to ensure that + // correct configuration is done. + ctx->ts_layer_flags = av_malloc_array(VPX_TS_MAX_PERIODICITY, sizeof(*ctx->ts_layer_flags)); + set_temporal_layer_pattern(ts_layering_mode, enccfg, ctx->ts_layer_flags, &enccfg->ts_periodicity); + } return 0; } @@ -510,6 +667,66 @@ static void set_color_range(AVCodecContext *avctx) #endif #endif +/** + * Set the target bitrate to VPX library default. Also set CRF to 32 if needed. + */ +static void set_vp8_defaults(AVCodecContext *avctx, + struct vpx_codec_enc_cfg *enccfg) +{ + VPxContext *ctx = avctx->priv_data; + av_assert0(!avctx->bit_rate); + avctx->bit_rate = enccfg->rc_target_bitrate * 1000; + if (enccfg->rc_end_usage == VPX_CQ) { + av_log(avctx, AV_LOG_WARNING, + "Bitrate not specified for constrained quality mode, using default of %dkbit/sec\n", + enccfg->rc_target_bitrate); + } else { + enccfg->rc_end_usage = VPX_CQ; + ctx->crf = 32; + av_log(avctx, AV_LOG_WARNING, + "Neither bitrate nor constrained quality specified, using default CRF of %d and bitrate of %dkbit/sec\n", + ctx->crf, enccfg->rc_target_bitrate); + } +} + + +#if CONFIG_LIBVPX_VP9_ENCODER +/** + * Keep the target bitrate at 0 to engage constant quality mode. If CRF is not + * set, use 32. + */ +static void set_vp9_defaults(AVCodecContext *avctx, + struct vpx_codec_enc_cfg *enccfg) +{ + VPxContext *ctx = avctx->priv_data; + av_assert0(!avctx->bit_rate); + if (enccfg->rc_end_usage != VPX_Q && ctx->lossless < 0) { + enccfg->rc_end_usage = VPX_Q; + ctx->crf = 32; + av_log(avctx, AV_LOG_WARNING, + "Neither bitrate nor constrained quality specified, using default CRF of %d\n", + ctx->crf); + } +} +#endif + +/** + * Called when the bitrate is not set. It sets appropriate default values for + * bitrate and CRF. + */ +static void set_vpx_defaults(AVCodecContext *avctx, + struct vpx_codec_enc_cfg *enccfg) +{ + av_assert0(!avctx->bit_rate); +#if CONFIG_LIBVPX_VP9_ENCODER + if (avctx->codec_id == AV_CODEC_ID_VP9) { + set_vp9_defaults(avctx, enccfg); + return; + } +#endif + set_vp8_defaults(avctx, enccfg); +} + static av_cold int vpx_init(AVCodecContext *avctx, const struct vpx_codec_iface *iface) { @@ -522,7 +739,9 @@ static av_cold int vpx_init(AVCodecContext *avctx, vpx_img_fmt_t img_fmt = VPX_IMG_FMT_I420; #if CONFIG_LIBVPX_VP9_ENCODER vpx_codec_caps_t codec_caps = vpx_codec_get_caps(iface); + vpx_svc_extra_cfg_t svc_params; #endif + AVDictionaryEntry* en = NULL; av_log(avctx, AV_LOG_INFO, "%s\n", vpx_codec_version_str()); av_log(avctx, AV_LOG_VERBOSE, "%s\n", vpx_codec_build_config()); @@ -581,17 +800,11 @@ static av_cold int vpx_init(AVCodecContext *avctx, enccfg.rc_target_bitrate = av_rescale_rnd(avctx->bit_rate, 1, 1000, AV_ROUND_NEAR_INF); #if CONFIG_LIBVPX_VP9_ENCODER - } else if (enccfg.rc_end_usage == VPX_Q) { + enccfg.ss_target_bitrate[0] = enccfg.rc_target_bitrate; #endif } else { - if (enccfg.rc_end_usage == VPX_CQ) { - enccfg.rc_target_bitrate = 1000000; - } else { - avctx->bit_rate = enccfg.rc_target_bitrate * 1000; - av_log(avctx, AV_LOG_WARNING, - "Neither bitrate nor constrained quality specified, using default bitrate of %dkbit/sec\n", - enccfg.rc_target_bitrate); - } + // Set bitrate to default value. Also sets CRF to default if needed. + set_vpx_defaults(avctx, &enccfg); } if (avctx->codec_id == AV_CODEC_ID_VP9 && ctx->lossless == 1) { @@ -698,20 +911,11 @@ FF_ENABLE_DEPRECATION_WARNINGS enccfg.g_error_resilient = ctx->error_resilient || ctx->flags & VP8F_ERROR_RESILIENT; - if (CONFIG_LIBVPX_VP8_ENCODER && avctx->codec_id == AV_CODEC_ID_VP8 && ctx->vp8_ts_parameters) { - AVDictionary *dict = NULL; - AVDictionaryEntry* en = NULL; - - if (!av_dict_parse_string(&dict, ctx->vp8_ts_parameters, "=", ":", 0)) { - while ((en = av_dict_get(dict, "", en, AV_DICT_IGNORE_SUFFIX))) { - if (vp8_ts_param_parse(&enccfg, en->key, en->value) < 0) - av_log(avctx, AV_LOG_WARNING, - "Error parsing option '%s = %s'.\n", - en->key, en->value); - } - - av_dict_free(&dict); - } + while ((en = av_dict_get(ctx->vpx_ts_parameters, "", en, AV_DICT_IGNORE_SUFFIX))) { + if (vpx_ts_param_parse(ctx, &enccfg, en->key, en->value, avctx->codec_id) < 0) + av_log(avctx, AV_LOG_WARNING, + "Error parsing option '%s = %s'.\n", + en->key, en->value); } dump_enc_cfg(avctx, &enccfg); @@ -721,7 +925,21 @@ FF_ENABLE_DEPRECATION_WARNINGS log_encoder_error(avctx, "Failed to initialize encoder"); return AVERROR(EINVAL); } - +#if CONFIG_LIBVPX_VP9_ENCODER + if (avctx->codec_id == AV_CODEC_ID_VP9 && enccfg.ts_number_layers > 1) { + memset(&svc_params, 0, sizeof(svc_params)); + for (int i = 0; i < enccfg.ts_number_layers; ++i) { + svc_params.max_quantizers[i] = enccfg.rc_max_quantizer; + svc_params.min_quantizers[i] = enccfg.rc_min_quantizer; + } + svc_params.scaling_factor_num[0] = enccfg.g_h; + svc_params.scaling_factor_den[0] = enccfg.g_h; +#if VPX_ENCODER_ABI_VERSION >= 12 + codecctl_int(avctx, VP9E_SET_SVC, 1); + codecctl_intp(avctx, VP9E_SET_SVC_PARAMETERS, (int *)&svc_params); +#endif + } +#endif if (ctx->is_alpha) { enccfg_alpha = enccfg; res = vpx_codec_enc_init(&ctx->encoder_alpha, iface, &enccfg_alpha, flags); @@ -816,10 +1034,6 @@ FF_ENABLE_DEPRECATION_WARNINGS ctx->rawimg.bit_depth = enccfg.g_bit_depth; #endif - if (ctx->is_alpha) - vpx_img_wrap(&ctx->rawimg_alpha, VPX_IMG_FMT_I420, avctx->width, avctx->height, 1, - (unsigned char*)1); - cpb_props = ff_add_cpb_side_data(avctx); if (!cpb_props) return AVERROR(ENOMEM); @@ -935,7 +1149,6 @@ FF_ENABLE_DEPRECATION_WARNINGS cx_frame->sz_alpha + 8); if(!side_data) { av_packet_unref(pkt); - av_free(pkt); return AVERROR(ENOMEM); } AV_WB64(side_data, 1); @@ -992,8 +1205,7 @@ static int queue_frames(AVCodecContext *avctx, AVPacket *pkt_out) if (size < 0) return size; } else { - struct FrameListData *cx_frame = - av_malloc(sizeof(struct FrameListData)); + struct FrameListData *cx_frame = av_malloc(sizeof(*cx_frame)); if (!cx_frame) { av_log(avctx, AV_LOG_ERROR, @@ -1057,6 +1269,213 @@ static int queue_frames(AVCodecContext *avctx, AVPacket *pkt_out) return size; } +static int set_roi_map(AVCodecContext *avctx, const AVFrameSideData *sd, int frame_width, int frame_height, + vpx_roi_map_t *roi_map, int block_size, int segment_cnt) +{ + /** + * range of vpx_roi_map_t.delta_q[i] is [-63, 63] + */ +#define MAX_DELTA_Q 63 + + const AVRegionOfInterest *roi = NULL; + int nb_rois; + uint32_t self_size; + int segment_id; + + /* record the mapping from delta_q to "segment id + 1" in segment_mapping[]. + * the range of delta_q is [-MAX_DELTA_Q, MAX_DELTA_Q], + * and its corresponding array index is [0, 2 * MAX_DELTA_Q], + * and so the length of the mapping array is 2 * MAX_DELTA_Q + 1. + * "segment id + 1", so we can say there's no mapping if the value of array element is zero. + */ + int segment_mapping[2 * MAX_DELTA_Q + 1] = { 0 }; + + memset(roi_map, 0, sizeof(*roi_map)); + + /* segment id 0 in roi_map is reserved for the areas not covered by AVRegionOfInterest. + * segment id 0 in roi_map is also for the areas with AVRegionOfInterest.qoffset near 0. + * (delta_q of segment id 0 is 0). + */ + segment_mapping[MAX_DELTA_Q] = 1; + segment_id = 1; + + roi = (const AVRegionOfInterest*)sd->data; + self_size = roi->self_size; + if (!self_size || sd->size % self_size) { + av_log(avctx, AV_LOG_ERROR, "Invalid AVRegionOfInterest.self_size.\n"); + return AVERROR(EINVAL); + } + nb_rois = sd->size / self_size; + + /* This list must be iterated from zero because regions are + * defined in order of decreasing importance. So discard less + * important areas if they exceed the segment count. + */ + for (int i = 0; i < nb_rois; i++) { + int delta_q; + int mapping_index; + + roi = (const AVRegionOfInterest*)(sd->data + self_size * i); + if (!roi->qoffset.den) { + av_log(avctx, AV_LOG_ERROR, "AVRegionOfInterest.qoffset.den must not be zero.\n"); + return AVERROR(EINVAL); + } + + delta_q = (int)(roi->qoffset.num * 1.0f / roi->qoffset.den * MAX_DELTA_Q); + delta_q = av_clip(delta_q, -MAX_DELTA_Q, MAX_DELTA_Q); + + mapping_index = delta_q + MAX_DELTA_Q; + if (!segment_mapping[mapping_index]) { + if (segment_id == segment_cnt) { + av_log(avctx, AV_LOG_WARNING, + "ROI only supports %d segments (and segment 0 is reserved for non-ROIs), skipping the left ones.\n", + segment_cnt); + break; + } + + segment_mapping[mapping_index] = segment_id + 1; + roi_map->delta_q[segment_id] = delta_q; + segment_id++; + } + } + + roi_map->rows = (frame_height + block_size - 1) / block_size; + roi_map->cols = (frame_width + block_size - 1) / block_size; + roi_map->roi_map = av_mallocz_array(roi_map->rows * roi_map->cols, sizeof(*roi_map->roi_map)); + if (!roi_map->roi_map) { + av_log(avctx, AV_LOG_ERROR, "roi_map alloc failed.\n"); + return AVERROR(ENOMEM); + } + + /* This list must be iterated in reverse, so for the case that + * two regions are overlapping, the more important area takes effect. + */ + for (int i = nb_rois - 1; i >= 0; i--) { + int delta_q; + int mapping_value; + int starty, endy, startx, endx; + + roi = (const AVRegionOfInterest*)(sd->data + self_size * i); + + starty = av_clip(roi->top / block_size, 0, roi_map->rows); + endy = av_clip((roi->bottom + block_size - 1) / block_size, 0, roi_map->rows); + startx = av_clip(roi->left / block_size, 0, roi_map->cols); + endx = av_clip((roi->right + block_size - 1) / block_size, 0, roi_map->cols); + + delta_q = (int)(roi->qoffset.num * 1.0f / roi->qoffset.den * MAX_DELTA_Q); + delta_q = av_clip(delta_q, -MAX_DELTA_Q, MAX_DELTA_Q); + + mapping_value = segment_mapping[delta_q + MAX_DELTA_Q]; + if (mapping_value) { + for (int y = starty; y < endy; y++) + for (int x = startx; x < endx; x++) + roi_map->roi_map[x + y * roi_map->cols] = mapping_value - 1; + } + } + + return 0; +} + +static int vp9_encode_set_roi(AVCodecContext *avctx, int frame_width, int frame_height, const AVFrameSideData *sd) +{ + VPxContext *ctx = avctx->priv_data; + +#ifdef VPX_CTRL_VP9E_SET_ROI_MAP + int version = vpx_codec_version(); + int major = VPX_VERSION_MAJOR(version); + int minor = VPX_VERSION_MINOR(version); + int patch = VPX_VERSION_PATCH(version); + + if (major > 1 || (major == 1 && minor > 8) || (major == 1 && minor == 8 && patch >= 1)) { + vpx_roi_map_t roi_map; + const int segment_cnt = 8; + const int block_size = 8; + int ret; + + if (ctx->aq_mode > 0 || ctx->cpu_used < 5 || ctx->deadline != VPX_DL_REALTIME) { + if (!ctx->roi_warned) { + ctx->roi_warned = 1; + av_log(avctx, AV_LOG_WARNING, "ROI is only enabled when aq_mode is 0, cpu_used >= 5 " + "and deadline is REALTIME, so skipping ROI.\n"); + return AVERROR(EINVAL); + } + } + + ret = set_roi_map(avctx, sd, frame_width, frame_height, &roi_map, block_size, segment_cnt); + if (ret) { + log_encoder_error(avctx, "Failed to set_roi_map.\n"); + return ret; + } + + memset(roi_map.ref_frame, -1, sizeof(roi_map.ref_frame)); + + if (vpx_codec_control(&ctx->encoder, VP9E_SET_ROI_MAP, &roi_map)) { + log_encoder_error(avctx, "Failed to set VP9E_SET_ROI_MAP codec control.\n"); + ret = AVERROR_INVALIDDATA; + } + av_freep(&roi_map.roi_map); + return ret; + } +#endif + + if (!ctx->roi_warned) { + ctx->roi_warned = 1; + av_log(avctx, AV_LOG_WARNING, "ROI is not supported, please upgrade libvpx to version >= 1.8.1. " + "You may need to rebuild ffmpeg.\n"); + } + return 0; +} + +static int vp8_encode_set_roi(AVCodecContext *avctx, int frame_width, int frame_height, const AVFrameSideData *sd) +{ + vpx_roi_map_t roi_map; + const int segment_cnt = 4; + const int block_size = 16; + VPxContext *ctx = avctx->priv_data; + + int ret = set_roi_map(avctx, sd, frame_width, frame_height, &roi_map, block_size, segment_cnt); + if (ret) { + log_encoder_error(avctx, "Failed to set_roi_map.\n"); + return ret; + } + + if (vpx_codec_control(&ctx->encoder, VP8E_SET_ROI_MAP, &roi_map)) { + log_encoder_error(avctx, "Failed to set VP8E_SET_ROI_MAP codec control.\n"); + ret = AVERROR_INVALIDDATA; + } + + av_freep(&roi_map.roi_map); + return ret; +} + +static int realloc_alpha_uv(AVCodecContext *avctx, int width, int height) +{ + VPxContext *ctx = avctx->priv_data; + struct vpx_image *rawimg_alpha = &ctx->rawimg_alpha; + unsigned char **planes = rawimg_alpha->planes; + int *stride = rawimg_alpha->stride; + + if (!planes[VPX_PLANE_U] || + !planes[VPX_PLANE_V] || + width != (int)rawimg_alpha->d_w || + height != (int)rawimg_alpha->d_h) { + av_freep(&planes[VPX_PLANE_U]); + av_freep(&planes[VPX_PLANE_V]); + + vpx_img_wrap(rawimg_alpha, VPX_IMG_FMT_I420, width, height, 1, + (unsigned char*)1); + planes[VPX_PLANE_U] = av_malloc_array(stride[VPX_PLANE_U], height); + planes[VPX_PLANE_V] = av_malloc_array(stride[VPX_PLANE_V], height); + if (!planes[VPX_PLANE_U] || !planes[VPX_PLANE_V]) + return AVERROR(ENOMEM); + + memset(planes[VPX_PLANE_U], 0x80, stride[VPX_PLANE_U] * height); + memset(planes[VPX_PLANE_V], 0x80, stride[VPX_PLANE_V] * height); + } + + return 0; +} + static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet) { @@ -1066,8 +1485,12 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, int64_t timestamp = 0; int res, coded_size; vpx_enc_frame_flags_t flags = 0; + const struct vpx_codec_enc_cfg *enccfg = ctx->encoder.config.enc; + vpx_svc_layer_id_t layer_id; + int layer_id_valid = 0; if (frame) { + const AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST); rawimg = &ctx->rawimg; rawimg->planes[VPX_PLANE_Y] = frame->data[0]; rawimg->planes[VPX_PLANE_U] = frame->data[1]; @@ -1076,23 +1499,12 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, rawimg->stride[VPX_PLANE_U] = frame->linesize[1]; rawimg->stride[VPX_PLANE_V] = frame->linesize[2]; if (ctx->is_alpha) { - uint8_t *u_plane, *v_plane; rawimg_alpha = &ctx->rawimg_alpha; + res = realloc_alpha_uv(avctx, frame->width, frame->height); + if (res < 0) + return res; rawimg_alpha->planes[VPX_PLANE_Y] = frame->data[3]; - u_plane = av_malloc(frame->linesize[1] * frame->height); - v_plane = av_malloc(frame->linesize[2] * frame->height); - if (!u_plane || !v_plane) { - av_free(u_plane); - av_free(v_plane); - return AVERROR(ENOMEM); - } - memset(u_plane, 0x80, frame->linesize[1] * frame->height); - rawimg_alpha->planes[VPX_PLANE_U] = u_plane; - memset(v_plane, 0x80, frame->linesize[2] * frame->height); - rawimg_alpha->planes[VPX_PLANE_V] = v_plane; - rawimg_alpha->stride[VPX_PLANE_Y] = frame->linesize[0]; - rawimg_alpha->stride[VPX_PLANE_U] = frame->linesize[1]; - rawimg_alpha->stride[VPX_PLANE_V] = frame->linesize[2]; + rawimg_alpha->stride[VPX_PLANE_Y] = frame->linesize[3]; } timestamp = frame->pts; #if VPX_IMAGE_ABI_VERSION >= 4 @@ -1107,14 +1519,69 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, #endif if (frame->pict_type == AV_PICTURE_TYPE_I) flags |= VPX_EFLAG_FORCE_KF; - if (CONFIG_LIBVPX_VP8_ENCODER && avctx->codec_id == AV_CODEC_ID_VP8 && frame->metadata) { + if (frame->metadata) { AVDictionaryEntry* en = av_dict_get(frame->metadata, "vp8-flags", NULL, 0); if (en) { flags |= strtoul(en->value, NULL, 10); } + + memset(&layer_id, 0, sizeof(layer_id)); + + en = av_dict_get(frame->metadata, "temporal_id", NULL, 0); + if (en) { + layer_id.temporal_layer_id = strtoul(en->value, NULL, 10); +#ifdef VPX_CTRL_VP9E_SET_MAX_INTER_BITRATE_PCT + layer_id.temporal_layer_id_per_spatial[0] = layer_id.temporal_layer_id; +#endif + layer_id_valid = 1; + } + } + + if (sd) { + if (avctx->codec_id == AV_CODEC_ID_VP8) { + vp8_encode_set_roi(avctx, frame->width, frame->height, sd); + } else { + vp9_encode_set_roi(avctx, frame->width, frame->height, sd); + } } } + // this is for encoding with preset temporal layering patterns defined in + // set_temporal_layer_pattern function. + if (enccfg->ts_number_layers > 1 && ctx->ts_layer_flags) { + if (flags & VPX_EFLAG_FORCE_KF) { + // keyframe, reset temporal layering. + ctx->current_temporal_idx = 0; + flags = VPX_EFLAG_FORCE_KF; + } else { + flags = 0; + } + + /* get the flags from the temporal layer configuration. */ + flags |= ctx->ts_layer_flags[ctx->current_temporal_idx]; + + memset(&layer_id, 0, sizeof(layer_id)); +#if VPX_ENCODER_ABI_VERSION >= 12 + layer_id.spatial_layer_id = 0; +#endif + layer_id.temporal_layer_id = enccfg->ts_layer_id[ctx->current_temporal_idx]; +#ifdef VPX_CTRL_VP9E_SET_MAX_INTER_BITRATE_PCT + layer_id.temporal_layer_id_per_spatial[0] = layer_id.temporal_layer_id; +#endif + layer_id_valid = 1; + } + + if (layer_id_valid) { + if (avctx->codec_id == AV_CODEC_ID_VP8) { + codecctl_int(avctx, VP8E_SET_TEMPORAL_LAYER_ID, layer_id.temporal_layer_id); + } +#if CONFIG_LIBVPX_VP9_ENCODER && VPX_ENCODER_ABI_VERSION >= 12 + else if (avctx->codec_id == AV_CODEC_ID_VP9) { + codecctl_intp(avctx, VP9E_SET_SVC_LAYER_ID, (int *)&layer_id); + } +#endif + } + res = vpx_codec_encode(&ctx->encoder, rawimg, timestamp, avctx->ticks_per_frame, flags, ctx->deadline); if (res != VPX_CODEC_OK) { @@ -1144,11 +1611,8 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, } av_base64_encode(avctx->stats_out, b64_size, ctx->twopass_stats.buf, ctx->twopass_stats.sz); - } - - if (rawimg_alpha) { - av_freep(&rawimg_alpha->planes[VPX_PLANE_U]); - av_freep(&rawimg_alpha->planes[VPX_PLANE_V]); + } else if (enccfg->ts_number_layers > 1 && ctx->ts_layer_flags) { + ctx->current_temporal_idx = (ctx->current_temporal_idx + 1) % enccfg->ts_periodicity; } *got_packet = !!coded_size; @@ -1179,7 +1643,7 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, { "default", "Improve resiliency against losses of whole frames", 0, AV_OPT_TYPE_CONST, {.i64 = VPX_ERROR_RESILIENT_DEFAULT}, 0, 0, VE, "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 predicition" \ + "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"}, \ { "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 }, \ @@ -1187,6 +1651,7 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, { "noise-sensitivity", "Noise sensitivity", OFFSET(noise_sensitivity), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 4, VE}, \ { "undershoot-pct", "Datarate undershoot (min) target (%)", OFFSET(rc_undershoot_pct), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 100, VE }, \ { "overshoot-pct", "Datarate overshoot (max) target (%)", OFFSET(rc_overshoot_pct), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1000, VE }, \ + { "ts-parameters", "Temporal scaling configuration using a :-separated list of key=value parameters", OFFSET(vpx_ts_parameters), AV_OPT_TYPE_DICT, {.str=NULL}, 0, 0, VE}, \ #define LEGACY_OPTIONS \ {"speed", "", offsetof(VPxContext, cpu_used), AV_OPT_TYPE_INT, {.i64 = 1}, -16, 16, VE}, \ @@ -1206,8 +1671,6 @@ 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}, - { "ts-parameters", "Temporal scaling configuration using a " - ":-separated list of key=value parameters", OFFSET(vp8_ts_parameters), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, VE}, LEGACY_OPTIONS { NULL } }; @@ -1266,6 +1729,7 @@ static const AVOption vp9_options[] = { #undef LEGACY_OPTIONS static const AVCodecDefault defaults[] = { + { "b", "0" }, { "qmin", "-1" }, { "qmax", "-1" }, { "g", "-1" }, diff --git a/libavcodec/libwebpenc_common.c b/libavcodec/libwebpenc_common.c index 21d7adaf56f..3c4c3e22943 100644 --- a/libavcodec/libwebpenc_common.c +++ b/libavcodec/libwebpenc_common.c @@ -142,7 +142,7 @@ int ff_libwebp_get_frame(AVCodecContext *avctx, LibWebPContextCommon *s, alt_frame->format = frame->format; if (s->cr_threshold) alt_frame->format = AV_PIX_FMT_YUVA420P; - ret = av_frame_get_buffer(alt_frame, 32); + ret = av_frame_get_buffer(alt_frame, 0); if (ret < 0) goto end; alt_frame->format = frame->format; diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c index dc4b4b100de..d4b1fd0d53e 100644 --- a/libavcodec/libx264.c +++ b/libavcodec/libx264.c @@ -25,9 +25,11 @@ #include "libavutil/mem.h" #include "libavutil/pixdesc.h" #include "libavutil/stereo3d.h" +#include "libavutil/time.h" #include "libavutil/intreadwrite.h" #include "avcodec.h" #include "internal.h" +#include "packet_internal.h" #if defined(_MSC_VER) #define X264_API_IMPORTS 1 @@ -44,6 +46,11 @@ // blocks of pixels (with respect to the luma plane) #define MB_SIZE 16 +typedef struct X264Opaque { + int64_t reordered_opaque; + int64_t wallclock; +} X264Opaque; + typedef struct X264Context { AVClass *class; x264_param_t params; @@ -95,10 +102,16 @@ typedef struct X264Context { int scenechange_threshold; int noise_reduction; - char *x264_params; + AVDictionary *x264_params; int nb_reordered_opaque, next_reordered_opaque; - int64_t *reordered_opaque; + X264Opaque *reordered_opaque; + + /** + * If the encoder does not support ROI then warn the first time we + * encounter a frame with ROI side data. + */ + int roi_warned; } X264Context; static void X264_log(void *p, int level, const char *fmt, va_list args) @@ -183,51 +196,51 @@ static void reconfig_encoder(AVCodecContext *ctx, const AVFrame *frame) AVFrameSideData *side_data; - if (x4->avcintra_class < 0) { - if (x4->params.b_interlaced && x4->params.b_tff != frame->top_field_first) { + if (x4->avcintra_class < 0) { + if (x4->params.b_interlaced && x4->params.b_tff != frame->top_field_first) { - x4->params.b_tff = frame->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) { - x4->params.vui.i_sar_height = ctx->sample_aspect_ratio.den; - x4->params.vui.i_sar_width = ctx->sample_aspect_ratio.num; - x264_encoder_reconfig(x4->enc, &x4->params); - } + x4->params.b_tff = frame->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) { + x4->params.vui.i_sar_height = ctx->sample_aspect_ratio.den; + x4->params.vui.i_sar_width = ctx->sample_aspect_ratio.num; + x264_encoder_reconfig(x4->enc, &x4->params); + } - if (x4->params.rc.i_vbv_buffer_size != ctx->rc_buffer_size / 1000 || - x4->params.rc.i_vbv_max_bitrate != ctx->rc_max_rate / 1000) { - x4->params.rc.i_vbv_buffer_size = ctx->rc_buffer_size / 1000; - x4->params.rc.i_vbv_max_bitrate = ctx->rc_max_rate / 1000; - x264_encoder_reconfig(x4->enc, &x4->params); - } + if (x4->params.rc.i_vbv_buffer_size != ctx->rc_buffer_size / 1000 || + x4->params.rc.i_vbv_max_bitrate != ctx->rc_max_rate / 1000) { + x4->params.rc.i_vbv_buffer_size = ctx->rc_buffer_size / 1000; + x4->params.rc.i_vbv_max_bitrate = ctx->rc_max_rate / 1000; + x264_encoder_reconfig(x4->enc, &x4->params); + } - if (x4->params.rc.i_rc_method == X264_RC_ABR && - x4->params.rc.i_bitrate != ctx->bit_rate / 1000) { - x4->params.rc.i_bitrate = ctx->bit_rate / 1000; - x264_encoder_reconfig(x4->enc, &x4->params); - } + if (x4->params.rc.i_rc_method == X264_RC_ABR && + x4->params.rc.i_bitrate != ctx->bit_rate / 1000) { + x4->params.rc.i_bitrate = ctx->bit_rate / 1000; + x264_encoder_reconfig(x4->enc, &x4->params); + } - if (x4->crf >= 0 && - x4->params.rc.i_rc_method == X264_RC_CRF && - x4->params.rc.f_rf_constant != x4->crf) { - x4->params.rc.f_rf_constant = x4->crf; - x264_encoder_reconfig(x4->enc, &x4->params); - } + if (x4->crf >= 0 && + x4->params.rc.i_rc_method == X264_RC_CRF && + x4->params.rc.f_rf_constant != x4->crf) { + x4->params.rc.f_rf_constant = x4->crf; + x264_encoder_reconfig(x4->enc, &x4->params); + } - if (x4->params.rc.i_rc_method == X264_RC_CQP && - x4->cqp >= 0 && - x4->params.rc.i_qp_constant != x4->cqp) { - x4->params.rc.i_qp_constant = x4->cqp; - x264_encoder_reconfig(x4->enc, &x4->params); - } + if (x4->params.rc.i_rc_method == X264_RC_CQP && + x4->cqp >= 0 && + x4->params.rc.i_qp_constant != x4->cqp) { + x4->params.rc.i_qp_constant = x4->cqp; + x264_encoder_reconfig(x4->enc, &x4->params); + } - if (x4->crf_max >= 0 && - x4->params.rc.f_rf_constant_max != x4->crf_max) { - x4->params.rc.f_rf_constant_max = x4->crf_max; - x264_encoder_reconfig(x4->enc, &x4->params); + if (x4->crf_max >= 0 && + x4->params.rc.f_rf_constant_max != x4->crf_max) { + x4->params.rc.f_rf_constant_max = x4->crf_max; + x264_encoder_reconfig(x4->enc, &x4->params); + } } - } side_data = av_frame_get_side_data(frame, AV_FRAME_DATA_STEREO3D); if (side_data) { @@ -286,7 +299,8 @@ static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame, x264_picture_t pic_out = {0}; int pict_type; int bit_depth; - int64_t *out_opaque; + int64_t wallclock = 0; + X264Opaque *out_opaque; AVFrameSideData *sd; x264_picture_init( &x4->pic ); @@ -308,7 +322,10 @@ static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame, x4->pic.i_pts = frame->pts; - x4->reordered_opaque[x4->next_reordered_opaque] = frame->reordered_opaque; + x4->reordered_opaque[x4->next_reordered_opaque].reordered_opaque = frame->reordered_opaque; + x4->reordered_opaque[x4->next_reordered_opaque].wallclock = wallclock; + if (ctx->export_side_data & AV_CODEC_EXPORT_DATA_PRFT) + x4->reordered_opaque[x4->next_reordered_opaque].wallclock = av_gettime(); x4->pic.opaque = &x4->reordered_opaque[x4->next_reordered_opaque]; x4->next_reordered_opaque++; x4->next_reordered_opaque %= x4->nb_reordered_opaque; @@ -356,7 +373,10 @@ static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame, sd = av_frame_get_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST); if (sd) { if (x4->params.rc.i_aq_mode == X264_AQ_NONE) { - av_log(ctx, AV_LOG_WARNING, "Adaptive quantization must be enabled to use ROI encoding, skipping ROI.\n"); + if (!x4->roi_warned) { + x4->roi_warned = 1; + av_log(ctx, AV_LOG_WARNING, "Adaptive quantization must be enabled to use ROI encoding, skipping ROI.\n"); + } } else { if (frame->interlaced_frame == 0) { int mbx = (frame->width + MB_SIZE - 1) / MB_SIZE; @@ -410,7 +430,10 @@ static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame, x4->pic.prop.quant_offsets = qoffsets; x4->pic.prop.quant_offsets_free = av_free; } else { - av_log(ctx, AV_LOG_WARNING, "interlaced_frame not supported for ROI encoding yet, skipping ROI.\n"); + 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"); + } } } } @@ -425,13 +448,17 @@ static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame, return ret; } while (!ret && !frame && x264_encoder_delayed_frames(x4->enc)); + if (!ret) + return 0; + pkt->pts = pic_out.i_pts; pkt->dts = pic_out.i_dts; out_opaque = pic_out.opaque; if (out_opaque >= x4->reordered_opaque && out_opaque < &x4->reordered_opaque[x4->nb_reordered_opaque]) { - ctx->reordered_opaque = *out_opaque; + ctx->reordered_opaque = out_opaque->reordered_opaque; + wallclock = out_opaque->wallclock; } else { // Unexpected opaque pointer on picture output ctx->reordered_opaque = 0; @@ -450,7 +477,8 @@ static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame, pict_type = AV_PICTURE_TYPE_B; break; default: - pict_type = AV_PICTURE_TYPE_NONE; + av_log(ctx, AV_LOG_ERROR, "Unknown picture type encountered.\n"); + return AVERROR_EXTERNAL; } #if FF_API_CODED_FRAME FF_DISABLE_DEPRECATION_WARNINGS @@ -461,6 +489,8 @@ 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); + if (wallclock) + ff_side_data_set_prft(pkt, wallclock); #if FF_API_CODED_FRAME FF_DISABLE_DEPRECATION_WARNINGS @@ -594,6 +624,10 @@ static av_cold int X264_init(AVCodecContext *avctx) PARSE_X264_OPT("weightp", wpredp); if (avctx->bit_rate) { + if (avctx->bit_rate / 1000 > INT_MAX || avctx->rc_max_rate / 1000 > INT_MAX) { + av_log(avctx, AV_LOG_ERROR, "bit_rate and rc_max_rate > %d000 not supported by libx264\n", INT_MAX); + return AVERROR(EINVAL); + } x4->params.rc.i_bitrate = avctx->bit_rate / 1000; x4->params.rc.i_rc_method = X264_RC_ABR; } @@ -663,25 +697,13 @@ FF_ENABLE_DEPRECATION_WARNINGS x4->params.rc.f_qcompress = avctx->qcompress; /* 0.0 => cbr, 1.0 => constant qp */ if (avctx->refs >= 0) x4->params.i_frame_reference = avctx->refs; - else if (x4->level) { + else if (x4->params.i_level_idc > 0) { int i; int mbn = AV_CEIL_RSHIFT(avctx->width, 4) * AV_CEIL_RSHIFT(avctx->height, 4); - int level_id = -1; - char *tail; int scale = X264_BUILD < 129 ? 384 : 1; - if (!strcmp(x4->level, "1b")) { - level_id = 9; - } else if (strlen(x4->level) <= 3){ - level_id = av_strtod(x4->level, &tail) * 10 + 0.5; - if (*tail) - level_id = -1; - } - if (level_id <= 0) - av_log(avctx, AV_LOG_WARNING, "Failed to parse level\n"); - for (i = 0; iparams.i_level_idc) x4->params.i_frame_reference = av_clip(x264_levels[i].dpb / mbn / scale, 1, x4->params.i_frame_reference); } @@ -876,19 +898,14 @@ FF_ENABLE_DEPRECATION_WARNINGS } } - if (x4->x264_params) { - AVDictionary *dict = NULL; - AVDictionaryEntry *en = NULL; - - if (!av_dict_parse_string(&dict, x4->x264_params, "=", ":", 0)) { - while ((en = av_dict_get(dict, "", en, AV_DICT_IGNORE_SUFFIX))) { - if (x264_param_parse(&x4->params, en->key, en->value) < 0) - av_log(avctx, AV_LOG_WARNING, - "Error parsing option '%s = %s'.\n", - en->key, en->value); - } - av_dict_free(&dict); + { + AVDictionaryEntry *en = NULL; + while (en = av_dict_get(x4->x264_params, "", en, AV_DICT_IGNORE_SUFFIX)) { + if (x264_param_parse(&x4->params, en->key, en->value) < 0) + av_log(avctx, AV_LOG_WARNING, + "Error parsing option '%s = %s'.\n", + en->key, en->value); } } @@ -898,7 +915,7 @@ FF_ENABLE_DEPRECATION_WARNINGS if (avctx->max_b_frames < 0) avctx->max_b_frames = 0; - avctx->bit_rate = x4->params.rc.i_bitrate*1000; + avctx->bit_rate = x4->params.rc.i_bitrate*1000LL; x4->enc = x264_encoder_open(&x4->params); if (!x4->enc) @@ -935,8 +952,8 @@ FF_ENABLE_DEPRECATION_WARNINGS if (!cpb_props) return AVERROR(ENOMEM); cpb_props->buffer_size = x4->params.rc.i_vbv_buffer_size * 1000; - cpb_props->max_bitrate = x4->params.rc.i_vbv_max_bitrate * 1000; - cpb_props->avg_bitrate = x4->params.rc.i_bitrate * 1000; + cpb_props->max_bitrate = x4->params.rc.i_vbv_max_bitrate * 1000LL; + cpb_props->avg_bitrate = x4->params.rc.i_bitrate * 1000LL; // Overestimate the reordered opaque buffer size, in case a runtime // reconfigure would increase the delay (which it shouldn't). @@ -1100,7 +1117,7 @@ static const AVOption options[] = { { "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 }, - { "x264-params", "Override the x264 configuration using a :-separated list of key=value parameters", OFFSET(x264_params), AV_OPT_TYPE_STRING, { 0 }, 0, 0, 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 }, { NULL }, }; @@ -1164,7 +1181,11 @@ AVCodec ff_libx264_encoder = { .priv_class = &x264_class, .defaults = x264_defaults, .init_static_data = X264_init_static, +#if X264_BUILD >= 158 + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_INIT_THREADSAFE, +#else .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +#endif .wrapper_name = "libx264", }; #endif @@ -1191,6 +1212,11 @@ AVCodec ff_libx264rgb_encoder = { .priv_class = &rgbclass, .defaults = x264_defaults, .pix_fmts = pix_fmts_8bit_rgb, +#if X264_BUILD >= 158 + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_INIT_THREADSAFE, +#else + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +#endif .wrapper_name = "libx264", }; #endif diff --git a/libavcodec/libx265.c b/libavcodec/libx265.c index 665b780643e..f560d7f62f8 100644 --- a/libavcodec/libx265.c +++ b/libavcodec/libx265.c @@ -33,6 +33,7 @@ #include "libavutil/pixdesc.h" #include "avcodec.h" #include "internal.h" +#include "packet_internal.h" typedef struct libx265Context { const AVClass *class; @@ -42,11 +43,18 @@ typedef struct libx265Context { const x265_api *api; float crf; + int cqp; int forced_idr; char *preset; char *tune; char *profile; - char *x265_opts; + AVDictionary *x265_opts; + + /** + * If the encoder does not support ROI then warn the first time we + * encounter a frame with ROI side data. + */ + int roi_warned; } libx265Context; static int is_keyframe(NalUnitType naltype) @@ -76,10 +84,41 @@ static av_cold int libx265_encode_close(AVCodecContext *avctx) return 0; } +static av_cold int libx265_param_parse_float(AVCodecContext *avctx, + const char *key, float value) +{ + libx265Context *ctx = avctx->priv_data; + char buf[256]; + + snprintf(buf, sizeof(buf), "%2.2f", value); + if (ctx->api->param_parse(ctx->params, key, buf) == X265_PARAM_BAD_VALUE) { + av_log(avctx, AV_LOG_ERROR, "Invalid value %2.2f for param \"%s\".\n", value, key); + return AVERROR(EINVAL); + } + + return 0; +} + +static av_cold int libx265_param_parse_int(AVCodecContext *avctx, + const char *key, int value) +{ + libx265Context *ctx = avctx->priv_data; + char buf[256]; + + snprintf(buf, sizeof(buf), "%d", value); + if (ctx->api->param_parse(ctx->params, key, buf) == X265_PARAM_BAD_VALUE) { + av_log(avctx, AV_LOG_ERROR, "Invalid value %d for param \"%s\".\n", value, key); + return AVERROR(EINVAL); + } + + return 0; +} + static av_cold int libx265_encode_init(AVCodecContext *avctx) { libx265Context *ctx = avctx->priv_data; AVCPBProperties *cpb_props = NULL; + int ret; ctx->api = x265_api_get(av_pix_fmt_desc_get(avctx->pix_fmt)->comp[0].depth); if (!ctx->api) @@ -153,6 +192,10 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) // x265 validates the parameters internally ctx->params->vui.colorPrimaries = avctx->color_primaries; ctx->params->vui.transferCharacteristics = avctx->color_trc; +#if X265_BUILD >= 159 + if (avctx->color_trc == AVCOL_TRC_ARIB_STD_B67) + ctx->params->preferredTransferCharacteristics = ctx->params->vui.transferCharacteristics; +#endif ctx->params->vui.matrixCoeffs = avctx->colorspace; } @@ -216,6 +259,48 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) } else if (avctx->bit_rate > 0) { ctx->params->rc.bitrate = avctx->bit_rate / 1000; ctx->params->rc.rateControlMode = X265_RC_ABR; + } else if (ctx->cqp >= 0) { + ret = libx265_param_parse_int(avctx, "qp", ctx->cqp); + if (ret < 0) + return ret; + } + +#if X265_BUILD >= 89 + if (avctx->qmin >= 0) { + ret = libx265_param_parse_int(avctx, "qpmin", avctx->qmin); + if (ret < 0) + return ret; + } + if (avctx->qmax >= 0) { + ret = libx265_param_parse_int(avctx, "qpmax", avctx->qmax); + if (ret < 0) + return ret; + } +#endif + if (avctx->max_qdiff >= 0) { + ret = libx265_param_parse_int(avctx, "qpstep", avctx->max_qdiff); + if (ret < 0) + return ret; + } + if (avctx->qblur >= 0) { + ret = libx265_param_parse_float(avctx, "qblur", avctx->qblur); + if (ret < 0) + return ret; + } + if (avctx->qcompress >= 0) { + ret = libx265_param_parse_float(avctx, "qcomp", avctx->qcompress); + if (ret < 0) + return ret; + } + if (avctx->i_quant_factor >= 0) { + ret = libx265_param_parse_float(avctx, "ipratio", avctx->i_quant_factor); + if (ret < 0) + return ret; + } + if (avctx->b_quant_factor >= 0) { + ret = libx265_param_parse_float(avctx, "pbratio", avctx->b_quant_factor); + if (ret < 0) + return ret; } ctx->params->rc.vbvBufferSize = avctx->rc_buffer_size / 1000; @@ -231,28 +316,44 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) if (!(avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)) ctx->params->bRepeatHeaders = 1; - if (ctx->x265_opts) { - AVDictionary *dict = NULL; - AVDictionaryEntry *en = NULL; + if (avctx->gop_size >= 0) { + ret = libx265_param_parse_int(avctx, "keyint", avctx->gop_size); + if (ret < 0) + return ret; + } + if (avctx->keyint_min > 0) { + ret = libx265_param_parse_int(avctx, "min-keyint", avctx->keyint_min); + if (ret < 0) + return ret; + } + if (avctx->max_b_frames >= 0) { + ret = libx265_param_parse_int(avctx, "bframes", avctx->max_b_frames); + if (ret < 0) + return ret; + } + if (avctx->refs >= 0) { + ret = libx265_param_parse_int(avctx, "ref", avctx->refs); + if (ret < 0) + return ret; + } - if (!av_dict_parse_string(&dict, ctx->x265_opts, "=", ":", 0)) { - while ((en = av_dict_get(dict, "", en, AV_DICT_IGNORE_SUFFIX))) { - int parse_ret = ctx->api->param_parse(ctx->params, en->key, en->value); - - switch (parse_ret) { - case X265_PARAM_BAD_NAME: - av_log(avctx, AV_LOG_WARNING, - "Unknown option: %s.\n", en->key); - break; - case X265_PARAM_BAD_VALUE: - av_log(avctx, AV_LOG_WARNING, - "Invalid value for %s: %s.\n", en->key, en->value); - break; - default: - break; - } + { + AVDictionaryEntry *en = NULL; + while ((en = av_dict_get(ctx->x265_opts, "", en, AV_DICT_IGNORE_SUFFIX))) { + int parse_ret = ctx->api->param_parse(ctx->params, en->key, en->value); + + switch (parse_ret) { + case X265_PARAM_BAD_NAME: + av_log(avctx, AV_LOG_WARNING, + "Unknown option: %s.\n", en->key); + break; + case X265_PARAM_BAD_VALUE: + av_log(avctx, AV_LOG_WARNING, + "Invalid value for %s: %s.\n", en->key, en->value); + break; + default: + break; } - av_dict_free(&dict); } } @@ -300,6 +401,7 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) } memcpy(avctx->extradata, nal[0].payload, avctx->extradata_size); + memset(avctx->extradata + avctx->extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); } return 0; @@ -310,7 +412,10 @@ static av_cold int libx265_encode_set_roi(libx265Context *ctx, const AVFrame *fr AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST); if (sd) { if (ctx->params->rc.aqMode == X265_AQ_NONE) { - av_log(ctx, AV_LOG_WARNING, "Adaptive quantization must be enabled to use ROI encoding, skipping ROI.\n"); + if (!ctx->roi_warned) { + ctx->roi_warned = 1; + av_log(ctx, AV_LOG_WARNING, "Adaptive quantization must be enabled to use ROI encoding, skipping ROI.\n"); + } } else { /* 8x8 block when qg-size is 8, 16*16 block otherwise. */ int mb_size = (ctx->params->rc.qgSize == 8) ? 8 : 16; @@ -374,6 +479,7 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, x265_picture x265pic_out = { 0 }; x265_nal *nal; uint8_t *dst; + int pict_type; int payload = 0; int nnal; int ret; @@ -399,6 +505,16 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, ret = libx265_encode_set_roi(ctx, pic, &x265pic); if (ret < 0) return ret; + + if (pic->reordered_opaque) { + x265pic.userData = av_malloc(sizeof(pic->reordered_opaque)); + if (!x265pic.userData) { + av_freep(&x265pic.quantOffsets); + return AVERROR(ENOMEM); + } + + memcpy(x265pic.userData, &pic->reordered_opaque, sizeof(pic->reordered_opaque)); + } } ret = ctx->api->encoder_encode(ctx->encoder, &nal, &nnal, @@ -433,20 +549,26 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, pkt->pts = x265pic_out.pts; pkt->dts = x265pic_out.dts; -#if FF_API_CODED_FRAME -FF_DISABLE_DEPRECATION_WARNINGS switch (x265pic_out.sliceType) { case X265_TYPE_IDR: case X265_TYPE_I: - avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I; + pict_type = AV_PICTURE_TYPE_I; break; case X265_TYPE_P: - avctx->coded_frame->pict_type = AV_PICTURE_TYPE_P; + pict_type = AV_PICTURE_TYPE_P; break; case X265_TYPE_B: - avctx->coded_frame->pict_type = AV_PICTURE_TYPE_B; + case X265_TYPE_BREF: + pict_type = AV_PICTURE_TYPE_B; break; + default: + av_log(avctx, AV_LOG_ERROR, "Unknown picture type encountered.\n"); + return AVERROR_EXTERNAL; } + +#if FF_API_CODED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + avctx->coded_frame->pict_type = pict_type; FF_ENABLE_DEPRECATION_WARNINGS #endif @@ -457,6 +579,14 @@ FF_ENABLE_DEPRECATION_WARNINGS #endif pkt->flags |= AV_PKT_FLAG_DISPOSABLE; + ff_side_data_set_encoder_stats(pkt, x265pic_out.frameData.qp * FF_QP2LAMBDA, NULL, 0, pict_type); + + if (x265pic_out.userData) { + memcpy(&avctx->reordered_opaque, x265pic_out.userData, sizeof(avctx->reordered_opaque)); + av_freep(&x265pic_out.userData); + } else + avctx->reordered_opaque = 0; + *got_packet = 1; return 0; } @@ -526,11 +656,12 @@ static av_cold void libx265_encode_init_csp(AVCodec *codec) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { { "crf", "set the x265 crf", OFFSET(crf), AV_OPT_TYPE_FLOAT, { .dbl = -1 }, -1, FLT_MAX, VE }, + { "qp", "set the x265 qp", OFFSET(cqp), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE }, { "forced-idr", "if forcing keyframes, force them as IDR frames", OFFSET(forced_idr),AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "preset", "set the x265 preset", OFFSET(preset), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE }, { "tune", "set the x265 tune parameter", OFFSET(tune), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE }, { "profile", "set the x265 profile", OFFSET(profile), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE }, - { "x265-params", "set the x265 configuration using a :-separated list of key=value parameters", OFFSET(x265_opts), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE }, + { "x265-params", "set the x265 configuration using a :-separated list of key=value parameters", OFFSET(x265_opts), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE }, { NULL } }; @@ -543,6 +674,17 @@ static const AVClass class = { static const AVCodecDefault x265_defaults[] = { { "b", "0" }, + { "bf", "-1" }, + { "g", "-1" }, + { "keyint_min", "-1" }, + { "refs", "-1" }, + { "qmin", "-1" }, + { "qmax", "-1" }, + { "qdiff", "-1" }, + { "qblur", "-1" }, + { "qcomp", "-1" }, + { "i_qfactor", "-1" }, + { "b_qfactor", "-1" }, { NULL }, }; @@ -558,6 +700,7 @@ AVCodec ff_libx265_encoder = { .priv_data_size = sizeof(libx265Context), .priv_class = &class, .defaults = x265_defaults, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .wrapper_name = "libx265", }; diff --git a/libavcodec/libxavs.c b/libavcodec/libxavs.c index 801a05dbb58..752ff66dfa3 100644 --- a/libavcodec/libxavs.c +++ b/libavcodec/libxavs.c @@ -28,6 +28,7 @@ #include #include "avcodec.h" #include "internal.h" +#include "packet_internal.h" #include "libavutil/internal.h" #include "libavutil/mem.h" #include "libavutil/opt.h" diff --git a/libavcodec/libxavs2.c b/libavcodec/libxavs2.c index d5c455797d9..76b57e731e8 100644 --- a/libavcodec/libxavs2.c +++ b/libavcodec/libxavs2.c @@ -31,7 +31,7 @@ int err; \ av_strlcatf(opt_str, sizeof(opt_str), format, __VA_ARGS__); \ err = cae->api->opt_set2(cae->param, name, opt_str); \ - if (err) {\ + if (err < 0) {\ av_log(avctx, AV_LOG_WARNING, "Invalid value for %s: %s\n", name, opt_str);\ }\ } while(0); @@ -48,7 +48,7 @@ typedef struct XAVS2EContext { int log_level; void *encoder; - char *xavs2_opts; + AVDictionary *xavs2_opts; xavs2_outpacket_t packet; xavs2_param_t *param; @@ -59,7 +59,7 @@ typedef struct XAVS2EContext { static av_cold int xavs2_init(AVCodecContext *avctx) { - XAVS2EContext *cae= avctx->priv_data; + XAVS2EContext *cae = avctx->priv_data; int bit_depth, code; bit_depth = avctx->pix_fmt == AV_PIX_FMT_YUV420P ? 8 : 10; @@ -67,13 +67,13 @@ static av_cold int xavs2_init(AVCodecContext *avctx) /* get API handler */ cae->api = xavs2_api_get(bit_depth); if (!cae->api) { - av_log(avctx, AV_LOG_ERROR, "api get failed\n"); + av_log(avctx, AV_LOG_ERROR, "Failed to get xavs2 api context\n"); return AVERROR_EXTERNAL; } cae->param = cae->api->opt_alloc(); if (!cae->param) { - av_log(avctx, AV_LOG_ERROR, "param alloc failed\n"); + av_log(avctx, AV_LOG_ERROR, "Failed to alloc xavs2 parameters\n"); return AVERROR(ENOMEM); } @@ -92,16 +92,10 @@ static av_cold int xavs2_init(AVCodecContext *avctx) xavs2_opt_set2("OpenGOP", "%d", !(avctx->flags & AV_CODEC_FLAG_CLOSED_GOP)); - if (cae->xavs2_opts) { - AVDictionary *dict = NULL; + { AVDictionaryEntry *en = NULL; - - if (!av_dict_parse_string(&dict, cae->xavs2_opts, "=", ":", 0)) { - while ((en = av_dict_get(dict, "", en, AV_DICT_IGNORE_SUFFIX))) { - xavs2_opt_set2(en->key, "%s", en->value); - } - av_dict_free(&dict); - } + while ((en = av_dict_get(cae->xavs2_opts, "", en, AV_DICT_IGNORE_SUFFIX))) + xavs2_opt_set2(en->key, "%s", en->value); } /* Rate control */ @@ -115,15 +109,13 @@ static av_cold int xavs2_init(AVCodecContext *avctx) xavs2_opt_set2("InitialQP", "%d", cae->qp); } - ff_mpeg12_find_best_frame_rate(avctx->framerate, &code, NULL, NULL, 0); - xavs2_opt_set2("FrameRate", "%d", code); cae->encoder = cae->api->encoder_create(cae->param); if (!cae->encoder) { - av_log(avctx,AV_LOG_ERROR, "Can not create encoder. Null pointer returned\n"); + av_log(avctx, AV_LOG_ERROR, "Failed to create xavs2 encoder instance.\n"); return AVERROR(EINVAL); } @@ -132,29 +124,42 @@ static av_cold int xavs2_init(AVCodecContext *avctx) static void xavs2_copy_frame_with_shift(xavs2_picture_t *pic, const AVFrame *frame, const int shift_in) { - int j, k; - for (k = 0; k < 3; k++) { - int i_stride = pic->img.i_stride[k]; - for (j = 0; j < pic->img.i_lines[k]; j++) { - uint16_t *p_plane = (uint16_t *)&pic->img.img_planes[k][j * i_stride]; - int i; - uint8_t *p_buffer = frame->data[k] + frame->linesize[k] * j; - memset(p_plane, 0, i_stride); - for (i = 0; i < pic->img.i_width[k]; i++) { - p_plane[i] = p_buffer[i] << shift_in; + uint16_t *p_plane; + uint8_t *p_buffer; + int plane; + int hIdx; + int wIdx; + + for (plane = 0; plane < 3; plane++) { + p_plane = (uint16_t *)pic->img.img_planes[plane]; + p_buffer = frame->data[plane]; + for (hIdx = 0; hIdx < pic->img.i_lines[plane]; hIdx++) { + memset(p_plane, 0, pic->img.i_stride[plane]); + for (wIdx = 0; wIdx < pic->img.i_width[plane]; wIdx++) { + p_plane[wIdx] = p_buffer[wIdx] << shift_in; } + p_plane += pic->img.i_stride[plane]; + p_buffer += frame->linesize[plane]; } } } static void xavs2_copy_frame(xavs2_picture_t *pic, const AVFrame *frame) { - int j, k; - for (k = 0; k < 3; k++) { - for (j = 0; j < pic->img.i_lines[k]; j++) { - memcpy( pic->img.img_planes[k] + pic->img.i_stride[k] * j, - frame->data[k]+frame->linesize[k] * j, - pic->img.i_width[k] * pic->img.in_sample_size); + uint8_t *p_plane; + uint8_t *p_buffer; + int plane; + int hIdx; + int stride; + + for (plane = 0; plane < 3; plane++) { + p_plane = pic->img.img_planes[plane]; + p_buffer = frame->data[plane]; + stride = pic->img.i_width[plane] * pic->img.in_sample_size; + for (hIdx = 0; hIdx < pic->img.i_lines[plane]; hIdx++) { + memcpy(p_plane, p_buffer, stride); + p_plane += pic->img.i_stride[plane]; + p_buffer += frame->linesize[plane]; } } } @@ -169,7 +174,7 @@ static int xavs2_encode_frame(AVCodecContext *avctx, AVPacket *pkt, /* create the XAVS2 video encoder */ /* read frame data and send to the XAVS2 video encoder */ if (cae->api->encoder_get_buffer(cae->encoder, &pic) < 0) { - av_log(avctx,AV_LOG_ERROR, "failed to get frame buffer\n"); + av_log(avctx, AV_LOG_ERROR, "Failed to get xavs2 frame buffer\n"); return AVERROR_EXTERNAL; } if (frame) { @@ -200,7 +205,7 @@ static int xavs2_encode_frame(AVCodecContext *avctx, AVPacket *pkt, ret = cae->api->encoder_encode(cae->encoder, &pic, &cae->packet); if (ret) { - av_log(avctx, AV_LOG_ERROR, "encode failed\n"); + av_log(avctx, AV_LOG_ERROR, "Encoding error occured.\n"); return AVERROR_EXTERNAL; } @@ -208,10 +213,9 @@ static int xavs2_encode_frame(AVCodecContext *avctx, AVPacket *pkt, cae->api->encoder_encode(cae->encoder, NULL, &cae->packet); } - if ((cae->packet.len) && (cae->packet.state != XAVS2_STATE_FLUSH_END)){ - - if (av_new_packet(pkt, cae->packet.len) < 0){ - av_log(avctx, AV_LOG_ERROR, "packet alloc failed\n"); + if ((cae->packet.len) && (cae->packet.state != XAVS2_STATE_FLUSH_END)) { + if (av_new_packet(pkt, cae->packet.len) < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to alloc xavs2 packet.\n"); cae->api->encoder_packet_unref(cae->encoder, &cae->packet); return AVERROR(ENOMEM); } @@ -257,7 +261,7 @@ static const AVOption options[] = { { "min_qp" , "min qp for rate control" , OFFSET(min_qp) , AV_OPT_TYPE_INT, {.i64 = 20 }, 0, 63, VE }, { "speed_level" , "Speed level, higher is better but slower", OFFSET(preset_level) , AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 9, VE }, { "log_level" , "log level: -1: none, 0: error, 1: warning, 2: info, 3: debug", OFFSET(log_level) , AV_OPT_TYPE_INT, {.i64 = 0 }, -1, 3, VE }, - { "xavs2-params" , "set the xavs2 configuration using a :-separated list of key=value parameters", OFFSET(xavs2_opts), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE }, + { "xavs2-params" , "set the xavs2 configuration using a :-separated list of key=value parameters", OFFSET(xavs2_opts), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE }, { NULL }, }; diff --git a/libavcodec/libxvid.c b/libavcodec/libxvid.c index cdaae2094e5..857077dc3b0 100644 --- a/libavcodec/libxvid.c +++ b/libavcodec/libxvid.c @@ -42,6 +42,7 @@ #include "internal.h" #include "libxvid.h" #include "mpegutils.h" +#include "packet_internal.h" #if HAVE_UNISTD_H #include diff --git a/libavcodec/libxvid_rc.c b/libavcodec/libxvid_rc.c deleted file mode 100644 index 076c32c4135..00000000000 --- a/libavcodec/libxvid_rc.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Xvid rate control wrapper for lavc video encoders - * - * 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 "config.h" - -#if HAVE_IO_H -#include -#endif - -#if HAVE_UNISTD_H -#include -#endif - -#include - -#include "libavutil/attributes.h" -#include "libavutil/internal.h" - -#include "avcodec.h" -#include "libxvid.h" -#include "mpegvideo.h" - -av_cold int ff_xvid_rate_control_init(MpegEncContext *s) -{ - char *tmp_name; - int fd, i; - xvid_plg_create_t xvid_plg_create = { 0 }; - xvid_plugin_2pass2_t xvid_2pass2 = { 0 }; - - fd = avpriv_tempfile("xvidrc.", &tmp_name, 0, s->avctx); - if (fd < 0) { - av_log(s, AV_LOG_ERROR, "Can't create temporary pass2 file.\n"); - return fd; - } - - for (i = 0; i < s->rc_context.num_entries; i++) { - static const char frame_types[] = " ipbs"; - char tmp[256]; - RateControlEntry *rce; - - rce = &s->rc_context.entry[i]; - - snprintf(tmp, sizeof(tmp), "%c %d %d %d %d %d %d\n", - frame_types[rce->pict_type], - (int) lrintf(rce->qscale / FF_QP2LAMBDA), - rce->i_count, s->mb_num - rce->i_count - rce->skip_count, - rce->skip_count, - (rce->i_tex_bits + rce->p_tex_bits + rce->misc_bits + 7) / 8, - (rce->header_bits + rce->mv_bits + 7) / 8); - - if (write(fd, tmp, strlen(tmp)) < 0) { - int ret = AVERROR(errno); - av_log(s, AV_LOG_ERROR, "Error %s writing 2pass logfile\n", av_err2str(ret)); - av_free(tmp_name); - close(fd); - return ret; - } - } - - close(fd); - - xvid_2pass2.version = XVID_MAKE_VERSION(1, 1, 0); - xvid_2pass2.filename = tmp_name; - xvid_2pass2.bitrate = s->avctx->bit_rate; - xvid_2pass2.vbv_size = s->avctx->rc_buffer_size; - xvid_2pass2.vbv_maxrate = s->avctx->rc_max_rate; - xvid_2pass2.vbv_initial = s->avctx->rc_initial_buffer_occupancy; - - xvid_plg_create.version = XVID_MAKE_VERSION(1, 1, 0); - xvid_plg_create.fbase = s->avctx->time_base.den; - xvid_plg_create.fincr = s->avctx->time_base.num; - xvid_plg_create.param = &xvid_2pass2; - - if (xvid_plugin_2pass2(NULL, XVID_PLG_CREATE, &xvid_plg_create, - &s->rc_context.non_lavc_opaque) < 0) { - av_log(s, AV_LOG_ERROR, "xvid_plugin_2pass2 failed\n"); - return -1; - } - return 0; -} - -float ff_xvid_rate_estimate_qscale(MpegEncContext *s, int dry_run) -{ - xvid_plg_data_t xvid_plg_data = { 0 }; - - xvid_plg_data.version = XVID_MAKE_VERSION(1, 1, 0); - xvid_plg_data.width = s->width; - xvid_plg_data.height = s->height; - xvid_plg_data.mb_width = s->mb_width; - xvid_plg_data.mb_height = s->mb_height; - xvid_plg_data.fbase = s->avctx->time_base.den; - xvid_plg_data.fincr = s->avctx->time_base.num; - xvid_plg_data.min_quant[0] = s->avctx->qmin; - xvid_plg_data.min_quant[1] = s->avctx->qmin; - xvid_plg_data.min_quant[2] = s->avctx->qmin; // FIXME i/b factor & offset - xvid_plg_data.max_quant[0] = s->avctx->qmax; - xvid_plg_data.max_quant[1] = s->avctx->qmax; - xvid_plg_data.max_quant[2] = s->avctx->qmax; // FIXME i/b factor & offset - xvid_plg_data.bquant_offset = 0; // 100 * s->avctx->b_quant_offset; - xvid_plg_data.bquant_ratio = 100; // * s->avctx->b_quant_factor; - - if (!s->rc_context.dry_run_qscale) { - if (s->picture_number) { - xvid_plg_data.length = - xvid_plg_data.stats.length = (s->frame_bits + 7) / 8; - xvid_plg_data.frame_num = s->rc_context.last_picture_number; - xvid_plg_data.quant = s->qscale; - xvid_plg_data.type = s->last_pict_type; - if (xvid_plugin_2pass2(s->rc_context.non_lavc_opaque, - XVID_PLG_AFTER, &xvid_plg_data, NULL)) { - av_log(s, AV_LOG_ERROR, - "xvid_plugin_2pass2(handle, XVID_PLG_AFTER, ...) FAILED\n"); - return -1; - } - } - s->rc_context.last_picture_number = - xvid_plg_data.frame_num = s->picture_number; - xvid_plg_data.quant = 0; - if (xvid_plugin_2pass2(s->rc_context.non_lavc_opaque, - XVID_PLG_BEFORE, &xvid_plg_data, NULL)) { - av_log(s, AV_LOG_ERROR, - "xvid_plugin_2pass2(handle, XVID_PLG_BEFORE, ...) FAILED\n"); - return -1; - } - s->rc_context.dry_run_qscale = xvid_plg_data.quant; - } - xvid_plg_data.quant = s->rc_context.dry_run_qscale; - if (!dry_run) - s->rc_context.dry_run_qscale = 0; - - // FIXME this is not exactly identical to Xvid - if (s->pict_type == AV_PICTURE_TYPE_B) - return xvid_plg_data.quant * FF_QP2LAMBDA * s->avctx->b_quant_factor + - s->avctx->b_quant_offset; - else - return xvid_plg_data.quant * FF_QP2LAMBDA; -} - -av_cold void ff_xvid_rate_control_uninit(MpegEncContext *s) -{ - xvid_plg_destroy_t xvid_plg_destroy; - - xvid_plugin_2pass2(s->rc_context.non_lavc_opaque, XVID_PLG_DESTROY, - &xvid_plg_destroy, NULL); -} diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c index 3515f339248..0cc389a28e0 100644 --- a/libavcodec/libzvbi-teletextdec.c +++ b/libavcodec/libzvbi-teletextdec.c @@ -55,6 +55,7 @@ typedef struct TeletextContext { AVClass *class; char *pgno; + int default_region; int x_offset; int y_offset; int format_id; /* 0 = bitmap, 1 = text/ass, 2 = ass */ @@ -635,7 +636,7 @@ static int slice_to_vbi_lines(TeletextContext *ctx, uint8_t* buf, int size) return lines; } -static int teletext_decode_frame(AVCodecContext *avctx, void *data, int *data_size, AVPacket *pkt) +static int teletext_decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr, AVPacket *pkt) { TeletextContext *ctx = avctx->priv_data; AVSubtitle *sub = data; @@ -645,6 +646,10 @@ static int teletext_decode_frame(AVCodecContext *avctx, void *data, int *data_si if (!ctx->vbi) { if (!(ctx->vbi = vbi_decoder_new())) return AVERROR(ENOMEM); + if (ctx->default_region != -1) { + av_log(avctx, AV_LOG_INFO, "Setting default zvbi region to %i\n", ctx->default_region); + vbi_teletext_set_default_region(ctx->vbi, ctx->default_region); + } if (!vbi_event_handler_register(ctx->vbi, VBI_EVENT_TTX_PAGE, handler, ctx)) { vbi_decoder_delete(ctx->vbi); ctx->vbi = NULL; @@ -719,9 +724,9 @@ FF_ENABLE_DEPRECATION_WARNINGS ctx->nb_pages--; if (ret >= 0) - *data_size = 1; + *got_sub_ptr = 1; } else - *data_size = 0; + *got_sub_ptr = 0; return ret; } @@ -792,6 +797,7 @@ static void teletext_flush(AVCodecContext *avctx) #define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM 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"}, diff --git a/libavcodec/ljpegenc.c b/libavcodec/ljpegenc.c index 924d2e2fff6..70eff9e6fec 100644 --- a/libavcodec/ljpegenc.c +++ b/libavcodec/ljpegenc.c @@ -360,7 +360,7 @@ AVCodec ff_ljpeg_encoder = { .init = ljpeg_encode_init, .encode2 = ljpeg_encode_frame, .close = ljpeg_encode_close, - .capabilities = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_FRAME_THREADS, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_BGR24 , AV_PIX_FMT_BGRA , AV_PIX_FMT_BGR0, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ422P, diff --git a/libavcodec/loco.c b/libavcodec/loco.c index d8bf68a1009..25dd1575bac 100644 --- a/libavcodec/loco.c +++ b/libavcodec/loco.c @@ -82,7 +82,7 @@ static inline void loco_update_rice_param(RICEContext *r, int val) static inline int loco_get_rice(RICEContext *r) { - int v; + unsigned v; if (r->run > 0) { /* we have zero run */ r->run--; loco_update_rice_param(r, 0); @@ -131,7 +131,7 @@ static int loco_decode_plane(LOCOContext *l, uint8_t *data, int width, int heigh int stride, const uint8_t *buf, int buf_size) { RICEContext rc; - int val; + unsigned val; int ret; int i, j; @@ -155,6 +155,8 @@ static int loco_decode_plane(LOCOContext *l, uint8_t *data, int width, int heigh /* restore top line */ for (i = 1; i < width; i++) { val = loco_get_rice(&rc); + if (val == INT_MIN) + return AVERROR_INVALIDDATA; data[i] = data[i - 1] + val; } data += stride; diff --git a/libavcodec/lossless_audiodsp.c b/libavcodec/lossless_audiodsp.c index 3a9f9b20bb2..378165924db 100644 --- a/libavcodec/lossless_audiodsp.c +++ b/libavcodec/lossless_audiodsp.c @@ -27,7 +27,7 @@ static int32_t scalarproduct_and_madd_int16_c(int16_t *v1, const int16_t *v2, const int16_t *v3, int order, int mul) { - int res = 0; + unsigned res = 0; do { res += *v1 * *v2++; diff --git a/libavcodec/lsp.c b/libavcodec/lsp.c index 9aba020ebbb..fb4da47894e 100644 --- a/libavcodec/lsp.c +++ b/libavcodec/lsp.c @@ -108,7 +108,7 @@ static void lsp2poly(int* f, const int16_t* lsp, int lp_half_order) int i, j; f[0] = 0x400000; // 1.0 in (3.22) - f[1] = -lsp[0] << 8; // *2 and (0.15) -> (3.22) + f[1] = -lsp[0] * 256; // *2 and (0.15) -> (3.22) for(i=2; i<=lp_half_order; i++) { @@ -116,7 +116,7 @@ static void lsp2poly(int* f, const int16_t* lsp, int lp_half_order) for(j=i; j>1; j--) f[j] -= MULL(f[j-1], lsp[2*i-2], FRAC_BITS) - f[j-2]; - f[1] -= lsp[2*i-2] << 8; + f[1] -= lsp[2*i-2] * 256; } } diff --git a/libavcodec/lzf.c b/libavcodec/lzf.c index 5b7526ef185..1e3c86c88c3 100644 --- a/libavcodec/lzf.c +++ b/libavcodec/lzf.c @@ -49,7 +49,7 @@ int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, int64_t *size) if (s < LZF_LITERAL_MAX) { s++; if (s > *size - len) { - *size += *size /2; + *size += s + *size /2; ret = av_reallocp(buf, *size); if (ret < 0) return ret; @@ -72,7 +72,7 @@ int ff_lzf_uncompress(GetByteContext *gb, uint8_t **buf, int64_t *size) return AVERROR_INVALIDDATA; if (l > *size - len) { - *size += *size / 2; + *size += l + *size / 2; ret = av_reallocp(buf, *size); if (ret < 0) return ret; diff --git a/libavcodec/magicyuv.c b/libavcodec/magicyuv.c index f4fb2a78097..5d76274d54b 100644 --- a/libavcodec/magicyuv.c +++ b/libavcodec/magicyuv.c @@ -547,10 +547,7 @@ static int magy_decode_frame(AVCodecContext *avctx, void *data, s->hshift[2] = s->vshift[2] = 0; s->decorrelate = 0; - s->max = 256; s->bps = 8; - s->huff_build = huff_build; - s->magy_decode_slice = magy_decode_slice; format = bytestream2_get_byte(&gbyte); switch (format) { @@ -587,61 +584,46 @@ static int magy_decode_frame(AVCodecContext *avctx, void *data, avctx->pix_fmt = AV_PIX_FMT_YUV422P10; s->hshift[1] = s->hshift[2] = 1; - s->max = 1024; - s->huff_build = huff_build10; - s->magy_decode_slice = magy_decode_slice10; s->bps = 10; break; case 0x76: avctx->pix_fmt = AV_PIX_FMT_YUV444P10; - s->max = 1024; - s->huff_build = huff_build10; - s->magy_decode_slice = magy_decode_slice10; s->bps = 10; break; case 0x6d: avctx->pix_fmt = AV_PIX_FMT_GBRP10; s->decorrelate = 1; - s->max = 1024; - s->huff_build = huff_build10; - s->magy_decode_slice = magy_decode_slice10; s->bps = 10; break; case 0x6e: avctx->pix_fmt = AV_PIX_FMT_GBRAP10; s->decorrelate = 1; - s->max = 1024; - s->huff_build = huff_build10; - s->magy_decode_slice = magy_decode_slice10; s->bps = 10; break; case 0x6f: avctx->pix_fmt = AV_PIX_FMT_GBRP12; s->decorrelate = 1; - s->max = 4096; - s->huff_build = huff_build12; - s->magy_decode_slice = magy_decode_slice10; s->bps = 12; break; case 0x70: avctx->pix_fmt = AV_PIX_FMT_GBRAP12; s->decorrelate = 1; - s->max = 4096; - s->huff_build = huff_build12; - s->magy_decode_slice = magy_decode_slice10; s->bps = 12; break; case 0x73: avctx->pix_fmt = AV_PIX_FMT_GRAY10; - s->max = 1024; - s->huff_build = huff_build10; - s->magy_decode_slice = magy_decode_slice10; s->bps = 10; break; default: avpriv_request_sample(avctx, "Format 0x%X", format); return AVERROR_PATCHWELCOME; } + s->max = 1 << s->bps; + s->magy_decode_slice = s->bps == 8 ? magy_decode_slice : magy_decode_slice10; + if ( s->bps == 8) + s->huff_build = huff_build; + else + s->huff_build = s->bps == 10 ? huff_build10 : huff_build12; s->planes = av_pix_fmt_count_planes(avctx->pix_fmt); bytestream2_skip(&gbyte, 1); @@ -677,6 +659,17 @@ static int magy_decode_frame(AVCodecContext *avctx, void *data, return AVERROR_INVALIDDATA; } + if (s->interlaced) { + if ((s->slice_height >> s->vshift[1]) < 2) { + av_log(avctx, AV_LOG_ERROR, "impossible slice height\n"); + return AVERROR_INVALIDDATA; + } + if ((avctx->coded_height % s->slice_height) && ((avctx->coded_height % s->slice_height) >> s->vshift[1]) < 2) { + av_log(avctx, AV_LOG_ERROR, "impossible height\n"); + return AVERROR_INVALIDDATA; + } + } + for (i = 0; i < s->planes; i++) { av_fast_malloc(&s->slices[i], &s->slices_size[i], s->nb_slices * sizeof(Slice)); if (!s->slices[i]) @@ -756,21 +749,6 @@ static int magy_decode_frame(AVCodecContext *avctx, void *data, return avpkt->size; } -#if HAVE_THREADS -static int magy_init_thread_copy(AVCodecContext *avctx) -{ - MagicYUVContext *s = avctx->priv_data; - int i; - - for (i = 0; i < FF_ARRAY_ELEMS(s->slices); i++) { - s->slices[i] = NULL; - s->slices_size[i] = 0; - } - - return 0; -} -#endif - static av_cold int magy_decode_init(AVCodecContext *avctx) { MagicYUVContext *s = avctx->priv_data; @@ -799,7 +777,6 @@ AVCodec ff_magicyuv_decoder = { .id = AV_CODEC_ID_MAGICYUV, .priv_data_size = sizeof(MagicYUVContext), .init = magy_decode_init, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(magy_init_thread_copy), .close = magy_decode_end, .decode = magy_decode_frame, .capabilities = AV_CODEC_CAP_DR1 | diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index 16e9a1c28d4..e9fe3bf519d 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -581,7 +581,7 @@ AVCodec ff_magicyuv_encoder = { .init = magy_encode_init, .close = magy_encode_close, .encode2 = magy_encode_frame, - .capabilities = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_FRAME_THREADS, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P, AV_PIX_FMT_GRAY8, diff --git a/libavcodec/mdec.c b/libavcodec/mdec.c index 330b7612790..7e34ec568e7 100644 --- a/libavcodec/mdec.c +++ b/libavcodec/mdec.c @@ -240,17 +240,6 @@ static av_cold int decode_init(AVCodecContext *avctx) return 0; } -#if HAVE_THREADS -static av_cold int decode_init_thread_copy(AVCodecContext *avctx) -{ - MDECContext * const a = avctx->priv_data; - - a->avctx = avctx; - - return 0; -} -#endif - static av_cold int decode_end(AVCodecContext *avctx) { MDECContext * const a = avctx->priv_data; @@ -271,5 +260,4 @@ AVCodec ff_mdec_decoder = { .close = decode_end, .decode = decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy) }; diff --git a/libavcodec/mediacodec_surface.c b/libavcodec/mediacodec_surface.c index aada1ecebe0..09a42295d2f 100644 --- a/libavcodec/mediacodec_surface.c +++ b/libavcodec/mediacodec_surface.c @@ -25,23 +25,19 @@ #include "ffjni.h" #include "mediacodec_surface.h" -void *ff_mediacodec_surface_ref(void *surface, void *log_ctx) +FFANativeWindow *ff_mediacodec_surface_ref(void *surface, void *log_ctx) { JNIEnv *env = NULL; - void *reference = NULL; - env = ff_jni_get_env(log_ctx); if (!env) { return NULL; } - reference = (*env)->NewGlobalRef(env, surface); - - return reference; + return (*env)->NewGlobalRef(env, surface); } -int ff_mediacodec_surface_unref(void *surface, void *log_ctx) +int ff_mediacodec_surface_unref(FFANativeWindow *window, void *log_ctx) { JNIEnv *env = NULL; @@ -50,7 +46,7 @@ int ff_mediacodec_surface_unref(void *surface, void *log_ctx) return AVERROR_EXTERNAL; } - (*env)->DeleteGlobalRef(env, surface); + (*env)->DeleteGlobalRef(env, window); return 0; } diff --git a/libavcodec/mediacodec_surface.h b/libavcodec/mediacodec_surface.h index 0178b8ae717..933dc2bf51a 100644 --- a/libavcodec/mediacodec_surface.h +++ b/libavcodec/mediacodec_surface.h @@ -25,7 +25,10 @@ #include "libavcodec/avcodec.h" -void *ff_mediacodec_surface_ref(void *surface, void *log_ctx); -int ff_mediacodec_surface_unref(void *surface, void *log_ctx); +struct FFANativeWindow; +typedef struct FFANativeWindow FFANativeWindow; + +FFANativeWindow *ff_mediacodec_surface_ref(void *surface, void *log_ctx); +int ff_mediacodec_surface_unref(FFANativeWindow *window, void *log_ctx); #endif /* AVCODEC_MEDIACODEC_SURFACE_H */ diff --git a/libavcodec/mediacodec_wrapper.c b/libavcodec/mediacodec_wrapper.c index 5213cf640a7..79abc8b6aa9 100644 --- a/libavcodec/mediacodec_wrapper.c +++ b/libavcodec/mediacodec_wrapper.c @@ -1303,6 +1303,12 @@ int ff_AMediaCodec_delete(FFAMediaCodec* codec) ret = AVERROR_EXTERNAL; } + (*env)->DeleteGlobalRef(env, codec->input_buffers); + codec->input_buffers = NULL; + + (*env)->DeleteGlobalRef(env, codec->output_buffers); + codec->output_buffers = NULL; + (*env)->DeleteGlobalRef(env, codec->object); codec->object = NULL; diff --git a/libavcodec/mediacodecdec.c b/libavcodec/mediacodecdec.c index e353e34bd54..25410021e83 100644 --- a/libavcodec/mediacodecdec.c +++ b/libavcodec/mediacodecdec.c @@ -34,7 +34,7 @@ #include "decode.h" #include "h264_parse.h" #include "hevc_parse.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "internal.h" #include "mediacodec_wrapper.h" #include "mediacodecdec_common.h" @@ -440,8 +440,13 @@ static int mediacodec_receive_frame(AVCodecContext *avctx, AVFrame *frame) if (ret >= 0) { s->buffered_pkt.size -= ret; s->buffered_pkt.data += ret; - if (s->buffered_pkt.size <= 0) + if (s->buffered_pkt.size <= 0) { av_packet_unref(&s->buffered_pkt); + } else { + av_log(avctx, AV_LOG_WARNING, + "could not send entire packet in single input buffer (%d < %d)\n", + ret, s->buffered_pkt.size+ret); + } } else if (ret < 0 && ret != AVERROR(EAGAIN)) { return ret; } diff --git a/libavcodec/mediacodecdec_common.c b/libavcodec/mediacodecdec_common.c index 1656cd66645..404ed282275 100644 --- a/libavcodec/mediacodecdec_common.c +++ b/libavcodec/mediacodecdec_common.c @@ -85,6 +85,85 @@ #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, @@ -220,6 +299,10 @@ FF_DISABLE_DEPRECATION_WARNINGS FF_ENABLE_DEPRECATION_WARNINGS #endif frame->pkt_dts = AV_NOPTS_VALUE; + frame->color_range = avctx->color_range; + frame->color_primaries = avctx->color_primaries; + frame->color_trc = avctx->color_trc; + frame->colorspace = avctx->colorspace; buffer = av_mallocz(sizeof(AVMediaCodecBuffer)); if (!buffer) { @@ -368,6 +451,9 @@ static int mediacodec_dec_parse_format(AVCodecContext *avctx, MediaCodecDecConte int ret = 0; int width = 0; int height = 0; + int color_range = 0; + int color_standard = 0; + int color_transfer = 0; char *format = NULL; if (!s->format) { @@ -426,6 +512,20 @@ static int mediacodec_dec_parse_format(AVCodecContext *avctx, MediaCodecDecConte ff_set_sar(avctx, sar); } + AMEDIAFORMAT_GET_INT32(color_range, "color-range", 0); + if (color_range) + avctx->color_range = mcdec_get_color_range(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); + } + + AMEDIAFORMAT_GET_INT32(color_transfer, "color-transfer", 0); + if (color_transfer) + avctx->color_trc = mcdec_get_color_trc(color_transfer); + av_log(avctx, AV_LOG_INFO, "Output crop parameters top=%d bottom=%d left=%d right=%d, " "resulting dimensions width=%d height=%d\n", @@ -525,8 +625,8 @@ int ff_mediacodec_dec_init(AVCodecContext *avctx, MediaCodecDecContext *s, if (status < 0) { char *desc = ff_AMediaFormat_toString(format); av_log(avctx, AV_LOG_ERROR, - "Failed to configure codec (status = %d) with format %s\n", - status, desc); + "Failed to configure codec %s (status = %d) with format %s\n", + s->codec_name, status, desc); av_freep(&desc); ret = AVERROR_EXTERNAL; @@ -537,8 +637,8 @@ int ff_mediacodec_dec_init(AVCodecContext *avctx, MediaCodecDecContext *s, if (status < 0) { char *desc = ff_AMediaFormat_toString(format); av_log(avctx, AV_LOG_ERROR, - "Failed to start codec (status = %d) with format %s\n", - status, desc); + "Failed to start codec %s (status = %d) with format %s\n", + s->codec_name, status, desc); av_freep(&desc); ret = AVERROR_EXTERNAL; goto fail; @@ -569,7 +669,6 @@ int ff_mediacodec_dec_send(AVCodecContext *avctx, MediaCodecDecContext *s, int offset = 0; int need_draining = 0; uint8_t *data; - ssize_t index = s->current_input_buffer; size_t size; FFAMediaCodec *codec = s->codec; int status; @@ -591,6 +690,7 @@ int ff_mediacodec_dec_send(AVCodecContext *avctx, MediaCodecDecContext *s, } while (offset < pkt->size || (need_draining && !s->draining)) { + ssize_t index = s->current_input_buffer; if (index < 0) { index = ff_AMediaCodec_dequeueInputBuffer(codec, input_dequeue_timeout_us); if (ff_AMediaCodec_infoTryAgainLater(codec, index)) { @@ -612,7 +712,11 @@ int ff_mediacodec_dec_send(AVCodecContext *avctx, MediaCodecDecContext *s, } pts = pkt->pts; - if (pts != AV_NOPTS_VALUE && avctx->pkt_timebase.num && avctx->pkt_timebase.den) { + if (pts == AV_NOPTS_VALUE) { + av_log(avctx, AV_LOG_WARNING, "Input packet is missing PTS\n"); + pts = 0; + } + if (pts && avctx->pkt_timebase.num && avctx->pkt_timebase.den) { pts = av_rescale_q(pts, avctx->pkt_timebase, AV_TIME_BASE_Q); } @@ -628,7 +732,7 @@ int ff_mediacodec_dec_send(AVCodecContext *avctx, MediaCodecDecContext *s, } av_log(avctx, AV_LOG_TRACE, - "Queued input buffer %zd size=%zd ts=%"PRIi64"\n", index, size, pts); + "Queued empty EOS input buffer %zd with flags=%d\n", index, flags); s->draining = 1; return 0; diff --git a/libavcodec/mf_utils.c b/libavcodec/mf_utils.c new file mode 100644 index 00000000000..eeabd0ce0bd --- /dev/null +++ b/libavcodec/mf_utils.c @@ -0,0 +1,680 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 COBJMACROS +#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0602 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0602 +#endif + +#include "mf_utils.h" +#include "libavutil/pixdesc.h" + +HRESULT ff_MFGetAttributeSize(IMFAttributes *pattr, REFGUID guid, + UINT32 *pw, UINT32 *ph) +{ + UINT64 t; + HRESULT hr = IMFAttributes_GetUINT64(pattr, guid, &t); + if (!FAILED(hr)) { + *pw = t >> 32; + *ph = (UINT32)t; + } + return hr; +} + +HRESULT ff_MFSetAttributeSize(IMFAttributes *pattr, REFGUID guid, + UINT32 uw, UINT32 uh) +{ + UINT64 t = (((UINT64)uw) << 32) | uh; + return IMFAttributes_SetUINT64(pattr, guid, t); +} + +#define ff_MFSetAttributeRatio ff_MFSetAttributeSize +#define ff_MFGetAttributeRatio ff_MFGetAttributeSize + +// MFTEnumEx was missing from mingw-w64's mfplat import library until +// mingw-w64 v6.0.0, thus wrap it and load it using GetProcAddress. +// It's also missing in Windows Vista's mfplat.dll. +HRESULT ff_MFTEnumEx(GUID guidCategory, UINT32 Flags, + const MFT_REGISTER_TYPE_INFO *pInputType, + const MFT_REGISTER_TYPE_INFO *pOutputType, + IMFActivate ***pppMFTActivate, UINT32 *pnumMFTActivate) +{ + HRESULT (WINAPI *MFTEnumEx_ptr)(GUID guidCategory, UINT32 Flags, + const MFT_REGISTER_TYPE_INFO *pInputType, + const MFT_REGISTER_TYPE_INFO *pOutputType, + IMFActivate ***pppMFTActivate, + UINT32 *pnumMFTActivate) = NULL; +#if !HAVE_UWP + HANDLE lib = GetModuleHandleW(L"mfplat.dll"); + if (lib) + MFTEnumEx_ptr = (void *)GetProcAddress(lib, "MFTEnumEx"); +#else + // In UWP (which lacks GetModuleHandle), just link directly against + // the functions - this requires building with new/complete enough + // import libraries. + MFTEnumEx_ptr = MFTEnumEx; +#endif + if (!MFTEnumEx_ptr) + return E_FAIL; + return MFTEnumEx_ptr(guidCategory, + Flags, + pInputType, + pOutputType, + pppMFTActivate, + pnumMFTActivate); +} + +char *ff_hr_str_buf(char *buf, size_t size, HRESULT hr) +{ +#define HR(x) case x: return (char *) # x; + switch (hr) { + HR(S_OK) + HR(E_UNEXPECTED) + HR(MF_E_INVALIDMEDIATYPE) + HR(MF_E_INVALIDSTREAMNUMBER) + HR(MF_E_INVALIDTYPE) + HR(MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING) + HR(MF_E_TRANSFORM_TYPE_NOT_SET) + HR(MF_E_UNSUPPORTED_D3D_TYPE) + HR(MF_E_TRANSFORM_NEED_MORE_INPUT) + HR(MF_E_TRANSFORM_STREAM_CHANGE) + HR(MF_E_NOTACCEPTING) + HR(MF_E_NO_SAMPLE_TIMESTAMP) + HR(MF_E_NO_SAMPLE_DURATION) +#undef HR + } + snprintf(buf, size, "%x", (unsigned)hr); + return buf; +} + +// If fill_data!=NULL, initialize the buffer and set the length. (This is a +// subtle but important difference: some decoders want CurrentLength==0 on +// provided output buffers.) +IMFSample *ff_create_memory_sample(void *fill_data, size_t size, size_t align) +{ + HRESULT hr; + IMFSample *sample; + IMFMediaBuffer *buffer; + + hr = MFCreateSample(&sample); + if (FAILED(hr)) + return NULL; + + align = FFMAX(align, 16); // 16 is "recommended", even if not required + + hr = MFCreateAlignedMemoryBuffer(size, align - 1, &buffer); + if (FAILED(hr)) + return NULL; + + if (fill_data) { + BYTE *tmp; + + hr = IMFMediaBuffer_Lock(buffer, &tmp, NULL, NULL); + if (FAILED(hr)) { + IMFMediaBuffer_Release(buffer); + IMFSample_Release(sample); + return NULL; + } + memcpy(tmp, fill_data, size); + + IMFMediaBuffer_SetCurrentLength(buffer, size); + IMFMediaBuffer_Unlock(buffer); + } + + IMFSample_AddBuffer(sample, buffer); + IMFMediaBuffer_Release(buffer); + + return sample; +} + +enum AVSampleFormat ff_media_type_to_sample_fmt(IMFAttributes *type) +{ + HRESULT hr; + UINT32 bits; + GUID subtype; + + hr = IMFAttributes_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &bits); + if (FAILED(hr)) + return AV_SAMPLE_FMT_NONE; + + hr = IMFAttributes_GetGUID(type, &MF_MT_SUBTYPE, &subtype); + if (FAILED(hr)) + return AV_SAMPLE_FMT_NONE; + + if (IsEqualGUID(&subtype, &MFAudioFormat_PCM)) { + switch (bits) { + case 8: return AV_SAMPLE_FMT_U8; + case 16: return AV_SAMPLE_FMT_S16; + case 32: return AV_SAMPLE_FMT_S32; + } + } else if (IsEqualGUID(&subtype, &MFAudioFormat_Float)) { + switch (bits) { + case 32: return AV_SAMPLE_FMT_FLT; + case 64: return AV_SAMPLE_FMT_DBL; + } + } + + return AV_SAMPLE_FMT_NONE; +} + +struct mf_pix_fmt_entry { + const GUID *guid; + enum AVPixelFormat pix_fmt; +}; + +static const struct mf_pix_fmt_entry mf_pix_fmts[] = { + {&MFVideoFormat_IYUV, AV_PIX_FMT_YUV420P}, + {&MFVideoFormat_I420, AV_PIX_FMT_YUV420P}, + {&MFVideoFormat_NV12, AV_PIX_FMT_NV12}, + {&MFVideoFormat_P010, AV_PIX_FMT_P010}, + {&MFVideoFormat_P016, AV_PIX_FMT_P010}, // not equal, but compatible + {&MFVideoFormat_YUY2, AV_PIX_FMT_YUYV422}, +}; + +enum AVPixelFormat ff_media_type_to_pix_fmt(IMFAttributes *type) +{ + HRESULT hr; + GUID subtype; + int i; + + hr = IMFAttributes_GetGUID(type, &MF_MT_SUBTYPE, &subtype); + if (FAILED(hr)) + return AV_PIX_FMT_NONE; + + for (i = 0; i < FF_ARRAY_ELEMS(mf_pix_fmts); i++) { + if (IsEqualGUID(&subtype, mf_pix_fmts[i].guid)) + return mf_pix_fmts[i].pix_fmt; + } + + return AV_PIX_FMT_NONE; +} + +const GUID *ff_pix_fmt_to_guid(enum AVPixelFormat pix_fmt) +{ + int i; + + for (i = 0; i < FF_ARRAY_ELEMS(mf_pix_fmts); i++) { + if (mf_pix_fmts[i].pix_fmt == pix_fmt) + return mf_pix_fmts[i].guid; + } + + return NULL; +} + +// If this GUID is of the form XXXXXXXX-0000-0010-8000-00AA00389B71, then +// extract the XXXXXXXX prefix as FourCC (oh the pain). +int ff_fourcc_from_guid(const GUID *guid, uint32_t *out_fourcc) +{ + if (guid->Data2 == 0 && guid->Data3 == 0x0010 && + guid->Data4[0] == 0x80 && + guid->Data4[1] == 0x00 && + guid->Data4[2] == 0x00 && + guid->Data4[3] == 0xAA && + guid->Data4[4] == 0x00 && + guid->Data4[5] == 0x38 && + guid->Data4[6] == 0x9B && + guid->Data4[7] == 0x71) { + *out_fourcc = guid->Data1; + return 0; + } + + *out_fourcc = 0; + return AVERROR_UNKNOWN; +} + +struct GUID_Entry { + const GUID *guid; + const char *name; +}; + +#define GUID_ENTRY(var) {&(var), # var} + +static struct GUID_Entry guid_names[] = { + GUID_ENTRY(MFT_FRIENDLY_NAME_Attribute), + GUID_ENTRY(MFT_TRANSFORM_CLSID_Attribute), + GUID_ENTRY(MFT_ENUM_HARDWARE_URL_Attribute), + GUID_ENTRY(MFT_CONNECTED_STREAM_ATTRIBUTE), + GUID_ENTRY(MFT_CONNECTED_TO_HW_STREAM), + GUID_ENTRY(MF_SA_D3D_AWARE), + GUID_ENTRY(ff_MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT), + GUID_ENTRY(ff_MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT_PROGRESSIVE), + GUID_ENTRY(ff_MF_SA_D3D11_BINDFLAGS), + GUID_ENTRY(ff_MF_SA_D3D11_USAGE), + GUID_ENTRY(ff_MF_SA_D3D11_AWARE), + GUID_ENTRY(ff_MF_SA_D3D11_SHARED), + GUID_ENTRY(ff_MF_SA_D3D11_SHARED_WITHOUT_MUTEX), + GUID_ENTRY(MF_MT_SUBTYPE), + GUID_ENTRY(MF_MT_MAJOR_TYPE), + GUID_ENTRY(MF_MT_AUDIO_SAMPLES_PER_SECOND), + GUID_ENTRY(MF_MT_AUDIO_NUM_CHANNELS), + GUID_ENTRY(MF_MT_AUDIO_CHANNEL_MASK), + GUID_ENTRY(MF_MT_FRAME_SIZE), + GUID_ENTRY(MF_MT_INTERLACE_MODE), + GUID_ENTRY(MF_MT_USER_DATA), + GUID_ENTRY(MF_MT_PIXEL_ASPECT_RATIO), + GUID_ENTRY(MFMediaType_Audio), + GUID_ENTRY(MFMediaType_Video), + GUID_ENTRY(MFAudioFormat_PCM), + GUID_ENTRY(MFAudioFormat_Float), + GUID_ENTRY(MFVideoFormat_H264), + GUID_ENTRY(MFVideoFormat_H264_ES), + GUID_ENTRY(ff_MFVideoFormat_HEVC), + GUID_ENTRY(ff_MFVideoFormat_HEVC_ES), + GUID_ENTRY(MFVideoFormat_MPEG2), + GUID_ENTRY(MFVideoFormat_MP43), + GUID_ENTRY(MFVideoFormat_MP4V), + GUID_ENTRY(MFVideoFormat_WMV1), + GUID_ENTRY(MFVideoFormat_WMV2), + GUID_ENTRY(MFVideoFormat_WMV3), + GUID_ENTRY(MFVideoFormat_WVC1), + GUID_ENTRY(MFAudioFormat_Dolby_AC3), + GUID_ENTRY(MFAudioFormat_Dolby_DDPlus), + GUID_ENTRY(MFAudioFormat_AAC), + GUID_ENTRY(MFAudioFormat_MP3), + GUID_ENTRY(MFAudioFormat_MSP1), + GUID_ENTRY(MFAudioFormat_WMAudioV8), + GUID_ENTRY(MFAudioFormat_WMAudioV9), + GUID_ENTRY(MFAudioFormat_WMAudio_Lossless), + GUID_ENTRY(MF_MT_ALL_SAMPLES_INDEPENDENT), + GUID_ENTRY(MF_MT_COMPRESSED), + GUID_ENTRY(MF_MT_FIXED_SIZE_SAMPLES), + GUID_ENTRY(MF_MT_SAMPLE_SIZE), + GUID_ENTRY(MF_MT_WRAPPED_TYPE), + GUID_ENTRY(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION), + GUID_ENTRY(MF_MT_AAC_PAYLOAD_TYPE), + GUID_ENTRY(MF_MT_AUDIO_AVG_BYTES_PER_SECOND), + GUID_ENTRY(MF_MT_AUDIO_BITS_PER_SAMPLE), + GUID_ENTRY(MF_MT_AUDIO_BLOCK_ALIGNMENT), + GUID_ENTRY(MF_MT_AUDIO_CHANNEL_MASK), + GUID_ENTRY(MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND), + GUID_ENTRY(MF_MT_AUDIO_FOLDDOWN_MATRIX), + GUID_ENTRY(MF_MT_AUDIO_NUM_CHANNELS), + GUID_ENTRY(MF_MT_AUDIO_PREFER_WAVEFORMATEX), + GUID_ENTRY(MF_MT_AUDIO_SAMPLES_PER_BLOCK), + GUID_ENTRY(MF_MT_AUDIO_SAMPLES_PER_SECOND), + GUID_ENTRY(MF_MT_AUDIO_VALID_BITS_PER_SAMPLE), + GUID_ENTRY(MF_MT_AUDIO_WMADRC_AVGREF), + GUID_ENTRY(MF_MT_AUDIO_WMADRC_AVGTARGET), + GUID_ENTRY(MF_MT_AUDIO_WMADRC_PEAKREF), + GUID_ENTRY(MF_MT_AUDIO_WMADRC_PEAKTARGET), + GUID_ENTRY(MF_MT_AVG_BIT_ERROR_RATE), + GUID_ENTRY(MF_MT_AVG_BITRATE), + GUID_ENTRY(MF_MT_DEFAULT_STRIDE), + GUID_ENTRY(MF_MT_DRM_FLAGS), + GUID_ENTRY(MF_MT_FRAME_RATE), + GUID_ENTRY(MF_MT_FRAME_RATE_RANGE_MAX), + GUID_ENTRY(MF_MT_FRAME_RATE_RANGE_MIN), + GUID_ENTRY(MF_MT_FRAME_SIZE), + GUID_ENTRY(MF_MT_GEOMETRIC_APERTURE), + GUID_ENTRY(MF_MT_INTERLACE_MODE), + GUID_ENTRY(MF_MT_MAX_KEYFRAME_SPACING), + GUID_ENTRY(MF_MT_MINIMUM_DISPLAY_APERTURE), + GUID_ENTRY(MF_MT_MPEG_SEQUENCE_HEADER), + GUID_ENTRY(MF_MT_MPEG_START_TIME_CODE), + GUID_ENTRY(MF_MT_MPEG2_FLAGS), + GUID_ENTRY(MF_MT_MPEG2_LEVEL), + GUID_ENTRY(MF_MT_MPEG2_PROFILE), + GUID_ENTRY(MF_MT_PAD_CONTROL_FLAGS), + GUID_ENTRY(MF_MT_PALETTE), + GUID_ENTRY(MF_MT_PAN_SCAN_APERTURE), + GUID_ENTRY(MF_MT_PAN_SCAN_ENABLED), + GUID_ENTRY(MF_MT_PIXEL_ASPECT_RATIO), + GUID_ENTRY(MF_MT_SOURCE_CONTENT_HINT), + GUID_ENTRY(MF_MT_TRANSFER_FUNCTION), + GUID_ENTRY(MF_MT_VIDEO_CHROMA_SITING), + GUID_ENTRY(MF_MT_VIDEO_LIGHTING), + GUID_ENTRY(MF_MT_VIDEO_NOMINAL_RANGE), + GUID_ENTRY(MF_MT_VIDEO_PRIMARIES), + GUID_ENTRY(MF_MT_VIDEO_ROTATION), + GUID_ENTRY(MF_MT_YUV_MATRIX), + GUID_ENTRY(ff_CODECAPI_AVDecVideoThumbnailGenerationMode), + GUID_ENTRY(ff_CODECAPI_AVDecVideoDropPicWithMissingRef), + GUID_ENTRY(ff_CODECAPI_AVDecVideoSoftwareDeinterlaceMode), + GUID_ENTRY(ff_CODECAPI_AVDecVideoFastDecodeMode), + GUID_ENTRY(ff_CODECAPI_AVLowLatencyMode), + GUID_ENTRY(ff_CODECAPI_AVDecVideoH264ErrorConcealment), + GUID_ENTRY(ff_CODECAPI_AVDecVideoMPEG2ErrorConcealment), + GUID_ENTRY(ff_CODECAPI_AVDecVideoCodecType), + GUID_ENTRY(ff_CODECAPI_AVDecVideoDXVAMode), + GUID_ENTRY(ff_CODECAPI_AVDecVideoDXVABusEncryption), + GUID_ENTRY(ff_CODECAPI_AVDecVideoSWPowerLevel), + GUID_ENTRY(ff_CODECAPI_AVDecVideoMaxCodedWidth), + GUID_ENTRY(ff_CODECAPI_AVDecVideoMaxCodedHeight), + GUID_ENTRY(ff_CODECAPI_AVDecNumWorkerThreads), + GUID_ENTRY(ff_CODECAPI_AVDecSoftwareDynamicFormatChange), + GUID_ENTRY(ff_CODECAPI_AVDecDisableVideoPostProcessing), +}; + +char *ff_guid_str_buf(char *buf, size_t buf_size, const GUID *guid) +{ + uint32_t fourcc; + int n; + for (n = 0; n < FF_ARRAY_ELEMS(guid_names); n++) { + if (IsEqualGUID(guid, guid_names[n].guid)) { + snprintf(buf, buf_size, "%s", guid_names[n].name); + return buf; + } + } + + if (ff_fourcc_from_guid(guid, &fourcc) >= 0) { + snprintf(buf, buf_size, "", av_fourcc2str(fourcc)); + return buf; + } + + snprintf(buf, buf_size, + "{%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}", + (unsigned) guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], + guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], + guid->Data4[6], guid->Data4[7]); + return buf; +} + +void ff_attributes_dump(void *log, IMFAttributes *attrs) +{ + HRESULT hr; + UINT32 count; + int n; + + hr = IMFAttributes_GetCount(attrs, &count); + if (FAILED(hr)) + return; + + for (n = 0; n < count; n++) { + GUID key; + MF_ATTRIBUTE_TYPE type; + char extra[80] = {0}; + const char *name = NULL; + + hr = IMFAttributes_GetItemByIndex(attrs, n, &key, NULL); + if (FAILED(hr)) + goto err; + + name = ff_guid_str(&key); + + if (IsEqualGUID(&key, &MF_MT_AUDIO_CHANNEL_MASK)) { + UINT32 v; + hr = IMFAttributes_GetUINT32(attrs, &key, &v); + if (FAILED(hr)) + goto err; + snprintf(extra, sizeof(extra), " (0x%x)", (unsigned)v); + } else if (IsEqualGUID(&key, &MF_MT_FRAME_SIZE)) { + UINT32 w, h; + + hr = ff_MFGetAttributeSize(attrs, &MF_MT_FRAME_SIZE, &w, &h); + if (FAILED(hr)) + goto err; + snprintf(extra, sizeof(extra), " (%dx%d)", (int)w, (int)h); + } else if (IsEqualGUID(&key, &MF_MT_PIXEL_ASPECT_RATIO) || + IsEqualGUID(&key, &MF_MT_FRAME_RATE)) { + UINT32 num, den; + + hr = ff_MFGetAttributeRatio(attrs, &key, &num, &den); + if (FAILED(hr)) + goto err; + snprintf(extra, sizeof(extra), " (%d:%d)", (int)num, (int)den); + } + + hr = IMFAttributes_GetItemType(attrs, &key, &type); + if (FAILED(hr)) + goto err; + + switch (type) { + case MF_ATTRIBUTE_UINT32: { + UINT32 v; + hr = IMFAttributes_GetUINT32(attrs, &key, &v); + if (FAILED(hr)) + goto err; + av_log(log, AV_LOG_VERBOSE, " %s=%d%s\n", name, (int)v, extra); + break; + case MF_ATTRIBUTE_UINT64: { + UINT64 v; + hr = IMFAttributes_GetUINT64(attrs, &key, &v); + if (FAILED(hr)) + goto err; + av_log(log, AV_LOG_VERBOSE, " %s=%lld%s\n", name, (long long)v, extra); + break; + } + case MF_ATTRIBUTE_DOUBLE: { + DOUBLE v; + hr = IMFAttributes_GetDouble(attrs, &key, &v); + if (FAILED(hr)) + goto err; + av_log(log, AV_LOG_VERBOSE, " %s=%f%s\n", name, (double)v, extra); + break; + } + case MF_ATTRIBUTE_STRING: { + wchar_t s[512]; // being lazy here + hr = IMFAttributes_GetString(attrs, &key, s, sizeof(s), NULL); + if (FAILED(hr)) + goto err; + av_log(log, AV_LOG_VERBOSE, " %s='%ls'%s\n", name, s, extra); + break; + } + case MF_ATTRIBUTE_GUID: { + GUID v; + hr = IMFAttributes_GetGUID(attrs, &key, &v); + if (FAILED(hr)) + goto err; + av_log(log, AV_LOG_VERBOSE, " %s=%s%s\n", name, ff_guid_str(&v), extra); + break; + } + case MF_ATTRIBUTE_BLOB: { + UINT32 sz; + UINT8 buffer[100]; + hr = IMFAttributes_GetBlobSize(attrs, &key, &sz); + if (FAILED(hr)) + goto err; + if (sz <= sizeof(buffer)) { + // hex-dump it + char str[512] = {0}; + size_t pos = 0; + hr = IMFAttributes_GetBlob(attrs, &key, buffer, sizeof(buffer), &sz); + if (FAILED(hr)) + goto err; + for (pos = 0; pos < sz; pos++) { + const char *hex = "0123456789ABCDEF"; + if (pos * 3 + 3 > sizeof(str)) + break; + str[pos * 3 + 0] = hex[buffer[pos] >> 4]; + str[pos * 3 + 1] = hex[buffer[pos] & 15]; + str[pos * 3 + 2] = ' '; + } + str[pos * 3 + 0] = 0; + av_log(log, AV_LOG_VERBOSE, " %s=%s\n", name, (int)sz, str, extra); + } else { + av_log(log, AV_LOG_VERBOSE, " %s=%s\n", name, (int)sz, extra); + } + break; + } + case MF_ATTRIBUTE_IUNKNOWN: { + av_log(log, AV_LOG_VERBOSE, " %s=%s\n", name, extra); + break; + } + default: + av_log(log, AV_LOG_VERBOSE, " %s=%s\n", name, extra); + break; + } + } + + if (IsEqualGUID(&key, &MF_MT_SUBTYPE)) { + const char *fmt; + fmt = av_get_sample_fmt_name(ff_media_type_to_sample_fmt(attrs)); + if (fmt) + av_log(log, AV_LOG_VERBOSE, " FF-sample-format=%s\n", fmt); + + fmt = av_get_pix_fmt_name(ff_media_type_to_pix_fmt(attrs)); + if (fmt) + av_log(log, AV_LOG_VERBOSE, " FF-pixel-format=%s\n", fmt); + } + + continue; + err: + av_log(log, AV_LOG_VERBOSE, " %s=\n", name ? name : "?"); + } +} + +void ff_media_type_dump(void *log, IMFMediaType *type) +{ + ff_attributes_dump(log, (IMFAttributes *)type); +} + +const CLSID *ff_codec_to_mf_subtype(enum AVCodecID codec) +{ + switch (codec) { + case AV_CODEC_ID_H264: return &MFVideoFormat_H264; + case AV_CODEC_ID_HEVC: return &ff_MFVideoFormat_HEVC; + case AV_CODEC_ID_AC3: return &MFAudioFormat_Dolby_AC3; + case AV_CODEC_ID_AAC: return &MFAudioFormat_AAC; + case AV_CODEC_ID_MP3: return &MFAudioFormat_MP3; + default: return NULL; + } +} + +static int init_com_mf(void *log) +{ + HRESULT hr; + + hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (hr == RPC_E_CHANGED_MODE) { + av_log(log, AV_LOG_ERROR, "COM must not be in STA mode\n"); + return AVERROR(EINVAL); + } else if (FAILED(hr)) { + av_log(log, AV_LOG_ERROR, "could not initialize COM\n"); + return AVERROR(ENOSYS); + } + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + if (FAILED(hr)) { + av_log(log, AV_LOG_ERROR, "could not initialize MediaFoundation\n"); + CoUninitialize(); + return AVERROR(ENOSYS); + } + + return 0; +} + +static void uninit_com_mf(void) +{ + MFShutdown(); + CoUninitialize(); +} + +// Find and create a IMFTransform with the given input/output types. When done, +// you should use ff_free_mf() to destroy it, which will also uninit COM. +int ff_instantiate_mf(void *log, + GUID category, + MFT_REGISTER_TYPE_INFO *in_type, + MFT_REGISTER_TYPE_INFO *out_type, + int use_hw, + IMFTransform **res) +{ + HRESULT hr; + int n; + int ret; + IMFActivate **activate; + UINT32 num_activate; + IMFActivate *winner = 0; + UINT32 flags; + + ret = init_com_mf(log); + if (ret < 0) + return ret; + + flags = MFT_ENUM_FLAG_SORTANDFILTER; + + if (use_hw) { + flags |= MFT_ENUM_FLAG_HARDWARE; + } else { + flags |= MFT_ENUM_FLAG_SYNCMFT; + } + + hr = ff_MFTEnumEx(category, flags, in_type, out_type, &activate, + &num_activate); + if (FAILED(hr)) + goto error_uninit_mf; + + if (log) { + if (!num_activate) + av_log(log, AV_LOG_ERROR, "could not find any MFT for the given media type\n"); + + for (n = 0; n < num_activate; n++) { + av_log(log, AV_LOG_VERBOSE, "MF %d attributes:\n", n); + ff_attributes_dump(log, (IMFAttributes *)activate[n]); + } + } + + *res = NULL; + for (n = 0; n < num_activate; n++) { + if (log) + av_log(log, AV_LOG_VERBOSE, "activate MFT %d\n", n); + hr = IMFActivate_ActivateObject(activate[n], &IID_IMFTransform, + (void **)res); + if (*res) { + winner = activate[n]; + IMFActivate_AddRef(winner); + break; + } + } + + for (n = 0; n < num_activate; n++) + IMFActivate_Release(activate[n]); + CoTaskMemFree(activate); + + if (!*res) { + if (log) + av_log(log, AV_LOG_ERROR, "could not create MFT\n"); + goto error_uninit_mf; + } + + if (log) { + wchar_t s[512]; // being lazy here + IMFAttributes *attrs; + hr = IMFTransform_GetAttributes(*res, &attrs); + if (!FAILED(hr) && attrs) { + + av_log(log, AV_LOG_VERBOSE, "MFT attributes\n"); + ff_attributes_dump(log, attrs); + IMFAttributes_Release(attrs); + } + + hr = IMFActivate_GetString(winner, &MFT_FRIENDLY_NAME_Attribute, s, + sizeof(s), NULL); + if (!FAILED(hr)) + av_log(log, AV_LOG_INFO, "MFT name: '%ls'\n", s); + + } + + IMFActivate_Release(winner); + + return 0; + +error_uninit_mf: + uninit_com_mf(); + return AVERROR(ENOSYS); +} + +void ff_free_mf(IMFTransform **mft) +{ + if (*mft) + IMFTransform_Release(*mft); + *mft = NULL; + uninit_com_mf(); +} diff --git a/libavcodec/mf_utils.h b/libavcodec/mf_utils.h new file mode 100644 index 00000000000..d514723c3be --- /dev/null +++ b/libavcodec/mf_utils.h @@ -0,0 +1,169 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_MF_UTILS_H +#define AVCODEC_MF_UTILS_H + +#include +#include +#ifdef _MSC_VER +// The official way of including codecapi (via dshow.h) makes the ICodecAPI +// interface unavailable in UWP mode, but including icodecapi.h + codecapi.h +// seems to be equivalent. (These headers conflict with the official way +// of including it though, through strmif.h via dshow.h. And on mingw, the +// mf*.h headers below indirectly include strmif.h.) +#include +#else +#include +// Older versions of mingw-w64 need codecapi.h explicitly included, while newer +// ones include it implicitly from dshow.h (via uuids.h). +#include +#endif +#include +#include +#include +#include + +#include "avcodec.h" + +// These functions do exist in mfapi.h, but are only available within +// __cplusplus ifdefs. +HRESULT ff_MFGetAttributeSize(IMFAttributes *pattr, REFGUID guid, + UINT32 *pw, UINT32 *ph); +HRESULT ff_MFSetAttributeSize(IMFAttributes *pattr, REFGUID guid, + UINT32 uw, UINT32 uh); +#define ff_MFSetAttributeRatio ff_MFSetAttributeSize +#define ff_MFGetAttributeRatio ff_MFGetAttributeSize + +// MFTEnumEx was missing from mingw-w64's mfplat import library until +// mingw-w64 v6.0.0, thus wrap it and load it using GetProcAddress. +// It's also missing in Windows Vista's mfplat.dll. +HRESULT ff_MFTEnumEx(GUID guidCategory, UINT32 Flags, + const MFT_REGISTER_TYPE_INFO *pInputType, + const MFT_REGISTER_TYPE_INFO *pOutputType, + IMFActivate ***pppMFTActivate, UINT32 *pnumMFTActivate); + + +// These do exist in mingw-w64's codecapi.h, but they aren't properly defined +// by the header until after mingw-w64 v7.0.0. +DEFINE_GUID(ff_CODECAPI_AVDecVideoThumbnailGenerationMode, 0x2efd8eee,0x1150,0x4328,0x9c,0xf5,0x66,0xdc,0xe9,0x33,0xfc,0xf4); +DEFINE_GUID(ff_CODECAPI_AVDecVideoDropPicWithMissingRef, 0xf8226383,0x14c2,0x4567,0x97,0x34,0x50,0x04,0xe9,0x6f,0xf8,0x87); +DEFINE_GUID(ff_CODECAPI_AVDecVideoSoftwareDeinterlaceMode, 0x0c08d1ce,0x9ced,0x4540,0xba,0xe3,0xce,0xb3,0x80,0x14,0x11,0x09); +DEFINE_GUID(ff_CODECAPI_AVDecVideoFastDecodeMode, 0x6b529f7d,0xd3b1,0x49c6,0xa9,0x99,0x9e,0xc6,0x91,0x1b,0xed,0xbf); +DEFINE_GUID(ff_CODECAPI_AVLowLatencyMode, 0x9c27891a,0xed7a,0x40e1,0x88,0xe8,0xb2,0x27,0x27,0xa0,0x24,0xee); +DEFINE_GUID(ff_CODECAPI_AVDecVideoH264ErrorConcealment, 0xececace8,0x3436,0x462c,0x92,0x94,0xcd,0x7b,0xac,0xd7,0x58,0xa9); +DEFINE_GUID(ff_CODECAPI_AVDecVideoMPEG2ErrorConcealment, 0x9d2bfe18,0x728d,0x48d2,0xb3,0x58,0xbc,0x7e,0x43,0x6c,0x66,0x74); +DEFINE_GUID(ff_CODECAPI_AVDecVideoCodecType, 0x434528e5,0x21f0,0x46b6,0xb6,0x2c,0x9b,0x1b,0x6b,0x65,0x8c,0xd1); +DEFINE_GUID(ff_CODECAPI_AVDecVideoDXVAMode, 0xf758f09e,0x7337,0x4ae7,0x83,0x87,0x73,0xdc,0x2d,0x54,0xe6,0x7d); +DEFINE_GUID(ff_CODECAPI_AVDecVideoDXVABusEncryption, 0x42153c8b,0xfd0b,0x4765,0xa4,0x62,0xdd,0xd9,0xe8,0xbc,0xc3,0x88); +DEFINE_GUID(ff_CODECAPI_AVDecVideoSWPowerLevel, 0xfb5d2347,0x4dd8,0x4509,0xae,0xd0,0xdb,0x5f,0xa9,0xaa,0x93,0xf4); +DEFINE_GUID(ff_CODECAPI_AVDecVideoMaxCodedWidth, 0x5ae557b8,0x77af,0x41f5,0x9f,0xa6,0x4d,0xb2,0xfe,0x1d,0x4b,0xca); +DEFINE_GUID(ff_CODECAPI_AVDecVideoMaxCodedHeight, 0x7262a16a,0xd2dc,0x4e75,0x9b,0xa8,0x65,0xc0,0xc6,0xd3,0x2b,0x13); +DEFINE_GUID(ff_CODECAPI_AVDecNumWorkerThreads, 0x9561c3e8,0xea9e,0x4435,0x9b,0x1e,0xa9,0x3e,0x69,0x18,0x94,0xd8); +DEFINE_GUID(ff_CODECAPI_AVDecSoftwareDynamicFormatChange, 0x862e2f0a,0x507b,0x47ff,0xaf,0x47,0x01,0xe2,0x62,0x42,0x98,0xb7); +DEFINE_GUID(ff_CODECAPI_AVDecDisableVideoPostProcessing, 0xf8749193,0x667a,0x4f2c,0xa9,0xe8,0x5d,0x4a,0xf9,0x24,0xf0,0x8f); + +// These are missing from mingw-w64's headers until after mingw-w64 v7.0.0. +DEFINE_GUID(ff_CODECAPI_AVEncCommonRateControlMode, 0x1c0608e9, 0x370c, 0x4710, 0x8a, 0x58, 0xcb, 0x61, 0x81, 0xc4, 0x24, 0x23); +DEFINE_GUID(ff_CODECAPI_AVEncCommonQuality, 0xfcbf57a3, 0x7ea5, 0x4b0c, 0x96, 0x44, 0x69, 0xb4, 0x0c, 0x39, 0xc3, 0x91); +DEFINE_GUID(ff_CODECAPI_AVEncCommonMeanBitRate, 0xf7222374, 0x2144, 0x4815, 0xb5, 0x50, 0xa3, 0x7f, 0x8e, 0x12, 0xee, 0x52); +DEFINE_GUID(ff_CODECAPI_AVEncH264CABACEnable, 0xee6cad62, 0xd305, 0x4248, 0xa5, 0xe, 0xe1, 0xb2, 0x55, 0xf7, 0xca, 0xf8); +DEFINE_GUID(ff_CODECAPI_AVEncVideoForceKeyFrame, 0x398c1b98, 0x8353, 0x475a, 0x9e, 0xf2, 0x8f, 0x26, 0x5d, 0x26, 0x3, 0x45); +DEFINE_GUID(ff_CODECAPI_AVEncMPVDefaultBPictureCount, 0x8d390aac, 0xdc5c, 0x4200, 0xb5, 0x7f, 0x81, 0x4d, 0x04, 0xba, 0xba, 0xb2); +DEFINE_GUID(ff_CODECAPI_AVScenarioInfo, 0xb28a6e64,0x3ff9,0x446a,0x8a,0x4b,0x0d,0x7a,0x53,0x41,0x32,0x36); + +DEFINE_GUID(ff_MF_SA_D3D11_BINDFLAGS, 0xeacf97ad, 0x065c, 0x4408, 0xbe, 0xe3, 0xfd, 0xcb, 0xfd, 0x12, 0x8b, 0xe2); +DEFINE_GUID(ff_MF_SA_D3D11_USAGE, 0xe85fe442, 0x2ca3, 0x486e, 0xa9, 0xc7, 0x10, 0x9d, 0xda, 0x60, 0x98, 0x80); +DEFINE_GUID(ff_MF_SA_D3D11_AWARE, 0x206b4fc8, 0xfcf9, 0x4c51, 0xaf, 0xe3, 0x97, 0x64, 0x36, 0x9e, 0x33, 0xa0); +DEFINE_GUID(ff_MF_SA_D3D11_SHARED, 0x7b8f32c3, 0x6d96, 0x4b89, 0x92, 0x3, 0xdd, 0x38, 0xb6, 0x14, 0x14, 0xf3); +DEFINE_GUID(ff_MF_SA_D3D11_SHARED_WITHOUT_MUTEX, 0x39dbd44d, 0x2e44, 0x4931, 0xa4, 0xc8, 0x35, 0x2d, 0x3d, 0xc4, 0x21, 0x15); +DEFINE_GUID(ff_MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT, 0x851745d5, 0xc3d6, 0x476d, 0x95, 0x27, 0x49, 0x8e, 0xf2, 0xd1, 0xd, 0x18); +DEFINE_GUID(ff_MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT_PROGRESSIVE, 0xf5523a5, 0x1cb2, 0x47c5, 0xa5, 0x50, 0x2e, 0xeb, 0x84, 0xb4, 0xd1, 0x4a); + +DEFINE_MEDIATYPE_GUID(ff_MFVideoFormat_HEVC, 0x43564548); // FCC('HEVC') +DEFINE_MEDIATYPE_GUID(ff_MFVideoFormat_HEVC_ES, 0x53564548); // FCC('HEVS') + + +// This enum is missing from mingw-w64's codecapi.h by v7.0.0. +enum ff_eAVEncCommonRateControlMode { + ff_eAVEncCommonRateControlMode_CBR = 0, + ff_eAVEncCommonRateControlMode_PeakConstrainedVBR = 1, + ff_eAVEncCommonRateControlMode_UnconstrainedVBR = 2, + ff_eAVEncCommonRateControlMode_Quality = 3, + ff_eAVEncCommonRateControlMode_LowDelayVBR = 4, + ff_eAVEncCommonRateControlMode_GlobalVBR = 5, + ff_eAVEncCommonRateControlMode_GlobalLowDelayVBR = 6 +}; + +enum ff_eAVScenarioInfo { + ff_eAVScenarioInfo_Unknown = 0, + ff_eAVScenarioInfo_DisplayRemoting = 1, + ff_eAVScenarioInfo_VideoConference = 2, + ff_eAVScenarioInfo_Archive = 3, + ff_eAVScenarioInfo_LiveStreaming = 4, + ff_eAVScenarioInfo_CameraRecord = 5, + ff_eAVScenarioInfo_DisplayRemotingWithFeatureMap = 6 +}; + +// These do exist in mingw-w64's mfobjects.idl, but are missing from +// mfobjects.h that is generated from the former, due to incorrect use of +// ifdefs in the IDL file. +enum { + ff_METransformUnknown = 600, + ff_METransformNeedInput, + ff_METransformHaveOutput, + ff_METransformDrainComplete, + ff_METransformMarker, +}; + +// These do exist in all supported headers, but are manually defined here +// to avoid having to include codecapi.h, as there's problems including that +// header when targeting UWP (where including it with MSVC seems to work, +// but fails when built with clang in MSVC mode). +enum ff_eAVEncH264VProfile { + ff_eAVEncH264VProfile_Base = 66, + ff_eAVEncH264VProfile_Main = 77, + ff_eAVEncH264VProfile_High = 100, +}; + +char *ff_hr_str_buf(char *buf, size_t size, HRESULT hr); +#define ff_hr_str(hr) ff_hr_str_buf((char[80]){0}, 80, hr) + +// Possibly compiler-dependent; the MS/MinGW definition for this is just crazy. +#define FF_VARIANT_VALUE(type, contents) &(VARIANT){ .vt = (type), contents } + +#define FF_VAL_VT_UI4(v) FF_VARIANT_VALUE(VT_UI4, .ulVal = (v)) +#define FF_VAL_VT_BOOL(v) FF_VARIANT_VALUE(VT_BOOL, .boolVal = (v)) + +IMFSample *ff_create_memory_sample(void *fill_data, size_t size, size_t align); +enum AVSampleFormat ff_media_type_to_sample_fmt(IMFAttributes *type); +enum AVPixelFormat ff_media_type_to_pix_fmt(IMFAttributes *type); +const GUID *ff_pix_fmt_to_guid(enum AVPixelFormat pix_fmt); +int ff_fourcc_from_guid(const GUID *guid, uint32_t *out_fourcc); +char *ff_guid_str_buf(char *buf, size_t buf_size, const GUID *guid); +#define ff_guid_str(guid) ff_guid_str_buf((char[80]){0}, 80, guid) +void ff_attributes_dump(void *log, IMFAttributes *attrs); +void ff_media_type_dump(void *log, IMFMediaType *type); +const CLSID *ff_codec_to_mf_subtype(enum AVCodecID codec); +int ff_instantiate_mf(void *log, GUID category, + MFT_REGISTER_TYPE_INFO *in_type, + MFT_REGISTER_TYPE_INFO *out_type, + int use_hw, IMFTransform **res); +void ff_free_mf(IMFTransform **mft); + +#endif diff --git a/libavcodec/mfenc.c b/libavcodec/mfenc.c new file mode 100644 index 00000000000..ee3c164e693 --- /dev/null +++ b/libavcodec/mfenc.c @@ -0,0 +1,1198 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 COBJMACROS +#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0602 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0602 +#endif + +#include "mf_utils.h" +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/time.h" +#include "internal.h" + +typedef struct MFContext { + AVClass *av_class; + int is_video, is_audio; + GUID main_subtype; + IMFTransform *mft; + IMFMediaEventGenerator *async_events; + DWORD in_stream_id, out_stream_id; + MFT_INPUT_STREAM_INFO in_info; + MFT_OUTPUT_STREAM_INFO out_info; + int out_stream_provides_samples; + int draining, draining_done; + int sample_sent; + int async_need_input, async_have_output, async_marker; + int64_t reorder_delay; + ICodecAPI *codec_api; + // set by AVOption + int opt_enc_rc; + int opt_enc_quality; + int opt_enc_scenario; + int opt_enc_hw; +} MFContext; + +static int mf_choose_output_type(AVCodecContext *avctx); +static int mf_setup_context(AVCodecContext *avctx); + +#define MF_TIMEBASE (AVRational){1, 10000000} +// Sentinel value only used by us. +#define MF_INVALID_TIME AV_NOPTS_VALUE + +static int mf_wait_events(AVCodecContext *avctx) +{ + MFContext *c = avctx->priv_data; + + if (!c->async_events) + return 0; + + while (!(c->async_need_input || c->async_have_output || c->draining_done || c->async_marker)) { + IMFMediaEvent *ev = NULL; + MediaEventType ev_id = 0; + HRESULT hr = IMFMediaEventGenerator_GetEvent(c->async_events, 0, &ev); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "IMFMediaEventGenerator_GetEvent() failed: %s\n", + ff_hr_str(hr)); + return AVERROR_EXTERNAL; + } + IMFMediaEvent_GetType(ev, &ev_id); + switch (ev_id) { + case ff_METransformNeedInput: + if (!c->draining) + c->async_need_input = 1; + break; + case ff_METransformHaveOutput: + c->async_have_output = 1; + break; + case ff_METransformDrainComplete: + c->draining_done = 1; + break; + case ff_METransformMarker: + c->async_marker = 1; + break; + default: ; + } + IMFMediaEvent_Release(ev); + } + + return 0; +} + +static AVRational mf_get_tb(AVCodecContext *avctx) +{ + if (avctx->pkt_timebase.num > 0 && avctx->pkt_timebase.den > 0) + return avctx->pkt_timebase; + if (avctx->time_base.num > 0 && avctx->time_base.den > 0) + return avctx->time_base; + return MF_TIMEBASE; +} + +static LONGLONG mf_to_mf_time(AVCodecContext *avctx, int64_t av_pts) +{ + if (av_pts == AV_NOPTS_VALUE) + return MF_INVALID_TIME; + return av_rescale_q(av_pts, mf_get_tb(avctx), MF_TIMEBASE); +} + +static void mf_sample_set_pts(AVCodecContext *avctx, IMFSample *sample, int64_t av_pts) +{ + LONGLONG stime = mf_to_mf_time(avctx, av_pts); + if (stime != MF_INVALID_TIME) + IMFSample_SetSampleTime(sample, stime); +} + +static int64_t mf_from_mf_time(AVCodecContext *avctx, LONGLONG stime) +{ + return av_rescale_q(stime, MF_TIMEBASE, mf_get_tb(avctx)); +} + +static int64_t mf_sample_get_pts(AVCodecContext *avctx, IMFSample *sample) +{ + LONGLONG pts; + HRESULT hr = IMFSample_GetSampleTime(sample, &pts); + if (FAILED(hr)) + return AV_NOPTS_VALUE; + return mf_from_mf_time(avctx, pts); +} + +static int mf_enca_output_type_get(AVCodecContext *avctx, IMFMediaType *type) +{ + MFContext *c = avctx->priv_data; + HRESULT hr; + UINT32 sz; + + if (avctx->codec_id != AV_CODEC_ID_MP3 && avctx->codec_id != AV_CODEC_ID_AC3) { + hr = IMFAttributes_GetBlobSize(type, &MF_MT_USER_DATA, &sz); + if (!FAILED(hr) && sz > 0) { + avctx->extradata = av_mallocz(sz + AV_INPUT_BUFFER_PADDING_SIZE); + if (!avctx->extradata) + return AVERROR(ENOMEM); + avctx->extradata_size = sz; + hr = IMFAttributes_GetBlob(type, &MF_MT_USER_DATA, avctx->extradata, sz, NULL); + if (FAILED(hr)) + return AVERROR_EXTERNAL; + + if (avctx->codec_id == AV_CODEC_ID_AAC && avctx->extradata_size >= 12) { + // Get rid of HEAACWAVEINFO (after wfx field, 12 bytes). + avctx->extradata_size = avctx->extradata_size - 12; + memmove(avctx->extradata, avctx->extradata + 12, avctx->extradata_size); + } + } + } + + // I don't know where it's documented that we need this. It happens with the + // MS mp3 encoder MFT. The idea for the workaround is taken from NAudio. + // (Certainly any lossy codec will have frames much smaller than 1 second.) + if (!c->out_info.cbSize && !c->out_stream_provides_samples) { + hr = IMFAttributes_GetUINT32(type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &sz); + if (!FAILED(hr)) { + av_log(avctx, AV_LOG_VERBOSE, "MFT_OUTPUT_STREAM_INFO.cbSize set to 0, " + "assuming %d bytes instead.\n", (int)sz); + c->out_info.cbSize = sz; + } + } + + return 0; +} + +static int mf_encv_output_type_get(AVCodecContext *avctx, IMFMediaType *type) +{ + HRESULT hr; + UINT32 sz; + + hr = IMFAttributes_GetBlobSize(type, &MF_MT_MPEG_SEQUENCE_HEADER, &sz); + if (!FAILED(hr) && sz > 0) { + uint8_t *extradata = av_mallocz(sz + AV_INPUT_BUFFER_PADDING_SIZE); + if (!extradata) + return AVERROR(ENOMEM); + hr = IMFAttributes_GetBlob(type, &MF_MT_MPEG_SEQUENCE_HEADER, extradata, sz, NULL); + if (FAILED(hr)) { + av_free(extradata); + return AVERROR_EXTERNAL; + } + av_freep(&avctx->extradata); + avctx->extradata = extradata; + avctx->extradata_size = sz; + } + + return 0; +} + +static int mf_output_type_get(AVCodecContext *avctx) +{ + MFContext *c = avctx->priv_data; + HRESULT hr; + IMFMediaType *type; + int ret; + + hr = IMFTransform_GetOutputCurrentType(c->mft, c->out_stream_id, &type); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "could not get output type\n"); + return AVERROR_EXTERNAL; + } + + av_log(avctx, AV_LOG_VERBOSE, "final output type:\n"); + ff_media_type_dump(avctx, type); + + ret = 0; + if (c->is_video) { + ret = mf_encv_output_type_get(avctx, type); + } else if (c->is_audio) { + ret = mf_enca_output_type_get(avctx, type); + } + + if (ret < 0) + av_log(avctx, AV_LOG_ERROR, "output type not supported\n"); + + IMFMediaType_Release(type); + return ret; +} + +static int mf_sample_to_avpacket(AVCodecContext *avctx, IMFSample *sample, AVPacket *avpkt) +{ + MFContext *c = avctx->priv_data; + HRESULT hr; + int ret; + DWORD len; + IMFMediaBuffer *buffer; + BYTE *data; + UINT64 t; + UINT32 t32; + + hr = IMFSample_GetTotalLength(sample, &len); + if (FAILED(hr)) + return AVERROR_EXTERNAL; + + if ((ret = av_new_packet(avpkt, len)) < 0) + return ret; + + IMFSample_ConvertToContiguousBuffer(sample, &buffer); + if (FAILED(hr)) + return AVERROR_EXTERNAL; + + hr = IMFMediaBuffer_Lock(buffer, &data, NULL, NULL); + if (FAILED(hr)) { + IMFMediaBuffer_Release(buffer); + return AVERROR_EXTERNAL; + } + + memcpy(avpkt->data, data, len); + + IMFMediaBuffer_Unlock(buffer); + IMFMediaBuffer_Release(buffer); + + avpkt->pts = avpkt->dts = mf_sample_get_pts(avctx, sample); + + hr = IMFAttributes_GetUINT32(sample, &MFSampleExtension_CleanPoint, &t32); + if (c->is_audio || (!FAILED(hr) && t32 != 0)) + avpkt->flags |= AV_PKT_FLAG_KEY; + + hr = IMFAttributes_GetUINT64(sample, &MFSampleExtension_DecodeTimestamp, &t); + if (!FAILED(hr)) { + avpkt->dts = mf_from_mf_time(avctx, t); + // At least on Qualcomm's HEVC encoder on SD 835, the output dts + // starts from the input pts of the first frame, while the output pts + // is shifted forward. Therefore, shift the output values back so that + // the output pts matches the input. + if (c->reorder_delay == AV_NOPTS_VALUE) + c->reorder_delay = avpkt->pts - avpkt->dts; + avpkt->dts -= c->reorder_delay; + avpkt->pts -= c->reorder_delay; + } + + return 0; +} + +static IMFSample *mf_a_avframe_to_sample(AVCodecContext *avctx, const AVFrame *frame) +{ + MFContext *c = avctx->priv_data; + size_t len; + size_t bps; + IMFSample *sample; + + bps = av_get_bytes_per_sample(avctx->sample_fmt) * avctx->channels; + len = frame->nb_samples * bps; + + sample = ff_create_memory_sample(frame->data[0], len, c->in_info.cbAlignment); + if (sample) + IMFSample_SetSampleDuration(sample, mf_to_mf_time(avctx, frame->nb_samples)); + return sample; +} + +static IMFSample *mf_v_avframe_to_sample(AVCodecContext *avctx, const AVFrame *frame) +{ + MFContext *c = avctx->priv_data; + IMFSample *sample; + IMFMediaBuffer *buffer; + BYTE *data; + HRESULT hr; + int ret; + int size; + + size = av_image_get_buffer_size(avctx->pix_fmt, avctx->width, avctx->height, 1); + if (size < 0) + return NULL; + + sample = ff_create_memory_sample(NULL, size, c->in_info.cbAlignment); + if (!sample) + return NULL; + + hr = IMFSample_GetBufferByIndex(sample, 0, &buffer); + if (FAILED(hr)) { + IMFSample_Release(sample); + return NULL; + } + + hr = IMFMediaBuffer_Lock(buffer, &data, NULL, NULL); + if (FAILED(hr)) { + IMFMediaBuffer_Release(buffer); + IMFSample_Release(sample); + return NULL; + } + + ret = av_image_copy_to_buffer((uint8_t *)data, size, (void *)frame->data, frame->linesize, + avctx->pix_fmt, avctx->width, avctx->height, 1); + IMFMediaBuffer_SetCurrentLength(buffer, size); + IMFMediaBuffer_Unlock(buffer); + IMFMediaBuffer_Release(buffer); + if (ret < 0) { + IMFSample_Release(sample); + return NULL; + } + + IMFSample_SetSampleDuration(sample, mf_to_mf_time(avctx, frame->pkt_duration)); + + return sample; +} + +static IMFSample *mf_avframe_to_sample(AVCodecContext *avctx, const AVFrame *frame) +{ + MFContext *c = avctx->priv_data; + IMFSample *sample; + + if (c->is_audio) { + sample = mf_a_avframe_to_sample(avctx, frame); + } else { + sample = mf_v_avframe_to_sample(avctx, frame); + } + + if (sample) + mf_sample_set_pts(avctx, sample, frame->pts); + + return sample; +} + +static int mf_send_sample(AVCodecContext *avctx, IMFSample *sample) +{ + MFContext *c = avctx->priv_data; + HRESULT hr; + int ret; + + if (sample) { + if (c->async_events) { + if ((ret = mf_wait_events(avctx)) < 0) + return ret; + if (!c->async_need_input) + return AVERROR(EAGAIN); + } + if (!c->sample_sent) + IMFSample_SetUINT32(sample, &MFSampleExtension_Discontinuity, TRUE); + c->sample_sent = 1; + hr = IMFTransform_ProcessInput(c->mft, c->in_stream_id, sample, 0); + if (hr == MF_E_NOTACCEPTING) { + return AVERROR(EAGAIN); + } else if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "failed processing input: %s\n", ff_hr_str(hr)); + return AVERROR_EXTERNAL; + } + c->async_need_input = 0; + } else if (!c->draining) { + hr = IMFTransform_ProcessMessage(c->mft, MFT_MESSAGE_COMMAND_DRAIN, 0); + if (FAILED(hr)) + av_log(avctx, AV_LOG_ERROR, "failed draining: %s\n", ff_hr_str(hr)); + // Some MFTs (AC3) will send a frame after each drain command (???), so + // this is required to make draining actually terminate. + c->draining = 1; + c->async_need_input = 0; + } else { + return AVERROR_EOF; + } + return 0; +} + +static int mf_send_frame(AVCodecContext *avctx, const AVFrame *frame) +{ + MFContext *c = avctx->priv_data; + int ret; + IMFSample *sample = NULL; + if (frame) { + sample = mf_avframe_to_sample(avctx, frame); + if (!sample) + return AVERROR(ENOMEM); + if (c->is_video && c->codec_api) { + if (frame->pict_type == AV_PICTURE_TYPE_I || !c->sample_sent) + ICodecAPI_SetValue(c->codec_api, &ff_CODECAPI_AVEncVideoForceKeyFrame, FF_VAL_VT_UI4(1)); + } + } + ret = mf_send_sample(avctx, sample); + if (sample) + IMFSample_Release(sample); + return ret; +} + +static int mf_receive_sample(AVCodecContext *avctx, IMFSample **out_sample) +{ + MFContext *c = avctx->priv_data; + HRESULT hr; + DWORD st; + MFT_OUTPUT_DATA_BUFFER out_buffers; + IMFSample *sample; + int ret = 0; + + while (1) { + *out_sample = NULL; + sample = NULL; + + if (c->async_events) { + if ((ret = mf_wait_events(avctx)) < 0) + return ret; + if (!c->async_have_output || c->draining_done) { + ret = 0; + break; + } + } + + if (!c->out_stream_provides_samples) { + sample = ff_create_memory_sample(NULL, c->out_info.cbSize, c->out_info.cbAlignment); + if (!sample) + return AVERROR(ENOMEM); + } + + out_buffers = (MFT_OUTPUT_DATA_BUFFER) { + .dwStreamID = c->out_stream_id, + .pSample = sample, + }; + + st = 0; + hr = IMFTransform_ProcessOutput(c->mft, 0, 1, &out_buffers, &st); + + if (out_buffers.pEvents) + IMFCollection_Release(out_buffers.pEvents); + + if (!FAILED(hr)) { + *out_sample = out_buffers.pSample; + ret = 0; + break; + } + + if (out_buffers.pSample) + IMFSample_Release(out_buffers.pSample); + + if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { + if (c->draining) + c->draining_done = 1; + ret = 0; + } else if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { + av_log(avctx, AV_LOG_WARNING, "stream format change\n"); + ret = mf_choose_output_type(avctx); + if (ret == 0) // we don't expect renegotiating the input type + ret = AVERROR_EXTERNAL; + if (ret > 0) { + ret = mf_setup_context(avctx); + if (ret >= 0) { + c->async_have_output = 0; + continue; + } + } + } else { + av_log(avctx, AV_LOG_ERROR, "failed processing output: %s\n", ff_hr_str(hr)); + ret = AVERROR_EXTERNAL; + } + + break; + } + + c->async_have_output = 0; + + if (ret >= 0 && !*out_sample) + ret = c->draining_done ? AVERROR_EOF : AVERROR(EAGAIN); + + return ret; +} + +static int mf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) +{ + IMFSample *sample; + int ret; + + ret = mf_receive_sample(avctx, &sample); + if (ret < 0) + return ret; + + ret = mf_sample_to_avpacket(avctx, sample, avpkt); + IMFSample_Release(sample); + + return ret; +} + +// Most encoders seem to enumerate supported audio formats on the output types, +// at least as far as channel configuration and sample rate is concerned. Pick +// the one which seems to match best. +static int64_t mf_enca_output_score(AVCodecContext *avctx, IMFMediaType *type) +{ + MFContext *c = avctx->priv_data; + HRESULT hr; + UINT32 t; + GUID tg; + int64_t score = 0; + + hr = IMFAttributes_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &t); + if (!FAILED(hr) && t == avctx->sample_rate) + score |= 1LL << 32; + + hr = IMFAttributes_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &t); + if (!FAILED(hr) && t == avctx->channels) + score |= 2LL << 32; + + hr = IMFAttributes_GetGUID(type, &MF_MT_SUBTYPE, &tg); + if (!FAILED(hr)) { + if (IsEqualGUID(&c->main_subtype, &tg)) + score |= 4LL << 32; + } + + // Select the bitrate (lowest priority). + hr = IMFAttributes_GetUINT32(type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &t); + if (!FAILED(hr)) { + int diff = (int)t - avctx->bit_rate / 8; + if (diff >= 0) { + score |= (1LL << 31) - diff; // prefer lower bitrate + } else { + score |= (1LL << 30) + diff; // prefer higher bitrate + } + } + + hr = IMFAttributes_GetUINT32(type, &MF_MT_AAC_PAYLOAD_TYPE, &t); + if (!FAILED(hr) && t != 0) + return -1; + + return score; +} + +static int mf_enca_output_adjust(AVCodecContext *avctx, IMFMediaType *type) +{ + // (some decoders allow adjusting this freely, but it can also cause failure + // to set the output type - so it's commented for being too fragile) + //IMFAttributes_SetUINT32(type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, avctx->bit_rate / 8); + //IMFAttributes_SetUINT32(type, &MF_MT_AVG_BITRATE, avctx->bit_rate); + + return 0; +} + +static int64_t mf_enca_input_score(AVCodecContext *avctx, IMFMediaType *type) +{ + HRESULT hr; + UINT32 t; + int64_t score = 0; + + enum AVSampleFormat sformat = ff_media_type_to_sample_fmt((IMFAttributes *)type); + if (sformat == AV_SAMPLE_FMT_NONE) + return -1; // can not use + + if (sformat == avctx->sample_fmt) + score |= 1; + + hr = IMFAttributes_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &t); + if (!FAILED(hr) && t == avctx->sample_rate) + score |= 2; + + hr = IMFAttributes_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &t); + if (!FAILED(hr) && t == avctx->channels) + score |= 4; + + return score; +} + +static int mf_enca_input_adjust(AVCodecContext *avctx, IMFMediaType *type) +{ + HRESULT hr; + UINT32 t; + + enum AVSampleFormat sformat = ff_media_type_to_sample_fmt((IMFAttributes *)type); + if (sformat != avctx->sample_fmt) { + av_log(avctx, AV_LOG_ERROR, "unsupported input sample format set\n"); + return AVERROR(EINVAL); + } + + hr = IMFAttributes_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &t); + if (FAILED(hr) || t != avctx->sample_rate) { + av_log(avctx, AV_LOG_ERROR, "unsupported input sample rate set\n"); + return AVERROR(EINVAL); + } + + hr = IMFAttributes_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &t); + if (FAILED(hr) || t != avctx->channels) { + av_log(avctx, AV_LOG_ERROR, "unsupported input channel number set\n"); + return AVERROR(EINVAL); + } + + return 0; +} + +static int64_t mf_encv_output_score(AVCodecContext *avctx, IMFMediaType *type) +{ + MFContext *c = avctx->priv_data; + GUID tg; + HRESULT hr; + int score = -1; + + hr = IMFAttributes_GetGUID(type, &MF_MT_SUBTYPE, &tg); + if (!FAILED(hr)) { + if (IsEqualGUID(&c->main_subtype, &tg)) + score = 1; + } + + return score; +} + +static int mf_encv_output_adjust(AVCodecContext *avctx, IMFMediaType *type) +{ + MFContext *c = avctx->priv_data; + AVRational framerate; + + ff_MFSetAttributeSize((IMFAttributes *)type, &MF_MT_FRAME_SIZE, avctx->width, avctx->height); + IMFAttributes_SetUINT32(type, &MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); + + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { + framerate = avctx->framerate; + } else { + framerate = av_inv_q(avctx->time_base); + framerate.den *= avctx->ticks_per_frame; + } + + ff_MFSetAttributeRatio((IMFAttributes *)type, &MF_MT_FRAME_RATE, framerate.num, framerate.den); + + // (MS HEVC supports eAVEncH265VProfile_Main_420_8 only.) + if (avctx->codec_id == AV_CODEC_ID_H264) { + UINT32 profile = ff_eAVEncH264VProfile_Base; + switch (avctx->profile) { + case FF_PROFILE_H264_MAIN: + profile = ff_eAVEncH264VProfile_Main; + break; + case FF_PROFILE_H264_HIGH: + profile = ff_eAVEncH264VProfile_High; + break; + } + IMFAttributes_SetUINT32(type, &MF_MT_MPEG2_PROFILE, profile); + } + + IMFAttributes_SetUINT32(type, &MF_MT_AVG_BITRATE, avctx->bit_rate); + + // Note that some of the ICodecAPI options must be set before SetOutputType. + if (c->codec_api) { + if (avctx->bit_rate) + ICodecAPI_SetValue(c->codec_api, &ff_CODECAPI_AVEncCommonMeanBitRate, FF_VAL_VT_UI4(avctx->bit_rate)); + + if (c->opt_enc_rc >= 0) + ICodecAPI_SetValue(c->codec_api, &ff_CODECAPI_AVEncCommonRateControlMode, FF_VAL_VT_UI4(c->opt_enc_rc)); + + if (c->opt_enc_quality >= 0) + ICodecAPI_SetValue(c->codec_api, &ff_CODECAPI_AVEncCommonQuality, FF_VAL_VT_UI4(c->opt_enc_quality)); + + // Always set the number of b-frames. Qualcomm's HEVC encoder on SD835 + // defaults this to 1, and that setting is buggy with many of the + // rate control modes. (0 or 2 b-frames works fine with most rate + // control modes, but 2 seems buggy with the u_vbr mode.) Setting + // "scenario" to "camera_record" sets it in CFR mode (where the default + // is VFR), which makes the encoder avoid dropping frames. + ICodecAPI_SetValue(c->codec_api, &ff_CODECAPI_AVEncMPVDefaultBPictureCount, FF_VAL_VT_UI4(avctx->max_b_frames)); + avctx->has_b_frames = avctx->max_b_frames > 0; + + ICodecAPI_SetValue(c->codec_api, &ff_CODECAPI_AVEncH264CABACEnable, FF_VAL_VT_BOOL(1)); + + if (c->opt_enc_scenario >= 0) + ICodecAPI_SetValue(c->codec_api, &ff_CODECAPI_AVScenarioInfo, FF_VAL_VT_UI4(c->opt_enc_scenario)); + } + + return 0; +} + +static int64_t mf_encv_input_score(AVCodecContext *avctx, IMFMediaType *type) +{ + enum AVPixelFormat pix_fmt = ff_media_type_to_pix_fmt((IMFAttributes *)type); + if (pix_fmt != avctx->pix_fmt) + return -1; // can not use + + return 0; +} + +static int mf_encv_input_adjust(AVCodecContext *avctx, IMFMediaType *type) +{ + enum AVPixelFormat pix_fmt = ff_media_type_to_pix_fmt((IMFAttributes *)type); + if (pix_fmt != avctx->pix_fmt) { + av_log(avctx, AV_LOG_ERROR, "unsupported input pixel format set\n"); + return AVERROR(EINVAL); + } + + //ff_MFSetAttributeSize((IMFAttributes *)type, &MF_MT_FRAME_SIZE, avctx->width, avctx->height); + + return 0; +} + +static int mf_choose_output_type(AVCodecContext *avctx) +{ + MFContext *c = avctx->priv_data; + HRESULT hr; + int ret; + IMFMediaType *out_type = NULL; + int64_t out_type_score = -1; + int out_type_index = -1; + int n; + + av_log(avctx, AV_LOG_VERBOSE, "output types:\n"); + for (n = 0; ; n++) { + IMFMediaType *type; + int64_t score = -1; + + hr = IMFTransform_GetOutputAvailableType(c->mft, c->out_stream_id, n, &type); + if (hr == MF_E_NO_MORE_TYPES || hr == E_NOTIMPL) + break; + if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) { + av_log(avctx, AV_LOG_VERBOSE, "(need to set input type)\n"); + ret = 0; + goto done; + } + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "error getting output type: %s\n", ff_hr_str(hr)); + ret = AVERROR_EXTERNAL; + goto done; + } + + av_log(avctx, AV_LOG_VERBOSE, "output type %d:\n", n); + ff_media_type_dump(avctx, type); + + if (c->is_video) { + score = mf_encv_output_score(avctx, type); + } else if (c->is_audio) { + score = mf_enca_output_score(avctx, type); + } + + if (score > out_type_score) { + if (out_type) + IMFMediaType_Release(out_type); + out_type = type; + out_type_score = score; + out_type_index = n; + IMFMediaType_AddRef(out_type); + } + + IMFMediaType_Release(type); + } + + if (out_type) { + av_log(avctx, AV_LOG_VERBOSE, "picking output type %d.\n", out_type_index); + } else { + hr = MFCreateMediaType(&out_type); + if (FAILED(hr)) { + ret = AVERROR(ENOMEM); + goto done; + } + } + + ret = 0; + if (c->is_video) { + ret = mf_encv_output_adjust(avctx, out_type); + } else if (c->is_audio) { + ret = mf_enca_output_adjust(avctx, out_type); + } + + if (ret >= 0) { + av_log(avctx, AV_LOG_VERBOSE, "setting output type:\n"); + ff_media_type_dump(avctx, out_type); + + hr = IMFTransform_SetOutputType(c->mft, c->out_stream_id, out_type, 0); + if (!FAILED(hr)) { + ret = 1; + } else if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) { + av_log(avctx, AV_LOG_VERBOSE, "rejected - need to set input type\n"); + ret = 0; + } else { + av_log(avctx, AV_LOG_ERROR, "could not set output type (%s)\n", ff_hr_str(hr)); + ret = AVERROR_EXTERNAL; + } + } + +done: + if (out_type) + IMFMediaType_Release(out_type); + return ret; +} + +static int mf_choose_input_type(AVCodecContext *avctx) +{ + MFContext *c = avctx->priv_data; + HRESULT hr; + int ret; + IMFMediaType *in_type = NULL; + int64_t in_type_score = -1; + int in_type_index = -1; + int n; + + av_log(avctx, AV_LOG_VERBOSE, "input types:\n"); + for (n = 0; ; n++) { + IMFMediaType *type = NULL; + int64_t score = -1; + + hr = IMFTransform_GetInputAvailableType(c->mft, c->in_stream_id, n, &type); + if (hr == MF_E_NO_MORE_TYPES || hr == E_NOTIMPL) + break; + if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) { + av_log(avctx, AV_LOG_VERBOSE, "(need to set output type 1)\n"); + ret = 0; + goto done; + } + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "error getting input type: %s\n", ff_hr_str(hr)); + ret = AVERROR_EXTERNAL; + goto done; + } + + av_log(avctx, AV_LOG_VERBOSE, "input type %d:\n", n); + ff_media_type_dump(avctx, type); + + if (c->is_video) { + score = mf_encv_input_score(avctx, type); + } else if (c->is_audio) { + score = mf_enca_input_score(avctx, type); + } + + if (score > in_type_score) { + if (in_type) + IMFMediaType_Release(in_type); + in_type = type; + in_type_score = score; + in_type_index = n; + IMFMediaType_AddRef(in_type); + } + + IMFMediaType_Release(type); + } + + if (in_type) { + av_log(avctx, AV_LOG_VERBOSE, "picking input type %d.\n", in_type_index); + } else { + // Some buggy MFTs (WMA encoder) fail to return MF_E_TRANSFORM_TYPE_NOT_SET. + av_log(avctx, AV_LOG_VERBOSE, "(need to set output type 2)\n"); + ret = 0; + goto done; + } + + ret = 0; + if (c->is_video) { + ret = mf_encv_input_adjust(avctx, in_type); + } else if (c->is_audio) { + ret = mf_enca_input_adjust(avctx, in_type); + } + + if (ret >= 0) { + av_log(avctx, AV_LOG_VERBOSE, "setting input type:\n"); + ff_media_type_dump(avctx, in_type); + + hr = IMFTransform_SetInputType(c->mft, c->in_stream_id, in_type, 0); + if (!FAILED(hr)) { + ret = 1; + } else if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) { + av_log(avctx, AV_LOG_VERBOSE, "rejected - need to set output type\n"); + ret = 0; + } else { + av_log(avctx, AV_LOG_ERROR, "could not set input type (%s)\n", ff_hr_str(hr)); + ret = AVERROR_EXTERNAL; + } + } + +done: + if (in_type) + IMFMediaType_Release(in_type); + return ret; +} + +static int mf_negotiate_types(AVCodecContext *avctx) +{ + // This follows steps 1-5 on: + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa965264(v=vs.85).aspx + // If every MFT implementer does this correctly, this loop should at worst + // be repeated once. + int need_input = 1, need_output = 1; + int n; + for (n = 0; n < 2 && (need_input || need_output); n++) { + int ret; + ret = mf_choose_input_type(avctx); + if (ret < 0) + return ret; + need_input = ret < 1; + ret = mf_choose_output_type(avctx); + if (ret < 0) + return ret; + need_output = ret < 1; + } + if (need_input || need_output) { + av_log(avctx, AV_LOG_ERROR, "format negotiation failed (%d/%d)\n", + need_input, need_output); + return AVERROR_EXTERNAL; + } + return 0; +} + +static int mf_setup_context(AVCodecContext *avctx) +{ + MFContext *c = avctx->priv_data; + HRESULT hr; + int ret; + + hr = IMFTransform_GetInputStreamInfo(c->mft, c->in_stream_id, &c->in_info); + if (FAILED(hr)) + return AVERROR_EXTERNAL; + av_log(avctx, AV_LOG_VERBOSE, "in_info: size=%d, align=%d\n", + (int)c->in_info.cbSize, (int)c->in_info.cbAlignment); + + hr = IMFTransform_GetOutputStreamInfo(c->mft, c->out_stream_id, &c->out_info); + if (FAILED(hr)) + return AVERROR_EXTERNAL; + c->out_stream_provides_samples = + (c->out_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) || + (c->out_info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES); + av_log(avctx, AV_LOG_VERBOSE, "out_info: size=%d, align=%d%s\n", + (int)c->out_info.cbSize, (int)c->out_info.cbAlignment, + c->out_stream_provides_samples ? " (provides samples)" : ""); + + if ((ret = mf_output_type_get(avctx)) < 0) + return ret; + + return 0; +} + +static int mf_unlock_async(AVCodecContext *avctx) +{ + MFContext *c = avctx->priv_data; + HRESULT hr; + IMFAttributes *attrs; + UINT32 v; + int res = AVERROR_EXTERNAL; + + // For hw encoding we unfortunately need to use async mode, otherwise + // play it safe and avoid it. + if (!(c->is_video && c->opt_enc_hw)) + return 0; + + hr = IMFTransform_GetAttributes(c->mft, &attrs); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "error retrieving MFT attributes: %s\n", ff_hr_str(hr)); + goto err; + } + + hr = IMFAttributes_GetUINT32(attrs, &MF_TRANSFORM_ASYNC, &v); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "error querying async: %s\n", ff_hr_str(hr)); + goto err; + } + + if (!v) { + av_log(avctx, AV_LOG_ERROR, "hardware MFT is not async\n"); + goto err; + } + + hr = IMFAttributes_SetUINT32(attrs, &MF_TRANSFORM_ASYNC_UNLOCK, TRUE); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "could not set async unlock: %s\n", ff_hr_str(hr)); + goto err; + } + + hr = IMFTransform_QueryInterface(c->mft, &IID_IMFMediaEventGenerator, (void **)&c->async_events); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "could not get async interface\n"); + goto err; + } + + res = 0; + +err: + IMFAttributes_Release(attrs); + return res; +} + +static int mf_create(void *log, IMFTransform **mft, const AVCodec *codec, int use_hw) +{ + int is_audio = codec->type == AVMEDIA_TYPE_AUDIO; + const CLSID *subtype = ff_codec_to_mf_subtype(codec->id); + MFT_REGISTER_TYPE_INFO reg = {0}; + GUID category; + int ret; + + *mft = NULL; + + if (!subtype) + return AVERROR(ENOSYS); + + reg.guidSubtype = *subtype; + + if (is_audio) { + reg.guidMajorType = MFMediaType_Audio; + category = MFT_CATEGORY_AUDIO_ENCODER; + } else { + reg.guidMajorType = MFMediaType_Video; + category = MFT_CATEGORY_VIDEO_ENCODER; + } + + if ((ret = ff_instantiate_mf(log, category, NULL, ®, use_hw, mft)) < 0) + return ret; + + return 0; +} + +static int mf_init(AVCodecContext *avctx) +{ + MFContext *c = avctx->priv_data; + HRESULT hr; + int ret; + const CLSID *subtype = ff_codec_to_mf_subtype(avctx->codec_id); + int use_hw = 0; + + c->is_audio = avctx->codec_type == AVMEDIA_TYPE_AUDIO; + c->is_video = !c->is_audio; + c->reorder_delay = AV_NOPTS_VALUE; + + if (c->is_video && c->opt_enc_hw) + use_hw = 1; + + if (!subtype) + return AVERROR(ENOSYS); + + c->main_subtype = *subtype; + + if ((ret = mf_create(avctx, &c->mft, avctx->codec, use_hw)) < 0) + return ret; + + if ((ret = mf_unlock_async(avctx)) < 0) + return ret; + + hr = IMFTransform_QueryInterface(c->mft, &IID_ICodecAPI, (void **)&c->codec_api); + if (!FAILED(hr)) + av_log(avctx, AV_LOG_VERBOSE, "MFT supports ICodecAPI.\n"); + + + hr = IMFTransform_GetStreamIDs(c->mft, 1, &c->in_stream_id, 1, &c->out_stream_id); + if (hr == E_NOTIMPL) { + c->in_stream_id = c->out_stream_id = 0; + } else if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "could not get stream IDs (%s)\n", ff_hr_str(hr)); + return AVERROR_EXTERNAL; + } + + if ((ret = mf_negotiate_types(avctx)) < 0) + return ret; + + if ((ret = mf_setup_context(avctx)) < 0) + return ret; + + hr = IMFTransform_ProcessMessage(c->mft, MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "could not start streaming (%s)\n", ff_hr_str(hr)); + return AVERROR_EXTERNAL; + } + + hr = IMFTransform_ProcessMessage(c->mft, MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "could not start stream (%s)\n", ff_hr_str(hr)); + return AVERROR_EXTERNAL; + } + + if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER && c->async_events && + c->is_video && !avctx->extradata) { + int sleep = 10000, total = 0; + av_log(avctx, AV_LOG_VERBOSE, "Awaiting extradata\n"); + while (total < 70*1000) { + // The Qualcomm H264 encoder on SD835 doesn't provide extradata + // immediately, but it becomes available soon after init (without + // any waitable event). In practice, it's available after less + // than 10 ms, but wait for up to 70 ms before giving up. + // Some encoders (Qualcomm's HEVC encoder on SD835, some versions + // of the QSV H264 encoder at least) don't provide extradata this + // way at all, not even after encoding a frame - it's only + // available prepended to frames. + av_usleep(sleep); + total += sleep; + mf_output_type_get(avctx); + if (avctx->extradata) + break; + sleep *= 2; + } + av_log(avctx, AV_LOG_VERBOSE, "%s extradata in %d ms\n", + avctx->extradata ? "Got" : "Didn't get", total / 1000); + } + + return 0; +} + +static int mf_close(AVCodecContext *avctx) +{ + MFContext *c = avctx->priv_data; + + if (c->codec_api) + ICodecAPI_Release(c->codec_api); + + if (c->async_events) + IMFMediaEventGenerator_Release(c->async_events); + + ff_free_mf(&c->mft); + + av_freep(&avctx->extradata); + avctx->extradata_size = 0; + + return 0; +} + +#define OFFSET(x) offsetof(MFContext, x) + +#define MF_ENCODER(MEDIATYPE, NAME, ID, OPTS, EXTRA) \ + static const AVClass ff_ ## NAME ## _mf_encoder_class = { \ + .class_name = #NAME "_mf", \ + .item_name = av_default_item_name, \ + .option = OPTS, \ + .version = LIBAVUTIL_VERSION_INT, \ + }; \ + AVCodec ff_ ## NAME ## _mf_encoder = { \ + .priv_class = &ff_ ## NAME ## _mf_encoder_class, \ + .name = #NAME "_mf", \ + .long_name = NULL_IF_CONFIG_SMALL(#ID " via MediaFoundation"), \ + .type = AVMEDIA_TYPE_ ## MEDIATYPE, \ + .id = AV_CODEC_ID_ ## ID, \ + .priv_data_size = sizeof(MFContext), \ + .init = mf_init, \ + .close = mf_close, \ + .send_frame = mf_send_frame, \ + .receive_packet = mf_receive_packet, \ + EXTRA \ + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HYBRID, \ + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | \ + FF_CODEC_CAP_INIT_CLEANUP, \ + }; + +#define AFMTS \ + .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16, \ + AV_SAMPLE_FMT_NONE }, + +MF_ENCODER(AUDIO, aac, AAC, NULL, AFMTS); +MF_ENCODER(AUDIO, ac3, AC3, NULL, AFMTS); +MF_ENCODER(AUDIO, mp3, MP3, NULL, AFMTS); + +#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" }, + // 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"}, + + {"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}, + {NULL} +}; + +#define VFMTS \ + .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_NV12, \ + AV_PIX_FMT_YUV420P, \ + AV_PIX_FMT_NONE }, + +MF_ENCODER(VIDEO, h264, H264, venc_opts, VFMTS); +MF_ENCODER(VIDEO, hevc, HEVC, venc_opts, VFMTS); diff --git a/libavcodec/midivid.c b/libavcodec/midivid.c new file mode 100644 index 00000000000..2200440e2ca --- /dev/null +++ b/libavcodec/midivid.c @@ -0,0 +1,292 @@ +/* + * MidiVid decoder + * 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 +#include +#include + +#include "libavutil/imgutils.h" +#include "libavutil/internal.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/mem.h" + +#define BITSTREAM_READER_LE +#include "avcodec.h" +#include "get_bits.h" +#include "bytestream.h" +#include "internal.h" + +typedef struct MidiVidContext { + GetByteContext gb; + + uint8_t *uncompressed; + unsigned int uncompressed_size; + uint8_t *skip; + + AVFrame *frame; +} MidiVidContext; + +static int decode_mvdv(MidiVidContext *s, AVCodecContext *avctx, AVFrame *frame) +{ + GetByteContext *gb = &s->gb; + GetBitContext mask; + GetByteContext idx9; + uint16_t nb_vectors, intra_flag; + const uint8_t *vec; + const uint8_t *mask_start; + uint8_t *skip; + uint32_t mask_size; + int idx9bits = 0; + int idx9val = 0; + uint32_t nb_blocks; + + nb_vectors = bytestream2_get_le16(gb); + intra_flag = !!bytestream2_get_le16(gb); + if (intra_flag) { + nb_blocks = (avctx->width / 2) * (avctx->height / 2); + } else { + int ret, skip_linesize, padding; + + nb_blocks = bytestream2_get_le32(gb); + skip_linesize = avctx->width >> 1; + mask_start = gb->buffer_start + bytestream2_tell(gb); + mask_size = (FFALIGN(avctx->width, 32) >> 2) * (avctx->height >> 2) >> 3; + padding = (FFALIGN(avctx->width, 32) - avctx->width) >> 2; + + if (bytestream2_get_bytes_left(gb) < mask_size) + return AVERROR_INVALIDDATA; + + ret = init_get_bits8(&mask, mask_start, mask_size); + if (ret < 0) + return ret; + bytestream2_skip(gb, mask_size); + skip = s->skip; + + for (int y = 0; y < avctx->height >> 2; y++) { + for (int x = 0; x < avctx->width >> 2; x++) { + int flag = !get_bits1(&mask); + + skip[(y*2) *skip_linesize + x*2 ] = flag; + skip[(y*2) *skip_linesize + x*2+1] = flag; + skip[(y*2+1)*skip_linesize + x*2 ] = flag; + skip[(y*2+1)*skip_linesize + x*2+1] = flag; + } + skip_bits_long(&mask, padding); + } + } + + vec = gb->buffer_start + bytestream2_tell(gb); + if (bytestream2_get_bytes_left(gb) < nb_vectors * 12) + return AVERROR_INVALIDDATA; + bytestream2_skip(gb, nb_vectors * 12); + if (nb_vectors > 256) { + if (bytestream2_get_bytes_left(gb) < (nb_blocks + 7 * !intra_flag) / 8) + return AVERROR_INVALIDDATA; + bytestream2_init(&idx9, gb->buffer_start + bytestream2_tell(gb), (nb_blocks + 7 * !intra_flag) / 8); + bytestream2_skip(gb, (nb_blocks + 7 * !intra_flag) / 8); + } + + skip = s->skip; + + for (int y = avctx->height - 2; y >= 0; y -= 2) { + uint8_t *dsty = frame->data[0] + y * frame->linesize[0]; + uint8_t *dstu = frame->data[1] + y * frame->linesize[1]; + uint8_t *dstv = frame->data[2] + y * frame->linesize[2]; + + for (int x = 0; x < avctx->width; x += 2) { + int idx; + + if (!intra_flag && *skip++) + continue; + if (bytestream2_get_bytes_left(gb) <= 0) + return AVERROR_INVALIDDATA; + if (nb_vectors <= 256) { + idx = bytestream2_get_byte(gb); + } else { + if (idx9bits == 0) { + idx9val = bytestream2_get_byte(&idx9); + idx9bits = 8; + } + idx9bits--; + idx = bytestream2_get_byte(gb) | (((idx9val >> (7 - idx9bits)) & 1) << 8); + } + if (idx >= nb_vectors) + return AVERROR_INVALIDDATA; + + dsty[x +frame->linesize[0]] = vec[idx * 12 + 0]; + dsty[x+1+frame->linesize[0]] = vec[idx * 12 + 3]; + dsty[x] = vec[idx * 12 + 6]; + dsty[x+1] = vec[idx * 12 + 9]; + + dstu[x +frame->linesize[1]] = vec[idx * 12 + 1]; + dstu[x+1+frame->linesize[1]] = vec[idx * 12 + 4]; + dstu[x] = vec[idx * 12 + 7]; + dstu[x+1] = vec[idx * 12 +10]; + + dstv[x +frame->linesize[2]] = vec[idx * 12 + 2]; + dstv[x+1+frame->linesize[2]] = vec[idx * 12 + 5]; + dstv[x] = vec[idx * 12 + 8]; + dstv[x+1] = vec[idx * 12 +11]; + } + } + + return intra_flag; +} + +static ptrdiff_t lzss_uncompress(MidiVidContext *s, GetByteContext *gb, uint8_t *dst, unsigned int size) +{ + uint8_t *dst_start = dst; + uint8_t *dst_end = dst + size; + + for (;bytestream2_get_bytes_left(gb) >= 3;) { + int op = bytestream2_get_le16(gb); + + for (int i = 0; i < 16; i++) { + if (op & 1) { + int s0 = bytestream2_get_byte(gb); + int s1 = bytestream2_get_byte(gb); + int offset = ((s0 & 0xF0) << 4) | s1; + int length = (s0 & 0xF) + 3; + + if (dst + length > dst_end || + dst - offset < dst_start) + return AVERROR_INVALIDDATA; + if (offset > 0) { + for (int j = 0; j < length; j++) { + dst[j] = dst[j - offset]; + } + } + dst += length; + } else { + if (dst >= dst_end) + return AVERROR_INVALIDDATA; + *dst++ = bytestream2_get_byte(gb); + } + op >>= 1; + } + } + + return dst - dst_start; +} + +static int decode_frame(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *avpkt) +{ + MidiVidContext *s = avctx->priv_data; + GetByteContext *gb = &s->gb; + AVFrame *frame = s->frame; + int ret, key, uncompressed; + + if (avpkt->size <= 13) + return AVERROR_INVALIDDATA; + + bytestream2_init(gb, avpkt->data, avpkt->size); + bytestream2_skip(gb, 8); + uncompressed = bytestream2_get_le32(gb); + + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) + return ret; + + if (uncompressed) { + ret = decode_mvdv(s, avctx, frame); + } else { + av_fast_padded_malloc(&s->uncompressed, &s->uncompressed_size, 16LL * (avpkt->size - 12)); + if (!s->uncompressed) + return AVERROR(ENOMEM); + + ret = lzss_uncompress(s, gb, s->uncompressed, s->uncompressed_size); + if (ret < 0) + return ret; + bytestream2_init(gb, s->uncompressed, ret); + ret = decode_mvdv(s, avctx, frame); + } + + if (ret < 0) + return ret; + key = ret; + + if ((ret = av_frame_ref(data, s->frame)) < 0) + return ret; + + frame->pict_type = key ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + frame->key_frame = key; + *got_frame = 1; + + return avpkt->size; +} + +static av_cold int decode_init(AVCodecContext *avctx) +{ + MidiVidContext *s = avctx->priv_data; + int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx); + + if (avctx->width & 3 || avctx->height & 3) + ret = AVERROR_INVALIDDATA; + + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid image size %dx%d.\n", + avctx->width, avctx->height); + return ret; + } + + avctx->pix_fmt = AV_PIX_FMT_YUV444P; + + s->frame = av_frame_alloc(); + if (!s->frame) + return AVERROR(ENOMEM); + s->skip = av_calloc(avctx->width >> 1, avctx->height >> 1); + if (!s->skip) + return AVERROR(ENOMEM); + + return 0; +} + +static void decode_flush(AVCodecContext *avctx) +{ + MidiVidContext *s = avctx->priv_data; + + av_frame_unref(s->frame); +} + +static av_cold int decode_close(AVCodecContext *avctx) +{ + MidiVidContext *s = avctx->priv_data; + + av_frame_free(&s->frame); + av_freep(&s->uncompressed); + av_freep(&s->skip); + + return 0; +} + +AVCodec ff_mvdv_decoder = { + .name = "mvdv", + .long_name = NULL_IF_CONFIG_SMALL("MidiVid VQ"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_MVDV, + .priv_data_size = sizeof(MidiVidContext), + .init = decode_init, + .decode = decode_frame, + .flush = decode_flush, + .close = decode_close, + .capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/libavcodec/mimic.c b/libavcodec/mimic.c index 1d463e9962c..2563a49d53c 100644 --- a/libavcodec/mimic.c +++ b/libavcodec/mimic.c @@ -128,8 +128,7 @@ static av_cold int mimic_decode_end(AVCodecContext *avctx) av_frame_free(&ctx->frames[i].f); } - if (!avctx->internal->is_copy) - ff_free_vlc(&ctx->vlc); + ff_free_vlc(&ctx->vlc); return 0; } @@ -139,8 +138,6 @@ static av_cold int mimic_decode_init(AVCodecContext *avctx) MimicContext *ctx = avctx->priv_data; int ret, i; - avctx->internal->allocate_progress = 1; - ctx->prev_index = 0; ctx->cur_index = 15; @@ -451,24 +448,6 @@ static int mimic_decode_frame(AVCodecContext *avctx, void *data, return buf_size; } -#if HAVE_THREADS -static av_cold int mimic_init_thread_copy(AVCodecContext *avctx) -{ - MimicContext *ctx = avctx->priv_data; - int i; - - for (i = 0; i < FF_ARRAY_ELEMS(ctx->frames); i++) { - ctx->frames[i].f = av_frame_alloc(); - if (!ctx->frames[i].f) { - mimic_decode_end(avctx); - return AVERROR(ENOMEM); - } - } - - return 0; -} -#endif - AVCodec ff_mimic_decoder = { .name = "mimic", .long_name = NULL_IF_CONFIG_SMALL("Mimic"), @@ -480,5 +459,5 @@ AVCodec ff_mimic_decoder = { .decode = mimic_decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, .update_thread_context = ONLY_IF_THREADS_ENABLED(mimic_decode_update_thread_context), - .init_thread_copy = ONLY_IF_THREADS_ENABLED(mimic_init_thread_copy), + .caps_internal = FF_CODEC_CAP_ALLOCATE_PROGRESS, }; diff --git a/libavcodec/mips/Makefile b/libavcodec/mips/Makefile index c5b54d55c08..b4993f6e766 100644 --- a/libavcodec/mips/Makefile +++ b/libavcodec/mips/Makefile @@ -89,3 +89,4 @@ MMI-OBJS-$(CONFIG_WMV2DSP) += mips/wmv2dsp_mmi.o MMI-OBJS-$(CONFIG_HEVC_DECODER) += mips/hevcdsp_mmi.o MMI-OBJS-$(CONFIG_VP3DSP) += mips/vp3dsp_idct_mmi.o MMI-OBJS-$(CONFIG_VP9_DECODER) += mips/vp9_mc_mmi.o +MSA-OBJS-$(CONFIG_VC1_DECODER) += mips/vc1dsp_msa.o diff --git a/libavcodec/mips/aacdec_mips.c b/libavcodec/mips/aacdec_mips.c index 253cdeb80b0..8e306529351 100644 --- a/libavcodec/mips/aacdec_mips.c +++ b/libavcodec/mips/aacdec_mips.c @@ -59,6 +59,7 @@ #include "libavutil/mips/asmdefs.h" #if HAVE_INLINE_ASM +#if HAVE_MIPSFPU static av_always_inline void float_copy(float *dst, const float *src, int count) { // Copy 'count' floats from src to dst @@ -237,9 +238,9 @@ static void apply_ltp_mips(AACContext *ac, SingleChannelElement *sce) if (ltp->lag < 1024) num_samples = ltp->lag + 1024; - j = (2048 - num_samples) >> 2; - k = (2048 - num_samples) & 3; - p_predTime = &predTime[num_samples]; + j = (2048 - num_samples) >> 2; + k = (2048 - num_samples) & 3; + p_predTime = &predTime[num_samples]; for (i = 0; i < num_samples; i++) predTime[i] = sce->ltp_state[i + 2048 - ltp->lag] * ltp->coef; @@ -282,7 +283,6 @@ static void apply_ltp_mips(AACContext *ac, SingleChannelElement *sce) } } -#if HAVE_MIPSFPU static av_always_inline void fmul_and_reverse(float *dst, const float *src0, const float *src1, int count) { /* Multiply 'count' floats in src0 by src1 and store the results in dst in reverse */ @@ -433,9 +433,9 @@ static void update_ltp_mips(AACContext *ac, SingleChannelElement *sce) void ff_aacdec_init_mips(AACContext *c) { #if HAVE_INLINE_ASM +#if HAVE_MIPSFPU c->imdct_and_windowing = imdct_and_windowing_mips; c->apply_ltp = apply_ltp_mips; -#if HAVE_MIPSFPU c->update_ltp = update_ltp_mips; #endif /* HAVE_MIPSFPU */ #endif /* HAVE_INLINE_ASM */ diff --git a/libavcodec/mips/aacpsdsp_mips.c b/libavcodec/mips/aacpsdsp_mips.c index 83fdc2f9dbd..ef47e31a9ea 100644 --- a/libavcodec/mips/aacpsdsp_mips.c +++ b/libavcodec/mips/aacpsdsp_mips.c @@ -57,6 +57,7 @@ #include "libavutil/mips/asmdefs.h" #if HAVE_INLINE_ASM +#if HAVE_MIPSFPU static void ps_hybrid_analysis_ileave_mips(float (*out)[32][2], float L[2][38][64], int i, int len) { @@ -187,7 +188,6 @@ static void ps_hybrid_synthesis_deint_mips(float out[2][38][64], } } -#if HAVE_MIPSFPU #if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 static void ps_add_squares_mips(float *dst, const float (*src)[2], int n) { @@ -450,9 +450,9 @@ static void ps_stereo_interpolate_mips(float (*l)[2], float (*r)[2], void ff_psdsp_init_mips(PSDSPContext *s) { #if HAVE_INLINE_ASM +#if HAVE_MIPSFPU s->hybrid_analysis_ileave = ps_hybrid_analysis_ileave_mips; s->hybrid_synthesis_deint = ps_hybrid_synthesis_deint_mips; -#if HAVE_MIPSFPU #if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 s->add_squares = ps_add_squares_mips; s->mul_pair_single = ps_mul_pair_single_mips; diff --git a/libavcodec/mips/aacsbr_mips.c b/libavcodec/mips/aacsbr_mips.c index 56aa4e86822..2e0cd723d7e 100644 --- a/libavcodec/mips/aacsbr_mips.c +++ b/libavcodec/mips/aacsbr_mips.c @@ -58,6 +58,7 @@ #define ENVELOPE_ADJUSTMENT_OFFSET 2 #if HAVE_INLINE_ASM +#if HAVE_MIPSFPU static int sbr_lf_gen_mips(AACContext *ac, SpectralBandReplication *sbr, float X_low[32][40][2], const float W[2][32][32][2], int buf_idx) @@ -310,7 +311,6 @@ static int sbr_x_gen_mips(SpectralBandReplication *sbr, float X[2][38][64], return 0; } -#if HAVE_MIPSFPU #if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 static void sbr_hf_assemble_mips(float Y1[38][64][2], const float X_high[64][40][2], @@ -611,9 +611,9 @@ static void sbr_hf_inverse_filter_mips(SBRDSPContext *dsp, void ff_aacsbr_func_ptr_init_mips(AACSBRContext *c) { #if HAVE_INLINE_ASM +#if HAVE_MIPSFPU c->sbr_lf_gen = sbr_lf_gen_mips; c->sbr_x_gen = sbr_x_gen_mips; -#if HAVE_MIPSFPU #if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 c->sbr_hf_inverse_filter = sbr_hf_inverse_filter_mips; c->sbr_hf_assemble = sbr_hf_assemble_mips; diff --git a/libavcodec/mips/cabac.h b/libavcodec/mips/cabac.h index 2a05e5ab3cc..03b5010edcc 100644 --- a/libavcodec/mips/cabac.h +++ b/libavcodec/mips/cabac.h @@ -29,7 +29,7 @@ #include "config.h" #define get_cabac_inline get_cabac_inline_mips -static av_always_inline int get_cabac_inline(CABACContext *c, +static av_always_inline int get_cabac_inline_mips(CABACContext *c, uint8_t * const state){ mips_reg tmp0, tmp1, tmp2, bit; diff --git a/libavcodec/mips/h264dsp_mmi.c b/libavcodec/mips/h264dsp_mmi.c index ac65a20db0c..0459711b82d 100644 --- a/libavcodec/mips/h264dsp_mmi.c +++ b/libavcodec/mips/h264dsp_mmi.c @@ -38,6 +38,9 @@ void ff_h264_add_pixels4_8_mmi(uint8_t *dst, int16_t *src, int stride) MMI_LDC1(%[ftmp2], %[src], 0x08) MMI_LDC1(%[ftmp3], %[src], 0x10) MMI_LDC1(%[ftmp4], %[src], 0x18) + /* memset(src, 0, 32); */ + "gssqc1 %[ftmp0], %[ftmp0], 0x00(%[src]) \n\t" + "gssqc1 %[ftmp0], %[ftmp0], 0x10(%[src]) \n\t" MMI_ULWC1(%[ftmp5], %[dst0], 0x00) MMI_ULWC1(%[ftmp6], %[dst1], 0x00) MMI_ULWC1(%[ftmp7], %[dst2], 0x00) @@ -58,11 +61,6 @@ void ff_h264_add_pixels4_8_mmi(uint8_t *dst, int16_t *src, int stride) MMI_SWC1(%[ftmp2], %[dst1], 0x00) MMI_SWC1(%[ftmp3], %[dst2], 0x00) MMI_SWC1(%[ftmp4], %[dst3], 0x00) - - /* memset(src, 0, 32); */ - "xor %[ftmp0], %[ftmp0], %[ftmp0] \n\t" - "gssqc1 %[ftmp0], %[ftmp0], 0x00(%[src]) \n\t" - "gssqc1 %[ftmp0], %[ftmp0], 0x10(%[src]) \n\t" : [ftmp0]"=&f"(ftmp[0]), [ftmp1]"=&f"(ftmp[1]), [ftmp2]"=&f"(ftmp[2]), [ftmp3]"=&f"(ftmp[3]), [ftmp4]"=&f"(ftmp[4]), [ftmp5]"=&f"(ftmp[5]), @@ -85,15 +83,19 @@ void ff_h264_idct_add_8_mmi(uint8_t *dst, int16_t *block, int stride) DECLARE_VAR_ADDRT; __asm__ volatile ( - "dli %[tmp0], 0x01 \n\t" MMI_LDC1(%[ftmp0], %[block], 0x00) - "mtc1 %[tmp0], %[ftmp8] \n\t" MMI_LDC1(%[ftmp1], %[block], 0x08) - "dli %[tmp0], 0x06 \n\t" MMI_LDC1(%[ftmp2], %[block], 0x10) + MMI_LDC1(%[ftmp3], %[block], 0x18) + /* memset(block, 0, 32) */ + "xor %[ftmp4], %[ftmp4], %[ftmp4] \n\t" + "gssqc1 %[ftmp4], %[ftmp4], 0x00(%[block]) \n\t" + "gssqc1 %[ftmp4], %[ftmp4], 0x10(%[block]) \n\t" + "dli %[tmp0], 0x01 \n\t" + "mtc1 %[tmp0], %[ftmp8] \n\t" + "dli %[tmp0], 0x06 \n\t" "mtc1 %[tmp0], %[ftmp9] \n\t" "psrah %[ftmp4], %[ftmp1], %[ftmp8] \n\t" - MMI_LDC1(%[ftmp3], %[block], 0x18) "psrah %[ftmp5], %[ftmp3], %[ftmp8] \n\t" "psubh %[ftmp4], %[ftmp4], %[ftmp3] \n\t" "paddh %[ftmp5], %[ftmp5], %[ftmp1] \n\t" @@ -121,15 +123,11 @@ void ff_h264_idct_add_8_mmi(uint8_t *dst, int16_t *block, int stride) "paddh %[ftmp10], %[ftmp3], %[ftmp1] \n\t" "psubh %[ftmp1], %[ftmp1], %[ftmp3] \n\t" "paddh %[ftmp11], %[ftmp4], %[ftmp5] \n\t" - "xor %[ftmp7], %[ftmp7], %[ftmp7] \n\t" "psubh %[ftmp5], %[ftmp5], %[ftmp4] \n\t" - MMI_SDC1(%[ftmp7], %[block], 0x00) - MMI_SDC1(%[ftmp7], %[block], 0x08) - MMI_SDC1(%[ftmp7], %[block], 0x10) - MMI_SDC1(%[ftmp7], %[block], 0x18) MMI_ULWC1(%[ftmp2], %[dst], 0x00) - "psrah %[ftmp3], %[ftmp10], %[ftmp9] \n\t" MMI_LWXC1(%[ftmp0], %[dst], %[stride], 0x00) + "xor %[ftmp7], %[ftmp7], %[ftmp7] \n\t" + "psrah %[ftmp3], %[ftmp10], %[ftmp9] \n\t" "psrah %[ftmp4], %[ftmp11], %[ftmp9] \n\t" "punpcklbh %[ftmp2], %[ftmp2], %[ftmp7] \n\t" "punpcklbh %[ftmp0], %[ftmp0], %[ftmp7] \n\t" @@ -153,11 +151,6 @@ void ff_h264_idct_add_8_mmi(uint8_t *dst, int16_t *block, int stride) MMI_SWC1(%[ftmp2], %[dst], 0x00) "packushb %[ftmp0], %[ftmp0], %[ftmp7] \n\t" MMI_SWXC1(%[ftmp0], %[dst], %[stride], 0x00) - - /* memset(block, 0, 32) */ - "xor %[ftmp0], %[ftmp0], %[ftmp0] \n\t" - "gssqc1 %[ftmp0], %[ftmp0], 0x00(%[block]) \n\t" - "gssqc1 %[ftmp0], %[ftmp0], 0x10(%[block]) \n\t" : [ftmp0]"=&f"(ftmp[0]), [ftmp1]"=&f"(ftmp[1]), [ftmp2]"=&f"(ftmp[2]), [ftmp3]"=&f"(ftmp[3]), [ftmp4]"=&f"(ftmp[4]), [ftmp5]"=&f"(ftmp[5]), @@ -620,17 +613,6 @@ void ff_h264_idct8_add_8_mmi(uint8_t *dst, int16_t *block, int stride) MMI_SWC1(%[ftmp6], %[addr0], 0x00) MMI_SWXC1(%[ftmp7], %[addr0], %[stride], 0x00) PTR_ADDIU "$29, $29, 0x20 \n\t" - - /* memset(block, 0, 128) */ - "xor %[ftmp0], %[ftmp0], %[ftmp0] \n\t" - "gssqc1 %[ftmp0], %[ftmp0], 0x00(%[block]) \n\t" - "gssqc1 %[ftmp0], %[ftmp0], 0x10(%[block]) \n\t" - "gssqc1 %[ftmp0], %[ftmp0], 0x20(%[block]) \n\t" - "gssqc1 %[ftmp0], %[ftmp0], 0x30(%[block]) \n\t" - "gssqc1 %[ftmp0], %[ftmp0], 0x40(%[block]) \n\t" - "gssqc1 %[ftmp0], %[ftmp0], 0x50(%[block]) \n\t" - "gssqc1 %[ftmp0], %[ftmp0], 0x60(%[block]) \n\t" - "gssqc1 %[ftmp0], %[ftmp0], 0x70(%[block]) \n\t" : [ftmp0]"=&f"(ftmp[0]), [ftmp1]"=&f"(ftmp[1]), [ftmp2]"=&f"(ftmp[2]), [ftmp3]"=&f"(ftmp[3]), [ftmp4]"=&f"(ftmp[4]), [ftmp5]"=&f"(ftmp[5]), diff --git a/libavcodec/mips/h264dsp_msa.c b/libavcodec/mips/h264dsp_msa.c index 89fe399469c..dd0598291e7 100644 --- a/libavcodec/mips/h264dsp_msa.c +++ b/libavcodec/mips/h264dsp_msa.c @@ -413,8 +413,7 @@ static void avc_biwgt_8x8_msa(uint8_t *src, uint8_t *dst, int32_t stride, tmp7 = __msa_dpadd_s_h(offset, wgt, vec7); SRA_4V(tmp0, tmp1, tmp2, tmp3, denom); SRA_4V(tmp4, tmp5, tmp6, tmp7, denom); - CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); - CLIP_SH4_0_255(tmp4, tmp5, tmp6, tmp7); + CLIP_SH8_0_255(tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, dst0, dst1); PCKEV_B2_UB(tmp5, tmp4, tmp7, tmp6, dst2, dst3); ST_D8(dst0, dst1, dst2, dst3, 0, 1, 0, 1, 0, 1, 0, 1, dst, stride); @@ -475,8 +474,7 @@ static void avc_biwgt_8x16_msa(uint8_t *src, uint8_t *dst, int32_t stride, SRA_4V(temp0, temp1, temp2, temp3, denom); SRA_4V(temp4, temp5, temp6, temp7, denom); - CLIP_SH4_0_255(temp0, temp1, temp2, temp3); - CLIP_SH4_0_255(temp4, temp5, temp6, temp7); + CLIP_SH8_0_255(temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7); PCKEV_B4_UB(temp1, temp0, temp3, temp2, temp5, temp4, temp7, temp6, dst0, dst1, dst2, dst3); ST_D8(dst0, dst1, dst2, dst3, 0, 1, 0, 1, 0, 1, 0, 1, dst, stride); @@ -531,7 +529,7 @@ static void avc_biwgt_8x16_msa(uint8_t *src, uint8_t *dst, int32_t stride, temp = p1_or_q1_org_in << 1; \ clip3 = clip3 - temp; \ clip3 = __msa_ave_s_h(p2_or_q2_org_in, clip3); \ - clip3 = CLIP_SH(clip3, negate_tc_in, tc_in); \ + CLIP_SH(clip3, negate_tc_in, tc_in); \ p1_or_q1_out = p1_or_q1_org_in + clip3; \ } @@ -549,7 +547,7 @@ static void avc_biwgt_8x16_msa(uint8_t *src, uint8_t *dst, int32_t stride, delta = q0_sub_p0 + p1_sub_q1; \ delta >>= 3; \ \ - delta = CLIP_SH(delta, negate_threshold_in, threshold_in); \ + CLIP_SH(delta, negate_threshold_in, threshold_in); \ \ p0_or_q0_out = p0_or_q0_org_in + delta; \ q0_or_p0_out = q0_or_p0_org_in - delta; \ @@ -598,7 +596,7 @@ static void avc_biwgt_8x16_msa(uint8_t *src, uint8_t *dst, int32_t stride, delta = q0_sub_p0 + p1_sub_q1; \ delta = __msa_srari_h(delta, 3); \ \ - delta = CLIP_SH(delta, -tc, tc); \ + CLIP_SH(delta, -tc, tc); \ \ ILVR_B2_SH(zeros, src1, zeros, src2, res0_r, res1_r); \ \ @@ -620,7 +618,7 @@ static void avc_biwgt_8x16_msa(uint8_t *src, uint8_t *dst, int32_t stride, \ out0 = (v16u8) __msa_ilvr_b((v16i8) in1, (v16i8) in0); \ out1 = (v16u8) __msa_sldi_b(zero_m, (v16i8) out0, 2); \ - SLDI_B2_0_UB(out1, out2, out2, out3, 2); \ + SLDI_B2_UB(zero_m, out1, zero_m, out2, 2, out2, out3); \ } #define AVC_LPF_H_2BYTE_CHROMA_422(src, stride, tc_val, alpha, beta, res) \ @@ -662,7 +660,7 @@ static void avc_biwgt_8x16_msa(uint8_t *src, uint8_t *dst, int32_t stride, q0_sub_p0 <<= 2; \ delta = q0_sub_p0 + p1_sub_q1; \ delta = __msa_srari_h(delta, 3); \ - delta = CLIP_SH(delta, -tc, tc); \ + CLIP_SH(delta, -tc, tc); \ \ ILVR_B2_SH(zeros, src1, zeros, src2, res0_r, res1_r); \ \ @@ -1025,7 +1023,8 @@ static void avc_h_loop_filter_luma_mbaff_intra_msa(uint8_t *src, int32_t stride, ILVR_W2_SB(tmp2, tmp0, tmp3, tmp1, src6, src3); ILVL_W2_SB(tmp2, tmp0, tmp3, tmp1, src1, src5); - SLDI_B4_0_SB(src6, src1, src3, src5, src0, src2, src4, src7, 8); + SLDI_B4_SB(zeros, src6, zeros, src1, zeros, src3, zeros, src5, + 8, src0, src2, src4, src7); p0_asub_q0 = __msa_asub_u_b((v16u8) src2, (v16u8) src3); p1_asub_p0 = __msa_asub_u_b((v16u8) src1, (v16u8) src2); @@ -1116,10 +1115,10 @@ static void avc_h_loop_filter_luma_mbaff_intra_msa(uint8_t *src, int32_t stride, ILVRL_H2_SH(zeros, dst2_x, tmp2, tmp3); ILVR_W2_UB(tmp2, tmp0, tmp3, tmp1, dst0, dst4); - SLDI_B2_0_UB(dst0, dst4, dst1, dst5, 8); + SLDI_B2_UB(zeros, dst0, zeros, dst4, 8, dst1, dst5); dst2_x = (v16u8) __msa_ilvl_w((v4i32) tmp2, (v4i32) tmp0); dst2_y = (v16u8) __msa_ilvl_w((v4i32) tmp3, (v4i32) tmp1); - SLDI_B2_0_UB(dst2_x, dst2_y, dst3_x, dst3_y, 8); + SLDI_B2_UB(zeros, dst2_x, zeros, dst2_y, 8, dst3_x, dst3_y); out0 = __msa_copy_u_w((v4i32) dst0, 0); out1 = __msa_copy_u_h((v8i16) dst0, 2); @@ -1741,7 +1740,7 @@ static void avc_h_loop_filter_luma_mbaff_msa(uint8_t *in, int32_t stride, v8i16 tc, tc_orig_r, tc_plus1; v16u8 is_tc_orig1, is_tc_orig2, tc_orig = { 0 }; v8i16 p0_ilvr_q0, p0_add_q0, q0_sub_p0, p1_sub_q1; - v8u16 src2_r, src3_r; + v8i16 src2_r, src3_r; v8i16 p2_r, p1_r, q2_r, q1_r; v16u8 p2, q2, p0, q0; v4i32 dst0, dst1; @@ -1839,8 +1838,8 @@ static void avc_h_loop_filter_luma_mbaff_msa(uint8_t *in, int32_t stride, tc_orig_r = (v8i16) __msa_ilvr_b(zeros, (v16i8) tc_orig); tc = tc_orig_r; - p2_r = CLIP_SH(p2_r, -tc_orig_r, tc_orig_r); - q2_r = CLIP_SH(q2_r, -tc_orig_r, tc_orig_r); + CLIP_SH(p2_r, -tc_orig_r, tc_orig_r); + CLIP_SH(q2_r, -tc_orig_r, tc_orig_r); p2_r += p1_r; q2_r += q1_r; @@ -1872,14 +1871,13 @@ static void avc_h_loop_filter_luma_mbaff_msa(uint8_t *in, int32_t stride, (v16i8) is_less_than_beta2); tc = (v8i16) __msa_bmnz_v((v16u8) tc, (v16u8) tc_plus1, is_less_than_beta2); - q0_sub_p0 = CLIP_SH(q0_sub_p0, -tc, tc); + CLIP_SH(q0_sub_p0, -tc, tc); - ILVR_B2_UH(zeros, src2, zeros, src3, src2_r, src3_r); + ILVR_B2_SH(zeros, src2, zeros, src3, src2_r, src3_r); src2_r += q0_sub_p0; src3_r -= q0_sub_p0; - src2_r = (v8u16) CLIP_SH_0_255(src2_r); - src3_r = (v8u16) CLIP_SH_0_255(src3_r); + CLIP_SH2_0_255(src2_r, src3_r); PCKEV_B2_UB(src2_r, src2_r, src3_r, src3_r, p0, q0); @@ -2509,10 +2507,8 @@ void ff_biweight_h264_pixels16_8_msa(uint8_t *dst, uint8_t *src, SRA_4V(tmp4, tmp5, tmp6, tmp7, denom); SRA_4V(tmp8, tmp9, tmp10, tmp11, denom); SRA_4V(tmp12, tmp13, tmp14, tmp15, denom); - CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); - CLIP_SH4_0_255(tmp4, tmp5, tmp6, tmp7); - CLIP_SH4_0_255(tmp8, tmp9, tmp10, tmp11); - CLIP_SH4_0_255(tmp12, tmp13, tmp14, tmp15); + CLIP_SH8_0_255(tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7); + CLIP_SH8_0_255(tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15); PCKEV_B4_UB(tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, tmp7, tmp6, dst0, dst1, dst2, dst3); PCKEV_B4_UB(tmp9, tmp8, tmp11, tmp10, tmp13, tmp12, tmp15, tmp14, dst4, @@ -2553,10 +2549,8 @@ void ff_biweight_h264_pixels16_8_msa(uint8_t *dst, uint8_t *src, SRA_4V(tmp4, tmp5, tmp6, tmp7, denom); SRA_4V(tmp8, tmp9, tmp10, tmp11, denom); SRA_4V(tmp12, tmp13, tmp14, tmp15, denom); - CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); - CLIP_SH4_0_255(tmp4, tmp5, tmp6, tmp7); - CLIP_SH4_0_255(tmp8, tmp9, tmp10, tmp11); - CLIP_SH4_0_255(tmp12, tmp13, tmp14, tmp15); + CLIP_SH8_0_255(tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7); + CLIP_SH8_0_255(tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15); PCKEV_B4_UB(tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, tmp7, tmp6, dst0, dst1, dst2, dst3); PCKEV_B4_UB(tmp9, tmp8, tmp11, tmp10, tmp13, tmp12, tmp15, tmp14, dst4, diff --git a/libavcodec/mips/h264idct_msa.c b/libavcodec/mips/h264idct_msa.c index 7851bfdf4b6..fbf7795e27f 100644 --- a/libavcodec/mips/h264idct_msa.c +++ b/libavcodec/mips/h264idct_msa.c @@ -233,8 +233,7 @@ static void avc_idct8_addblk_msa(uint8_t *dst, int16_t *src, int32_t dst_stride) res0, res1, res2, res3); ADD4(res4, tmp4, res5, tmp5, res6, tmp6, res7, tmp7, res4, res5, res6, res7); - CLIP_SH4_0_255(res0, res1, res2, res3); - CLIP_SH4_0_255(res4, res5, res6, res7); + CLIP_SH8_0_255(res0, res1, res2, res3, res4, res5, res6, res7); PCKEV_B4_SB(res1, res0, res3, res2, res5, res4, res7, res6, dst0, dst1, dst2, dst3); ST_D8(dst0, dst1, dst2, dst3, 0, 1, 0, 1, 0, 1, 0, 1, dst, dst_stride) @@ -263,8 +262,8 @@ static void avc_idct8_dc_addblk_msa(uint8_t *dst, int16_t *src, dst0_r, dst1_r, dst2_r, dst3_r); ADD4(dst4_r, dc, dst5_r, dc, dst6_r, dc, dst7_r, dc, dst4_r, dst5_r, dst6_r, dst7_r); - CLIP_SH4_0_255(dst0_r, dst1_r, dst2_r, dst3_r); - CLIP_SH4_0_255(dst4_r, dst5_r, dst6_r, dst7_r); + CLIP_SH8_0_255(dst0_r, dst1_r, dst2_r, dst3_r, + dst4_r, dst5_r, dst6_r, dst7_r); PCKEV_B4_SB(dst1_r, dst0_r, dst3_r, dst2_r, dst5_r, dst4_r, dst7_r, dst6_r, dst0, dst1, dst2, dst3); ST_D8(dst0, dst1, dst2, dst3, 0, 1, 0, 1, 0, 1, 0, 1, dst, dst_stride) diff --git a/libavcodec/mips/h264pred_init_mips.c b/libavcodec/mips/h264pred_init_mips.c index 63637b87327..e537ad8bd4e 100644 --- a/libavcodec/mips/h264pred_init_mips.c +++ b/libavcodec/mips/h264pred_init_mips.c @@ -73,10 +73,7 @@ static av_cold void h264_pred_init_msa(H264PredContext *h, int codec_id, switch (codec_id) { case AV_CODEC_ID_SVQ3: - ; - break; case AV_CODEC_ID_RV40: - ; break; case AV_CODEC_ID_VP7: case AV_CODEC_ID_VP8: diff --git a/libavcodec/mips/h264qpel_msa.c b/libavcodec/mips/h264qpel_msa.c index df7e3e2a3f7..e435c187504 100644 --- a/libavcodec/mips/h264qpel_msa.c +++ b/libavcodec/mips/h264qpel_msa.c @@ -790,8 +790,8 @@ void ff_put_h264_qpel16_mc10_msa(uint8_t *dst, const uint8_t *src, minus5b, res4, res5, res6, res7); DPADD_SB4_SH(vec2, vec5, vec8, vec11, plus20b, plus20b, plus20b, plus20b, res4, res5, res6, res7); - SLDI_B2_SB(src1, src3, src0, src2, src0, src2, 2); - SLDI_B2_SB(src5, src7, src4, src6, src4, src6, 2); + SLDI_B4_SB(src1, src0, src3, src2, src5, src4, src7, src6, 2, + src0, src2, src4, src6); SRARI_H4_SH(res0, res1, res2, res3, 5); SRARI_H4_SH(res4, res5, res6, res7, 5); SAT_SH4_SH(res0, res1, res2, res3, 7); @@ -858,8 +858,8 @@ void ff_put_h264_qpel16_mc30_msa(uint8_t *dst, const uint8_t *src, minus5b, res4, res5, res6, res7); DPADD_SB4_SH(vec2, vec5, vec8, vec11, plus20b, plus20b, plus20b, plus20b, res4, res5, res6, res7); - SLDI_B2_SB(src1, src3, src0, src2, src0, src2, 3); - SLDI_B2_SB(src5, src7, src4, src6, src4, src6, 3); + SLDI_B4_SB(src1, src0, src3, src2, src5, src4, src7, src6, 3, + src0, src2, src4, src6); SRARI_H4_SH(res0, res1, res2, res3, 5); SRARI_H4_SH(res4, res5, res6, res7, 5); SAT_SH4_SH(res0, res1, res2, res3, 7); @@ -911,10 +911,10 @@ void ff_put_h264_qpel8_mc10_msa(uint8_t *dst, const uint8_t *src, VSHF_B2_SB(src6, src6, src7, src7, mask2, mask2, vec10, vec11); DPADD_SB4_SH(vec8, vec9, vec10, vec11, plus20b, plus20b, plus20b, plus20b, res4, res5, res6, res7); - SLDI_B2_SB(src0, src1, src0, src1, src0, src1, 2); - SLDI_B2_SB(src2, src3, src2, src3, src2, src3, 2); - SLDI_B2_SB(src4, src5, src4, src5, src4, src5, 2); - SLDI_B2_SB(src6, src7, src6, src7, src6, src7, 2); + SLDI_B4_SB(src0, src0, src1, src1, src2, src2, src3, src3, 2, + src0, src1, src2, src3); + SLDI_B4_SB(src4, src4, src5, src5, src6, src6, src7, src7, 2, + src4, src5, src6, src7); PCKEV_D2_SB(src1, src0, src3, src2, src0, src1); PCKEV_D2_SB(src5, src4, src7, src6, src4, src5); SRARI_H4_SH(res0, res1, res2, res3, 5); @@ -966,10 +966,10 @@ void ff_put_h264_qpel8_mc30_msa(uint8_t *dst, const uint8_t *src, VSHF_B2_SB(src6, src6, src7, src7, mask2, mask2, vec10, vec11); DPADD_SB4_SH(vec8, vec9, vec10, vec11, plus20b, plus20b, plus20b, plus20b, res4, res5, res6, res7); - SLDI_B2_SB(src0, src1, src0, src1, src0, src1, 3); - SLDI_B2_SB(src2, src3, src2, src3, src2, src3, 3); - SLDI_B2_SB(src4, src5, src4, src5, src4, src5, 3); - SLDI_B2_SB(src6, src7, src6, src7, src6, src7, 3); + SLDI_B4_SB(src0, src0, src1, src1, src2, src2, src3, src3, 3, + src0, src1, src2, src3); + SLDI_B4_SB(src4, src4, src5, src5, src6, src6, src7, src7, 3, + src4, src5, src6, src7); PCKEV_D2_SB(src1, src0, src3, src2, src0, src1); PCKEV_D2_SB(src5, src4, src7, src6, src4, src5); SRARI_H4_SH(res0, res1, res2, res3, 5); @@ -1007,8 +1007,8 @@ void ff_put_h264_qpel4_mc10_msa(uint8_t *dst, const uint8_t *src, SRARI_H2_SH(res0, res1, 5); SAT_SH2_SH(res0, res1, 7); res = __msa_pckev_b((v16i8) res1, (v16i8) res0); - SLDI_B2_SB(src0, src1, src0, src1, src0, src1, 2); - SLDI_B2_SB(src2, src3, src2, src3, src2, src3, 2); + SLDI_B4_SB(src0, src0, src1, src1, src2, src2, src3, src3, 2, + src0, src1, src2, src3); src0 = (v16i8) __msa_insve_w((v4i32) src0, 1, (v4i32) src1); src1 = (v16i8) __msa_insve_w((v4i32) src2, 1, (v4i32) src3); src0 = (v16i8) __msa_insve_d((v2i64) src0, 1, (v2i64) src1); @@ -1038,8 +1038,8 @@ void ff_put_h264_qpel4_mc30_msa(uint8_t *dst, const uint8_t *src, SRARI_H2_SH(res0, res1, 5); SAT_SH2_SH(res0, res1, 7); res = __msa_pckev_b((v16i8) res1, (v16i8) res0); - SLDI_B2_SB(src0, src1, src0, src1, src0, src1, 3); - SLDI_B2_SB(src2, src3, src2, src3, src2, src3, 3); + SLDI_B4_SB(src0, src0, src1, src1, src2, src2, src3, src3, 3, + src0, src1, src2, src3); src0 = (v16i8) __msa_insve_w((v4i32) src0, 1, (v4i32) src1); src1 = (v16i8) __msa_insve_w((v4i32) src2, 1, (v4i32) src3); src0 = (v16i8) __msa_insve_d((v2i64) src0, 1, (v2i64) src1); @@ -3194,8 +3194,8 @@ void ff_avg_h264_qpel16_mc10_msa(uint8_t *dst, const uint8_t *src, minus5b, res4, res5, res6, res7); DPADD_SB4_SH(vec2, vec5, vec8, vec11, plus20b, plus20b, plus20b, plus20b, res4, res5, res6, res7); - SLDI_B2_SB(src1, src3, src0, src2, src0, src2, 2); - SLDI_B2_SB(src5, src7, src4, src6, src4, src6, 2); + SLDI_B4_SB(src1, src0, src3, src2, src5, src4, src7, src6, 2, + src0, src2, src4, src6); SRARI_H4_SH(res0, res1, res2, res3, 5); SRARI_H4_SH(res4, res5, res6, res7, 5); SAT_SH4_SH(res0, res1, res2, res3, 7); @@ -3266,8 +3266,8 @@ void ff_avg_h264_qpel16_mc30_msa(uint8_t *dst, const uint8_t *src, minus5b, res4, res5, res6, res7); DPADD_SB4_SH(vec2, vec5, vec8, vec11, plus20b, plus20b, plus20b, plus20b, res4, res5, res6, res7); - SLDI_B2_SB(src1, src3, src0, src2, src0, src2, 3); - SLDI_B2_SB(src5, src7, src4, src6, src4, src6, 3); + SLDI_B4_SB(src1, src0, src3, src2, src5, src4, src7, src6, 3, + src0, src2, src4, src6); SRARI_H4_SH(res0, res1, res2, res3, 5); SRARI_H4_SH(res4, res5, res6, res7, 5); SAT_SH4_SH(res0, res1, res2, res3, 7); @@ -3323,10 +3323,10 @@ void ff_avg_h264_qpel8_mc10_msa(uint8_t *dst, const uint8_t *src, VSHF_B2_SB(src6, src6, src7, src7, mask2, mask2, vec10, vec11); DPADD_SB4_SH(vec8, vec9, vec10, vec11, plus20b, plus20b, plus20b, plus20b, res4, res5, res6, res7); - SLDI_B2_SB(src0, src1, src0, src1, src0, src1, 2); - SLDI_B2_SB(src2, src3, src2, src3, src2, src3, 2); - SLDI_B2_SB(src4, src5, src4, src5, src4, src5, 2); - SLDI_B2_SB(src6, src7, src6, src7, src6, src7, 2); + SLDI_B4_SB(src0, src0, src1, src1, src2, src2, src3, src3, 2, + src0, src1, src2, src3); + SLDI_B4_SB(src4, src4, src5, src5, src6, src6, src7, src7, 2, + src4, src5, src6, src7); PCKEV_D2_SB(src1, src0, src3, src2, src0, src1); PCKEV_D2_SB(src5, src4, src7, src6, src4, src5); SRARI_H4_SH(res0, res1, res2, res3, 5); @@ -3388,10 +3388,10 @@ void ff_avg_h264_qpel8_mc30_msa(uint8_t *dst, const uint8_t *src, VSHF_B2_SB(src6, src6, src7, src7, mask2, mask2, vec10, vec11); DPADD_SB4_SH(vec8, vec9, vec10, vec11, plus20b, plus20b, plus20b, plus20b, res4, res5, res6, res7); - SLDI_B2_SB(src0, src1, src0, src1, src0, src1, 3); - SLDI_B2_SB(src2, src3, src2, src3, src2, src3, 3); - SLDI_B2_SB(src4, src5, src4, src5, src4, src5, 3); - SLDI_B2_SB(src6, src7, src6, src7, src6, src7, 3); + SLDI_B4_SB(src0, src0, src1, src1, src2, src2, src3, src3, 3, + src0, src1, src2, src3); + SLDI_B4_SB(src4, src4, src5, src5, src6, src6, src7, src7, 3, + src4, src5, src6, src7); PCKEV_D2_SB(src1, src0, src3, src2, src0, src1); PCKEV_D2_SB(src5, src4, src7, src6, src4, src5); SRARI_H4_SH(res0, res1, res2, res3, 5); @@ -3439,8 +3439,8 @@ void ff_avg_h264_qpel4_mc10_msa(uint8_t *dst, const uint8_t *src, SRARI_H2_SH(out0, out1, 5); SAT_SH2_SH(out0, out1, 7); res = __msa_pckev_b((v16i8) out1, (v16i8) out0); - SLDI_B2_SB(src0, src1, src0, src1, src0, src1, 2); - SLDI_B2_SB(src2, src3, src2, src3, src2, src3, 2); + SLDI_B4_SB(src0, src0, src1, src1, src2, src2, src3, src3, 2, + src0, src1, src2, src3); src0 = (v16i8) __msa_insve_w((v4i32) src0, 1, (v4i32) src1); src1 = (v16i8) __msa_insve_w((v4i32) src2, 1, (v4i32) src3); src0 = (v16i8) __msa_insve_d((v2i64) src0, 1, (v2i64) src1); @@ -3475,8 +3475,8 @@ void ff_avg_h264_qpel4_mc30_msa(uint8_t *dst, const uint8_t *src, SRARI_H2_SH(out0, out1, 5); SAT_SH2_SH(out0, out1, 7); res = __msa_pckev_b((v16i8) out1, (v16i8) out0); - SLDI_B2_SB(src0, src1, src0, src1, src0, src1, 3); - SLDI_B2_SB(src2, src3, src2, src3, src2, src3, 3); + SLDI_B4_SB(src0, src0, src1, src1, src2, src2, src3, src3, 3, + src0, src1, src2, src3); src0 = (v16i8) __msa_insve_w((v4i32) src0, 1, (v4i32) src1); src1 = (v16i8) __msa_insve_w((v4i32) src2, 1, (v4i32) src3); src0 = (v16i8) __msa_insve_d((v2i64) src0, 1, (v2i64) src1); diff --git a/libavcodec/mips/hevc_idct_msa.c b/libavcodec/mips/hevc_idct_msa.c index b14aec95ebc..5ab6acd1df5 100644 --- a/libavcodec/mips/hevc_idct_msa.c +++ b/libavcodec/mips/hevc_idct_msa.c @@ -803,8 +803,9 @@ static void hevc_addblk_16x16_msa(int16_t *coeffs, uint8_t *dst, int32_t stride) LD_SH4((coeffs + 8), 16, in1, in3, in5, in7); coeffs += 64; - CLIP_SH4_0_255(dst_r0, dst_l0, dst_r1, dst_l1); - CLIP_SH4_0_255(dst_r2, dst_l2, dst_r3, dst_l3); + CLIP_SH8_0_255(dst_r0, dst_l0, dst_r1, dst_l1, + dst_r2, dst_l2, dst_r3, dst_l3); + PCKEV_B4_UB(dst_l0, dst_r0, dst_l1, dst_r1, dst_l2, dst_r2, dst_l3, dst_r3, dst0, dst1, dst2, dst3); ST_UB4(dst0, dst1, dst2, dst3, dst, stride); @@ -825,8 +826,8 @@ static void hevc_addblk_16x16_msa(int16_t *coeffs, uint8_t *dst, int32_t stride) dst_r3 += in6; dst_l3 += in7; - CLIP_SH4_0_255(dst_r0, dst_l0, dst_r1, dst_l1); - CLIP_SH4_0_255(dst_r2, dst_l2, dst_r3, dst_l3); + CLIP_SH8_0_255(dst_r0, dst_l0, dst_r1, dst_l1, + dst_r2, dst_l2, dst_r3, dst_l3); PCKEV_B4_UB(dst_l0, dst_r0, dst_l1, dst_r1, dst_l2, dst_r2, dst_l3, dst_r3, dst0, dst1, dst2, dst3); ST_UB4(dst0, dst1, dst2, dst3, dst, stride); @@ -873,8 +874,8 @@ static void hevc_addblk_32x32_msa(int16_t *coeffs, uint8_t *dst, int32_t stride) LD_SH4((coeffs + 8), 16, in1, in3, in5, in7); coeffs += 64; - CLIP_SH4_0_255(dst_r0, dst_l0, dst_r1, dst_l1); - CLIP_SH4_0_255(dst_r2, dst_l2, dst_r3, dst_l3); + CLIP_SH8_0_255(dst_r0, dst_l0, dst_r1, dst_l1, + dst_r2, dst_l2, dst_r3, dst_l3); PCKEV_B4_UB(dst_l0, dst_r0, dst_l1, dst_r1, dst_l2, dst_r2, dst_l3, dst_r3, dst0, dst1, dst2, dst3); ST_UB2(dst0, dst1, dst, 16); @@ -905,8 +906,8 @@ static void hevc_addblk_32x32_msa(int16_t *coeffs, uint8_t *dst, int32_t stride) LD_SH4(coeffs, 16, in0, in2, in4, in6); LD_SH4((coeffs + 8), 16, in1, in3, in5, in7); - CLIP_SH4_0_255(dst_r0, dst_l0, dst_r1, dst_l1); - CLIP_SH4_0_255(dst_r2, dst_l2, dst_r3, dst_l3); + CLIP_SH8_0_255(dst_r0, dst_l0, dst_r1, dst_l1, + dst_r2, dst_l2, dst_r3, dst_l3); PCKEV_B4_UB(dst_l0, dst_r0, dst_l1, dst_r1, dst_l2, dst_r2, dst_l3, dst_r3, dst0, dst1, dst2, dst3); ST_UB2(dst0, dst1, dst, 16); @@ -928,8 +929,8 @@ static void hevc_addblk_32x32_msa(int16_t *coeffs, uint8_t *dst, int32_t stride) dst_r3 += in6; dst_l3 += in7; - CLIP_SH4_0_255(dst_r0, dst_l0, dst_r1, dst_l1); - CLIP_SH4_0_255(dst_r2, dst_l2, dst_r3, dst_l3); + CLIP_SH8_0_255(dst_r0, dst_l0, dst_r1, dst_l1, + dst_r2, dst_l2, dst_r3, dst_l3); PCKEV_B4_UB(dst_l0, dst_r0, dst_l1, dst_r1, dst_l2, dst_r2, dst_l3, dst_r3, dst0, dst1, dst2, dst3); ST_UB2(dst0, dst1, dst, 16); diff --git a/libavcodec/mips/hevc_lpf_sao_msa.c b/libavcodec/mips/hevc_lpf_sao_msa.c index ac218064043..26663dd89bc 100644 --- a/libavcodec/mips/hevc_lpf_sao_msa.c +++ b/libavcodec/mips/hevc_lpf_sao_msa.c @@ -140,19 +140,19 @@ static void hevc_loopfilter_luma_hor_msa(uint8_t *src, int32_t stride, temp1 = ((p3_src + p2_src) << 1) + p2_src + temp0; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - p2_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst0 = (v16u8) (temp2 + (v8i16) p2_src); temp1 = temp0 + p2_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 2); temp2 = (v8i16) (temp1 - p1_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst1 = (v16u8) (temp2 + (v8i16) p1_src); temp1 = (temp0 << 1) + p2_src + q1_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - p0_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst2 = (v16u8) (temp2 + (v8i16) p0_src); dst0 = __msa_bmz_v(dst0, (v16u8) p2_src, (v16u8) p_is_pcm_vec); @@ -165,19 +165,19 @@ static void hevc_loopfilter_luma_hor_msa(uint8_t *src, int32_t stride, temp1 = ((q3_src + q2_src) << 1) + q2_src + temp0; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - q2_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst5 = (v16u8) (temp2 + (v8i16) q2_src); temp1 = temp0 + q2_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 2); temp2 = (v8i16) (temp1 - q1_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst4 = (v16u8) (temp2 + (v8i16) q1_src); temp1 = (temp0 << 1) + p1_src + q2_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - q0_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst3 = (v16u8) (temp2 + (v8i16) q0_src); dst3 = __msa_bmz_v(dst3, (v16u8) q0_src, (v16u8) q_is_pcm_vec); @@ -218,15 +218,15 @@ static void hevc_loopfilter_luma_hor_msa(uint8_t *src, int32_t stride, abs_delta0 = __msa_add_a_h(delta0, (v8i16) zero); abs_delta0 = (v8u16) abs_delta0 < temp1; - delta0 = CLIP_SH(delta0, tc_neg, tc_pos); + CLIP_SH(delta0, tc_neg, tc_pos); - temp0 = (v8u16) (delta0 + p0_src); - temp0 = (v8u16) CLIP_SH_0_255(temp0); - temp0 = (v8u16) __msa_bmz_v((v16u8) temp0, (v16u8) p0_src, + temp2 = (v8i16) (delta0 + p0_src); + CLIP_SH_0_255(temp2); + temp0 = (v8u16) __msa_bmz_v((v16u8) temp2, (v16u8) p0_src, (v16u8) p_is_pcm_vec); temp2 = (v8i16) (q0_src - delta0); - temp2 = CLIP_SH_0_255(temp2); + CLIP_SH_0_255(temp2); temp2 = (v8i16) __msa_bmz_v((v16u8) temp2, (v16u8) q0_src, (v16u8) q_is_pcm_vec); @@ -252,9 +252,9 @@ static void hevc_loopfilter_luma_hor_msa(uint8_t *src, int32_t stride, delta1 -= (v8i16) p1_src; delta1 += delta0; delta1 >>= 1; - delta1 = CLIP_SH(delta1, tc_neg, tc_pos); + CLIP_SH(delta1, tc_neg, tc_pos); delta1 = (v8i16) p1_src + (v8i16) delta1; - delta1 = CLIP_SH_0_255(delta1); + CLIP_SH_0_255(delta1); delta1 = (v8i16) __msa_bmnz_v((v16u8) delta1, (v16u8) p1_src, (v16u8) p_is_pcm_vec); @@ -262,9 +262,9 @@ static void hevc_loopfilter_luma_hor_msa(uint8_t *src, int32_t stride, delta2 = delta2 - (v8i16) q1_src; delta2 = delta2 - delta0; delta2 = delta2 >> 1; - delta2 = CLIP_SH(delta2, tc_neg, tc_pos); + CLIP_SH(delta2, tc_neg, tc_pos); delta2 = (v8i16) q1_src + (v8i16) delta2; - delta2 = CLIP_SH_0_255(delta2); + CLIP_SH_0_255(delta2); delta2 = (v8i16) __msa_bmnz_v((v16u8) delta2, (v16u8) q1_src, (v16u8) q_is_pcm_vec); @@ -298,19 +298,19 @@ static void hevc_loopfilter_luma_hor_msa(uint8_t *src, int32_t stride, temp1 = ((p3_src + p2_src) << 1) + p2_src + temp0; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - p2_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst0 = (v16u8) (temp2 + (v8i16) p2_src); temp1 = temp0 + p2_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 2); temp2 = (v8i16) (temp1 - p1_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst1 = (v16u8) (temp2 + (v8i16) p1_src); temp1 = (temp0 << 1) + p2_src + q1_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - p0_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst2 = (v16u8) (temp2 + (v8i16) p0_src); dst0 = __msa_bmz_v(dst0, (v16u8) p2_src, (v16u8) p_is_pcm_vec); @@ -323,19 +323,19 @@ static void hevc_loopfilter_luma_hor_msa(uint8_t *src, int32_t stride, temp1 = ((q3_src + q2_src) << 1) + q2_src + temp0; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - q2_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst5 = (v16u8) (temp2 + (v8i16) q2_src); temp1 = temp0 + q2_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 2); temp2 = (v8i16) (temp1 - q1_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst4 = (v16u8) (temp2 + (v8i16) q1_src); temp1 = (temp0 << 1) + p1_src + q2_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - q0_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst3 = (v16u8) (temp2 + (v8i16) q0_src); dst3 = __msa_bmz_v(dst3, (v16u8) q0_src, (v16u8) q_is_pcm_vec); @@ -362,15 +362,15 @@ static void hevc_loopfilter_luma_hor_msa(uint8_t *src, int32_t stride, abs_delta0 = __msa_add_a_h(delta0, (v8i16) zero); abs_delta0 = (v8u16) abs_delta0 < temp1; - delta0 = CLIP_SH(delta0, tc_neg, tc_pos); + CLIP_SH(delta0, tc_neg, tc_pos); - temp0 = (v8u16) (delta0 + p0_src); - temp0 = (v8u16) CLIP_SH_0_255(temp0); - temp0 = (v8u16) __msa_bmz_v((v16u8) temp0, (v16u8) p0_src, + temp2 = (v8i16) (delta0 + p0_src); + CLIP_SH_0_255(temp2); + temp0 = (v8u16) __msa_bmz_v((v16u8) temp2, (v16u8) p0_src, (v16u8) p_is_pcm_vec); temp2 = (v8i16) (q0_src - delta0); - temp2 = CLIP_SH_0_255(temp2); + CLIP_SH_0_255(temp2); temp2 = (v8i16) __msa_bmz_v((v16u8) temp2, (v16u8) q0_src, (v16u8) q_is_pcm_vec); @@ -394,9 +394,9 @@ static void hevc_loopfilter_luma_hor_msa(uint8_t *src, int32_t stride, delta1 -= (v8i16) p1_src; delta1 += delta0; delta1 >>= 1; - delta1 = CLIP_SH(delta1, tc_neg, tc_pos); + CLIP_SH(delta1, tc_neg, tc_pos); delta1 = (v8i16) p1_src + (v8i16) delta1; - delta1 = CLIP_SH_0_255(delta1); + CLIP_SH_0_255(delta1); delta1 = (v8i16) __msa_bmnz_v((v16u8) delta1, (v16u8) p1_src, (v16u8) p_is_pcm_vec); @@ -404,9 +404,9 @@ static void hevc_loopfilter_luma_hor_msa(uint8_t *src, int32_t stride, delta2 = delta2 - (v8i16) q1_src; delta2 = delta2 - delta0; delta2 = delta2 >> 1; - delta2 = CLIP_SH(delta2, tc_neg, tc_pos); + CLIP_SH(delta2, tc_neg, tc_pos); delta2 = (v8i16) q1_src + (v8i16) delta2; - delta2 = CLIP_SH_0_255(delta2); + CLIP_SH_0_255(delta2); delta2 = (v8i16) __msa_bmnz_v((v16u8) delta2, (v16u8) q1_src, (v16u8) q_is_pcm_vec); @@ -561,19 +561,19 @@ static void hevc_loopfilter_luma_ver_msa(uint8_t *src, int32_t stride, temp1 = ((p3_src + p2_src) << 1) + p2_src + temp0; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - p2_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst0 = (v16u8) (temp2 + (v8i16) p2_src); temp1 = temp0 + p2_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 2); temp2 = (v8i16) (temp1 - p1_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst1 = (v16u8) (temp2 + (v8i16) p1_src); temp1 = (temp0 << 1) + p2_src + q1_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - p0_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst2 = (v16u8) (temp2 + (v8i16) p0_src); dst0 = __msa_bmz_v(dst0, (v16u8) p2_src, (v16u8) p_is_pcm_vec); @@ -585,19 +585,19 @@ static void hevc_loopfilter_luma_ver_msa(uint8_t *src, int32_t stride, temp1 = ((q3_src + q2_src) << 1) + q2_src + temp0; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - q2_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst5 = (v16u8) (temp2 + (v8i16) q2_src); temp1 = temp0 + q2_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 2); temp2 = (v8i16) (temp1 - q1_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst4 = (v16u8) (temp2 + (v8i16) q1_src); temp1 = (temp0 << 1) + p1_src + q2_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - q0_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst3 = (v16u8) (temp2 + (v8i16) q0_src); dst3 = __msa_bmz_v(dst3, (v16u8) q0_src, (v16u8) q_is_pcm_vec); @@ -620,14 +620,14 @@ static void hevc_loopfilter_luma_ver_msa(uint8_t *src, int32_t stride, abs_delta0 = __msa_add_a_h(delta0, (v8i16) zero); abs_delta0 = (v8u16) abs_delta0 < temp1; - delta0 = CLIP_SH(delta0, tc_neg, tc_pos); - temp0 = (v8u16) (delta0 + p0_src); - temp0 = (v8u16) CLIP_SH_0_255(temp0); - temp0 = (v8u16) __msa_bmz_v((v16u8) temp0, (v16u8) p0_src, + CLIP_SH(delta0, tc_neg, tc_pos); + temp2 = (v8i16) (delta0 + p0_src); + CLIP_SH_0_255(temp2); + temp0 = (v8u16) __msa_bmz_v((v16u8) temp2, (v16u8) p0_src, (v16u8) p_is_pcm_vec); temp2 = (v8i16) (q0_src - delta0); - temp2 = CLIP_SH_0_255(temp2); + CLIP_SH_0_255(temp2); temp2 = (v8i16) __msa_bmz_v((v16u8) temp2, (v16u8) q0_src, (v16u8) q_is_pcm_vec); @@ -649,9 +649,9 @@ static void hevc_loopfilter_luma_ver_msa(uint8_t *src, int32_t stride, delta1 -= (v8i16) p1_src; delta1 += delta0; delta1 >>= 1; - delta1 = CLIP_SH(delta1, tc_neg, tc_pos); + CLIP_SH(delta1, tc_neg, tc_pos); delta1 = (v8i16) p1_src + (v8i16) delta1; - delta1 = CLIP_SH_0_255(delta1); + CLIP_SH_0_255(delta1); delta1 = (v8i16) __msa_bmnz_v((v16u8) delta1, (v16u8) p1_src, (v16u8) p_is_pcm_vec); @@ -659,9 +659,9 @@ static void hevc_loopfilter_luma_ver_msa(uint8_t *src, int32_t stride, delta2 = delta2 - (v8i16) q1_src; delta2 = delta2 - delta0; delta2 = delta2 >> 1; - delta2 = CLIP_SH(delta2, tc_neg, tc_pos); + CLIP_SH(delta2, tc_neg, tc_pos); delta2 = (v8i16) q1_src + (v8i16) delta2; - delta2 = CLIP_SH_0_255(delta2); + CLIP_SH_0_255(delta2); delta2 = (v8i16) __msa_bmnz_v((v16u8) delta2, (v16u8) q1_src, (v16u8) q_is_pcm_vec); @@ -726,19 +726,19 @@ static void hevc_loopfilter_luma_ver_msa(uint8_t *src, int32_t stride, temp1 = ((p3_src + p2_src) << 1) + p2_src + temp0; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - p2_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst0 = (v16u8) (temp2 + (v8i16) p2_src); temp1 = temp0 + p2_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 2); temp2 = (v8i16) (temp1 - p1_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst1 = (v16u8) (temp2 + (v8i16) p1_src); temp1 = (temp0 << 1) + p2_src + q1_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - p0_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst2 = (v16u8) (temp2 + (v8i16) p0_src); dst0 = __msa_bmz_v(dst0, (v16u8) p2_src, (v16u8) p_is_pcm_vec); @@ -750,19 +750,19 @@ static void hevc_loopfilter_luma_ver_msa(uint8_t *src, int32_t stride, temp1 = ((q3_src + q2_src) << 1) + q2_src + temp0; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - q2_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst5 = (v16u8) (temp2 + (v8i16) q2_src); temp1 = temp0 + q2_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 2); temp2 = (v8i16) (temp1 - q1_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst4 = (v16u8) (temp2 + (v8i16) q1_src); temp1 = (temp0 << 1) + p1_src + q2_src; temp1 = (v8u16) __msa_srari_h((v8i16) temp1, 3); temp2 = (v8i16) (temp1 - q0_src); - temp2 = CLIP_SH(temp2, tc_neg, tc_pos); + CLIP_SH(temp2, tc_neg, tc_pos); dst3 = (v16u8) (temp2 + (v8i16) q0_src); dst3 = __msa_bmz_v(dst3, (v16u8) q0_src, (v16u8) q_is_pcm_vec); @@ -785,15 +785,15 @@ static void hevc_loopfilter_luma_ver_msa(uint8_t *src, int32_t stride, abs_delta0 = __msa_add_a_h(delta0, (v8i16) zero); abs_delta0 = (v8u16) abs_delta0 < temp1; - delta0 = CLIP_SH(delta0, tc_neg, tc_pos); + CLIP_SH(delta0, tc_neg, tc_pos); - temp0 = (v8u16) (delta0 + p0_src); - temp0 = (v8u16) CLIP_SH_0_255(temp0); - temp0 = (v8u16) __msa_bmz_v((v16u8) temp0, (v16u8) p0_src, + temp2 = (v8i16) (delta0 + p0_src); + CLIP_SH_0_255(temp2); + temp0 = (v8u16) __msa_bmz_v((v16u8) temp2, (v16u8) p0_src, (v16u8) p_is_pcm_vec); temp2 = (v8i16) (q0_src - delta0); - temp2 = CLIP_SH_0_255(temp2); + CLIP_SH_0_255(temp2); temp2 = (v8i16) __msa_bmz_v((v16u8) temp2, (v16u8) q0_src, (v16u8) q_is_pcm_vec); @@ -815,9 +815,9 @@ static void hevc_loopfilter_luma_ver_msa(uint8_t *src, int32_t stride, delta1 -= (v8i16) p1_src; delta1 += delta0; delta1 >>= 1; - delta1 = CLIP_SH(delta1, tc_neg, tc_pos); + CLIP_SH(delta1, tc_neg, tc_pos); delta1 = (v8i16) p1_src + (v8i16) delta1; - delta1 = CLIP_SH_0_255(delta1); + CLIP_SH_0_255(delta1); delta1 = (v8i16) __msa_bmnz_v((v16u8) delta1, (v16u8) p1_src, (v16u8) p_is_pcm_vec); @@ -825,9 +825,9 @@ static void hevc_loopfilter_luma_ver_msa(uint8_t *src, int32_t stride, delta2 = delta2 - (v8i16) q1_src; delta2 = delta2 - delta0; delta2 = delta2 >> 1; - delta2 = CLIP_SH(delta2, tc_neg, tc_pos); + CLIP_SH(delta2, tc_neg, tc_pos); delta2 = (v8i16) q1_src + (v8i16) delta2; - delta2 = CLIP_SH_0_255(delta2); + CLIP_SH_0_255(delta2); delta2 = (v8i16) __msa_bmnz_v((v16u8) delta2, (v16u8) q1_src, (v16u8) q_is_pcm_vec); delta1 = (v8i16) __msa_bmz_v((v16u8) delta1, (v16u8) p1_src, @@ -955,15 +955,15 @@ static void hevc_loopfilter_chroma_hor_msa(uint8_t *src, int32_t stride, temp0 <<= 2; temp0 += temp1; delta = __msa_srari_h((v8i16) temp0, 3); - delta = CLIP_SH(delta, tc_neg, tc_pos); + CLIP_SH(delta, tc_neg, tc_pos); temp0 = (v8i16) ((v8i16) p0 + delta); - temp0 = CLIP_SH_0_255(temp0); + CLIP_SH_0_255(temp0); temp0 = (v8i16) __msa_bmz_v((v16u8) temp0, (v16u8) p0, (v16u8) p_is_pcm_vec); temp1 = (v8i16) ((v8i16) q0 - delta); - temp1 = CLIP_SH_0_255(temp1); + CLIP_SH_0_255(temp1); temp1 = (v8i16) __msa_bmz_v((v16u8) temp1, (v16u8) q0, (v16u8) q_is_pcm_vec); @@ -1014,15 +1014,15 @@ static void hevc_loopfilter_chroma_ver_msa(uint8_t *src, int32_t stride, temp0 <<= 2; temp0 += temp1; delta = __msa_srari_h((v8i16) temp0, 3); - delta = CLIP_SH(delta, tc_neg, tc_pos); + CLIP_SH(delta, tc_neg, tc_pos); temp0 = (v8i16) ((v8i16) p0 + delta); - temp0 = CLIP_SH_0_255(temp0); + CLIP_SH_0_255(temp0); temp0 = (v8i16) __msa_bmz_v((v16u8) temp0, (v16u8) p0, (v16u8) p_is_pcm_vec); temp1 = (v8i16) ((v8i16) q0 - delta); - temp1 = CLIP_SH_0_255(temp1); + CLIP_SH_0_255(temp1); temp1 = (v8i16) __msa_bmz_v((v16u8) temp1, (v16u8) q0, (v16u8) q_is_pcm_vec); @@ -1357,6 +1357,7 @@ static void hevc_sao_edge_filter_0degree_8width_msa(uint8_t *dst, v16u8 cmp_minus10, diff_minus10, diff_minus11; v16u8 src0, src1, dst0, src_minus10, src_minus11, src_plus10, src_plus11; v16i8 offset, sao_offset = LD_SB(sao_offset_val); + v16i8 zeros = { 0 }; sao_offset = __msa_pckev_b(sao_offset, sao_offset); src -= 1; @@ -1367,8 +1368,8 @@ static void hevc_sao_edge_filter_0degree_8width_msa(uint8_t *dst, for (height -= 2; height; height -= 2) { src += (src_stride << 1); - SLDI_B2_0_UB(src_minus10, src_minus11, src0, src1, 1); - SLDI_B2_0_UB(src_minus10, src_minus11, src_plus10, src_plus11, 2); + SLDI_B2_UB(zeros, src_minus10, zeros, src_minus11, 1, src0, src1); + SLDI_B2_UB(zeros, src_minus10, zeros, src_minus11, 2, src_plus10, src_plus11); PCKEV_D2_UB(src_minus11, src_minus10, src_plus11, src_plus10, src_minus10, src_plus10); @@ -1404,8 +1405,8 @@ static void hevc_sao_edge_filter_0degree_8width_msa(uint8_t *dst, dst += dst_stride; } - SLDI_B2_0_UB(src_minus10, src_minus11, src0, src1, 1); - SLDI_B2_0_UB(src_minus10, src_minus11, src_plus10, src_plus11, 2); + SLDI_B2_UB(zeros, src_minus10, zeros, src_minus11, 1, src0, src1); + SLDI_B2_UB(zeros, src_minus10, zeros, src_minus11, 2, src_plus10, src_plus11); PCKEV_D2_UB(src_minus11, src_minus10, src_plus11, src_plus10, src_minus10, src_plus10); @@ -1473,14 +1474,12 @@ static void hevc_sao_edge_filter_0degree_16multiple_msa(uint8_t *dst, dst_ptr = dst + v_cnt; LD_UB4(src_minus1, src_stride, src10, src11, src12, src13); - SLDI_B2_SB(src10, src11, src_minus10, src_minus11, src_zero0, - src_zero1, 1); - SLDI_B2_SB(src12, src13, src_minus12, src_minus13, src_zero2, - src_zero3, 1); - SLDI_B2_SB(src10, src11, src_minus10, src_minus11, src_plus10, - src_plus11, 2); - SLDI_B2_SB(src12, src13, src_minus12, src_minus13, src_plus12, - src_plus13, 2); + SLDI_B4_SB(src10, src_minus10, src11, src_minus11, + src12, src_minus12, src13, src_minus13, 1, + src_zero0, src_zero1, src_zero2, src_zero3); + SLDI_B4_SB(src10, src_minus10, src11, src_minus11, + src12, src_minus12, src13, src_minus13, 2, + src_plus10, src_plus11, src_plus12, src_plus13); cmp_minus10 = ((v16u8) src_zero0 == src_minus10); cmp_plus10 = ((v16u8) src_zero0 == (v16u8) src_plus10); @@ -1880,6 +1879,7 @@ static void hevc_sao_edge_filter_45degree_4width_msa(uint8_t *dst, v16u8 src_minus11, src10, src11; v16i8 src_plus0, src_zero0, src_plus1, src_zero1, dst0; v8i16 offset_mask0, offset_mask1; + v16i8 zeros = { 0 }; sao_offset = __msa_pckev_b(sao_offset, sao_offset); @@ -1892,8 +1892,8 @@ static void hevc_sao_edge_filter_45degree_4width_msa(uint8_t *dst, for (height -= 2; height; height -= 2) { src_orig += (src_stride << 1); - SLDI_B2_0_SB(src_minus11, src10, src_zero0, src_zero1, 1); - SLDI_B2_0_SB(src10, src11, src_plus0, src_plus1, 2); + SLDI_B2_SB(zeros, src_minus11, zeros, src10, 1, src_zero0, src_zero1); + SLDI_B2_SB(zeros, src10, zeros, src11, 2, src_plus0, src_plus1); ILVR_B2_UB(src_plus0, src_minus10, src_plus1, src_minus11, src_minus10, src_minus11); @@ -1938,8 +1938,8 @@ static void hevc_sao_edge_filter_45degree_4width_msa(uint8_t *dst, dst += dst_stride; } - SLDI_B2_0_SB(src_minus11, src10, src_zero0, src_zero1, 1); - SLDI_B2_0_SB(src10, src11, src_plus0, src_plus1, 2); + SLDI_B2_SB(zeros, src_minus11, zeros, src10, 1, src_zero0, src_zero1); + SLDI_B2_SB(zeros, src10, zeros, src11, 2, src_plus0, src_plus1); ILVR_B2_UB(src_plus0, src_minus10, src_plus1, src_minus11, src_minus10, src_minus11); @@ -1992,6 +1992,7 @@ static void hevc_sao_edge_filter_45degree_8width_msa(uint8_t *dst, v16u8 src_minus10, src10, src_minus11, src11; v16i8 src_zero0, src_plus10, src_zero1, src_plus11, dst0; v8i16 offset_mask0, offset_mask1; + v16i8 zeros = { 0 }; sao_offset = __msa_pckev_b(sao_offset, sao_offset); src_orig = src - 1; @@ -2003,8 +2004,8 @@ static void hevc_sao_edge_filter_45degree_8width_msa(uint8_t *dst, for (height -= 2; height; height -= 2) { src_orig += (src_stride << 1); - SLDI_B2_0_SB(src_minus11, src10, src_zero0, src_zero1, 1); - SLDI_B2_0_SB(src10, src11, src_plus10, src_plus11, 2); + SLDI_B2_SB(zeros, src_minus11, zeros, src10, 1, src_zero0, src_zero1); + SLDI_B2_SB(zeros, src10, zeros, src11, 2, src_plus10, src_plus11); ILVR_B2_UB(src_plus10, src_minus10, src_plus11, src_minus11, src_minus10, src_minus11); @@ -2048,8 +2049,8 @@ static void hevc_sao_edge_filter_45degree_8width_msa(uint8_t *dst, dst += dst_stride; } - SLDI_B2_0_SB(src_minus11, src10, src_zero0, src_zero1, 1); - SLDI_B2_0_SB(src10, src11, src_plus10, src_plus11, 2); + SLDI_B2_SB(zeros, src_minus11, zeros, src10, 1, src_zero0, src_zero1); + SLDI_B2_SB(zeros, src10, zeros, src11, 2, src_plus10, src_plus11); ILVR_B2_UB(src_plus10, src_minus10, src_plus11, src_minus11, src_minus10, src_minus11); ILVR_B2_SB(src_zero0, src_zero0, src_zero1, src_zero1, src_zero0, @@ -2130,12 +2131,11 @@ static void hevc_sao_edge_filter_45degree_16multiple_msa(uint8_t *dst, src_plus13 = LD_UB(src + 1 + v_cnt + (src_stride << 2)); src_orig += 16; - SLDI_B2_SB(src10, src11, src_minus11, src_minus12, src_zero0, - src_zero1, 1); - SLDI_B2_SB(src12, src13, src_minus13, src_minus14, src_zero2, - src_zero3, 1); - SLDI_B2_SB(src11, src12, src_minus12, src_minus13, src_plus10, - src_plus11, 2); + SLDI_B4_SB(src10, src_minus11, src11, src_minus12, + src12, src_minus13, src13, src_minus14, 1, + src_zero0, src_zero1, src_zero2, src_zero3); + SLDI_B2_SB(src11, src_minus12, src12, src_minus13, 2, src_plus10, + src_plus11); src_plus12 = __msa_sldi_b((v16i8) src13, (v16i8) src_minus14, 2); @@ -2228,6 +2228,7 @@ static void hevc_sao_edge_filter_135degree_4width_msa(uint8_t *dst, v16u8 cmp_minus10, diff_minus10, cmp_minus11, diff_minus11; v16u8 src_minus10, src10, src_minus11, src11; v8i16 offset_mask0, offset_mask1; + v16i8 zeros = { 0 }; sao_offset = __msa_pckev_b(sao_offset, sao_offset); src_orig = src - 1; @@ -2239,8 +2240,8 @@ static void hevc_sao_edge_filter_135degree_4width_msa(uint8_t *dst, for (height -= 2; height; height -= 2) { src_orig += (src_stride << 1); - SLDI_B2_0_SB(src_minus11, src10, src_zero0, src_zero1, 1); - SLDI_B2_0_UB(src_minus10, src_minus11, src_minus10, src_minus11, 2); + SLDI_B2_SB(zeros, src_minus11, zeros, src10, 1, src_zero0, src_zero1); + SLDI_B2_UB(zeros, src_minus10, zeros, src_minus11, 2, src_minus10, src_minus11); ILVR_B2_UB(src10, src_minus10, src11, src_minus11, src_minus10, src_minus11); @@ -2286,8 +2287,8 @@ static void hevc_sao_edge_filter_135degree_4width_msa(uint8_t *dst, dst += dst_stride; } - SLDI_B2_0_SB(src_minus11, src10, src_zero0, src_zero1, 1); - SLDI_B2_0_UB(src_minus10, src_minus11, src_minus10, src_minus11, 2); + SLDI_B2_SB(zeros, src_minus11, zeros, src10, 1, src_zero0, src_zero1); + SLDI_B2_UB(zeros, src_minus10, zeros, src_minus11, 2, src_minus10, src_minus11); ILVR_B2_UB(src10, src_minus10, src11, src_minus11, src_minus10, src_minus11); @@ -2342,6 +2343,7 @@ static void hevc_sao_edge_filter_135degree_8width_msa(uint8_t *dst, v16u8 src_minus10, src10, src_minus11, src11; v16i8 src_zero0, src_zero1, dst0; v8i16 offset_mask0, offset_mask1; + v16i8 zeros = { 0 }; sao_offset = __msa_pckev_b(sao_offset, sao_offset); src_orig = src - 1; @@ -2353,8 +2355,8 @@ static void hevc_sao_edge_filter_135degree_8width_msa(uint8_t *dst, for (height -= 2; height; height -= 2) { src_orig += (src_stride << 1); - SLDI_B2_0_SB(src_minus11, src10, src_zero0, src_zero1, 1); - SLDI_B2_0_UB(src_minus10, src_minus11, src_minus10, src_minus11, 2); + SLDI_B2_SB(zeros, src_minus11, zeros, src10, 1, src_zero0, src_zero1); + SLDI_B2_UB(zeros, src_minus10, zeros, src_minus11, 2, src_minus10, src_minus11); ILVR_B2_UB(src10, src_minus10, src11, src_minus11, src_minus10, src_minus11); ILVR_B2_SB(src_zero0, src_zero0, src_zero1, src_zero1, src_zero0, @@ -2398,8 +2400,8 @@ static void hevc_sao_edge_filter_135degree_8width_msa(uint8_t *dst, dst += dst_stride; } - SLDI_B2_0_SB(src_minus11, src10, src_zero0, src_zero1, 1); - SLDI_B2_0_UB(src_minus10, src_minus11, src_minus10, src_minus11, 2); + SLDI_B2_SB(zeros, src_minus11, zeros, src10, 1, src_zero0, src_zero1); + SLDI_B2_UB(zeros, src_minus10, zeros, src_minus11, 2, src_minus10, src_minus11); ILVR_B2_UB(src10, src_minus10, src11, src_minus11, src_minus10, src_minus11); ILVR_B2_SB(src_zero0, src_zero0, src_zero1, src_zero1, src_zero0, diff --git a/libavcodec/mips/hevc_mc_bi_msa.c b/libavcodec/mips/hevc_mc_bi_msa.c index 34613c84b87..c6c8d2705d0 100644 --- a/libavcodec/mips/hevc_mc_bi_msa.c +++ b/libavcodec/mips/hevc_mc_bi_msa.c @@ -48,7 +48,7 @@ static const uint8_t ff_hevc_mask_arr[16 * 2] __attribute__((aligned(0x40))) = { { \ ADDS_SH2_SH(vec0, in0, vec1, in1, out0, out1); \ SRARI_H2_SH(out0, out1, rnd_val); \ - CLIP_SH2_0_255_MAX_SATU(out0, out1); \ + CLIP_SH2_0_255(out0, out1); \ } #define HEVC_BI_RND_CLIP4_MAX_SATU(in0, in1, in2, in3, vec0, vec1, vec2, \ @@ -83,7 +83,7 @@ static void hevc_bi_copy_4w_msa(uint8_t *src0_ptr, dst0 <<= 6; dst0 += in0; dst0 = __msa_srari_h(dst0, 7); - dst0 = CLIP_SH_0_255_MAX_SATU(dst0); + CLIP_SH_0_255(dst0); dst0 = (v8i16) __msa_pckev_b((v16i8) dst0, (v16i8) dst0); ST_W2(dst0, 0, 1, dst, dst_stride); @@ -739,7 +739,7 @@ static void hevc_hz_bi_8t_12w_msa(uint8_t *src0_ptr, HEVC_BI_RND_CLIP2(in0, in1, dst0, dst1, 7, dst0, dst1); dst2 = __msa_adds_s_h(in2, dst2); dst2 = __msa_srari_h(dst2, 7); - dst2 = CLIP_SH_0_255(dst2); + CLIP_SH_0_255(dst2); PCKEV_B2_SH(dst1, dst0, dst2, dst2, dst0, dst1); tmp2 = __msa_copy_s_d((v2i64) dst0, 0); @@ -888,7 +888,7 @@ static void hevc_hz_bi_8t_24w_msa(uint8_t *src0_ptr, HEVC_BI_RND_CLIP2(in0, in1, dst0, dst1, 7, dst0, dst1); dst2 = __msa_adds_s_h(dst2, in2); dst2 = __msa_srari_h(dst2, 7); - dst2 = CLIP_SH_0_255(dst2); + CLIP_SH_0_255(dst2); PCKEV_B2_SB(dst1, dst0, dst2, dst2, tmp0, tmp1); dst_val0 = __msa_copy_u_d((v2i64) tmp1, 0); @@ -1726,7 +1726,7 @@ static void hevc_hv_bi_8t_4w_msa(uint8_t *src0_ptr, ADDS_SH2_SH(out0, in0, out1, in1, out0, out1); ADDS_SH2_SH(out0, const_vec, out1, const_vec, out0, out1); SRARI_H2_SH(out0, out1, 7); - CLIP_SH2_0_255_MAX_SATU(out0, out1); + CLIP_SH2_0_255(out0, out1); out = (v16u8) __msa_pckev_b((v16i8) out1, (v16i8) out0); ST_W4(out, 0, 1, 2, 3, dst, dst_stride); dst += (4 * dst_stride); @@ -1854,7 +1854,7 @@ static void hevc_hv_bi_8t_8multx1mult_msa(uint8_t *src0_ptr, tmp = __msa_pckev_h((v8i16) dst0_l, (v8i16) dst0_r); ADDS_SH2_SH(tmp, in0, tmp, const_vec, tmp, tmp); tmp = __msa_srari_h(tmp, 7); - tmp = CLIP_SH_0_255_MAX_SATU(tmp); + CLIP_SH_0_255(tmp); out = (v16u8) __msa_pckev_b((v16i8) tmp, (v16i8) tmp); ST_D1(out, 0, dst_tmp); dst_tmp += dst_stride; @@ -2000,7 +2000,7 @@ static void hevc_hv_bi_8t_12w_msa(uint8_t *src0_ptr, tmp = __msa_pckev_h((v8i16) dst0_l, (v8i16) dst0_r); ADDS_SH2_SH(tmp, in0, tmp, const_vec, tmp, tmp); tmp = __msa_srari_h(tmp, 7); - tmp = CLIP_SH_0_255_MAX_SATU(tmp); + CLIP_SH_0_255(tmp); out = (v16u8) __msa_pckev_b((v16i8) tmp, (v16i8) tmp); ST_D1(out, 0, dst_tmp); dst_tmp += dst_stride; @@ -2088,7 +2088,7 @@ static void hevc_hv_bi_8t_12w_msa(uint8_t *src0_ptr, ADDS_SH2_SH(out0, in0, out1, in1, out0, out1); ADDS_SH2_SH(out0, const_vec, out1, const_vec, out0, out1); SRARI_H2_SH(out0, out1, 7); - CLIP_SH2_0_255_MAX_SATU(out0, out1); + CLIP_SH2_0_255(out0, out1); out = (v16u8) __msa_pckev_b((v16i8) out1, (v16i8) out0); ST_W4(out, 0, 1, 2, 3, dst, dst_stride); dst += (4 * dst_stride); @@ -2215,7 +2215,7 @@ static void hevc_hz_bi_4t_4x2_msa(uint8_t *src0_ptr, tmp0 = __msa_adds_s_h(tmp0, in0); tmp0 = __msa_srari_h(tmp0, 7); - tmp0 = CLIP_SH_0_255(tmp0); + CLIP_SH_0_255(tmp0); dst0 = __msa_pckev_b((v16i8) tmp0, (v16i8) tmp0); ST_W2(dst0, 0, 1, dst, dst_stride); @@ -2943,7 +2943,7 @@ static void hevc_vt_bi_4t_4x2_msa(uint8_t *src0_ptr, DPADD_SB2_SH(src2110, src4332, filt0, filt1, dst10, dst10); dst10 = __msa_adds_s_h(dst10, in0); dst10 = __msa_srari_h(dst10, 7); - dst10 = CLIP_SH_0_255(dst10); + CLIP_SH_0_255(dst10); dst10 = (v8i16) __msa_pckev_b((v16i8) dst10, (v16i8) dst10); ST_W2(dst10, 0, 1, dst, dst_stride); @@ -3843,7 +3843,7 @@ static void hevc_hv_bi_4t_4x2_msa(uint8_t *src0_ptr, tmp = __msa_pckev_h((v8i16) dst1, (v8i16) dst0); tmp = __msa_adds_s_h(tmp, in0); tmp = __msa_srari_h(tmp, 7); - tmp = CLIP_SH_0_255_MAX_SATU(tmp); + CLIP_SH_0_255(tmp); out = (v16u8) __msa_pckev_b((v16i8) tmp, (v16i8) tmp); ST_W2(out, 0, 1, dst, dst_stride); } @@ -3919,7 +3919,7 @@ static void hevc_hv_bi_4t_4x4_msa(uint8_t *src0_ptr, PCKEV_H2_SH(dst1, dst0, dst3, dst2, tmp0, tmp1); ADDS_SH2_SH(tmp0, in0, tmp1, in1, tmp0, tmp1); SRARI_H2_SH(tmp0, tmp1, 7); - CLIP_SH2_0_255_MAX_SATU(tmp0, tmp1); + CLIP_SH2_0_255(tmp0, tmp1); out = (v16u8) __msa_pckev_b((v16i8) tmp1, (v16i8) tmp0); ST_W4(out, 0, 1, 2, 3, dst, dst_stride); } @@ -4032,7 +4032,7 @@ static void hevc_hv_bi_4t_4multx8mult_msa(uint8_t *src0_ptr, ADDS_SH4_SH(in0, tmp0, in1, tmp1, in2, tmp2, in3, tmp3, tmp0, tmp1, tmp2, tmp3); SRARI_H4_SH(tmp0, tmp1, tmp2, tmp3, 7); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_W8(out0, out1, 0, 1, 2, 3, 0, 1, 2, 3, dst, dst_stride); dst += (8 * dst_stride); @@ -4200,7 +4200,7 @@ static void hevc_hv_bi_4t_6w_msa(uint8_t *src0_ptr, ADDS_SH4_SH(in0, tmp0, in1, tmp1, in2, tmp2, in3, tmp3, tmp0, tmp1, tmp2, tmp3); SRARI_H4_SH(tmp0, tmp1, tmp2, tmp3, 7); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_W8(out0, out1, 0, 1, 2, 3, 0, 1, 2, 3, dst, dst_stride); @@ -4212,7 +4212,7 @@ static void hevc_hv_bi_4t_6w_msa(uint8_t *src0_ptr, ADDS_SH2_SH(in4, const_vec, in5, const_vec, in4, in5); ADDS_SH2_SH(in4, tmp4, in5, tmp5, tmp4, tmp5); SRARI_H2_SH(tmp4, tmp5, 7); - CLIP_SH2_0_255_MAX_SATU(tmp4, tmp5); + CLIP_SH2_0_255(tmp4, tmp5); out2 = (v16u8) __msa_pckev_b((v16i8) tmp5, (v16i8) tmp4); ST_H8(out2, 0, 1, 2, 3, 4, 5, 6, 7, dst + 4, dst_stride); } @@ -4286,7 +4286,7 @@ static void hevc_hv_bi_4t_8x2_msa(uint8_t *src0_ptr, PCKEV_H2_SH(dst0_l, dst0_r, dst1_l, dst1_r, tmp0, tmp1); ADDS_SH2_SH(in0, tmp0, in1, tmp1, tmp0, tmp1); SRARI_H2_SH(tmp0, tmp1, 7); - CLIP_SH2_0_255_MAX_SATU(tmp0, tmp1); + CLIP_SH2_0_255(tmp0, tmp1); out = (v16u8) __msa_pckev_b((v16i8) tmp1, (v16i8) tmp0); ST_D2(out, 0, 1, dst, dst_stride); } @@ -4380,7 +4380,7 @@ static void hevc_hv_bi_4t_8multx4_msa(uint8_t *src0_ptr, ADDS_SH4_SH(in0, tmp0, in1, tmp1, in2, tmp2, in3, tmp3, tmp0, tmp1, tmp2, tmp3); SRARI_H4_SH(tmp0, tmp1, tmp2, tmp3, 7); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_D4(out0, out1, 0, 1, 0, 1, dst, dst_stride); dst += 8; @@ -4495,8 +4495,8 @@ static void hevc_hv_bi_4t_8x6_msa(uint8_t *src0_ptr, ADDS_SH2_SH(in4, tmp4, in5, tmp5, tmp4, tmp5); SRARI_H4_SH(tmp0, tmp1, tmp2, tmp3, 7); SRARI_H2_SH(tmp4, tmp5, 7); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); - CLIP_SH2_0_255_MAX_SATU(tmp4, tmp5); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); + CLIP_SH2_0_255(tmp4, tmp5); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); out2 = (v16u8) __msa_pckev_b((v16i8) tmp5, (v16i8) tmp4); ST_D4(out0, out1, 0, 1, 0, 1, dst, dst_stride); @@ -4610,7 +4610,7 @@ static void hevc_hv_bi_4t_8multx4mult_msa(uint8_t *src0_ptr, ADDS_SH4_SH(in0, tmp0, in1, tmp1, in2, tmp2, in3, tmp3, tmp0, tmp1, tmp2, tmp3); SRARI_H4_SH(tmp0, tmp1, tmp2, tmp3, 7); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_D4(out0, out1, 0, 1, 0, 1, dst_tmp, dst_stride); dst_tmp += (4 * dst_stride); @@ -4760,7 +4760,7 @@ static void hevc_hv_bi_4t_12w_msa(uint8_t *src0_ptr, ADDS_SH4_SH(in0, tmp0, in1, tmp1, in2, tmp2, in3, tmp3, tmp0, tmp1, tmp2, tmp3); SRARI_H4_SH(tmp0, tmp1, tmp2, tmp3, 7); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_D4(out0, out1, 0, 1, 0, 1, dst_tmp, dst_stride); dst_tmp += (4 * dst_stride); @@ -4846,7 +4846,7 @@ static void hevc_hv_bi_4t_12w_msa(uint8_t *src0_ptr, ADDS_SH4_SH(in0, tmp0, in1, tmp1, in2, tmp2, in3, tmp3, tmp0, tmp1, tmp2, tmp3); SRARI_H4_SH(tmp0, tmp1, tmp2, tmp3, 7); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_W8(out0, out1, 0, 1, 2, 3, 0, 1, 2, 3, dst, dst_stride); dst += (8 * dst_stride); diff --git a/libavcodec/mips/hevc_mc_biw_msa.c b/libavcodec/mips/hevc_mc_biw_msa.c index 68f122ea48c..f775ea85923 100644 --- a/libavcodec/mips/hevc_mc_biw_msa.c +++ b/libavcodec/mips/hevc_mc_biw_msa.c @@ -66,7 +66,7 @@ static const uint8_t ff_hevc_mask_arr[16 * 2] __attribute__((aligned(0x40))) = { out1_l = __msa_dpadd_s_w(offset, (v8i16) out1_l, (v8i16) wgt); \ SRAR_W4_SW(out0_r, out1_r, out0_l, out1_l, rnd); \ PCKEV_H2_SH(out0_l, out0_r, out1_l, out1_r, out0, out1); \ - CLIP_SH2_0_255_MAX_SATU(out0, out1); \ + CLIP_SH2_0_255(out0, out1); \ } #define HEVC_BIW_RND_CLIP4_MAX_SATU(in0, in1, in2, in3, vec0, vec1, vec2, \ @@ -124,7 +124,7 @@ static void hevc_biwgt_copy_4w_msa(uint8_t *src0_ptr, dst0_l = __msa_dpadd_s_w(offset_vec, (v8i16) dst0_l, weight_vec); SRAR_W2_SW(dst0_r, dst0_l, rnd_vec); dst0 = (v8i16) __msa_pckev_h((v8i16) dst0_l, (v8i16) dst0_r); - dst0 = CLIP_SH_0_255_MAX_SATU(dst0); + CLIP_SH_0_255(dst0); out0 = (v16u8) __msa_pckev_b((v16i8) dst0, (v16i8) dst0); ST_W2(out0, 0, 1, dst, dst_stride); } else if (4 == height) { @@ -1069,8 +1069,8 @@ static void hevc_hz_biwgt_8t_24w_msa(uint8_t *src0_ptr, dst2_l = __msa_dpadd_s_w(offset_vec, (v8i16) dst2_l, (v8i16) weight_vec); SRAR_W2_SW(dst2_r, dst2_l, rnd_vec); - dst2_r = (v4i32) __msa_pckev_h((v8i16) dst2_l, (v8i16) dst2_r); - out2 = CLIP_SH_0_255(dst2_r); + out2 = __msa_pckev_h((v8i16) dst2_l, (v8i16) dst2_r); + CLIP_SH_0_255(out2); LD_SB2(src0_ptr, 16, src0, src1); src0_ptr += src_stride; @@ -1100,8 +1100,8 @@ static void hevc_hz_biwgt_8t_24w_msa(uint8_t *src0_ptr, dst2_r = __msa_dpadd_s_w(offset_vec, (v8i16) dst2_r, (v8i16) weight_vec); dst2_l = __msa_dpadd_s_w(offset_vec, (v8i16) dst2_l, (v8i16) weight_vec); SRAR_W2_SW(dst2_r, dst2_l, rnd_vec); - dst2_r = (v4i32) __msa_pckev_h((v8i16) dst2_l, (v8i16) dst2_r); - out2 = CLIP_SH_0_255(dst2_r); + out2 = __msa_pckev_h((v8i16) dst2_l, (v8i16) dst2_r); + CLIP_SH_0_255(out2); PCKEV_B2_SH(out1, out0, out2, out2, out0, out2); dst_val0 = __msa_copy_u_d((v2i64) out2, 0); ST_SH(out0, dst); @@ -1674,8 +1674,8 @@ static void hevc_vt_biwgt_8t_12w_msa(uint8_t *src0_ptr, dst2_l = __msa_dpadd_s_w(offset_vec, (v8i16) dst2_l, (v8i16) weight_vec); SRAR_W2_SW(dst2_r, dst2_l, rnd_vec); - dst2_r = (v4i32) __msa_pckev_h((v8i16) dst2_l, (v8i16) dst2_r); - out2 = CLIP_SH_0_255(dst2_r); + out2 = __msa_pckev_h((v8i16) dst2_l, (v8i16) dst2_r); + CLIP_SH_0_255(out2); PCKEV_B2_SH(out1, out0, out2, out2, out0, out2); ST_D2(out0, 0, 1, dst, dst_stride); ST_W2(out2, 0, 1, dst + 8, dst_stride); @@ -2048,7 +2048,7 @@ static void hevc_hv_biwgt_8t_4w_msa(uint8_t *src0_ptr, dst2 = __msa_dpadd_s_w(offset_vec, tmp2, weight_vec); dst3 = __msa_dpadd_s_w(offset_vec, tmp3, weight_vec); SRAR_W4_SW(dst0, dst1, dst2, dst3, rnd_vec); - CLIP_SW4_0_255_MAX_SATU(dst0, dst1, dst2, dst3); + CLIP_SW4_0_255(dst0, dst1, dst2, dst3); PCKEV_H2_SH(dst1, dst0, dst3, dst2, tmp0, tmp1); out = (v16u8) __msa_pckev_b((v16i8) tmp1, (v16i8) tmp0); ST_W4(out, 0, 1, 2, 3, dst, dst_stride); @@ -2226,7 +2226,7 @@ static void hevc_hv_biwgt_8t_8multx2mult_msa(uint8_t *src0_ptr, dst1_r = __msa_dpadd_s_w(offset_vec, tmp2, weight_vec); dst1_l = __msa_dpadd_s_w(offset_vec, tmp3, weight_vec); SRAR_W4_SW(dst0_l, dst0_r, dst1_l, dst1_r, rnd_vec); - CLIP_SW4_0_255_MAX_SATU(dst0_l, dst0_r, dst1_l, dst1_r); + CLIP_SW4_0_255(dst0_l, dst0_r, dst1_l, dst1_r); PCKEV_H2_SH(dst0_l, dst0_r, dst1_l, dst1_r, tmp0, tmp1); out = (v16u8) __msa_pckev_b((v16i8) tmp1, (v16i8) tmp0); ST_D2(out, 0, 1, dst_tmp, dst_stride); @@ -2412,7 +2412,7 @@ static void hevc_hv_biwgt_8t_12w_msa(uint8_t *src0_ptr, dst2 = __msa_dpadd_s_w(offset_vec, tmp2, weight_vec); dst3 = __msa_dpadd_s_w(offset_vec, tmp3, weight_vec); SRAR_W4_SW(dst1, dst0, dst3, dst2, rnd_vec); - CLIP_SW4_0_255_MAX_SATU(dst1, dst0, dst3, dst2); + CLIP_SW4_0_255(dst1, dst0, dst3, dst2); PCKEV_H2_SH(dst1, dst0, dst3, dst2, tmp0, tmp1); out = (v16u8) __msa_pckev_b((v16i8) tmp1, (v16i8) tmp0); ST_D2(out, 0, 1, dst_tmp, dst_stride); @@ -2503,7 +2503,7 @@ static void hevc_hv_biwgt_8t_12w_msa(uint8_t *src0_ptr, dst2 = __msa_dpadd_s_w(offset_vec, tmp2, weight_vec); dst3 = __msa_dpadd_s_w(offset_vec, tmp3, weight_vec); SRAR_W4_SW(dst0, dst1, dst2, dst3, rnd_vec); - CLIP_SW4_0_255_MAX_SATU(dst0, dst1, dst2, dst3); + CLIP_SW4_0_255(dst0, dst1, dst2, dst3); PCKEV_H2_SH(dst1, dst0, dst3, dst2, tmp0, tmp1); out = (v16u8) __msa_pckev_b((v16i8) tmp1, (v16i8) tmp0); ST_W4(out, 0, 1, 2, 3, dst, dst_stride); @@ -2683,8 +2683,8 @@ static void hevc_hz_biwgt_4t_4x2_msa(uint8_t *src0_ptr, dst0_r = __msa_dpadd_s_w(offset_vec, (v8i16) dst0_r, (v8i16) weight_vec); dst0_l = __msa_dpadd_s_w(offset_vec, (v8i16) dst0_l, (v8i16) weight_vec); SRAR_W2_SW(dst0_r, dst0_l, rnd_vec); - dst0_r = (v4i32) __msa_pckev_h((v8i16) dst0_l, (v8i16) dst0_r); - out0 = CLIP_SH_0_255(dst0_r); + out0 = __msa_pckev_h((v8i16) dst0_l, (v8i16) dst0_r); + CLIP_SH_0_255(out0); out0 = (v8i16) __msa_pckev_b((v16i8) out0, (v16i8) out0); ST_W2(out0, 0, 1, dst, dst_stride); } @@ -3554,8 +3554,8 @@ static void hevc_vt_biwgt_4t_4x2_msa(uint8_t *src0_ptr, dst10_r = __msa_dpadd_s_w(offset_vec, (v8i16) dst10_r, (v8i16) weight_vec); dst10_l = __msa_dpadd_s_w(offset_vec, (v8i16) dst10_l, (v8i16) weight_vec); SRAR_W2_SW(dst10_r, dst10_l, rnd_vec); - dst10_r = (v4i32) __msa_pckev_h((v8i16) dst10_l, (v8i16) dst10_r); - out = CLIP_SH_0_255(dst10_r); + out = __msa_pckev_h((v8i16) dst10_l, (v8i16) dst10_r); + CLIP_SH_0_255(out); out = (v8i16) __msa_pckev_b((v16i8) out, (v16i8) out); ST_W2(out, 0, 1, dst, dst_stride); } @@ -4575,7 +4575,7 @@ static void hevc_hv_biwgt_4t_4x2_msa(uint8_t *src0_ptr, dst1 = __msa_dpadd_s_w(offset_vec, tmp1, weight_vec); SRAR_W2_SW(dst0, dst1, rnd_vec); tmp = __msa_pckev_h((v8i16) dst1, (v8i16) dst0); - tmp = CLIP_SH_0_255_MAX_SATU(tmp); + CLIP_SH_0_255(tmp); out = (v16u8) __msa_pckev_b((v16i8) tmp, (v16i8) tmp); ST_W2(out, 0, 1, dst, dst_stride); } @@ -4672,7 +4672,7 @@ static void hevc_hv_biwgt_4t_4x4_msa(uint8_t *src0_ptr, dst3 = __msa_dpadd_s_w(offset_vec, tmp3, weight_vec); SRAR_W4_SW(dst0, dst1, dst2, dst3, rnd_vec); PCKEV_H2_SH(dst1, dst0, dst3, dst2, tmp0, tmp1); - CLIP_SH2_0_255_MAX_SATU(tmp0, tmp1); + CLIP_SH2_0_255(tmp0, tmp1); out = (v16u8) __msa_pckev_b((v16i8) tmp1, (v16i8) tmp0); ST_W4(out, 0, 1, 2, 3, dst, dst_stride); } @@ -4810,7 +4810,7 @@ static void hevc_hv_biwgt_4t_4multx8mult_msa(uint8_t *src0_ptr, SRAR_W4_SW(dst4, dst5, dst6, dst7, rnd_vec); PCKEV_H4_SH(dst1, dst0, dst3, dst2, dst5, dst4, dst7, dst6, tmp0, tmp1, tmp2, tmp3); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_W8(out0, out1, 0, 1, 2, 3, 0, 1, 2, 3, dst, dst_stride); dst += (8 * dst_stride); @@ -5008,7 +5008,7 @@ static void hevc_hv_biwgt_4t_6w_msa(uint8_t *src0_ptr, SRAR_W4_SW(dst4, dst5, dst6, dst7, rnd_vec); PCKEV_H4_SH(dst1, dst0, dst3, dst2, dst5, dst4, dst7, dst6, tmp0, tmp1, tmp2, tmp3); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_W8(out0, out1, 0, 1, 2, 3, 0, 1, 2, 3, dst, dst_stride); @@ -5030,7 +5030,7 @@ static void hevc_hv_biwgt_4t_6w_msa(uint8_t *src0_ptr, SRAR_W4_SW(dst0, dst1, dst2, dst3, rnd_vec); PCKEV_H2_SH(dst1, dst0, dst3, dst2, tmp4, tmp5); - CLIP_SH2_0_255_MAX_SATU(tmp4, tmp5); + CLIP_SH2_0_255(tmp4, tmp5); out2 = (v16u8) __msa_pckev_b((v16i8) tmp5, (v16i8) tmp4); ST_H8(out2, 0, 1, 2, 3, 4, 5, 6, 7, dst + 4, dst_stride); } @@ -5126,7 +5126,7 @@ static void hevc_hv_biwgt_4t_8x2_msa(uint8_t *src0_ptr, dst1_l = __msa_dpadd_s_w(offset_vec, tmp3, weight_vec); SRAR_W4_SW(dst0_r, dst0_l, dst1_r, dst1_l, rnd_vec); PCKEV_H2_SH(dst0_l, dst0_r, dst1_l, dst1_r, tmp0, tmp1); - CLIP_SH2_0_255_MAX_SATU(tmp0, tmp1); + CLIP_SH2_0_255(tmp0, tmp1); out = (v16u8) __msa_pckev_b((v16i8) tmp1, (v16i8) tmp0); ST_D2(out, 0, 1, dst, dst_stride); } @@ -5248,7 +5248,7 @@ static void hevc_hv_biwgt_4t_8multx4_msa(uint8_t *src0_ptr, SRAR_W4_SW(dst4, dst5, dst6, dst7, rnd_vec); PCKEV_H4_SH(dst1, dst0, dst3, dst2, dst5, dst4, dst7, dst6, tmp0, tmp1, tmp2, tmp3); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_D4(out0, out1, 0, 1, 0, 1, dst, dst_stride); dst += 8; @@ -5387,7 +5387,7 @@ static void hevc_hv_biwgt_4t_8x6_msa(uint8_t *src0_ptr, SRAR_W4_SW(dst4, dst5, dst6, dst7, rnd_vec); PCKEV_H4_SH(dst1, dst0, dst3, dst2, dst5, dst4, dst7, dst6, tmp0, tmp1, tmp2, tmp3); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); PCKEV_H2_SW(dst4_l, dst4_r, dst5_l, dst5_r, dst0, dst1); @@ -5399,7 +5399,7 @@ static void hevc_hv_biwgt_4t_8x6_msa(uint8_t *src0_ptr, dst3 = __msa_dpadd_s_w(offset_vec, tmp3, weight_vec); SRAR_W4_SW(dst0, dst1, dst2, dst3, rnd_vec); PCKEV_H2_SH(dst1, dst0, dst3, dst2, tmp4, tmp5); - CLIP_SH2_0_255_MAX_SATU(tmp4, tmp5); + CLIP_SH2_0_255(tmp4, tmp5); out2 = (v16u8) __msa_pckev_b((v16i8) tmp5, (v16i8) tmp4); ST_D4(out0, out1, 0, 1, 0, 1, dst, dst_stride); ST_D2(out2, 0, 1, dst + 4 * dst_stride, dst_stride); @@ -5537,7 +5537,7 @@ static void hevc_hv_biwgt_4t_8multx4mult_msa(uint8_t *src0_ptr, SRAR_W4_SW(dst4, dst5, dst6, dst7, rnd_vec); PCKEV_H4_SH(dst1, dst0, dst3, dst2, dst5, dst4, dst7, dst6, tmp0, tmp1, tmp2, tmp3); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_D4(out0, out1, 0, 1, 0, 1, dst_tmp, dst_stride); dst_tmp += (4 * dst_stride); @@ -5724,7 +5724,7 @@ static void hevc_hv_biwgt_4t_12w_msa(uint8_t *src0_ptr, SRAR_W4_SW(dst4, dst5, dst6, dst7, rnd_vec); PCKEV_H4_SH(dst1, dst0, dst3, dst2, dst5, dst4, dst7, dst6, tmp0, tmp1, tmp2, tmp3); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_D4(out0, out1, 0, 1, 0, 1, dst_tmp, dst_stride); dst_tmp += (4 * dst_stride); @@ -5820,7 +5820,7 @@ static void hevc_hv_biwgt_4t_12w_msa(uint8_t *src0_ptr, SRAR_W4_SW(dst4, dst5, dst6, dst7, rnd_vec); PCKEV_H4_SH(dst1, dst0, dst3, dst2, dst5, dst4, dst7, dst6, tmp0, tmp1, tmp2, tmp3); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_W8(out0, out1, 0, 1, 2, 3, 0, 1, 2, 3, dst, dst_stride); dst += (8 * dst_stride); diff --git a/libavcodec/mips/hevc_mc_uniw_msa.c b/libavcodec/mips/hevc_mc_uniw_msa.c index cad1240b40e..1a8c2515340 100644 --- a/libavcodec/mips/hevc_mc_uniw_msa.c +++ b/libavcodec/mips/hevc_mc_uniw_msa.c @@ -41,7 +41,7 @@ static const uint8_t ff_hevc_mask_arr[16 * 2] __attribute__((aligned(0x40))) = { SRAR_W4_SW(in0_r_m, in1_r_m, in0_l_m, in1_l_m, rnd_w); \ PCKEV_H2_SH(in0_l_m, in0_r_m, in1_l_m, in1_r_m, out0_h, out1_h); \ ADDS_SH2_SH(out0_h, offset_h, out1_h, offset_h, out0_h, out1_h); \ - CLIP_SH2_0_255_MAX_SATU(out0_h, out1_h); \ + CLIP_SH2_0_255(out0_h, out1_h); \ } #define HEVC_UNIW_RND_CLIP4_MAX_SATU_H(in0_h, in1_h, in2_h, in3_h, wgt_w, \ @@ -88,7 +88,7 @@ static void hevc_uniwgt_copy_4w_msa(uint8_t *src, SRAR_W2_SW(dst0_r, dst0_l, rnd_vec); dst0 = __msa_pckev_h((v8i16) dst0_l, (v8i16) dst0_r); dst0 += offset_vec; - dst0 = CLIP_SH_0_255_MAX_SATU(dst0); + CLIP_SH_0_255(dst0); out0 = (v16u8) __msa_pckev_b((v16i8) dst0, (v16i8) dst0); ST_W2(out0, 0, 1, dst, dst_stride); } else if (4 == height) { @@ -1863,7 +1863,7 @@ static void hevc_hv_uniwgt_8t_4w_msa(uint8_t *src, SRAR_W4_SW(dst0_r, dst1_r, dst2_r, dst3_r, rnd_vec); ADD2(dst0_r, offset_vec, dst1_r, offset_vec, dst0_r, dst1_r); ADD2(dst2_r, offset_vec, dst3_r, offset_vec, dst2_r, dst3_r); - CLIP_SW4_0_255_MAX_SATU(dst0_r, dst1_r, dst2_r, dst3_r); + CLIP_SW4_0_255(dst0_r, dst1_r, dst2_r, dst3_r); PCKEV_H2_SW(dst1_r, dst0_r, dst3_r, dst2_r, dst0_r, dst1_r); out = (v16u8) __msa_pckev_b((v16i8) dst1_r, (v16i8) dst0_r); ST_W4(out, 0, 1, 2, 3, dst, dst_stride); @@ -2014,7 +2014,7 @@ static void hevc_hv_uniwgt_8t_8multx2mult_msa(uint8_t *src, SRAR_W4_SW(dst0_r, dst1_r, dst0_l, dst1_l, rnd_vec); ADD2(dst0_r, offset_vec, dst0_l, offset_vec, dst0_r, dst0_l); ADD2(dst1_r, offset_vec, dst1_l, offset_vec, dst1_r, dst1_l); - CLIP_SW4_0_255_MAX_SATU(dst0_r, dst1_r, dst0_l, dst1_l); + CLIP_SW4_0_255(dst0_r, dst1_r, dst0_l, dst1_l); PCKEV_H2_SW(dst0_l, dst0_r, dst1_l, dst1_r, dst0_r, dst1_r); dst0_r = (v4i32) __msa_pckev_b((v16i8) dst1_r, (v16i8) dst0_r); @@ -2165,7 +2165,7 @@ static void hevc_hv_uniwgt_8t_12w_msa(uint8_t *src, MUL2(dst0_r, weight_vec, dst0_l, weight_vec, dst0_r, dst0_l); SRAR_W2_SW(dst0_r, dst0_l, rnd_vec); ADD2(dst0_r, offset_vec, dst0_l, offset_vec, dst0_r, dst0_l); - CLIP_SW2_0_255_MAX_SATU(dst0_r, dst0_l); + CLIP_SW2_0_255(dst0_r, dst0_l); dst0_r = (v4i32) __msa_pckev_h((v8i16) dst0_l, (v8i16) dst0_r); out = (v16u8) __msa_pckev_b((v16i8) dst0_r, (v16i8) dst0_r); ST_D1(out, 0, dst_tmp); @@ -2246,7 +2246,7 @@ static void hevc_hv_uniwgt_8t_12w_msa(uint8_t *src, SRAR_W4_SW(dst0_r, dst1_r, dst2_r, dst3_r, rnd_vec); ADD2(dst0_r, offset_vec, dst1_r, offset_vec, dst0_r, dst1_r); ADD2(dst2_r, offset_vec, dst3_r, offset_vec, dst2_r, dst3_r); - CLIP_SW4_0_255_MAX_SATU(dst0_r, dst1_r, dst2_r, dst3_r); + CLIP_SW4_0_255(dst0_r, dst1_r, dst2_r, dst3_r); PCKEV_H2_SW(dst1_r, dst0_r, dst3_r, dst2_r, dst0_r, dst1_r); out = (v16u8) __msa_pckev_b((v16i8) dst1_r, (v16i8) dst0_r); ST_W4(out, 0, 1, 2, 3, dst, dst_stride); @@ -2394,7 +2394,7 @@ static void hevc_hz_uniwgt_4t_4x2_msa(uint8_t *src, SRAR_W2_SW(dst0_r, dst0_l, rnd_vec); dst0 = __msa_pckev_h((v8i16) dst0_l, (v8i16) dst0_r); dst0 = __msa_adds_s_h(dst0, offset_vec); - dst0 = CLIP_SH_0_255_MAX_SATU(dst0); + CLIP_SH_0_255(dst0); out = (v16u8) __msa_pckev_b((v16i8) dst0, (v16i8) dst0); ST_W2(out, 0, 1, dst, dst_stride); dst += (4 * dst_stride); @@ -3295,7 +3295,7 @@ static void hevc_vt_uniwgt_4t_4x2_msa(uint8_t *src, SRAR_W2_SW(dst0_r, dst0_l, rnd_vec); dst0 = __msa_pckev_h((v8i16) dst0_l, (v8i16) dst0_r); dst0 = __msa_adds_s_h(dst0, offset_vec); - dst0 = CLIP_SH_0_255_MAX_SATU(dst0); + CLIP_SH_0_255(dst0); out = (v16u8) __msa_pckev_b((v16i8) dst0, (v16i8) dst0); ST_W2(out, 0, 1, dst, dst_stride); } @@ -4247,7 +4247,7 @@ static void hevc_hv_uniwgt_4t_4x2_msa(uint8_t *src, SRAR_W2_SW(dst0, dst1, rnd_vec); tmp = __msa_pckev_h((v8i16) dst1, (v8i16) dst0); tmp += offset_vec; - tmp = CLIP_SH_0_255_MAX_SATU(tmp); + CLIP_SH_0_255(tmp); out = (v16u8) __msa_pckev_b((v16i8) tmp, (v16i8) tmp); ST_W2(out, 0, 1, dst, dst_stride); } @@ -4316,7 +4316,7 @@ static void hevc_hv_uniwgt_4t_4x4_msa(uint8_t *src, SRAR_W4_SW(dst0, dst1, dst2, dst3, rnd_vec); PCKEV_H2_SH(dst1, dst0, dst3, dst2, tmp0, tmp1); ADD2(tmp0, offset_vec, tmp1, offset_vec, tmp0, tmp1); - CLIP_SH2_0_255_MAX_SATU(tmp0, tmp1); + CLIP_SH2_0_255(tmp0, tmp1); out = (v16u8) __msa_pckev_b((v16i8) tmp1, (v16i8) tmp0); ST_W4(out, 0, 1, 2, 3, dst, dst_stride); } @@ -4417,7 +4417,7 @@ static void hevc_hv_uniwgt_4t_4multx8mult_msa(uint8_t *src, tmp2, tmp3); ADD2(tmp0, offset_vec, tmp1, offset_vec, tmp0, tmp1); ADD2(tmp2, offset_vec, tmp3, offset_vec, tmp2, tmp3); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_W8(out0, out1, 0, 1, 2, 3, 0, 1, 2, 3, dst, dst_stride); dst += (8 * dst_stride); @@ -4574,8 +4574,8 @@ static void hevc_hv_uniwgt_4t_6w_msa(uint8_t *src, ADD2(tmp0, offset_vec, tmp1, offset_vec, tmp0, tmp1); ADD2(tmp2, offset_vec, tmp3, offset_vec, tmp2, tmp3); ADD2(tmp4, offset_vec, tmp5, offset_vec, tmp4, tmp5); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); - CLIP_SH2_0_255_MAX_SATU(tmp4, tmp5); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); + CLIP_SH2_0_255(tmp4, tmp5); PCKEV_B3_UB(tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, out0, out1, out2); ST_W8(out0, out1, 0, 1, 2, 3, 0, 1, 2, 3, dst, dst_stride); ST_H8(out2, 0, 1, 2, 3, 4, 5, 6, 7, dst + 4, dst_stride); @@ -4652,7 +4652,7 @@ static void hevc_hv_uniwgt_4t_8x2_msa(uint8_t *src, SRAR_W4_SW(dst0_r, dst0_l, dst1_r, dst1_l, rnd_vec); PCKEV_H2_SH(dst0_l, dst0_r, dst1_l, dst1_r, tmp0, tmp1); ADD2(tmp0, offset_vec, tmp1, offset_vec, tmp0, tmp1); - CLIP_SH2_0_255_MAX_SATU(tmp0, tmp1); + CLIP_SH2_0_255(tmp0, tmp1); out = (v16u8) __msa_pckev_b((v16i8) tmp1, (v16i8) tmp0); ST_D2(out, 0, 1, dst, dst_stride); } @@ -4745,7 +4745,7 @@ static void hevc_hv_uniwgt_4t_8multx4_msa(uint8_t *src, dst3_r, tmp0, tmp1, tmp2, tmp3); ADD2(tmp0, offset_vec, tmp1, offset_vec, tmp0, tmp1); ADD2(tmp2, offset_vec, tmp3, offset_vec, tmp2, tmp3); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_D4(out0, out1, 0, 1, 0, 1, dst, dst_stride); dst += 8; @@ -4861,8 +4861,8 @@ static void hevc_hv_uniwgt_4t_8x6_msa(uint8_t *src, ADD2(tmp0, offset_vec, tmp1, offset_vec, tmp0, tmp1); ADD2(tmp2, offset_vec, tmp3, offset_vec, tmp2, tmp3); ADD2(tmp4, offset_vec, tmp5, offset_vec, tmp4, tmp5); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); - CLIP_SH2_0_255_MAX_SATU(tmp4, tmp5); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); + CLIP_SH2_0_255(tmp4, tmp5); PCKEV_B3_UB(tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, out0, out1, out2); ST_D4(out0, out1, 0, 1, 0, 1, dst, dst_stride); ST_D2(out2, 0, 1, dst + 4 * dst_stride, dst_stride); @@ -4973,7 +4973,7 @@ static void hevc_hv_uniwgt_4t_8multx4mult_msa(uint8_t *src, dst3_r, tmp0, tmp1, tmp2, tmp3); ADD2(tmp0, offset_vec, tmp1, offset_vec, tmp0, tmp1); ADD2(tmp2, offset_vec, tmp3, offset_vec, tmp2, tmp3); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_D4(out0, out1, 0, 1, 0, 1, dst_tmp, dst_stride); dst_tmp += (4 * dst_stride); @@ -5120,7 +5120,7 @@ static void hevc_hv_uniwgt_4t_12w_msa(uint8_t *src, dst3_r, tmp0, tmp1, tmp2, tmp3); ADD2(tmp0, offset_vec, tmp1, offset_vec, tmp0, tmp1); ADD2(tmp2, offset_vec, tmp3, offset_vec, tmp2, tmp3); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_D4(out0, out1, 0, 1, 0, 1, dst_tmp, dst_stride); dst_tmp += (4 * dst_stride); @@ -5187,7 +5187,7 @@ static void hevc_hv_uniwgt_4t_12w_msa(uint8_t *src, tmp2, tmp3); ADD2(tmp0, offset_vec, tmp1, offset_vec, tmp0, tmp1); ADD2(tmp2, offset_vec, tmp3, offset_vec, tmp2, tmp3); - CLIP_SH4_0_255_MAX_SATU(tmp0, tmp1, tmp2, tmp3); + CLIP_SH4_0_255(tmp0, tmp1, tmp2, tmp3); PCKEV_B2_UB(tmp1, tmp0, tmp3, tmp2, out0, out1); ST_W8(out0, out1, 0, 1, 2, 3, 0, 1, 2, 3, dst, dst_stride); dst += (8 * dst_stride); diff --git a/libavcodec/mips/hevcpred_msa.c b/libavcodec/mips/hevcpred_msa.c index b8df089e0cb..f53276d34e0 100644 --- a/libavcodec/mips/hevcpred_msa.c +++ b/libavcodec/mips/hevcpred_msa.c @@ -83,7 +83,7 @@ static void hevc_intra_pred_vert_4x4_msa(const uint8_t *src_top, vec2 -= vec0; vec2 >>= 1; vec2 += vec1; - vec2 = CLIP_SH_0_255(vec2); + CLIP_SH_0_255(vec2); for (col = 0; col < 4; col++) { dst[stride * col] = (uint8_t) vec2[col]; @@ -122,7 +122,7 @@ static void hevc_intra_pred_vert_8x8_msa(const uint8_t *src_top, vec2 -= vec0; vec2 >>= 1; vec2 += vec1; - vec2 = CLIP_SH_0_255(vec2); + CLIP_SH_0_255(vec2); val0 = vec2[0]; val1 = vec2[1]; @@ -214,7 +214,7 @@ static void hevc_intra_pred_horiz_4x4_msa(const uint8_t *src_top, src0_r -= src_top_val; src0_r >>= 1; src0_r += src_left_val; - src0_r = CLIP_SH_0_255(src0_r); + CLIP_SH_0_255(src0_r); src0 = __msa_pckev_b((v16i8) src0_r, (v16i8) src0_r); val0 = __msa_copy_s_w((v4i32) src0, 0); SW(val0, dst); @@ -254,7 +254,7 @@ static void hevc_intra_pred_horiz_8x8_msa(const uint8_t *src_top, src0_r -= src_top_val; src0_r >>= 1; src0_r += src_left_val; - src0_r = CLIP_SH_0_255(src0_r); + CLIP_SH_0_255(src0_r); src0 = __msa_pckev_b((v16i8) src0_r, (v16i8) src0_r); val0 = __msa_copy_s_d((v2i64) src0, 0); SD(val0, dst); @@ -998,7 +998,8 @@ static void hevc_intra_pred_angular_upper_4width_msa(const uint8_t *src_top, ILVR_D2_SH(fact3, fact1, fact7, fact5, fact1, fact3); ILVR_B4_SH(zero, top0, zero, top1, zero, top2, zero, top3, diff0, diff2, diff4, diff6); - SLDI_B4_0_SH(diff0, diff2, diff4, diff6, diff1, diff3, diff5, diff7, 2); + SLDI_B4_SH(zero, diff0, zero, diff2, zero, diff4, zero, diff6, 2, + diff1, diff3, diff5, diff7); ILVR_D2_SH(diff2, diff0, diff6, diff4, diff0, diff2); ILVR_D2_SH(diff3, diff1, diff7, diff5, diff1, diff3); MUL2(diff1, fact0, diff3, fact2, diff1, diff3); @@ -1093,8 +1094,8 @@ static void hevc_intra_pred_angular_upper_8width_msa(const uint8_t *src_top, UNPCK_UB_SH(top2, diff4, diff5); UNPCK_UB_SH(top3, diff6, diff7); - SLDI_B2_SH(diff1, diff3, diff0, diff2, diff1, diff3, 2); - SLDI_B2_SH(diff5, diff7, diff4, diff6, diff5, diff7, 2); + SLDI_B4_SH(diff1, diff0, diff3, diff2, diff5, diff4, diff7, diff6, 2, + diff1, diff3, diff5, diff7); MUL4(diff1, fact0, diff3, fact2, diff5, fact4, diff7, fact6, diff1, diff3, diff5, diff7); @@ -1186,8 +1187,8 @@ static void hevc_intra_pred_angular_upper_16width_msa(const uint8_t *src_top, fact6 = __msa_fill_h(fact_val3); fact7 = __msa_fill_h(32 - fact_val3); - SLDI_B2_UB(top1, top3, top0, top2, top1, top3, 1); - SLDI_B2_UB(top5, top7, top4, top6, top5, top7, 1); + SLDI_B4_UB(top1, top0, top3, top2, top5, top4, top7, top6, 1, + top1, top3, top5, top7); UNPCK_UB_SH(top0, diff0, diff1); UNPCK_UB_SH(top1, diff2, diff3); UNPCK_UB_SH(top2, diff4, diff5); @@ -1297,8 +1298,8 @@ static void hevc_intra_pred_angular_upper_32width_msa(const uint8_t *src_top, top2 = top1; top6 = top5; - SLDI_B2_UB(top1, top3, top0, top2, top1, top3, 1); - SLDI_B2_UB(top5, top7, top4, top6, top5, top7, 1); + SLDI_B4_UB(top1, top0, top3, top2, top5, top4, top7, top6, 1, + top1, top3, top5, top7); UNPCK_UB_SH(top0, diff0, diff1); UNPCK_UB_SH(top1, diff2, diff3); UNPCK_UB_SH(top2, diff4, diff5); @@ -1407,7 +1408,8 @@ static void hevc_intra_pred_angular_lower_4width_msa(const uint8_t *src_top, ILVR_D2_SH(fact3, fact1, fact7, fact5, fact1, fact3); ILVR_B4_SH(zero, top0, zero, top1, zero, top2, zero, top3, diff0, diff2, diff4, diff6); - SLDI_B4_0_SH(diff0, diff2, diff4, diff6, diff1, diff3, diff5, diff7, 2); + SLDI_B4_SH(zero, diff0, zero, diff2, zero, diff4, zero, diff6, 2, + diff1, diff3, diff5, diff7); ILVR_D2_SH(diff2, diff0, diff6, diff4, diff0, diff2); ILVR_D2_SH(diff3, diff1, diff7, diff5, diff1, diff3); MUL2(diff1, fact0, diff3, fact2, diff1, diff3); @@ -1511,8 +1513,8 @@ static void hevc_intra_pred_angular_lower_8width_msa(const uint8_t *src_top, UNPCK_UB_SH(top1, diff2, diff3); UNPCK_UB_SH(top2, diff4, diff5); UNPCK_UB_SH(top3, diff6, diff7); - SLDI_B2_SH(diff1, diff3, diff0, diff2, diff1, diff3, 2); - SLDI_B2_SH(diff5, diff7, diff4, diff6, diff5, diff7, 2); + SLDI_B4_SH(diff1, diff0, diff3, diff2, diff5, diff4, diff7, diff6, 2, + diff1, diff3, diff5, diff7); MUL4(diff1, fact0, diff3, fact2, diff5, fact4, diff7, fact6, diff1, diff3, diff5, diff7); @@ -1606,8 +1608,8 @@ static void hevc_intra_pred_angular_lower_16width_msa(const uint8_t *src_top, fact6 = __msa_fill_h(fact_val3); fact7 = __msa_fill_h(32 - fact_val3); - SLDI_B2_SB(top1, top3, top0, top2, top1, top3, 1); - SLDI_B2_SB(top5, top7, top4, top6, top5, top7, 1); + SLDI_B4_SB(top1, top0, top3, top2, top5, top4, top7, top6, 1, + top1, top3, top5, top7); UNPCK_UB_SH(top0, diff0, diff1); UNPCK_UB_SH(top1, diff2, diff3); @@ -1713,8 +1715,8 @@ static void hevc_intra_pred_angular_lower_32width_msa(const uint8_t *src_top, top2 = top1; top6 = top5; - SLDI_B2_SB(top1, top3, top0, top2, top1, top3, 1); - SLDI_B2_SB(top5, top7, top4, top6, top5, top7, 1); + SLDI_B4_SB(top1, top0, top3, top2, top5, top4, top7, top6, 1, + top1, top3, top5, top7); UNPCK_UB_SH(top0, diff0, diff1); UNPCK_UB_SH(top1, diff2, diff3); diff --git a/libavcodec/mips/hpeldsp_msa.c b/libavcodec/mips/hpeldsp_msa.c index ad92f8f115f..2bbe4771d46 100644 --- a/libavcodec/mips/hpeldsp_msa.c +++ b/libavcodec/mips/hpeldsp_msa.c @@ -59,12 +59,13 @@ static void common_hz_bil_4w_msa(const uint8_t *src, int32_t src_stride, uint8_t loop_cnt; uint32_t out0, out1; v16u8 src0, src1, src0_sld1, src1_sld1, res0, res1; + v16i8 zeros = { 0 }; for (loop_cnt = (height >> 1); loop_cnt--;) { LD_UB2(src, src_stride, src0, src1); src += (2 * src_stride); - SLDI_B2_0_UB(src0, src1, src0_sld1, src1_sld1, 1); + SLDI_B2_UB(zeros, src0, zeros, src1, 1, src0_sld1, src1_sld1); AVER_UB2_UB(src0_sld1, src0, src1_sld1, src1, res0, res1); out0 = __msa_copy_u_w((v4i32) res0, 0); @@ -82,13 +83,14 @@ static void common_hz_bil_8w_msa(const uint8_t *src, int32_t src_stride, { uint8_t loop_cnt; v16i8 src0, src1, src2, src3, src0_sld1, src1_sld1, src2_sld1, src3_sld1; + v16i8 zeros = { 0 }; for (loop_cnt = (height >> 2); loop_cnt--;) { LD_SB4(src, src_stride, src0, src1, src2, src3); src += (4 * src_stride); - SLDI_B4_0_SB(src0, src1, src2, src3, - src0_sld1, src1_sld1, src2_sld1, src3_sld1, 1); + SLDI_B4_SB(zeros, src0, zeros, src1, zeros, src2, zeros, src3, 1, + src0_sld1, src1_sld1, src2_sld1, src3_sld1); AVER_ST8x4_UB(src0, src0_sld1, src1, src1_sld1, src2, src2_sld1, src3, src3_sld1, dst, dst_stride); dst += (4 * dst_stride); @@ -125,14 +127,15 @@ static void common_hz_bil_no_rnd_8x8_msa(const uint8_t *src, int32_t src_stride, v16i8 src0, src1, src2, src3, src4, src5, src6, src7; v16i8 src0_sld1, src1_sld1, src2_sld1, src3_sld1; v16i8 src4_sld1, src5_sld1, src6_sld1, src7_sld1; + v16i8 zeros = { 0 }; LD_SB8(src, src_stride, src0, src1, src2, src3, src4, src5, src6, src7); src += (8 * src_stride); - SLDI_B4_0_SB(src0, src1, src2, src3, - src0_sld1, src1_sld1, src2_sld1, src3_sld1, 1); - SLDI_B4_0_SB(src4, src5, src6, src7, - src4_sld1, src5_sld1, src6_sld1, src7_sld1, 1); + SLDI_B4_SB(zeros, src0, zeros, src1, zeros, src2, zeros, src3, 1, + src0_sld1, src1_sld1, src2_sld1, src3_sld1); + SLDI_B4_SB(zeros, src4, zeros, src5, zeros, src6, zeros, src7, 1, + src4_sld1, src5_sld1, src6_sld1, src7_sld1); AVE_ST8x4_UB(src0, src0_sld1, src1, src1_sld1, src2, src2_sld1, src3, src3_sld1, dst, dst_stride); @@ -145,10 +148,11 @@ static void common_hz_bil_no_rnd_4x8_msa(const uint8_t *src, int32_t src_stride, uint8_t *dst, int32_t dst_stride) { v16i8 src0, src1, src2, src3, src0_sld1, src1_sld1, src2_sld1, src3_sld1; + v16i8 zeros = { 0 }; LD_SB4(src, src_stride, src0, src1, src2, src3); - SLDI_B4_0_SB(src0, src1, src2, src3, - src0_sld1, src1_sld1, src2_sld1, src3_sld1, 1); + SLDI_B4_SB(zeros, src0, zeros, src1, zeros, src2, zeros, src3, 1, + src0_sld1, src1_sld1, src2_sld1, src3_sld1); AVE_ST8x4_UB(src0, src0_sld1, src1, src1_sld1, src2, src2_sld1, src3, src3_sld1, dst, dst_stride); } @@ -216,12 +220,13 @@ static void common_hz_bil_and_aver_dst_4w_msa(const uint8_t *src, v16u8 src0, src1, src0_sld1, src1_sld1, res0, res1; v16u8 tmp0 = { 0 }; v16u8 tmp1 = { 0 }; + v16i8 zeros = { 0 }; for (loop_cnt = (height >> 1); loop_cnt--;) { LD_UB2(src, src_stride, src0, src1); src += (2 * src_stride); - SLDI_B2_0_UB(src0, src1, src0_sld1, src1_sld1, 1); + SLDI_B2_UB(zeros, src0, zeros, src1, 1, src0_sld1, src1_sld1); dst0 = LW(dst); dst1 = LW(dst + dst_stride); @@ -247,13 +252,14 @@ static void common_hz_bil_and_aver_dst_8w_msa(const uint8_t *src, { uint8_t loop_cnt; v16i8 src0, src1, src2, src3, src0_sld1, src1_sld1, src2_sld1, src3_sld1; + v16i8 zeros = { 0 }; for (loop_cnt = (height >> 2); loop_cnt--;) { LD_SB4(src, src_stride, src0, src1, src2, src3); src += (4 * src_stride); - SLDI_B4_0_SB(src0, src1, src2, src3, - src0_sld1, src1_sld1, src2_sld1, src3_sld1, 1); + SLDI_B4_SB(zeros, src0, zeros, src1, zeros, src2, zeros, src3, 1, + src0_sld1, src1_sld1, src2_sld1, src3_sld1); AVER_DST_ST8x4_UB(src0, src0_sld1, src1, src1_sld1, src2, src2_sld1, src3, src3_sld1, dst, dst_stride); @@ -529,6 +535,7 @@ static void common_hv_bil_4w_msa(const uint8_t *src, int32_t src_stride, v16i8 src0, src1, src2, src0_sld1, src1_sld1, src2_sld1; v16u8 src0_r, src1_r, src2_r, res; v8u16 add0, add1, add2, sum0, sum1; + v16i8 zeros = { 0 }; src0 = LD_SB(src); src += src_stride; @@ -537,7 +544,8 @@ static void common_hv_bil_4w_msa(const uint8_t *src, int32_t src_stride, LD_SB2(src, src_stride, src1, src2); src += (2 * src_stride); - SLDI_B3_0_SB(src0, src1, src2, src0_sld1, src1_sld1, src2_sld1, 1); + SLDI_B3_SB(zeros, src0, zeros, src1, zeros, src2, 1, src0_sld1, + src1_sld1, src2_sld1); ILVR_B3_UB(src0_sld1, src0, src1_sld1, src1, src2_sld1, src2, src0_r, src1_r, src2_r); HADD_UB3_UH(src0_r, src1_r, src2_r, add0, add1, add2); @@ -565,6 +573,7 @@ static void common_hv_bil_8w_msa(const uint8_t *src, int32_t src_stride, v16u8 src0_r, src1_r, src2_r, src3_r, src4_r; v8u16 add0, add1, add2, add3, add4; v8u16 sum0, sum1, sum2, sum3; + v16i8 zeros = { 0 }; src0 = LD_SB(src); src += src_stride; @@ -573,8 +582,9 @@ static void common_hv_bil_8w_msa(const uint8_t *src, int32_t src_stride, LD_SB4(src, src_stride, src1, src2, src3, src4); src += (4 * src_stride); - SLDI_B3_0_SB(src0, src1, src2, src0_sld1, src1_sld1, src2_sld1, 1); - SLDI_B2_0_SB(src3, src4, src3_sld1, src4_sld1, 1); + SLDI_B3_SB(zeros, src0, zeros, src1, zeros, src2, 1, src0_sld1, + src1_sld1, src2_sld1); + SLDI_B2_SB(zeros, src3, zeros, src4, 1, src3_sld1, src4_sld1); ILVR_B3_UB(src0_sld1, src0, src1_sld1, src1, src2_sld1, src2, src0_r, src1_r, src2_r); ILVR_B2_UB(src3_sld1, src3, src4_sld1, src4, src3_r, src4_r); @@ -659,15 +669,17 @@ static void common_hv_bil_no_rnd_8x8_msa(const uint8_t *src, int32_t src_stride, v8u16 add0, add1, add2, add3, add4, add5, add6, add7, add8; v8u16 sum0, sum1, sum2, sum3, sum4, sum5, sum6, sum7; v16i8 out0, out1; + v16i8 zeros = { 0 }; LD_UB8(src, src_stride, src0, src1, src2, src3, src4, src5, src6, src7); src += (8 * src_stride); src8 = LD_UB(src); - SLDI_B4_0_UB(src0, src1, src2, src3, src0_sld1, src1_sld1, src2_sld1, - src3_sld1, 1); - SLDI_B3_0_UB(src4, src5, src6, src4_sld1, src5_sld1, src6_sld1, 1); - SLDI_B2_0_UB(src7, src8, src7_sld1, src8_sld1, 1); + SLDI_B4_UB(zeros, src0, zeros, src1, zeros, src2, zeros, src3, 1, + src0_sld1, src1_sld1, src2_sld1, src3_sld1); + SLDI_B3_UB(zeros, src4, zeros, src5, zeros, src6, 1, src4_sld1, + src5_sld1, src6_sld1); + SLDI_B2_UB(zeros, src7, zeros, src8, 1, src7_sld1, src8_sld1); ILVR_B4_UH(src0_sld1, src0, src1_sld1, src1, src2_sld1, src2, src3_sld1, src3, src0_r, src1_r, src2_r, src3_r); ILVR_B3_UH(src4_sld1, src4, src5_sld1, src5, src6_sld1, src6, src4_r, @@ -703,13 +715,15 @@ static void common_hv_bil_no_rnd_4x8_msa(const uint8_t *src, int32_t src_stride, v8u16 add0, add1, add2, add3, add4; v8u16 sum0, sum1, sum2, sum3; v16i8 out0, out1; + v16i8 zeros = { 0 }; LD_SB4(src, src_stride, src0, src1, src2, src3); src += (4 * src_stride); src4 = LD_SB(src); - SLDI_B3_0_SB(src0, src1, src2, src0_sld1, src1_sld1, src2_sld1, 1); - SLDI_B2_0_SB(src3, src4, src3_sld1, src4_sld1, 1); + SLDI_B3_SB(zeros, src0, zeros, src1, zeros, src2, 1, src0_sld1, + src1_sld1, src2_sld1); + SLDI_B2_SB(zeros, src3, zeros, src4, 1, src3_sld1, src4_sld1); ILVR_B3_UH(src0_sld1, src0, src1_sld1, src1, src2_sld1, src2, src0_r, src1_r, src2_r); ILVR_B2_UH(src3_sld1, src3, src4_sld1, src4, src3_r, src4_r); @@ -918,6 +932,7 @@ static void common_hv_bil_and_aver_dst_4w_msa(const uint8_t *src, v16u8 src0_r, src1_r, src2_r; v8u16 add0, add1, add2, sum0, sum1; v16u8 dst0, dst1, res0, res1; + v16i8 zeros = { 0 }; src0 = LD_SB(src); src += src_stride; @@ -927,7 +942,8 @@ static void common_hv_bil_and_aver_dst_4w_msa(const uint8_t *src, src += (2 * src_stride); LD_UB2(dst, dst_stride, dst0, dst1); - SLDI_B3_0_SB(src0, src1, src2, src0_sld1, src1_sld1, src2_sld1, 1); + SLDI_B3_SB(zeros, src0, zeros, src1, zeros, src2, 1, src0_sld1, + src1_sld1, src2_sld1); ILVR_B3_UB(src0_sld1, src0, src1_sld1, src1, src2_sld1, src2, src0_r, src1_r, src2_r); HADD_UB3_UH(src0_r, src1_r, src2_r, add0, add1, add2); @@ -959,6 +975,7 @@ static void common_hv_bil_and_aver_dst_8w_msa(const uint8_t *src, v16u8 src0_r, src1_r, src2_r, src3_r, src4_r; v8u16 add0, add1, add2, add3, add4; v8u16 sum0, sum1, sum2, sum3; + v16i8 zeros = { 0 }; src0 = LD_SB(src); src += src_stride; @@ -968,8 +985,9 @@ static void common_hv_bil_and_aver_dst_8w_msa(const uint8_t *src, src += (4 * src_stride); LD_UB4(dst, dst_stride, dst0, dst1, dst2, dst3); - SLDI_B3_0_SB(src0, src1, src2, src0_sld1, src1_sld1, src2_sld1, 1); - SLDI_B2_0_SB(src3, src4, src3_sld1, src4_sld1, 1); + SLDI_B3_SB(zeros, src0, zeros, src1, zeros, src2, 1, src0_sld1, + src1_sld1, src2_sld1); + SLDI_B2_SB(zeros, src3, zeros, src4, 1, src3_sld1, src4_sld1); ILVR_B3_UB(src0_sld1, src0, src1_sld1, src1, src2_sld1, src2, src0_r, src1_r, src2_r); ILVR_B2_UB(src3_sld1, src3, src4_sld1, src4, src3_r, src4_r); diff --git a/libavcodec/mips/idctdsp_msa.c b/libavcodec/mips/idctdsp_msa.c index b29e4205564..b6b98dc7fcc 100644 --- a/libavcodec/mips/idctdsp_msa.c +++ b/libavcodec/mips/idctdsp_msa.c @@ -28,8 +28,7 @@ static void put_pixels_clamped_msa(const int16_t *block, uint8_t *pixels, v8i16 in0, in1, in2, in3, in4, in5, in6, in7; LD_SH8(block, 8, in0, in1, in2, in3, in4, in5, in6, in7); - CLIP_SH4_0_255(in0, in1, in2, in3); - CLIP_SH4_0_255(in4, in5, in6, in7); + CLIP_SH8_0_255(in0, in1, in2, in3, in4, in5, in6, in7); PCKEV_B4_SH(in0, in0, in1, in1, in2, in2, in3, in3, in0, in1, in2, in3); PCKEV_B4_SH(in4, in4, in5, in5, in6, in6, in7, in7, in4, in5, in6, in7); @@ -63,8 +62,7 @@ static void put_signed_pixels_clamped_msa(const int16_t *block, uint8_t *pixels, in6 += 128; in7 += 128; - CLIP_SH4_0_255(in0, in1, in2, in3); - CLIP_SH4_0_255(in4, in5, in6, in7); + CLIP_SH8_0_255(in0, in1, in2, in3, in4, in5, in6, in7); PCKEV_B4_SH(in0, in0, in1, in1, in2, in2, in3, in3, in0, in1, in2, in3); PCKEV_B4_SH(in4, in4, in5, in5, in6, in6, in7, in7, in4, in5, in6, in7); @@ -109,8 +107,7 @@ static void add_pixels_clamped_msa(const int16_t *block, uint8_t *pixels, in6 += (v8i16) pix6; in7 += (v8i16) pix7; - CLIP_SH4_0_255(in0, in1, in2, in3); - CLIP_SH4_0_255(in4, in5, in6, in7); + CLIP_SH8_0_255(in0, in1, in2, in3, in4, in5, in6, in7); PCKEV_B4_SH(in0, in0, in1, in1, in2, in2, in3, in3, in0, in1, in2, in3); PCKEV_B4_SH(in4, in4, in5, in5, in6, in6, in7, in7, in4, in5, in6, in7); diff --git a/libavcodec/mips/me_cmp_msa.c b/libavcodec/mips/me_cmp_msa.c index 0e3165cd8f6..7cb7af00477 100644 --- a/libavcodec/mips/me_cmp_msa.c +++ b/libavcodec/mips/me_cmp_msa.c @@ -87,8 +87,8 @@ static uint32_t sad_horiz_bilinear_filter_8width_msa(uint8_t *src, PCKEV_D2_UB(src1, src0, src3, src2, src0, src1); PCKEV_D2_UB(ref1, ref0, ref3, ref2, ref4, ref5); - SLDI_B2_UB(ref0, ref1, ref0, ref1, ref0, ref1, 1); - SLDI_B2_UB(ref2, ref3, ref2, ref3, ref2, ref3, 1); + SLDI_B4_UB(ref0, ref0, ref1, ref1, ref2, ref2, ref3, ref3, 1, + ref0, ref1, ref2, ref3); PCKEV_D2_UB(ref1, ref0, ref3, ref2, ref0, ref1); AVER_UB2_UB(ref4, ref0, ref5, ref1, comp0, comp1); sad += SAD_UB2_UH(src0, src1, comp0, comp1); @@ -100,8 +100,8 @@ static uint32_t sad_horiz_bilinear_filter_8width_msa(uint8_t *src, PCKEV_D2_UB(src1, src0, src3, src2, src0, src1); PCKEV_D2_UB(ref1, ref0, ref3, ref2, ref4, ref5); - SLDI_B2_UB(ref0, ref1, ref0, ref1, ref0, ref1, 1); - SLDI_B2_UB(ref2, ref3, ref2, ref3, ref2, ref3, 1); + SLDI_B4_UB(ref0, ref0, ref1, ref1, ref2, ref2, ref3, ref3, 1, + ref0, ref1, ref2, ref3); PCKEV_D2_UB(ref1, ref0, ref3, ref2, ref0, ref1); AVER_UB2_UB(ref4, ref0, ref5, ref1, comp0, comp1); sad += SAD_UB2_UH(src0, src1, comp0, comp1); diff --git a/libavcodec/mips/qpeldsp_msa.c b/libavcodec/mips/qpeldsp_msa.c index fba42b30035..c7675f112ec 100644 --- a/libavcodec/mips/qpeldsp_msa.c +++ b/libavcodec/mips/qpeldsp_msa.c @@ -96,7 +96,7 @@ DPADD_UB2_UH(sum2_r, sum1_r, coef2, coef1, sum0_r, sum3_r); \ res0_r = (v8i16) (sum0_r - sum3_r); \ res0_r = __msa_srari_h(res0_r, 5); \ - res0_r = CLIP_SH_0_255(res0_r); \ + CLIP_SH_0_255(res0_r); \ out = (v16u8) __msa_pckev_b((v16i8) res0_r, (v16i8) res0_r); \ \ out; \ @@ -118,7 +118,7 @@ res0_r = (v8i16) (sum0_r - sum3_r); \ res0_r += 15; \ res0_r >>= 5; \ - res0_r = CLIP_SH_0_255(res0_r); \ + CLIP_SH_0_255(res0_r); \ out = (v16u8) __msa_pckev_b((v16i8) res0_r, (v16i8) res0_r); \ \ out; \ @@ -480,8 +480,8 @@ static void horiz_mc_qpel_aver_src1_8width_msa(const uint8_t *src, res1 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B4_UB(inp0, inp0, inp1, inp1, inp2, inp2, inp3, inp3, 1, + inp0, inp1, inp2, inp3); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); AVER_UB2_UB(inp0, res0, inp2, res1, res0, res1); @@ -710,8 +710,8 @@ static void horiz_mc_qpel_no_rnd_aver_src1_8width_msa(const uint8_t *src, res1 = APPLY_HORIZ_QPEL_NO_ROUND_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B4_UB(inp0, inp0, inp1, inp1, inp2, inp2, inp3, inp3, 1, + inp0, inp1, inp2, inp3); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); res0 = __msa_ave_u_b(inp0, res0); @@ -948,8 +948,8 @@ static void horiz_mc_qpel_avg_dst_aver_src1_8width_msa(const uint8_t *src, mask0, mask1, mask2, mask3, const20, const6, const3); LD_UB4(dst, dst_stride, dst0, dst1, dst2, dst3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B4_UB(inp0, inp0, inp1, inp1, inp2, inp2, inp3, inp3, 1, + inp0, inp1, inp2, inp3); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); dst0 = (v16u8) __msa_insve_d((v2i64) dst0, 1, (v2i64) dst1); @@ -3094,7 +3094,7 @@ static void hv_mc_qpel_no_rnd_aver_hv_src10_8x8_msa(const uint8_t *src, res0 = APPLY_HORIZ_QPEL_NO_ROUND_FILTER_8BYTE(inp0, inp1, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); horiz0 = __msa_ave_u_b(inp0, res0); @@ -3104,7 +3104,7 @@ static void hv_mc_qpel_no_rnd_aver_hv_src10_8x8_msa(const uint8_t *src, res1 = APPLY_HORIZ_QPEL_NO_ROUND_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); horiz2 = __msa_ave_u_b(inp2, res1); @@ -3114,7 +3114,7 @@ static void hv_mc_qpel_no_rnd_aver_hv_src10_8x8_msa(const uint8_t *src, res0 = APPLY_HORIZ_QPEL_NO_ROUND_FILTER_8BYTE(inp0, inp1, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); horiz4 = __msa_ave_u_b(inp0, res0); @@ -3134,7 +3134,7 @@ static void hv_mc_qpel_no_rnd_aver_hv_src10_8x8_msa(const uint8_t *src, res1 = APPLY_HORIZ_QPEL_NO_ROUND_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); horiz6 = __msa_ave_u_b(inp2, res1); @@ -3389,7 +3389,7 @@ static void hv_mc_qpel_no_rnd_aver_h_src1_8x8_msa(const uint8_t *src, res0 = APPLY_HORIZ_QPEL_NO_ROUND_FILTER_8BYTE(inp0, inp1, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); horiz0 = __msa_ave_u_b(inp0, res0); @@ -3399,7 +3399,7 @@ static void hv_mc_qpel_no_rnd_aver_h_src1_8x8_msa(const uint8_t *src, res1 = APPLY_HORIZ_QPEL_NO_ROUND_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); horiz2 = __msa_ave_u_b(inp2, res1); @@ -3409,7 +3409,7 @@ static void hv_mc_qpel_no_rnd_aver_h_src1_8x8_msa(const uint8_t *src, res0 = APPLY_HORIZ_QPEL_NO_ROUND_FILTER_8BYTE(inp0, inp1, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); horiz4 = __msa_ave_u_b(inp0, res0); @@ -3427,7 +3427,7 @@ static void hv_mc_qpel_no_rnd_aver_h_src1_8x8_msa(const uint8_t *src, res1 = APPLY_HORIZ_QPEL_NO_ROUND_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); horiz6 = __msa_ave_u_b(inp2, res1); @@ -3691,7 +3691,7 @@ static void hv_mc_qpel_no_rnd_aver_hv_src11_8x8_msa(const uint8_t *src, res0 = APPLY_HORIZ_QPEL_NO_ROUND_FILTER_8BYTE(inp0, inp1, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); horiz0 = __msa_ave_u_b(inp0, res0); @@ -3701,7 +3701,7 @@ static void hv_mc_qpel_no_rnd_aver_hv_src11_8x8_msa(const uint8_t *src, res1 = APPLY_HORIZ_QPEL_NO_ROUND_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); horiz2 = __msa_ave_u_b(inp2, res1); @@ -3712,7 +3712,7 @@ static void hv_mc_qpel_no_rnd_aver_hv_src11_8x8_msa(const uint8_t *src, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); horiz4 = __msa_ave_u_b(inp0, res0); horiz5 = (v16u8) __msa_splati_d((v2i64) horiz4, 1); @@ -3731,7 +3731,7 @@ static void hv_mc_qpel_no_rnd_aver_hv_src11_8x8_msa(const uint8_t *src, res1 = APPLY_HORIZ_QPEL_NO_ROUND_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); horiz6 = __msa_ave_u_b(inp2, res1); @@ -4134,12 +4134,12 @@ static void hv_mc_qpel_aver_hv_src10_8x8_msa(const uint8_t *src, const20, const6, const3); res1 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); horiz0 = __msa_aver_u_b(inp0, res0); horiz1 = (v16u8) __msa_splati_d((v2i64) horiz0, 1); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); horiz2 = __msa_aver_u_b(inp2, res1); @@ -4150,12 +4150,12 @@ static void hv_mc_qpel_aver_hv_src10_8x8_msa(const uint8_t *src, const20, const6, const3); res1 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); horiz4 = __msa_aver_u_b(inp0, res0); horiz5 = (v16u8) __msa_splati_d((v2i64) horiz4, 1); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); horiz6 = __msa_aver_u_b(inp2, res1); @@ -4410,12 +4410,12 @@ static void hv_mc_qpel_aver_h_src1_8x8_msa(const uint8_t *src, const20, const6, const3); res1 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); horiz0 = __msa_aver_u_b(inp0, res0); horiz1 = (v16u8) __msa_splati_d((v2i64) horiz0, 1); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); horiz2 = __msa_aver_u_b(inp2, res1); @@ -4426,12 +4426,12 @@ static void hv_mc_qpel_aver_h_src1_8x8_msa(const uint8_t *src, const20, const6, const3); res1 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); horiz4 = __msa_aver_u_b(inp0, res0); horiz5 = (v16u8) __msa_splati_d((v2i64) horiz4, 1); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); horiz6 = __msa_aver_u_b(inp2, res1); @@ -4690,14 +4690,14 @@ static void hv_mc_qpel_aver_hv_src11_8x8_msa(const uint8_t *src, res0 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp0, inp1, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); horiz0 = __msa_aver_u_b(inp0, res0); horiz1 = (v16u8) __msa_splati_d((v2i64) horiz0, 1); res1 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); horiz2 = __msa_aver_u_b(inp2, res1); @@ -4706,7 +4706,7 @@ static void hv_mc_qpel_aver_hv_src11_8x8_msa(const uint8_t *src, src += (2 * src_stride); res0 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp0, inp1, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_insve_d((v2i64) inp0, 1, (v2i64) inp1); horiz4 = __msa_aver_u_b(inp0, res0); @@ -4725,7 +4725,7 @@ static void hv_mc_qpel_aver_hv_src11_8x8_msa(const uint8_t *src, res1 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_insve_d((v2i64) inp2, 1, (v2i64) inp3); horiz6 = __msa_aver_u_b(inp2, res1); @@ -5020,7 +5020,7 @@ static void hv_mc_qpel_avg_dst_aver_hv_src10_8x8_msa(const uint8_t *src, LD_UB2(src, src_stride, inp2, inp3); src += (2 * src_stride); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_ilvr_d((v2i64) inp1, (v2i64) inp0); horiz0 = __msa_aver_u_b(inp0, res0); @@ -5029,7 +5029,7 @@ static void hv_mc_qpel_avg_dst_aver_hv_src10_8x8_msa(const uint8_t *src, const20, const6, const3); LD_UB2(src, src_stride, inp0, inp1); src += (2 * src_stride); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_ilvr_d((v2i64) inp3, (v2i64) inp2); horiz2 = __msa_aver_u_b(inp2, res1); @@ -5037,7 +5037,7 @@ static void hv_mc_qpel_avg_dst_aver_hv_src10_8x8_msa(const uint8_t *src, res0 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp0, inp1, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_ilvr_d((v2i64) inp1, (v2i64) inp0); horiz4 = __msa_aver_u_b(inp0, res0); @@ -5060,7 +5060,7 @@ static void hv_mc_qpel_avg_dst_aver_hv_src10_8x8_msa(const uint8_t *src, res1 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_ilvr_d((v2i64) inp3, (v2i64) inp2); horiz6 = __msa_aver_u_b(inp2, res1); @@ -5347,7 +5347,7 @@ static void hv_mc_qpel_avg_dst_aver_h_src1_8x8_msa(const uint8_t *src, const20, const6, const3); LD_UB2(src, src_stride, inp2, inp3); src += (2 * src_stride); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_ilvr_d((v2i64) inp1, (v2i64) inp0); horiz0 = __msa_aver_u_b(inp0, res0); @@ -5356,7 +5356,7 @@ static void hv_mc_qpel_avg_dst_aver_h_src1_8x8_msa(const uint8_t *src, const20, const6, const3); LD_UB2(src, src_stride, inp0, inp1); src += (2 * src_stride); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_ilvr_d((v2i64) inp3, (v2i64) inp2); horiz2 = __msa_aver_u_b(inp2, res1); @@ -5364,7 +5364,7 @@ static void hv_mc_qpel_avg_dst_aver_h_src1_8x8_msa(const uint8_t *src, res0 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp0, inp1, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_ilvr_d((v2i64) inp1, (v2i64) inp0); horiz4 = __msa_aver_u_b(inp0, res0); @@ -5385,7 +5385,7 @@ static void hv_mc_qpel_avg_dst_aver_h_src1_8x8_msa(const uint8_t *src, res1 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_ilvr_d((v2i64) inp3, (v2i64) inp2); horiz6 = __msa_aver_u_b(inp2, res1); @@ -5684,7 +5684,7 @@ static void hv_mc_qpel_avg_dst_aver_hv_src11_8x8_msa(const uint8_t *src, const20, const6, const3); LD_UB2(src, src_stride, inp2, inp3); src += (2 * src_stride); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_ilvr_d((v2i64) inp1, (v2i64) inp0); horiz0 = __msa_aver_u_b(inp0, res0); @@ -5693,14 +5693,14 @@ static void hv_mc_qpel_avg_dst_aver_hv_src11_8x8_msa(const uint8_t *src, const20, const6, const3); LD_UB2(src, src_stride, inp0, inp1); src += (2 * src_stride); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_ilvr_d((v2i64) inp3, (v2i64) inp2); horiz2 = __msa_aver_u_b(inp2, res1); horiz3 = (v16u8) __msa_splati_d((v2i64) horiz2, 1); res0 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp0, inp1, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp0, inp1, inp0, inp1, inp0, inp1, 1); + SLDI_B2_UB(inp0, inp0, inp1, inp1, 1, inp0, inp1); inp0 = (v16u8) __msa_ilvr_d((v2i64) inp1, (v2i64) inp0); horiz4 = __msa_aver_u_b(inp0, res0); @@ -5721,7 +5721,7 @@ static void hv_mc_qpel_avg_dst_aver_hv_src11_8x8_msa(const uint8_t *src, src += (2 * src_stride); res1 = APPLY_HORIZ_QPEL_FILTER_8BYTE(inp2, inp3, mask0, mask1, mask2, mask3, const20, const6, const3); - SLDI_B2_UB(inp2, inp3, inp2, inp3, inp2, inp3, 1); + SLDI_B2_UB(inp2, inp2, inp3, inp3, 1, inp2, inp3); inp2 = (v16u8) __msa_ilvr_d((v2i64) inp3, (v2i64) inp2); horiz6 = __msa_aver_u_b(inp2, res1); diff --git a/libavcodec/mips/sbrdsp_mips.c b/libavcodec/mips/sbrdsp_mips.c index 1b0a10608de..83039fd802a 100644 --- a/libavcodec/mips/sbrdsp_mips.c +++ b/libavcodec/mips/sbrdsp_mips.c @@ -59,6 +59,7 @@ #include "libavutil/mips/asmdefs.h" #if HAVE_INLINE_ASM +#if HAVE_MIPSFPU static void sbr_qmf_pre_shuffle_mips(float *z) { int Temp1, Temp2, Temp3, Temp4, Temp5, Temp6; @@ -165,7 +166,6 @@ static void sbr_qmf_post_shuffle_mips(float W[32][2], const float *z) ); } -#if HAVE_MIPSFPU #if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 static void sbr_sum64x5_mips(float *z) { @@ -890,9 +890,9 @@ static void sbr_hf_apply_noise_3_mips(float (*Y)[2], const float *s_m, void ff_sbrdsp_init_mips(SBRDSPContext *s) { #if HAVE_INLINE_ASM +#if HAVE_MIPSFPU s->qmf_pre_shuffle = sbr_qmf_pre_shuffle_mips; s->qmf_post_shuffle = sbr_qmf_post_shuffle_mips; -#if HAVE_MIPSFPU #if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 s->sum64x5 = sbr_sum64x5_mips; s->sum_square = sbr_sum_square_mips; diff --git a/libavcodec/mips/simple_idct_mmi.c b/libavcodec/mips/simple_idct_mmi.c index 7f4bb74fd20..73d797ffbc8 100644 --- a/libavcodec/mips/simple_idct_mmi.c +++ b/libavcodec/mips/simple_idct_mmi.c @@ -39,7 +39,7 @@ #define COL_SHIFT 20 #define DC_SHIFT 3 -DECLARE_ALIGNED(8, const int16_t, W_arr)[46] = { +DECLARE_ALIGNED(16, const int16_t, W_arr)[46] = { W4, W2, W4, W6, W1, W3, W5, W7, W4, W6, -W4, -W2, diff --git a/libavcodec/mips/simple_idct_msa.c b/libavcodec/mips/simple_idct_msa.c index 8a7235927e2..4bd3dd8a25e 100644 --- a/libavcodec/mips/simple_idct_msa.c +++ b/libavcodec/mips/simple_idct_msa.c @@ -336,35 +336,26 @@ static void simple_idct_put_msa(uint8_t *dst, int32_t dst_stride, SRA_4V(temp2_r, temp2_l, temp3_r, temp3_l, 20); SRA_4V(a3_r, a3_l, a2_r, a2_l, 20); SRA_4V(a1_r, a1_l, a0_r, a0_l, 20); - PCKEV_H4_SW(temp0_l, temp0_r, temp1_l, temp1_r, temp2_l, temp2_r, - temp3_l, temp3_r, temp0_r, temp1_r, temp2_r, temp3_r); - PCKEV_H4_SW(a0_l, a0_r, a1_l, a1_r, a2_l, a2_r, a3_l, a3_r, - a0_r, a1_r, a2_r, a3_r); - temp0_r = (v4i32) CLIP_SH_0_255(temp0_r); - temp1_r = (v4i32) CLIP_SH_0_255(temp1_r); - temp2_r = (v4i32) CLIP_SH_0_255(temp2_r); - temp3_r = (v4i32) CLIP_SH_0_255(temp3_r); - PCKEV_B4_SW(temp0_r, temp0_r, temp1_r, temp1_r, - temp2_r, temp2_r, temp3_r, temp3_r, - temp0_r, temp1_r, temp2_r, temp3_r); - tmp0 = __msa_copy_u_d((v2i64) temp0_r, 1); - tmp1 = __msa_copy_u_d((v2i64) temp1_r, 1); - tmp2 = __msa_copy_u_d((v2i64) temp2_r, 1); - tmp3 = __msa_copy_u_d((v2i64) temp3_r, 1); - SD4(tmp0, tmp1, tmp2, tmp3, dst, dst_stride); - dst += 4 * dst_stride; - a0_r = (v4i32) CLIP_SH_0_255(a0_r); - a1_r = (v4i32) CLIP_SH_0_255(a1_r); - a2_r = (v4i32) CLIP_SH_0_255(a2_r); - a3_r = (v4i32) CLIP_SH_0_255(a3_r); - PCKEV_B4_SW(a0_r, a0_r, a1_r, a1_r, - a2_r, a2_r, a3_r, a3_r, a0_r, a1_r, a2_r, a3_r); - tmp3 = __msa_copy_u_d((v2i64) a0_r, 1); - tmp2 = __msa_copy_u_d((v2i64) a1_r, 1); - tmp1 = __msa_copy_u_d((v2i64) a2_r, 1); - tmp0 = __msa_copy_u_d((v2i64) a3_r, 1); + PCKEV_H4_SH(temp0_l, temp0_r, temp1_l, temp1_r, temp2_l, temp2_r, + temp3_l, temp3_r, in0, in1, in2, in3); + PCKEV_H4_SH(a0_l, a0_r, a1_l, a1_r, a2_l, a2_r, a3_l, a3_r, + in4, in5, in6, in7); + CLIP_SH4_0_255(in0, in1, in2, in3); + PCKEV_B4_SH(in0, in0, in1, in1, in2, in2, in3, in3, + in0, in1, in2, in3); + tmp0 = __msa_copy_u_d((v2i64) in0, 1); + tmp1 = __msa_copy_u_d((v2i64) in1, 1); + tmp2 = __msa_copy_u_d((v2i64) in2, 1); + tmp3 = __msa_copy_u_d((v2i64) in3, 1); SD4(tmp0, tmp1, tmp2, tmp3, dst, dst_stride); - dst += 4 * dst_stride; + CLIP_SH4_0_255(in4, in5, in6, in7); + PCKEV_B4_SH(in4, in4, in5, in5, in6, in6, in7, in7, + in4, in5, in6, in7); + tmp3 = __msa_copy_u_d((v2i64) in4, 1); + tmp2 = __msa_copy_u_d((v2i64) in5, 1); + tmp1 = __msa_copy_u_d((v2i64) in6, 1); + tmp0 = __msa_copy_u_d((v2i64) in7, 1); + SD4(tmp0, tmp1, tmp2, tmp3, dst + 4 * dst_stride, dst_stride); } static void simple_idct_add_msa(uint8_t *dst, int32_t dst_stride, @@ -516,21 +507,17 @@ static void simple_idct_add_msa(uint8_t *dst, int32_t dst_stride, temp3_l, temp3_r, temp0_r, temp1_r, temp2_r, temp3_r); ILVR_B4_SW(zero, in0, zero, in1, zero, in2, zero, in3, temp0_l, temp1_l, temp2_l, temp3_l); - temp0_r = (v4i32) ((v8i16) (temp0_r) + (v8i16) (temp0_l)); - temp1_r = (v4i32) ((v8i16) (temp1_r) + (v8i16) (temp1_l)); - temp2_r = (v4i32) ((v8i16) (temp2_r) + (v8i16) (temp2_l)); - temp3_r = (v4i32) ((v8i16) (temp3_r) + (v8i16) (temp3_l)); - temp0_r = (v4i32) CLIP_SH_0_255(temp0_r); - temp1_r = (v4i32) CLIP_SH_0_255(temp1_r); - temp2_r = (v4i32) CLIP_SH_0_255(temp2_r); - temp3_r = (v4i32) CLIP_SH_0_255(temp3_r); - PCKEV_B4_SW(temp0_r, temp0_r, temp1_r, temp1_r, - temp2_r, temp2_r, temp3_r, temp3_r, - temp0_r, temp1_r, temp2_r, temp3_r); - tmp0 = __msa_copy_u_d((v2i64) temp0_r, 1); - tmp1 = __msa_copy_u_d((v2i64) temp1_r, 1); - tmp2 = __msa_copy_u_d((v2i64) temp2_r, 1); - tmp3 = __msa_copy_u_d((v2i64) temp3_r, 1); + in0 = (v8i16) (temp0_r) + (v8i16) (temp0_l); + in1 = (v8i16) (temp1_r) + (v8i16) (temp1_l); + in2 = (v8i16) (temp2_r) + (v8i16) (temp2_l); + in3 = (v8i16) (temp3_r) + (v8i16) (temp3_l); + CLIP_SH4_0_255(in0, in1, in2, in3); + PCKEV_B4_SH(in0, in0, in1, in1, in2, in2, in3, in3, + in0, in1, in2, in3); + tmp0 = __msa_copy_u_d((v2i64) in0, 1); + tmp1 = __msa_copy_u_d((v2i64) in1, 1); + tmp2 = __msa_copy_u_d((v2i64) in2, 1); + tmp3 = __msa_copy_u_d((v2i64) in3, 1); SD4(tmp0, tmp1, tmp2, tmp3, dst, dst_stride); SRA_4V(a3_r, a3_l, a2_r, a2_l, 20); @@ -540,20 +527,17 @@ static void simple_idct_add_msa(uint8_t *dst, int32_t dst_stride, a0_r, a1_r, a2_r, a3_r); ILVR_B4_SW(zero, in4, zero, in5, zero, in6, zero, in7, a3_l, a2_l, a1_l, a0_l); - a3_r = (v4i32) ((v8i16) (a3_r) + (v8i16) (a3_l)); - a2_r = (v4i32) ((v8i16) (a2_r) + (v8i16) (a2_l)); - a1_r = (v4i32) ((v8i16) (a1_r) + (v8i16) (a1_l)); - a0_r = (v4i32) ((v8i16) (a0_r) + (v8i16) (a0_l)); - a3_r = (v4i32) CLIP_SH_0_255(a3_r); - a2_r = (v4i32) CLIP_SH_0_255(a2_r); - a1_r = (v4i32) CLIP_SH_0_255(a1_r); - a0_r = (v4i32) CLIP_SH_0_255(a0_r); - PCKEV_B4_SW(a0_r, a0_r, a1_r, a1_r, - a2_r, a2_r, a3_r, a3_r, a0_r, a1_r, a2_r, a3_r); - tmp0 = __msa_copy_u_d((v2i64) a3_r, 1); - tmp1 = __msa_copy_u_d((v2i64) a2_r, 1); - tmp2 = __msa_copy_u_d((v2i64) a1_r, 1); - tmp3 = __msa_copy_u_d((v2i64) a0_r, 1); + in4 = (v8i16) (a3_r) + (v8i16) (a3_l); + in5 = (v8i16) (a2_r) + (v8i16) (a2_l); + in6 = (v8i16) (a1_r) + (v8i16) (a1_l); + in7 = (v8i16) (a0_r) + (v8i16) (a0_l); + CLIP_SH4_0_255(in4, in5, in6, in7); + PCKEV_B4_SH(in4, in4, in5, in5, in6, in6, in7, in7, + in4, in5, in6, in7); + tmp0 = __msa_copy_u_d((v2i64) in4, 1); + tmp1 = __msa_copy_u_d((v2i64) in5, 1); + tmp2 = __msa_copy_u_d((v2i64) in6, 1); + tmp3 = __msa_copy_u_d((v2i64) in7, 1); SD4(tmp0, tmp1, tmp2, tmp3, dst + 4 * dst_stride, dst_stride); } diff --git a/libavcodec/mips/vc1dsp_init_mips.c b/libavcodec/mips/vc1dsp_init_mips.c index 4adc9e1d4ea..c0007ff6504 100644 --- a/libavcodec/mips/vc1dsp_init_mips.c +++ b/libavcodec/mips/vc1dsp_init_mips.c @@ -23,6 +23,10 @@ #include "vc1dsp_mips.h" #include "config.h" +#define FN_ASSIGN(OP, X, Y, INSN) \ + dsp->OP##vc1_mspel_pixels_tab[1][X+4*Y] = ff_##OP##vc1_mspel_mc##X##Y##INSN; \ + dsp->OP##vc1_mspel_pixels_tab[0][X+4*Y] = ff_##OP##vc1_mspel_mc##X##Y##_16##INSN + #if HAVE_MMI static av_cold void vc1dsp_init_mmi(VC1DSPContext *dsp) { @@ -49,10 +53,6 @@ static av_cold void vc1dsp_init_mmi(VC1DSPContext *dsp) dsp->vc1_v_loop_filter16 = ff_vc1_v_loop_filter16_mmi; dsp->vc1_h_loop_filter16 = ff_vc1_h_loop_filter16_mmi; -#define FN_ASSIGN(OP, X, Y, INSN) \ - dsp->OP##vc1_mspel_pixels_tab[1][X+4*Y] = ff_##OP##vc1_mspel_mc##X##Y##INSN; \ - dsp->OP##vc1_mspel_pixels_tab[0][X+4*Y] = ff_##OP##vc1_mspel_mc##X##Y##_16##INSN - FN_ASSIGN(put_, 0, 0, _mmi); FN_ASSIGN(put_, 0, 1, _mmi); FN_ASSIGN(put_, 0, 2, _mmi); @@ -100,9 +100,31 @@ static av_cold void vc1dsp_init_mmi(VC1DSPContext *dsp) } #endif /* HAVE_MMI */ +#if HAVE_MSA +static av_cold void vc1dsp_init_msa(VC1DSPContext *dsp) +{ + dsp->vc1_inv_trans_8x8 = ff_vc1_inv_trans_8x8_msa; + dsp->vc1_inv_trans_4x8 = ff_vc1_inv_trans_4x8_msa; + dsp->vc1_inv_trans_8x4 = ff_vc1_inv_trans_8x4_msa; + + FN_ASSIGN(put_, 1, 1, _msa); + FN_ASSIGN(put_, 1, 2, _msa); + FN_ASSIGN(put_, 1, 3, _msa); + FN_ASSIGN(put_, 2, 1, _msa); + FN_ASSIGN(put_, 2, 2, _msa); + FN_ASSIGN(put_, 2, 3, _msa); + FN_ASSIGN(put_, 3, 1, _msa); + FN_ASSIGN(put_, 3, 2, _msa); + FN_ASSIGN(put_, 3, 3, _msa); +} +#endif /* HAVE_MSA */ + av_cold void ff_vc1dsp_init_mips(VC1DSPContext *dsp) { #if HAVE_MMI vc1dsp_init_mmi(dsp); #endif /* HAVE_MMI */ +#if HAVE_MSA + vc1dsp_init_msa(dsp); +#endif /* HAVE_MSA */ } diff --git a/libavcodec/mips/vc1dsp_mips.h b/libavcodec/mips/vc1dsp_mips.h index 0db85fac94f..5897daea8c6 100644 --- a/libavcodec/mips/vc1dsp_mips.h +++ b/libavcodec/mips/vc1dsp_mips.h @@ -180,15 +180,38 @@ void ff_vc1_h_loop_filter16_mmi(uint8_t *src, int stride, int pq); void ff_put_no_rnd_vc1_chroma_mc8_mmi(uint8_t *dst /* align 8 */, uint8_t *src /* align 1 */, - int stride, int h, int x, int y); + ptrdiff_t stride, int h, int x, int y); void ff_put_no_rnd_vc1_chroma_mc4_mmi(uint8_t *dst /* align 8 */, uint8_t *src /* align 1 */, - int stride, int h, int x, int y); + ptrdiff_t stride, int h, int x, int y); void ff_avg_no_rnd_vc1_chroma_mc8_mmi(uint8_t *dst /* align 8 */, uint8_t *src /* align 1 */, - int stride, int h, int x, int y); + ptrdiff_t stride, int h, int x, int y); void ff_avg_no_rnd_vc1_chroma_mc4_mmi(uint8_t *dst /* align 8 */, uint8_t *src /* align 1 */, - int stride, int h, int x, int y); + ptrdiff_t stride, int h, int x, int y); +void ff_vc1_inv_trans_8x8_msa(int16_t block[64]); +void ff_vc1_inv_trans_8x4_msa(uint8_t *dest, ptrdiff_t linesize, int16_t *block); +void ff_vc1_inv_trans_4x8_msa(uint8_t *dest, ptrdiff_t linesize, int16_t *block); + +#define FF_PUT_VC1_MSPEL_MC_MSA(hmode, vmode) \ +void ff_put_vc1_mspel_mc ## hmode ## vmode ## _msa(uint8_t *dst, \ + const uint8_t *src, \ + ptrdiff_t stride, int rnd); \ +void ff_put_vc1_mspel_mc ## hmode ## vmode ## _16_msa(uint8_t *dst, \ + const uint8_t *src, \ + ptrdiff_t stride, int rnd); + +FF_PUT_VC1_MSPEL_MC_MSA(1, 1); +FF_PUT_VC1_MSPEL_MC_MSA(1, 2); +FF_PUT_VC1_MSPEL_MC_MSA(1, 3); + +FF_PUT_VC1_MSPEL_MC_MSA(2, 1); +FF_PUT_VC1_MSPEL_MC_MSA(2, 2); +FF_PUT_VC1_MSPEL_MC_MSA(2, 3); + +FF_PUT_VC1_MSPEL_MC_MSA(3, 1); +FF_PUT_VC1_MSPEL_MC_MSA(3, 2); +FF_PUT_VC1_MSPEL_MC_MSA(3, 3); #endif /* AVCODEC_MIPS_VC1DSP_MIPS_H */ diff --git a/libavcodec/mips/vc1dsp_mmi.c b/libavcodec/mips/vc1dsp_mmi.c index db314de4960..98378683b8e 100644 --- a/libavcodec/mips/vc1dsp_mmi.c +++ b/libavcodec/mips/vc1dsp_mmi.c @@ -2241,7 +2241,7 @@ DECLARE_FUNCTION(3, 3) void ff_put_no_rnd_vc1_chroma_mc8_mmi(uint8_t *dst /* align 8 */, uint8_t *src /* align 1 */, - int stride, int h, int x, int y) + ptrdiff_t stride, int h, int x, int y) { const int A = (8 - x) * (8 - y); const int B = (x) * (8 - y); @@ -2296,7 +2296,7 @@ void ff_put_no_rnd_vc1_chroma_mc8_mmi(uint8_t *dst /* align 8 */, void ff_put_no_rnd_vc1_chroma_mc4_mmi(uint8_t *dst /* align 8 */, uint8_t *src /* align 1 */, - int stride, int h, int x, int y) + ptrdiff_t stride, int h, int x, int y) { const int A = (8 - x) * (8 - y); const int B = (x) * (8 - y); @@ -2349,7 +2349,7 @@ void ff_put_no_rnd_vc1_chroma_mc4_mmi(uint8_t *dst /* align 8 */, void ff_avg_no_rnd_vc1_chroma_mc8_mmi(uint8_t *dst /* align 8 */, uint8_t *src /* align 1 */, - int stride, int h, int x, int y) + ptrdiff_t stride, int h, int x, int y) { const int A = (8 - x) * (8 - y); const int B = (x) * (8 - y); @@ -2407,7 +2407,7 @@ void ff_avg_no_rnd_vc1_chroma_mc8_mmi(uint8_t *dst /* align 8 */, void ff_avg_no_rnd_vc1_chroma_mc4_mmi(uint8_t *dst /* align 8 */, uint8_t *src /* align 1 */, - int stride, int h, int x, int y) + ptrdiff_t stride, int h, int x, int y) { const int A = (8 - x) * (8 - y); const int B = ( x) * (8 - y); diff --git a/libavcodec/mips/vc1dsp_msa.c b/libavcodec/mips/vc1dsp_msa.c new file mode 100644 index 00000000000..6e588e825af --- /dev/null +++ b/libavcodec/mips/vc1dsp_msa.c @@ -0,0 +1,461 @@ +/* + * Loongson SIMD optimized vc1dsp + * + * Copyright (c) 2019 Loongson Technology Corporation Limited + * gxw + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 "vc1dsp_mips.h" +#include "constants.h" +#include "libavutil/mips/generic_macros_msa.h" + +void ff_vc1_inv_trans_8x8_msa(int16_t block[64]) +{ + v8i16 in0, in1, in2, in3, in4, in5, in6, in7; + v4i32 in_r0, in_r1, in_r2, in_r3, in_r4, in_r5, in_r6, in_r7; + v4i32 in_l0, in_l1, in_l2, in_l3, in_l4, in_l5, in_l6, in_l7; + v4i32 t_r1, t_r2, t_r3, t_r4, t_r5, t_r6, t_r7, t_r8; + v4i32 t_l1, t_l2, t_l3, t_l4, t_l5, t_l6, t_l7, t_l8; + v4i32 cnst_12 = {12, 12, 12, 12}; + v4i32 cnst_4 = {4, 4, 4, 4}; + v4i32 cnst_16 = {16, 16, 16, 16}; + v4i32 cnst_6 = {6, 6, 6, 6}; + v4i32 cnst_15 = {15, 15, 15, 15}; + v4i32 cnst_9 = {9, 9, 9, 9}; + v4i32 cnst_1 = {1, 1, 1, 1}; + v4i32 cnst_64 = {64, 64, 64, 64}; + + LD_SH8(block, 8, in0, in1, in2, in3, in4, in5, in6, in7); + UNPCK_SH_SW(in0, in_r0, in_l0); + UNPCK_SH_SW(in1, in_r1, in_l1); + UNPCK_SH_SW(in2, in_r2, in_l2); + UNPCK_SH_SW(in3, in_r3, in_l3); + UNPCK_SH_SW(in4, in_r4, in_l4); + UNPCK_SH_SW(in5, in_r5, in_l5); + UNPCK_SH_SW(in6, in_r6, in_l6); + UNPCK_SH_SW(in7, in_r7, in_l7); + // First loop + t_r1 = cnst_12 * (in_r0 + in_r4) + cnst_4; + t_l1 = cnst_12 * (in_l0 + in_l4) + cnst_4; + t_r2 = cnst_12 * (in_r0 - in_r4) + cnst_4; + t_l2 = cnst_12 * (in_l0 - in_l4) + cnst_4; + t_r3 = cnst_16 * in_r2 + cnst_6 * in_r6; + t_l3 = cnst_16 * in_l2 + cnst_6 * in_l6; + t_r4 = cnst_6 * in_r2 - cnst_16 * in_r6; + t_l4 = cnst_6 * in_l2 - cnst_16 * in_l6; + + ADD4(t_r1, t_r3, t_l1, t_l3, t_r2, t_r4, t_l2, t_l4, t_r5, t_l5, t_r6, t_l6); + SUB4(t_r2, t_r4, t_l2, t_l4, t_r1, t_r3, t_l1, t_l3, t_r7, t_l7, t_r8, t_l8); + t_r1 = cnst_16 * in_r1 + cnst_15 * in_r3 + cnst_9 * in_r5 + cnst_4 * in_r7; + t_l1 = cnst_16 * in_l1 + cnst_15 * in_l3 + cnst_9 * in_l5 + cnst_4 * in_l7; + t_r2 = cnst_15 * in_r1 - cnst_4 * in_r3 - cnst_16 * in_r5 - cnst_9 * in_r7; + t_l2 = cnst_15 * in_l1 - cnst_4 * in_l3 - cnst_16 * in_l5 - cnst_9 * in_l7; + t_r3 = cnst_9 * in_r1 - cnst_16 * in_r3 + cnst_4 * in_r5 + cnst_15 * in_r7; + t_l3 = cnst_9 * in_l1 - cnst_16 * in_l3 + cnst_4 * in_l5 + cnst_15 * in_l7; + t_r4 = cnst_4 * in_r1 - cnst_9 * in_r3 + cnst_15 * in_r5 - cnst_16 * in_r7; + t_l4 = cnst_4 * in_l1 - cnst_9 * in_l3 + cnst_15 * in_l5 - cnst_16 * in_l7; + + in_r0 = (t_r5 + t_r1) >> 3; + in_l0 = (t_l5 + t_l1) >> 3; + in_r1 = (t_r6 + t_r2) >> 3; + in_l1 = (t_l6 + t_l2) >> 3; + in_r2 = (t_r7 + t_r3) >> 3; + in_l2 = (t_l7 + t_l3) >> 3; + in_r3 = (t_r8 + t_r4) >> 3; + in_l3 = (t_l8 + t_l4) >> 3; + + in_r4 = (t_r8 - t_r4) >> 3; + in_l4 = (t_l8 - t_l4) >> 3; + in_r5 = (t_r7 - t_r3) >> 3; + in_l5 = (t_l7 - t_l3) >> 3; + in_r6 = (t_r6 - t_r2) >> 3; + in_l6 = (t_l6 - t_l2) >> 3; + in_r7 = (t_r5 - t_r1) >> 3; + in_l7 = (t_l5 - t_l1) >> 3; + TRANSPOSE4x4_SW_SW(in_r0, in_r1, in_r2, in_r3, in_r0, in_r1, in_r2, in_r3); + TRANSPOSE4x4_SW_SW(in_l0, in_l1, in_l2, in_l3, in_l0, in_l1, in_l2, in_l3); + TRANSPOSE4x4_SW_SW(in_r4, in_r5, in_r6, in_r7, in_r4, in_r5, in_r6, in_r7); + TRANSPOSE4x4_SW_SW(in_l4, in_l5, in_l6, in_l7, in_l4, in_l5, in_l6, in_l7); + // Second loop + t_r1 = cnst_12 * (in_r0 + in_l0) + cnst_64; + t_l1 = cnst_12 * (in_r4 + in_l4) + cnst_64; + t_r2 = cnst_12 * (in_r0 - in_l0) + cnst_64; + t_l2 = cnst_12 * (in_r4 - in_l4) + cnst_64; + t_r3 = cnst_16 * in_r2 + cnst_6 * in_l2; + t_l3 = cnst_16 * in_r6 + cnst_6 * in_l6; + t_r4 = cnst_6 * in_r2 - cnst_16 * in_l2; + t_l4 = cnst_6 * in_r6 - cnst_16 * in_l6; + + ADD4(t_r1, t_r3, t_l1, t_l3, t_r2, t_r4, t_l2, t_l4, t_r5, t_l5, t_r6, t_l6); + SUB4(t_r2, t_r4, t_l2, t_l4, t_r1, t_r3, t_l1, t_l3, t_r7, t_l7, t_r8, t_l8); + t_r1 = cnst_16 * in_r1 + cnst_15 * in_r3 + cnst_9 * in_l1 + cnst_4 * in_l3; + t_l1 = cnst_16 * in_r5 + cnst_15 * in_r7 + cnst_9 * in_l5 + cnst_4 * in_l7; + t_r2 = cnst_15 * in_r1 - cnst_4 * in_r3 - cnst_16 * in_l1 - cnst_9 * in_l3; + t_l2 = cnst_15 * in_r5 - cnst_4 * in_r7 - cnst_16 * in_l5 - cnst_9 * in_l7; + t_r3 = cnst_9 * in_r1 - cnst_16 * in_r3 + cnst_4 * in_l1 + cnst_15 * in_l3; + t_l3 = cnst_9 * in_r5 - cnst_16 * in_r7 + cnst_4 * in_l5 + cnst_15 * in_l7; + t_r4 = cnst_4 * in_r1 - cnst_9 * in_r3 + cnst_15 * in_l1 - cnst_16 * in_l3; + t_l4 = cnst_4 * in_r5 - cnst_9 * in_r7 + cnst_15 * in_l5 - cnst_16 * in_l7; + + in_r0 = (t_r5 + t_r1) >> 7; + in_l0 = (t_l5 + t_l1) >> 7; + in_r1 = (t_r6 + t_r2) >> 7; + in_l1 = (t_l6 + t_l2) >> 7; + in_r2 = (t_r7 + t_r3) >> 7; + in_l2 = (t_l7 + t_l3) >> 7; + in_r3 = (t_r8 + t_r4) >> 7; + in_l3 = (t_l8 + t_l4) >> 7; + + in_r4 = (t_r8 - t_r4 + cnst_1) >> 7; + in_l4 = (t_l8 - t_l4 + cnst_1) >> 7; + in_r5 = (t_r7 - t_r3 + cnst_1) >> 7; + in_l5 = (t_l7 - t_l3 + cnst_1) >> 7; + in_r6 = (t_r6 - t_r2 + cnst_1) >> 7; + in_l6 = (t_l6 - t_l2 + cnst_1) >> 7; + in_r7 = (t_r5 - t_r1 + cnst_1) >> 7; + in_l7 = (t_l5 - t_l1 + cnst_1) >> 7; + PCKEV_H4_SH(in_l0, in_r0, in_l1, in_r1, in_l2, in_r2, in_l3, in_r3, + in0, in1, in2, in3); + PCKEV_H4_SH(in_l4, in_r4, in_l5, in_r5, in_l6, in_r6, in_l7, in_r7, + in4, in5, in6, in7); + ST_SH8(in0, in1, in2, in3, in4, in5, in6, in7, block, 8); +} + +void ff_vc1_inv_trans_4x8_msa(uint8_t *dest, ptrdiff_t linesize, int16_t *block) +{ + v8i16 in0, in1, in2, in3, in4, in5, in6, in7; + v4i32 in_r0, in_r1, in_r2, in_r3, in_r4, in_r5, in_r6, in_r7; + v4i32 t1, t2, t3, t4, t5, t6, t7, t8; + v4i32 dst0, dst1, dst2, dst3, dst4, dst5, dst6, dst7; + v16i8 zero_m = { 0 }; + v4i32 cnst_17 = {17, 17, 17, 17}; + v4i32 cnst_22 = {22, 22, 22, 22}; + v4i32 cnst_10 = {10, 10, 10, 10}; + v4i32 cnst_12 = {12, 12, 12, 12}; + v4i32 cnst_64 = {64, 64, 64, 64}; + v4i32 cnst_16 = {16, 16, 16, 16}; + v4i32 cnst_15 = {15, 15, 15, 15}; + v4i32 cnst_4 = {4, 4, 4, 4}; + v4i32 cnst_6 = {6, 6, 6, 6}; + v4i32 cnst_9 = {9, 9, 9, 9}; + v4i32 cnst_1 = {1, 1, 1, 1}; + + LD_SH8(block, 8, in0, in1, in2, in3, in4, in5, in6, in7); + UNPCK_R_SH_SW(in0, in_r0); + UNPCK_R_SH_SW(in1, in_r1); + UNPCK_R_SH_SW(in2, in_r2); + UNPCK_R_SH_SW(in3, in_r3); + UNPCK_R_SH_SW(in4, in_r4); + UNPCK_R_SH_SW(in5, in_r5); + UNPCK_R_SH_SW(in6, in_r6); + UNPCK_R_SH_SW(in7, in_r7); + // First loop + TRANSPOSE4x4_SW_SW(in_r0, in_r1, in_r2, in_r3, in_r0, in_r1, in_r2, in_r3); + TRANSPOSE4x4_SW_SW(in_r4, in_r5, in_r6, in_r7, in_r4, in_r5, in_r6, in_r7); + t1 = cnst_17 * (in_r0 + in_r2) + cnst_4; + t5 = cnst_17 * (in_r4 + in_r6) + cnst_4; + t2 = cnst_17 * (in_r0 - in_r2) + cnst_4; + t6 = cnst_17 * (in_r4 - in_r6) + cnst_4; + t3 = cnst_22 * in_r1 + cnst_10 * in_r3; + t7 = cnst_22 * in_r5 + cnst_10 * in_r7; + t4 = cnst_22 * in_r3 - cnst_10 * in_r1; + t8 = cnst_22 * in_r7 - cnst_10 * in_r5; + + in_r0 = (t1 + t3) >> 3; + in_r4 = (t5 + t7) >> 3; + in_r1 = (t2 - t4) >> 3; + in_r5 = (t6 - t8) >> 3; + in_r2 = (t2 + t4) >> 3; + in_r6 = (t6 + t8) >> 3; + in_r3 = (t1 - t3) >> 3; + in_r7 = (t5 - t7) >> 3; + TRANSPOSE4x4_SW_SW(in_r0, in_r1, in_r2, in_r3, in_r0, in_r1, in_r2, in_r3); + TRANSPOSE4x4_SW_SW(in_r4, in_r5, in_r6, in_r7, in_r4, in_r5, in_r6, in_r7); + PCKEV_H4_SH(in_r1, in_r0, in_r3, in_r2, in_r5, in_r4, in_r7, in_r6, + in0, in1, in2, in3); + ST_D8(in0, in1, in2, in3, 0, 1, 0, 1, 0, 1, 0, 1, block, 8); + // Second loop + t1 = cnst_12 * (in_r0 + in_r4) + cnst_64; + t2 = cnst_12 * (in_r0 - in_r4) + cnst_64; + t3 = cnst_16 * in_r2 + cnst_6 * in_r6; + t4 = cnst_6 * in_r2 - cnst_16 * in_r6; + t5 = t1 + t3, t6 = t2 + t4; + t7 = t2 - t4, t8 = t1 - t3; + t1 = cnst_16 * in_r1 + cnst_15 * in_r3 + cnst_9 * in_r5 + cnst_4 * in_r7; + t2 = cnst_15 * in_r1 - cnst_4 * in_r3 - cnst_16 * in_r5 - cnst_9 * in_r7; + t3 = cnst_9 * in_r1 - cnst_16 * in_r3 + cnst_4 * in_r5 + cnst_15 * in_r7; + t4 = cnst_4 * in_r1 - cnst_9 * in_r3 + cnst_15 * in_r5 - cnst_16 * in_r7; + LD_SW8(dest, linesize, dst0, dst1, dst2, dst3, dst4, dst5, dst6, dst7); + ILVR_B8_SW(zero_m, dst0, zero_m, dst1, zero_m, dst2, zero_m, dst3, + zero_m, dst4, zero_m, dst5, zero_m, dst6, zero_m, dst7, + dst0, dst1, dst2, dst3, dst4, dst5, dst6, dst7); + ILVR_H4_SW(zero_m, dst0, zero_m, dst1, zero_m, dst2, zero_m, dst3, + dst0, dst1, dst2, dst3); + ILVR_H4_SW(zero_m, dst4, zero_m, dst5, zero_m, dst6, zero_m, dst7, + dst4, dst5, dst6, dst7); + in_r0 = (t5 + t1) >> 7; + in_r1 = (t6 + t2) >> 7; + in_r2 = (t7 + t3) >> 7; + in_r3 = (t8 + t4) >> 7; + in_r4 = (t8 - t4 + cnst_1) >> 7; + in_r5 = (t7 - t3 + cnst_1) >> 7; + in_r6 = (t6 - t2 + cnst_1) >> 7; + in_r7 = (t5 - t1 + cnst_1) >> 7; + ADD4(in_r0, dst0, in_r1, dst1, in_r2, dst2, in_r3, dst3, + in_r0, in_r1, in_r2, in_r3); + ADD4(in_r4, dst4, in_r5, dst5, in_r6, dst6, in_r7, dst7, + in_r4, in_r5, in_r6, in_r7); + CLIP_SW8_0_255(in_r0, in_r1, in_r2, in_r3, in_r4, in_r5, in_r6, in_r7); + PCKEV_H4_SH(in_r1, in_r0, in_r3, in_r2, in_r5, in_r4, in_r7, in_r6, + in0, in1, in2, in3); + PCKEV_B2_SH(in1, in0, in3, in2, in0, in1); + ST_W8(in0, in1, 0, 1, 2, 3, 0, 1, 2, 3, dest, linesize); +} + +void ff_vc1_inv_trans_8x4_msa(uint8_t *dest, ptrdiff_t linesize, int16_t *block) +{ + v4i32 in0, in1, in2, in3, in4, in5, in6, in7; + v4i32 t1, t2, t3, t4, t5, t6, t7, t8; + v4i32 dst0, dst1, dst2, dst3, dst4, dst5, dst6, dst7; + v16i8 zero_m = { 0 }; + v4i32 cnst_17 = {17, 17, 17, 17}; + v4i32 cnst_22 = {22, 22, 22, 22}; + v4i32 cnst_10 = {10, 10, 10, 10}; + v4i32 cnst_12 = {12, 12, 12, 12}; + v4i32 cnst_64 = {64, 64, 64, 64}; + v4i32 cnst_16 = {16, 16, 16, 16}; + v4i32 cnst_15 = {15, 15, 15, 15}; + v4i32 cnst_4 = {4, 4, 4, 4}; + v4i32 cnst_6 = {6, 6, 6, 6}; + v4i32 cnst_9 = {9, 9, 9, 9}; + + LD_SW4(block, 8, t1, t2, t3, t4); + UNPCK_SH_SW(t1, in0, in4); + UNPCK_SH_SW(t2, in1, in5); + UNPCK_SH_SW(t3, in2, in6); + UNPCK_SH_SW(t4, in3, in7); + TRANSPOSE4x4_SW_SW(in0, in1, in2, in3, in0, in1, in2, in3); + TRANSPOSE4x4_SW_SW(in4, in5, in6, in7, in4, in5, in6, in7); + // First loop + t1 = cnst_12 * (in0 + in4) + cnst_4; + t2 = cnst_12 * (in0 - in4) + cnst_4; + t3 = cnst_16 * in2 + cnst_6 * in6; + t4 = cnst_6 * in2 - cnst_16 * in6; + t5 = t1 + t3, t6 = t2 + t4; + t7 = t2 - t4, t8 = t1 - t3; + t1 = cnst_16 * in1 + cnst_15 * in3 + cnst_9 * in5 + cnst_4 * in7; + t2 = cnst_15 * in1 - cnst_4 * in3 - cnst_16 * in5 - cnst_9 * in7; + t3 = cnst_9 * in1 - cnst_16 * in3 + cnst_4 * in5 + cnst_15 * in7; + t4 = cnst_4 * in1 - cnst_9 * in3 + cnst_15 * in5 - cnst_16 * in7; + in0 = (t5 + t1) >> 3; + in1 = (t6 + t2) >> 3; + in2 = (t7 + t3) >> 3; + in3 = (t8 + t4) >> 3; + in4 = (t8 - t4) >> 3; + in5 = (t7 - t3) >> 3; + in6 = (t6 - t2) >> 3; + in7 = (t5 - t1) >> 3; + TRANSPOSE4x4_SW_SW(in0, in1, in2, in3, in0, in1, in2, in3); + TRANSPOSE4x4_SW_SW(in4, in5, in6, in7, in4, in5, in6, in7); + PCKEV_H4_SW(in4, in0, in5, in1, in6, in2, in7, in3, t1, t2, t3, t4); + ST_SW4(t1, t2, t3, t4, block, 8); + // Second loop + LD_SW4(dest, linesize, dst0, dst1, dst2, dst3); + ILVR_B4_SW(zero_m, dst0, zero_m, dst1, zero_m, dst2, zero_m, dst3, + dst0, dst1, dst2, dst3); + ILVL_H4_SW(zero_m, dst0, zero_m, dst1, zero_m, dst2, zero_m, dst3, + dst4, dst5, dst6, dst7); + ILVR_H4_SW(zero_m, dst0, zero_m, dst1, zero_m, dst2, zero_m, dst3, + dst0, dst1, dst2, dst3); + // Right part + t1 = cnst_17 * (in0 + in2) + cnst_64; + t2 = cnst_17 * (in0 - in2) + cnst_64; + t3 = cnst_22 * in1 + cnst_10 * in3; + t4 = cnst_22 * in3 - cnst_10 * in1; + in0 = (t1 + t3) >> 7; + in1 = (t2 - t4) >> 7; + in2 = (t2 + t4) >> 7; + in3 = (t1 - t3) >> 7; + ADD4(in0, dst0, in1, dst1, in2, dst2, in3, dst3, in0, in1, in2, in3); + CLIP_SW4_0_255(in0, in1, in2, in3); + // Left part + t5 = cnst_17 * (in4 + in6) + cnst_64; + t6 = cnst_17 * (in4 - in6) + cnst_64; + t7 = cnst_22 * in5 + cnst_10 * in7; + t8 = cnst_22 * in7 - cnst_10 * in5; + in4 = (t5 + t7) >> 7; + in5 = (t6 - t8) >> 7; + in6 = (t6 + t8) >> 7; + in7 = (t5 - t7) >> 7; + ADD4(in4, dst4, in5, dst5, in6, dst6, in7, dst7, in4, in5, in6, in7); + CLIP_SW4_0_255(in4, in5, in6, in7); + PCKEV_H4_SW(in4, in0, in5, in1, in6, in2, in7, in3, in0, in1, in2, in3); + PCKEV_B2_SW(in1, in0, in3, in2, in0, in1); + ST_D4(in0, in1, 0, 1, 0, 1, dest, linesize); +} + +static void put_vc1_mspel_mc_h_v_msa(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride, int hmode, int vmode, + int rnd) +{ + v8i16 in_r0, in_r1, in_r2, in_r3, in_l0, in_l1, in_l2, in_l3; + v8i16 t0, t1, t2, t3, t4, t5, t6, t7; + v8i16 t8, t9, t10, t11, t12, t13, t14, t15; + v8i16 cnst_para0, cnst_para1, cnst_para2, cnst_para3, cnst_r; + static const int para_value[][4] = {{4, 53, 18, 3}, + {1, 9, 9, 1}, + {3, 18, 53, 4}}; + static const int shift_value[] = {0, 5, 1, 5}; + int shift = (shift_value[hmode] + shift_value[vmode]) >> 1; + int r = (1 << (shift - 1)) + rnd - 1; + cnst_r = __msa_fill_h(r); + src -= 1, src -= stride; + cnst_para0 = __msa_fill_h(para_value[vmode - 1][0]); + cnst_para1 = __msa_fill_h(para_value[vmode - 1][1]); + cnst_para2 = __msa_fill_h(para_value[vmode - 1][2]); + cnst_para3 = __msa_fill_h(para_value[vmode - 1][3]); + LD_SH4(src, stride, in_l0, in_l1, in_l2, in_l3); + UNPCK_UB_SH(in_l0, in_r0, in_l0); + UNPCK_UB_SH(in_l1, in_r1, in_l1); + UNPCK_UB_SH(in_l2, in_r2, in_l2); + UNPCK_UB_SH(in_l3, in_r3, in_l3); + // row 0 + t0 = cnst_para1 * in_r1 + cnst_para2 * in_r2 + - cnst_para0 * in_r0 - cnst_para3 * in_r3; + t8 = cnst_para1 * in_l1 + cnst_para2 * in_l2 + - cnst_para0 * in_l0 - cnst_para3 * in_l3; + in_l0 = LD_SH(src + 4 * stride); + UNPCK_UB_SH(in_l0, in_r0, in_l0); + // row 1 + t1 = cnst_para1 * in_r2 + cnst_para2 * in_r3 + - cnst_para0 * in_r1 - cnst_para3 * in_r0; + t9 = cnst_para1 * in_l2 + cnst_para2 * in_l3 + - cnst_para0 * in_l1 - cnst_para3 * in_l0; + in_l1 = LD_SH(src + 5 * stride); + UNPCK_UB_SH(in_l1, in_r1, in_l1); + // row 2 + t2 = cnst_para1 * in_r3 + cnst_para2 * in_r0 + - cnst_para0 * in_r2 - cnst_para3 * in_r1; + t10 = cnst_para1 * in_l3 + cnst_para2 * in_l0 + - cnst_para0 * in_l2 - cnst_para3 * in_l1; + in_l2 = LD_SH(src + 6 * stride); + UNPCK_UB_SH(in_l2, in_r2, in_l2); + // row 3 + t3 = cnst_para1 * in_r0 + cnst_para2 * in_r1 + - cnst_para0 * in_r3 - cnst_para3 * in_r2; + t11 = cnst_para1 * in_l0 + cnst_para2 * in_l1 + - cnst_para0 * in_l3 - cnst_para3 * in_l2; + in_l3 = LD_SH(src + 7 * stride); + UNPCK_UB_SH(in_l3, in_r3, in_l3); + // row 4 + t4 = cnst_para1 * in_r1 + cnst_para2 * in_r2 + - cnst_para0 * in_r0 - cnst_para3 * in_r3; + t12 = cnst_para1 * in_l1 + cnst_para2 * in_l2 + - cnst_para0 * in_l0 - cnst_para3 * in_l3; + in_l0 = LD_SH(src + 8 * stride); + UNPCK_UB_SH(in_l0, in_r0, in_l0); + // row 5 + t5 = cnst_para1 * in_r2 + cnst_para2 * in_r3 + - cnst_para0 * in_r1 - cnst_para3 * in_r0; + t13 = cnst_para1 * in_l2 + cnst_para2 * in_l3 + - cnst_para0 * in_l1 - cnst_para3 * in_l0; + in_l1 = LD_SH(src + 9 * stride); + UNPCK_UB_SH(in_l1, in_r1, in_l1); + // row 6 + t6 = cnst_para1 * in_r3 + cnst_para2 * in_r0 + - cnst_para0 * in_r2 - cnst_para3 * in_r1; + t14 = cnst_para1 * in_l3 + cnst_para2 * in_l0 + - cnst_para0 * in_l2 - cnst_para3 * in_l1; + in_l2 = LD_SH(src + 10 * stride); + UNPCK_UB_SH(in_l2, in_r2, in_l2); + // row 7 + t7 = cnst_para1 * in_r0 + cnst_para2 * in_r1 + - cnst_para0 * in_r3 - cnst_para3 * in_r2; + t15 = cnst_para1 * in_l0 + cnst_para2 * in_l1 + - cnst_para0 * in_l3 - cnst_para3 * in_l2; + + ADD4(t0, cnst_r, t1, cnst_r, t2, cnst_r, t3, cnst_r, t0, t1, t2, t3); + ADD4(t4, cnst_r, t5, cnst_r, t6, cnst_r, t7, cnst_r, t4, t5, t6, t7); + ADD4(t8, cnst_r, t9, cnst_r, t10, cnst_r, t11, cnst_r, + t8, t9, t10, t11); + ADD4(t12, cnst_r, t13, cnst_r, t14, cnst_r, t15, cnst_r, + t12, t13, t14, t15); + t0 >>= shift, t1 >>= shift, t2 >>= shift, t3 >>= shift; + t4 >>= shift, t5 >>= shift, t6 >>= shift, t7 >>= shift; + t8 >>= shift, t9 >>= shift, t10 >>= shift, t11 >>= shift; + t12 >>= shift, t13 >>= shift, t14 >>= shift, t15 >>= shift; + TRANSPOSE8x8_SH_SH(t0, t1, t2, t3, t4, t5, t6, t7, + t0, t1, t2, t3, t4, t5, t6, t7); + TRANSPOSE8x8_SH_SH(t8, t9, t10, t11, t12, t13, t14, t15, + t8, t9, t10, t11, t12, t13, t14, t15); + cnst_para0 = __msa_fill_h(para_value[hmode - 1][0]); + cnst_para1 = __msa_fill_h(para_value[hmode - 1][1]); + cnst_para2 = __msa_fill_h(para_value[hmode - 1][2]); + cnst_para3 = __msa_fill_h(para_value[hmode - 1][3]); + r = 64 - rnd; + cnst_r = __msa_fill_h(r); + // col 0 ~ 7 + t0 = cnst_para1 * t1 + cnst_para2 * t2 - cnst_para0 * t0 - cnst_para3 * t3; + t1 = cnst_para1 * t2 + cnst_para2 * t3 - cnst_para0 * t1 - cnst_para3 * t4; + t2 = cnst_para1 * t3 + cnst_para2 * t4 - cnst_para0 * t2 - cnst_para3 * t5; + t3 = cnst_para1 * t4 + cnst_para2 * t5 - cnst_para0 * t3 - cnst_para3 * t6; + t4 = cnst_para1 * t5 + cnst_para2 * t6 - cnst_para0 * t4 - cnst_para3 * t7; + t5 = cnst_para1 * t6 + cnst_para2 * t7 - cnst_para0 * t5 - cnst_para3 * t8; + t6 = cnst_para1 * t7 + cnst_para2 * t8 - cnst_para0 * t6 - cnst_para3 * t9; + t7 = cnst_para1 * t8 + cnst_para2 * t9 - cnst_para0 * t7 - cnst_para3 * t10; + ADD4(t0, cnst_r, t1, cnst_r, t2, cnst_r, t3, cnst_r, t0, t1, t2, t3); + ADD4(t4, cnst_r, t5, cnst_r, t6, cnst_r, t7, cnst_r, t4, t5, t6, t7); + t0 >>= 7, t1 >>= 7, t2 >>= 7, t3 >>= 7; + t4 >>= 7, t5 >>= 7, t6 >>= 7, t7 >>= 7; + TRANSPOSE8x8_SH_SH(t0, t1, t2, t3, t4, t5, t6, t7, + t0, t1, t2, t3, t4, t5, t6, t7); + CLIP_SH8_0_255(t0, t1, t2, t3, t4, t5, t6, t7); + PCKEV_B4_SH(t1, t0, t3, t2, t5, t4, t7, t6, t0, t1, t2, t3); + ST_D8(t0, t1, t2, t3, 0, 1, 0, 1, 0, 1, 0, 1, dst, stride); +} + +#define PUT_VC1_MSPEL_MC_MSA(hmode, vmode) \ +void ff_put_vc1_mspel_mc ## hmode ## vmode ## _msa(uint8_t *dst, \ + const uint8_t *src, \ + ptrdiff_t stride, int rnd) \ +{ \ + put_vc1_mspel_mc_h_v_msa(dst, src, stride, hmode, vmode, rnd); \ +} \ +void ff_put_vc1_mspel_mc ## hmode ## vmode ## _16_msa(uint8_t *dst, \ + const uint8_t *src, \ + ptrdiff_t stride, int rnd) \ +{ \ + put_vc1_mspel_mc_h_v_msa(dst, src, stride, hmode, vmode, rnd); \ + put_vc1_mspel_mc_h_v_msa(dst + 8, src + 8, stride, hmode, vmode, rnd); \ + dst += 8 * stride, src += 8 * stride; \ + put_vc1_mspel_mc_h_v_msa(dst, src, stride, hmode, vmode, rnd); \ + put_vc1_mspel_mc_h_v_msa(dst + 8, src + 8, stride, hmode, vmode, rnd); \ +} + +PUT_VC1_MSPEL_MC_MSA(1, 1); +PUT_VC1_MSPEL_MC_MSA(1, 2); +PUT_VC1_MSPEL_MC_MSA(1, 3); + +PUT_VC1_MSPEL_MC_MSA(2, 1); +PUT_VC1_MSPEL_MC_MSA(2, 2); +PUT_VC1_MSPEL_MC_MSA(2, 3); + +PUT_VC1_MSPEL_MC_MSA(3, 1); +PUT_VC1_MSPEL_MC_MSA(3, 2); +PUT_VC1_MSPEL_MC_MSA(3, 3); diff --git a/libavcodec/mips/vp3dsp_idct_msa.c b/libavcodec/mips/vp3dsp_idct_msa.c index b2899eea4a5..90c578f1348 100644 --- a/libavcodec/mips/vp3dsp_idct_msa.c +++ b/libavcodec/mips/vp3dsp_idct_msa.c @@ -187,14 +187,7 @@ static void idct_msa(uint8_t *dst, int stride, int16_t *input, int type) G += c5; H += c6; } - A = CLIP_SW_0_255(A); - B = CLIP_SW_0_255(B); - C = CLIP_SW_0_255(C); - D = CLIP_SW_0_255(D); - E = CLIP_SW_0_255(E); - F = CLIP_SW_0_255(F); - G = CLIP_SW_0_255(G); - H = CLIP_SW_0_255(H); + CLIP_SW8_0_255(A, B, C, D, E, F, G, H); sign_l = __msa_or_v((v16u8)r1_r, (v16u8)r2_r); sign_l = __msa_or_v(sign_l, (v16u8)r3_r); sign_l = __msa_or_v(sign_l, (v16u8)r0_l); @@ -205,7 +198,7 @@ static void idct_msa(uint8_t *dst, int stride, int16_t *input, int type) Add = ((r0_r * cnst46341w) + (8 << 16)) >> 20; if (type == 1) { Bdd = Add + cnst128w; - Bdd = CLIP_SW_0_255(Bdd); + CLIP_SW_0_255(Bdd); Ad = Bdd; Bd = Bdd; Cd = Bdd; @@ -223,14 +216,7 @@ static void idct_msa(uint8_t *dst, int stride, int16_t *input, int type) Fd = Add + c5; Gd = Add + c6; Hd = Add + c7; - Ad = CLIP_SW_0_255(Ad); - Bd = CLIP_SW_0_255(Bd); - Cd = CLIP_SW_0_255(Cd); - Dd = CLIP_SW_0_255(Dd); - Ed = CLIP_SW_0_255(Ed); - Fd = CLIP_SW_0_255(Fd); - Gd = CLIP_SW_0_255(Gd); - Hd = CLIP_SW_0_255(Hd); + CLIP_SW8_0_255(Ad, Bd, Cd, Dd, Ed, Fd, Gd, Hd); } Ad = (v4i32)__msa_and_v((v16u8)Ad, (v16u8)sign_t); Bd = (v4i32)__msa_and_v((v16u8)Bd, (v16u8)sign_t); @@ -309,14 +295,7 @@ static void idct_msa(uint8_t *dst, int stride, int16_t *input, int type) G += c5; H += c6; } - A = CLIP_SW_0_255(A); - B = CLIP_SW_0_255(B); - C = CLIP_SW_0_255(C); - D = CLIP_SW_0_255(D); - E = CLIP_SW_0_255(E); - F = CLIP_SW_0_255(F); - G = CLIP_SW_0_255(G); - H = CLIP_SW_0_255(H); + CLIP_SW8_0_255(A, B, C, D, E, F, G, H); sign_l = __msa_or_v((v16u8)r5_r, (v16u8)r6_r); sign_l = __msa_or_v(sign_l, (v16u8)r7_r); sign_l = __msa_or_v(sign_l, (v16u8)r4_l); @@ -327,7 +306,7 @@ static void idct_msa(uint8_t *dst, int stride, int16_t *input, int type) Add = ((r4_r * cnst46341w) + (8 << 16)) >> 20; if (type == 1) { Bdd = Add + cnst128w; - Bdd = CLIP_SW_0_255(Bdd); + CLIP_SW_0_255(Bdd); Ad = Bdd; Bd = Bdd; Cd = Bdd; @@ -345,14 +324,7 @@ static void idct_msa(uint8_t *dst, int stride, int16_t *input, int type) Fd = Add + c5; Gd = Add + c6; Hd = Add + c7; - Ad = CLIP_SW_0_255(Ad); - Bd = CLIP_SW_0_255(Bd); - Cd = CLIP_SW_0_255(Cd); - Dd = CLIP_SW_0_255(Dd); - Ed = CLIP_SW_0_255(Ed); - Fd = CLIP_SW_0_255(Fd); - Gd = CLIP_SW_0_255(Gd); - Hd = CLIP_SW_0_255(Hd); + CLIP_SW8_0_255(Ad, Bd, Cd, Dd, Ed, Fd, Gd, Hd); } Ad = (v4i32)__msa_and_v((v16u8)Ad, (v16u8)sign_t); Bd = (v4i32)__msa_and_v((v16u8)Bd, (v16u8)sign_t); @@ -436,14 +408,7 @@ void ff_vp3_idct_dc_add_msa(uint8_t *dest, ptrdiff_t line_size, int16_t *block) e5 += dc; e6 += dc; e7 += dc; - e0 = CLIP_SW_0_255(e0); - e1 = CLIP_SW_0_255(e1); - e2 = CLIP_SW_0_255(e2); - e3 = CLIP_SW_0_255(e3); - e4 = CLIP_SW_0_255(e4); - e5 = CLIP_SW_0_255(e5); - e6 = CLIP_SW_0_255(e6); - e7 = CLIP_SW_0_255(e7); + CLIP_SW8_0_255(e0, e1, e2, e3, e4, e5, e6, e7); /* Left part */ ILVL_H4_SW(zero, c0, zero, c1, zero, c2, zero, c3, @@ -458,14 +423,7 @@ void ff_vp3_idct_dc_add_msa(uint8_t *dest, ptrdiff_t line_size, int16_t *block) r5 += dc; r6 += dc; r7 += dc; - r0 = CLIP_SW_0_255(r0); - r1 = CLIP_SW_0_255(r1); - r2 = CLIP_SW_0_255(r2); - r3 = CLIP_SW_0_255(r3); - r4 = CLIP_SW_0_255(r4); - r5 = CLIP_SW_0_255(r5); - r6 = CLIP_SW_0_255(r6); - r7 = CLIP_SW_0_255(r7); + CLIP_SW8_0_255(r0, r1, r2, r3, r4, r5, r6, r7); VSHF_B2_SB(e0, r0, e1, r1, mask, mask, d0, d1); VSHF_B2_SB(e2, r2, e3, r3, mask, mask, d2, d3); VSHF_B2_SB(e4, r4, e5, r5, mask, mask, d4, d5); @@ -516,10 +474,7 @@ void ff_vp3_v_loop_filter_msa(uint8_t *first_pixel, ptrdiff_t stride, f1 += e1; g0 -= e0; g1 -= e1; - f0 = CLIP_SW_0_255(f0); - f1 = CLIP_SW_0_255(f1); - g0 = CLIP_SW_0_255(g0); - g1 = CLIP_SW_0_255(g1); + CLIP_SW4_0_255(f0, f1, g0, g1); VSHF_B2_SB(f0, f1, g0, g1, mask, mask, d1, d2); /* Final move to first_pixel */ @@ -563,10 +518,7 @@ void ff_vp3_h_loop_filter_msa(uint8_t *first_pixel, ptrdiff_t stride, f1 += e1; g0 -= e0; g1 -= e1; - f0 = CLIP_SW_0_255(f0); - f1 = CLIP_SW_0_255(f1); - g0 = CLIP_SW_0_255(g0); - g1 = CLIP_SW_0_255(g1); + CLIP_SW4_0_255(f0, f1, g0, g1); VSHF_B2_SB(f0, g0, f1, g1, mask, mask, d1, d2); /* Final move to first_pixel */ ST_H4(d1, 0, 1, 2, 3, first_pixel - 1, stride); diff --git a/libavcodec/mips/vp8_idct_msa.c b/libavcodec/mips/vp8_idct_msa.c index ae6fec0d607..ce37ca18814 100644 --- a/libavcodec/mips/vp8_idct_msa.c +++ b/libavcodec/mips/vp8_idct_msa.c @@ -71,10 +71,7 @@ void ff_vp8_idct_add_msa(uint8_t *dst, int16_t input[16], ptrdiff_t stride) ILVR_H4_SW(zero, res0, zero, res1, zero, res2, zero, res3, res0, res1, res2, res3); ADD4(res0, vt0, res1, vt1, res2, vt2, res3, vt3, res0, res1, res2, res3); - res0 = CLIP_SW_0_255(res0); - res1 = CLIP_SW_0_255(res1); - res2 = CLIP_SW_0_255(res2); - res3 = CLIP_SW_0_255(res3); + CLIP_SW4_0_255(res0, res1, res2, res3); VSHF_B2_SB(res0, res1, res2, res3, mask, mask, dest0, dest1); ST_W2(dest0, 0, 1, dst, stride); ST_W2(dest1, 0, 1, dst + 2 * stride, stride); diff --git a/libavcodec/mips/vp8_mc_msa.c b/libavcodec/mips/vp8_mc_msa.c index 57af6b45f19..a61320625a4 100644 --- a/libavcodec/mips/vp8_mc_msa.c +++ b/libavcodec/mips/vp8_mc_msa.c @@ -1995,8 +1995,8 @@ static void common_hv_2ht_2vt_4x8_msa(uint8_t *src, int32_t src_stride, hz_out4 = HORIZ_2TAP_FILT_UH(src4, src5, mask, filt_hz, 7); hz_out6 = HORIZ_2TAP_FILT_UH(src6, src7, mask, filt_hz, 7); hz_out8 = HORIZ_2TAP_FILT_UH(src8, src8, mask, filt_hz, 7); - SLDI_B3_UH(hz_out2, hz_out4, hz_out6, hz_out0, hz_out2, hz_out4, hz_out1, - hz_out3, hz_out5, 8); + SLDI_B3_UH(hz_out2, hz_out0, hz_out4, hz_out2, hz_out6, hz_out4, 8, hz_out1, + hz_out3, hz_out5); hz_out7 = (v8u16) __msa_pckod_d((v2i64) hz_out8, (v2i64) hz_out6); ILVEV_B2_UB(hz_out0, hz_out1, hz_out2, hz_out3, vec0, vec1); diff --git a/libavcodec/mips/vp9_idct_msa.c b/libavcodec/mips/vp9_idct_msa.c index 1f327701396..53bfbb47656 100644 --- a/libavcodec/mips/vp9_idct_msa.c +++ b/libavcodec/mips/vp9_idct_msa.c @@ -249,6 +249,7 @@ static const int32_t sinpi_4_9 = 15212; v8i16 c0_m, c1_m, c2_m, c3_m; \ v8i16 step0_m, step1_m; \ v4i32 tmp0_m, tmp1_m, tmp2_m, tmp3_m; \ + v16i8 zeros = { 0 }; \ \ c0_m = VP9_SET_COSPI_PAIR(cospi_16_64, cospi_16_64); \ c1_m = VP9_SET_COSPI_PAIR(cospi_16_64, -cospi_16_64); \ @@ -262,7 +263,7 @@ static const int32_t sinpi_4_9 = 15212; SRARI_W4_SW(tmp0_m, tmp1_m, tmp2_m, tmp3_m, VP9_DCT_CONST_BITS); \ \ PCKEV_H2_SW(tmp1_m, tmp0_m, tmp3_m, tmp2_m, tmp0_m, tmp2_m); \ - SLDI_B2_0_SW(tmp0_m, tmp2_m, tmp1_m, tmp3_m, 8); \ + SLDI_B2_SW(zeros, tmp0_m, zeros, tmp2_m, 8, tmp1_m, tmp3_m); \ BUTTERFLY_4((v8i16) tmp0_m, (v8i16) tmp1_m, \ (v8i16) tmp2_m, (v8i16) tmp3_m, \ out0, out1, out2, out3); \ @@ -763,13 +764,13 @@ static void vp9_iadst8x8_colcol_addblk_msa(int16_t *input, uint8_t *dst, res0 = (v8i16) __msa_ilvr_b((v16i8) zero, (v16i8) dst0); res0 += out0; - res0 = CLIP_SH_0_255(res0); + CLIP_SH_0_255(res0); res0 = (v8i16) __msa_pckev_b((v16i8) res0, (v16i8) res0); ST_D1(res0, 0, dst); res7 = (v8i16) __msa_ilvr_b((v16i8) zero, (v16i8) dst7); res7 += out7; - res7 = CLIP_SH_0_255(res7); + CLIP_SH_0_255(res7); res7 = (v8i16) __msa_pckev_b((v16i8) res7, (v16i8) res7); ST_D1(res7, 0, dst + 7 * dst_stride); @@ -1192,8 +1193,7 @@ static void vp9_idct16x16_1_add_msa(int16_t *input, uint8_t *dst, res3); ADD4(res4, vec, res5, vec, res6, vec, res7, vec, res4, res5, res6, res7); - CLIP_SH4_0_255(res0, res1, res2, res3); - CLIP_SH4_0_255(res4, res5, res6, res7); + CLIP_SH8_0_255(res0, res1, res2, res3, res4, res5, res6, res7); PCKEV_B4_UB(res4, res0, res5, res1, res6, res2, res7, res3, tmp0, tmp1, tmp2, tmp3); ST_UB4(tmp0, tmp1, tmp2, tmp3, dst, dst_stride); @@ -1981,8 +1981,7 @@ static void vp9_idct32x32_1_add_msa(int16_t *input, uint8_t *dst, res3); ADD4(res4, vec, res5, vec, res6, vec, res7, vec, res4, res5, res6, res7); - CLIP_SH4_0_255(res0, res1, res2, res3); - CLIP_SH4_0_255(res4, res5, res6, res7); + CLIP_SH8_0_255(res0, res1, res2, res3, res4, res5, res6, res7); PCKEV_B4_UB(res4, res0, res5, res1, res6, res2, res7, res3, tmp0, tmp1, tmp2, tmp3); diff --git a/libavcodec/mips/vp9_lpf_msa.c b/libavcodec/mips/vp9_lpf_msa.c index 2450c741d4b..cbb140950e7 100644 --- a/libavcodec/mips/vp9_lpf_msa.c +++ b/libavcodec/mips/vp9_lpf_msa.c @@ -1673,6 +1673,7 @@ static void vp9_transpose_16x8_to_8x16(uint8_t *input, int32_t in_pitch, v16u8 p7_org, p6_org, p5_org, p4_org, p3_org, p2_org, p1_org, p0_org; v16i8 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; v16u8 p7, p6, p5, p4, p3, p2, p1, p0, q0, q1, q2, q3, q4, q5, q6, q7; + v16i8 zeros = { 0 }; LD_UB8(input, in_pitch, p7_org, p6_org, p5_org, p4_org, p3_org, p2_org, p1_org, p0_org); @@ -1686,7 +1687,7 @@ static void vp9_transpose_16x8_to_8x16(uint8_t *input, int32_t in_pitch, ILVL_B2_SB(tmp1, tmp0, tmp3, tmp2, tmp5, tmp7); ILVR_W2_UB(tmp6, tmp4, tmp7, tmp5, q0, q4); ILVL_W2_UB(tmp6, tmp4, tmp7, tmp5, q2, q6); - SLDI_B4_0_UB(q0, q2, q4, q6, q1, q3, q5, q7, 8); + SLDI_B4_UB(zeros, q0, zeros, q2, zeros, q4, zeros, q6, 8, q1, q3, q5, q7); ST_UB8(p7, p6, p5, p4, p3, p2, p1, p0, output, out_pitch); output += (8 * out_pitch); diff --git a/libavcodec/mips/vp9_mc_msa.c b/libavcodec/mips/vp9_mc_msa.c index 1d8a8927689..57ea425727d 100644 --- a/libavcodec/mips/vp9_mc_msa.c +++ b/libavcodec/mips/vp9_mc_msa.c @@ -795,7 +795,7 @@ static void common_hv_8ht_8vt_4w_msa(const uint8_t *src, int32_t src_stride, filt_hz1, filt_hz2, filt_hz3); hz_out5 = HORIZ_8TAP_FILT(src5, src6, mask0, mask1, mask2, mask3, filt_hz0, filt_hz1, filt_hz2, filt_hz3); - SLDI_B2_SH(hz_out2, hz_out4, hz_out0, hz_out2, hz_out1, hz_out3, 8); + SLDI_B2_SH(hz_out2, hz_out0, hz_out4, hz_out2, 8, hz_out1, hz_out3); filt = LD_SH(filter_vert); SPLATI_H4_SH(filt, 0, 1, 2, 3, filt_vt0, filt_vt1, filt_vt2, filt_vt3); @@ -1585,7 +1585,7 @@ static void common_hv_8ht_8vt_and_aver_dst_4w_msa(const uint8_t *src, filt_hz1, filt_hz2, filt_hz3); hz_out5 = HORIZ_8TAP_FILT(src5, src6, mask0, mask1, mask2, mask3, filt_hz0, filt_hz1, filt_hz2, filt_hz3); - SLDI_B2_SH(hz_out2, hz_out4, hz_out0, hz_out2, hz_out1, hz_out3, 8); + SLDI_B2_SH(hz_out2, hz_out0, hz_out4, hz_out2, 8, hz_out1, hz_out3); filt = LD_SH(filter_vert); SPLATI_H4_SH(filt, 0, 1, 2, 3, filt_vt0, filt_vt1, filt_vt2, filt_vt3); @@ -2093,7 +2093,7 @@ void ff_put_bilin_64h_msa(uint8_t *dst, ptrdiff_t dst_stride, src4 = LD_SB(src + 32); src6 = LD_SB(src + 48); src7 = LD_SB(src + 56); - SLDI_B3_SB(src2, src4, src6, src0, src2, src4, src1, src3, src5, 8); + SLDI_B3_SB(src2, src0, src4, src2, src6, src4, 8, src1, src3, src5); src += src_stride; VSHF_B2_UB(src0, src0, src1, src1, mask, mask, vec0, vec1); @@ -2544,8 +2544,8 @@ static void common_hv_2ht_2vt_4x8_msa(const uint8_t *src, int32_t src_stride, hz_out4 = HORIZ_2TAP_FILT_UH(src4, src5, mask, filt_hz, 7); hz_out6 = HORIZ_2TAP_FILT_UH(src6, src7, mask, filt_hz, 7); hz_out8 = HORIZ_2TAP_FILT_UH(src8, src8, mask, filt_hz, 7); - SLDI_B3_UH(hz_out2, hz_out4, hz_out6, hz_out0, hz_out2, hz_out4, hz_out1, - hz_out3, hz_out5, 8); + SLDI_B3_UH(hz_out2, hz_out0, hz_out4, hz_out2, hz_out6, hz_out4, 8, hz_out1, + hz_out3, hz_out5); hz_out7 = (v8u16) __msa_pckod_d((v2i64) hz_out8, (v2i64) hz_out6); ILVEV_B2_UB(hz_out0, hz_out1, hz_out2, hz_out3, vec0, vec1); @@ -3146,7 +3146,7 @@ void ff_avg_bilin_64h_msa(uint8_t *dst, ptrdiff_t dst_stride, for (loop_cnt = height; loop_cnt--;) { LD_SB4(src, 16, src0, src2, src4, src6); src7 = LD_SB(src + 56); - SLDI_B3_SB(src2, src4, src6, src0, src2, src4, src1, src3, src5, 8); + SLDI_B3_SB(src2, src0, src4, src2, src6, src4, 8, src1, src3, src5); src += src_stride; VSHF_B2_UB(src0, src0, src1, src1, mask, mask, vec0, vec1); @@ -3655,8 +3655,8 @@ static void common_hv_2ht_2vt_and_aver_dst_4x8_msa(const uint8_t *src, hz_out4 = HORIZ_2TAP_FILT_UH(src4, src5, mask, filt_hz, 7); hz_out6 = HORIZ_2TAP_FILT_UH(src6, src7, mask, filt_hz, 7); hz_out8 = HORIZ_2TAP_FILT_UH(src8, src8, mask, filt_hz, 7); - SLDI_B3_UH(hz_out2, hz_out4, hz_out6, hz_out0, hz_out2, hz_out4, hz_out1, - hz_out3, hz_out5, 8); + SLDI_B3_UH(hz_out2, hz_out0, hz_out4, hz_out2, hz_out6, hz_out4, 8, hz_out1, + hz_out3, hz_out5); hz_out7 = (v8u16) __msa_pckod_d((v2i64) hz_out8, (v2i64) hz_out6); LW4(dst, dst_stride, tp0, tp1, tp2, tp3); diff --git a/libavcodec/mjpeg2jpeg_bsf.c b/libavcodec/mjpeg2jpeg_bsf.c index 6f02bc033ca..b30f391bf95 100644 --- a/libavcodec/mjpeg2jpeg_bsf.c +++ b/libavcodec/mjpeg2jpeg_bsf.c @@ -27,11 +27,10 @@ #include #include "libavutil/error.h" -#include "libavutil/mem.h" #include "libavutil/intreadwrite.h" -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "jpegtables.h" #include "mjpeg.h" diff --git a/libavcodec/mjpeg_parser.c b/libavcodec/mjpeg_parser.c index 07a6b2bdc65..f54fdd37cba 100644 --- a/libavcodec/mjpeg_parser.c +++ b/libavcodec/mjpeg_parser.c @@ -50,7 +50,7 @@ static int find_frame_end(MJPEGParserContext *m, const uint8_t *buf, int buf_siz for(i=0; i=0xFFC00000 && state<=0xFFFEFFFF){ - if(state>=0xFFD80000 && state<=0xFFD8FFFF){ + if(state>=0xFFD8FFC0 && state<=0xFFD8FFFF){ i++; vop_found=1; break; @@ -76,12 +76,14 @@ static int find_frame_end(MJPEGParserContext *m, const uint8_t *buf, int buf_siz for(; i=0xFFC00000 && state<=0xFFFEFFFF){ - if(state>=0xFFD80000 && state<=0xFFD8FFFF){ + if(state>=0xFFD8FFC0 && state<=0xFFD8FFFF){ pc->frame_start_found=0; pc->state=0; return i-3; } else if(state<0xFFD00000 || state>0xFFD9FFFF){ m->size= (state&0xFFFF)-1; + if (m->size >= 0xF000) + m->size = 0; } } if(m->size>0){ diff --git a/libavcodec/mjpega_dump_header_bsf.c b/libavcodec/mjpega_dump_header_bsf.c index ca5fb3a3773..ab68f9c3e98 100644 --- a/libavcodec/mjpega_dump_header_bsf.c +++ b/libavcodec/mjpega_dump_header_bsf.c @@ -25,8 +25,8 @@ * modifies bitstream to be decoded by quicktime */ -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "bytestream.h" #include "mjpeg.h" diff --git a/libavcodec/mjpegbdec.c b/libavcodec/mjpegbdec.c index 37d7bb82286..70ff4cf563a 100644 --- a/libavcodec/mjpegbdec.c +++ b/libavcodec/mjpegbdec.c @@ -56,6 +56,7 @@ static int mjpegb_decode_frame(AVCodecContext *avctx, buf_ptr = buf; buf_end = buf + buf_size; s->got_picture = 0; + s->adobe_transform = -1; read_header: /* reset on every SOI */ diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index a65bc8df15d..e7a4e08c1cf 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -36,7 +36,7 @@ #include "avcodec.h" #include "blockdsp.h" #include "copy_block.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "idctdsp.h" #include "internal.h" #include "jpegtables.h" @@ -412,6 +412,18 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) return AVERROR_PATCHWELCOME; } + if (s->bayer) { + if (nb_components == 2) { + /* Bayer images embedded in DNGs can contain 2 interleaved components and the + width stored in their SOF3 markers is the width of each one. We only output + a single component, therefore we need to adjust the output image width. We + handle the deinterleaving (but not the debayering) in this file. */ + width *= 2; + } + /* They can also contain 1 component, which is double the width and half the height + of the final image (rows are interleaved). We don't handle the decoding in this + file, but leave that to the TIFF/DNG decoder. */ + } /* if different size, realloc/alloc picture */ if (width != s->width || height != s->height || bits != s->bits || @@ -487,7 +499,17 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) } } + if (s->bayer) { + if (pix_fmt_id != 0x11110000 && pix_fmt_id != 0x11000000) + goto unk_pixfmt; + } + switch (pix_fmt_id) { + case 0x11110000: /* for bayer-encoded huffman lossless JPEGs embedded in DNGs */ + if (!s->bayer) + goto unk_pixfmt; + s->avctx->pix_fmt = AV_PIX_FMT_GRAY16LE; + break; case 0x11111100: if (s->rgb) s->avctx->pix_fmt = s->bits <= 9 ? AV_PIX_FMT_BGR24 : AV_PIX_FMT_BGR48; @@ -538,11 +560,6 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) case 0x22122100: case 0x21211100: case 0x22211200: - if (s->bits <= 8) s->avctx->pix_fmt = s->cs_itu601 ? AV_PIX_FMT_YUV444P : AV_PIX_FMT_YUVJ444P; - else - goto unk_pixfmt; - s->avctx->color_range = s->cs_itu601 ? AVCOL_RANGE_MPEG : AVCOL_RANGE_JPEG; - break; case 0x22221100: case 0x22112200: case 0x11222200: @@ -1041,17 +1058,24 @@ static int handle_rstn(MJpegDecodeContext *s, int nb_components) return reset; } +/* Handles 1 to 4 components */ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s, int nb_components, int predictor, int point_transform) { int i, mb_x, mb_y; + unsigned width; uint16_t (*buffer)[4]; int left[4], top[4], topleft[4]; const int linesize = s->linesize[0]; const int mask = ((1 << s->bits) - 1) << point_transform; int resync_mb_y = 0; int resync_mb_x = 0; + int vpred[6]; - if (s->nb_components != 3 && s->nb_components != 4) + if (!s->bayer && s->nb_components < 3) + return AVERROR_INVALIDDATA; + if (s->bayer && s->nb_components > 2) + return AVERROR_INVALIDDATA; + if (s->nb_components <= 0 || s->nb_components > 4) return AVERROR_INVALIDDATA; if (s->v_max != 1 || s->h_max != 1 || !s->lossless) return AVERROR_INVALIDDATA; @@ -1059,8 +1083,15 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s, int nb_components, int p s->restart_count = s->restart_interval; - av_fast_malloc(&s->ljpeg_buffer, &s->ljpeg_buffer_size, - (unsigned)s->mb_width * 4 * sizeof(s->ljpeg_buffer[0][0])); + if (s->restart_interval == 0) + s->restart_interval = INT_MAX; + + if (s->bayer) + width = s->mb_width / nb_components; /* Interleaved, width stored is the total so need to divide */ + else + width = s->mb_width; + + av_fast_malloc(&s->ljpeg_buffer, &s->ljpeg_buffer_size, width * 4 * sizeof(s->ljpeg_buffer[0][0])); if (!s->ljpeg_buffer) return AVERROR(ENOMEM); @@ -1078,7 +1109,12 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s, int nb_components, int p for (i = 0; i < 4; i++) top[i] = left[i] = topleft[i] = buffer[0][i]; - for (mb_x = 0; mb_x < s->mb_width; mb_x++) { + if ((mb_y * s->width) % s->restart_interval == 0) { + for (i = 0; i < 6; i++) + vpred[i] = 1 << (s->bits-1); + } + + for (mb_x = 0; mb_x < width; mb_x++) { int modified_predictor = predictor; if (get_bits_left(&s->gb) < 1) { @@ -1102,12 +1138,19 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s, int nb_components, int p topleft[i] = top[i]; top[i] = buffer[mb_x][i]; - PREDICT(pred, topleft[i], top[i], left[i], modified_predictor); - dc = mjpeg_decode_dc(s, s->dc_index[i]); if(dc == 0xFFFFF) return -1; + if (!s->bayer || mb_x) { + pred = left[i]; + } else { /* This path runs only for the first line in bayer images */ + vpred[i] += dc; + pred = vpred[i] - dc; + } + + PREDICT(pred, topleft[i], top[i], pred, modified_predictor); + left[i] = buffer[mb_x][i] = mask & (pred + (unsigned)(dc * (1 << point_transform))); } @@ -1151,6 +1194,17 @@ static int ljpeg_decode_rgb_scan(MJpegDecodeContext *s, int nb_components, int p ptr[3*mb_x + 0] = buffer[mb_x][1] + ptr[3*mb_x + 1]; ptr[3*mb_x + 2] = buffer[mb_x][2] + ptr[3*mb_x + 1]; } + } else if (s->bayer) { + if (nb_components == 1) { + /* Leave decoding to the TIFF/DNG decoder (see comment in ff_mjpeg_decode_sof) */ + for (mb_x = 0; mb_x < width; mb_x++) + ((uint16_t*)ptr)[mb_x] = buffer[mb_x][0]; + } else if (nb_components == 2) { + for (mb_x = 0; mb_x < width; mb_x++) { + ((uint16_t*)ptr)[2*mb_x + 0] = buffer[mb_x][0]; + ((uint16_t*)ptr)[2*mb_x + 1] = buffer[mb_x][1]; + } + } } else { for(i=0; icomp_index[i]; @@ -1695,7 +1749,7 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, point_transform, ilv)) < 0) return ret; } else { - if (s->rgb) { + if (s->rgb || s->bayer) { if ((ret = ljpeg_decode_rgb_scan(s, nb_components, predictor, point_transform)) < 0) return ret; } else { @@ -1759,8 +1813,15 @@ static int mjpeg_decode_app(MJpegDecodeContext *s) int len, id, i; len = get_bits(&s->gb, 16); - if (len < 6) - return AVERROR_INVALIDDATA; + if (len < 6) { + if (s->bayer) { + // Pentax K-1 (digital camera) JPEG images embedded in DNG images contain unknown APP0 markers + av_log(s->avctx, AV_LOG_WARNING, "skipping APPx (len=%"PRId32") for bayer-encoded image\n", len); + skip_bits(&s->gb, len); + return 0; + } else + return AVERROR_INVALIDDATA; + } if (8 * len > get_bits_left(&s->gb)) return AVERROR_INVALIDDATA; @@ -1993,7 +2054,7 @@ static int mjpeg_decode_app(MJpegDecodeContext *s) unsigned nummarkers; id = get_bits_long(&s->gb, 32); - id2 = get_bits_long(&s->gb, 24); + id2 = get_bits(&s->gb, 24); len -= 7; if (id != AV_RB32("PROF") || id2 != AV_RB24("ILE")) { av_log(s->avctx, AV_LOG_WARNING, "Invalid ICC_PROFILE header in APP2\n"); diff --git a/libavcodec/mjpegdec.h b/libavcodec/mjpegdec.h index 653fe7cae6e..9d1666bebdb 100644 --- a/libavcodec/mjpegdec.h +++ b/libavcodec/mjpegdec.h @@ -64,6 +64,7 @@ typedef struct MJpegDecodeContext { int lossless; int ls; int progressive; + int bayer; /* true if it's a bayer-encoded JPEG embedded in a DNG */ int rgb; uint8_t upscale_h[4]; uint8_t upscale_v[4]; diff --git a/libavcodec/mjpegenc.c b/libavcodec/mjpegenc.c index 0ea7bd3d103..56ccbc5fb19 100644 --- a/libavcodec/mjpegenc.c +++ b/libavcodec/mjpegenc.c @@ -414,7 +414,8 @@ AVCodec ff_mjpeg_encoder = { .init = ff_mpv_encode_init, .encode2 = ff_mpv_encode_picture, .close = ff_mpv_encode_end, - .capabilities = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_NONE }, @@ -440,6 +441,7 @@ AVCodec ff_amv_encoder = { .init = ff_mpv_encode_init, .encode2 = amv_encode_picture, .close = ff_mpv_encode_end, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_NONE }, diff --git a/libavcodec/mjpegenc_common.c b/libavcodec/mjpegenc_common.c index 31868c9beda..3038ebde6e1 100644 --- a/libavcodec/mjpegenc_common.c +++ b/libavcodec/mjpegenc_common.c @@ -573,7 +573,7 @@ int ff_mjpeg_encode_stuffing(MpegEncContext *s) ff_mjpeg_escape_FF(pbc, s->esc_pos); - if((s->avctx->active_thread_type & FF_THREAD_SLICE) && mb_y < s->mb_height) + if((s->avctx->active_thread_type & FF_THREAD_SLICE) && mb_y < s->mb_height - 1) put_marker(pbc, RST0 + (mb_y&7)); s->esc_pos = put_bits_count(pbc) >> 3; fail: diff --git a/libavcodec/mlp_parse.c b/libavcodec/mlp_parse.c index 067735303c1..45715352c28 100644 --- a/libavcodec/mlp_parse.c +++ b/libavcodec/mlp_parse.c @@ -60,56 +60,6 @@ static const uint64_t mlp_layout[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static const uint8_t thd_chancount[13] = { -// LR C LFE LRs LRvh LRc LRrs Cs Ts LRsd LRw Cvh LFE2 - 2, 1, 1, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1 -}; - -static const uint64_t thd_layout[13] = { - AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT, // LR - AV_CH_FRONT_CENTER, // C - AV_CH_LOW_FREQUENCY, // LFE - AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, // LRs - AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT, // LRvh - AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER, // LRc - AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT, // LRrs - AV_CH_BACK_CENTER, // Cs - AV_CH_TOP_CENTER, // Ts - AV_CH_SURROUND_DIRECT_LEFT|AV_CH_SURROUND_DIRECT_RIGHT, // LRsd - AV_CH_WIDE_LEFT|AV_CH_WIDE_RIGHT, // LRw - AV_CH_TOP_FRONT_CENTER, // Cvh - AV_CH_LOW_FREQUENCY_2, // LFE2 -}; - -static int mlp_samplerate(int in) -{ - if (in == 0xF) - return 0; - - return (in & 8 ? 44100 : 48000) << (in & 7) ; -} - -static int truehd_channels(int chanmap) -{ - int channels = 0, i; - - for (i = 0; i < 13; i++) - channels += thd_chancount[i] * ((chanmap >> i) & 1); - - return channels; -} - -static uint64_t truehd_layout(int chanmap) -{ - int i; - uint64_t layout = 0; - - for (i = 0; i < 13; i++) - layout |= thd_layout[i] * ((chanmap >> i) & 1); - - return layout; -} - static int mlp_get_major_sync_size(const uint8_t * buf, int bufsize) { int has_extension, extensions = 0; @@ -152,7 +102,7 @@ int ff_mlp_read_major_sync(void *log, MLPHeaderInfo *mh, GetBitContext *gb) return AVERROR_INVALIDDATA; } - if (get_bits_long(gb, 24) != 0xf8726f) /* Sync words */ + if (get_bits(gb, 24) != 0xf8726f) /* Sync words */ return AVERROR_INVALIDDATA; mh->stream_type = get_bits(gb, 8); diff --git a/libavcodec/mlp_parse.h b/libavcodec/mlp_parse.h index c6025d1a185..a0790ae8c71 100644 --- a/libavcodec/mlp_parse.h +++ b/libavcodec/mlp_parse.h @@ -56,6 +56,55 @@ typedef struct MLPHeaderInfo int num_substreams; ///< Number of substreams within stream } MLPHeaderInfo; +static const uint8_t thd_chancount[13] = { +// LR C LFE LRs LRvh LRc LRrs Cs Ts LRsd LRw Cvh LFE2 + 2, 1, 1, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1 +}; + +static const uint64_t thd_layout[13] = { + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT, // LR + AV_CH_FRONT_CENTER, // C + AV_CH_LOW_FREQUENCY, // LFE + AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, // LRs + AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT, // LRvh + AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER, // LRc + AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT, // LRrs + AV_CH_BACK_CENTER, // Cs + AV_CH_TOP_CENTER, // Ts + AV_CH_SURROUND_DIRECT_LEFT|AV_CH_SURROUND_DIRECT_RIGHT, // LRsd + AV_CH_WIDE_LEFT|AV_CH_WIDE_RIGHT, // LRw + AV_CH_TOP_FRONT_CENTER, // Cvh + AV_CH_LOW_FREQUENCY_2, // LFE2 +}; + +static inline int mlp_samplerate(int in) +{ + if (in == 0xF) + return 0; + + return (in & 8 ? 44100 : 48000) << (in & 7) ; +} + +static inline int truehd_channels(int chanmap) +{ + int channels = 0, i; + + for (i = 0; i < 13; i++) + channels += thd_chancount[i] * ((chanmap >> i) & 1); + + return channels; +} + +static inline uint64_t truehd_layout(int chanmap) +{ + int i; + uint64_t layout = 0; + + for (i = 0; i < 13; i++) + layout |= thd_layout[i] * ((chanmap >> i) & 1); + + return layout; +} int ff_mlp_read_major_sync(void *log, MLPHeaderInfo *mh, GetBitContext *gb); diff --git a/libavcodec/mlp_parser.c b/libavcodec/mlp_parser.c index 9a076f6a7f4..e7162f4aa83 100644 --- a/libavcodec/mlp_parser.c +++ b/libavcodec/mlp_parser.c @@ -61,7 +61,10 @@ static int mlp_parse(AVCodecParserContext *s, int ret; int i, p = 0; + s->key_frame = 0; + *poutbuf_size = 0; + *poutbuf = NULL; if (buf_size == 0) return 0; @@ -136,6 +139,8 @@ static int mlp_parse(AVCodecParserContext *s, * access unit header and all the 2- or 4-byte substream headers. */ // Only check when this isn't a sync frame - syncs have a checksum. + s->key_frame = 0; + parity_bits = 0; for (i = -1; i < mp->num_substreams; i++) { parity_bits ^= buf[p++]; @@ -159,12 +164,15 @@ static int mlp_parse(AVCodecParserContext *s, if (ff_mlp_read_major_sync(avctx, &mh, &gb) < 0) goto lost_sync; + s->key_frame = 1; + avctx->bits_per_raw_sample = mh.group1_bits; if (avctx->bits_per_raw_sample > 16) avctx->sample_fmt = AV_SAMPLE_FMT_S32; else avctx->sample_fmt = AV_SAMPLE_FMT_S16; avctx->sample_rate = mh.group1_samplerate; + avctx->frame_size = s->duration = mh.access_unit_size; if(!avctx->channels || !avctx->channel_layout) { diff --git a/libavcodec/mlp_parser.h b/libavcodec/mlp_parser.h deleted file mode 100644 index c5a28839204..00000000000 --- a/libavcodec/mlp_parser.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * MLP parser prototypes - * Copyright (c) 2007 Ian Caulfield - * - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General 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 - * MLP parser prototypes - */ - -#ifndef AVCODEC_MLP_PARSER_H -#define AVCODEC_MLP_PARSER_H - -#include "get_bits.h" - -typedef struct MLPHeaderInfo -{ - int stream_type; ///< 0xBB for MLP, 0xBA for TrueHD - int header_size; ///< Size of the major sync header, in bytes - - int group1_bits; ///< The bit depth of the first substream - int group2_bits; ///< Bit depth of the second substream (MLP only) - - int group1_samplerate; ///< Sample rate of first substream - int group2_samplerate; ///< Sample rate of second substream (MLP only) - - int channel_arrangement; - - int channel_modifier_thd_stream0; ///< Channel modifier for substream 0 of TrueHD streams ("2-channel presentation") - int channel_modifier_thd_stream1; ///< Channel modifier for substream 1 of TrueHD streams ("6-channel presentation") - int channel_modifier_thd_stream2; ///< Channel modifier for substream 2 of TrueHD streams ("8-channel presentation") - - int channels_mlp; ///< Channel count for MLP streams - int channels_thd_stream1; ///< Channel count for substream 1 of TrueHD streams ("6-channel presentation") - int channels_thd_stream2; ///< Channel count for substream 2 of TrueHD streams ("8-channel presentation") - uint64_t channel_layout_mlp; ///< Channel layout for MLP streams - uint64_t channel_layout_thd_stream1; ///< Channel layout for substream 1 of TrueHD streams ("6-channel presentation") - uint64_t channel_layout_thd_stream2; ///< Channel layout for substream 2 of TrueHD streams ("8-channel presentation") - - int access_unit_size; ///< Number of samples per coded frame - int access_unit_size_pow2; ///< Next power of two above number of samples per frame - - int is_vbr; ///< Stream is VBR instead of CBR - int peak_bitrate; ///< Peak bitrate for VBR, actual bitrate (==peak) for CBR - - int num_substreams; ///< Number of substreams within stream -} MLPHeaderInfo; - - -int ff_mlp_read_major_sync(void *log, MLPHeaderInfo *mh, GetBitContext *gb); -uint64_t ff_truehd_layout(int chanmap); - -extern const uint64_t ff_mlp_layout[32]; - -#endif /* AVCODEC_MLP_PARSER_H */ diff --git a/libavcodec/mlpdec.c b/libavcodec/mlpdec.c index 39c40914cd8..1a2c0f29ace 100644 --- a/libavcodec/mlpdec.c +++ b/libavcodec/mlpdec.c @@ -266,7 +266,7 @@ static inline int read_huff_channels(MLPDecodeContext *m, GetBitContext *gbp, return AVERROR_INVALIDDATA; if (lsb_bits > 0) - result = (result << lsb_bits) + get_bits(gbp, lsb_bits); + result = (result << lsb_bits) + get_bits_long(gbp, lsb_bits); result += cp->sign_huff_offset; result *= 1 << quant_step_size; @@ -829,7 +829,7 @@ static int read_channel_params(MLPDecodeContext *m, unsigned int substr, cp->codebook = get_bits(gbp, 2); cp->huff_lsbs = get_bits(gbp, 5); - if (cp->huff_lsbs > 24) { + if (cp->codebook > 0 && cp->huff_lsbs > 24) { av_log(m->avctx, AV_LOG_ERROR, "Invalid huff_lsbs.\n"); cp->huff_lsbs = 0; return AVERROR_INVALIDDATA; diff --git a/libavcodec/mlpdsp.c b/libavcodec/mlpdsp.c index 32a4503b64e..12bef3a721d 100644 --- a/libavcodec/mlpdsp.c +++ b/libavcodec/mlpdsp.c @@ -79,7 +79,7 @@ void ff_mlp_rematrix_channel(int32_t *samples, if (matrix_noise_shift) { index &= access_unit_size_pow2 - 1; - accum += noise_buffer[index] << (matrix_noise_shift + 7); + accum += noise_buffer[index] * (1 << (matrix_noise_shift + 7)); index += index2; } diff --git a/libavcodec/mlpenc.c b/libavcodec/mlpenc.c index deb171645cc..c6a7963c222 100644 --- a/libavcodec/mlpenc.c +++ b/libavcodec/mlpenc.c @@ -1,6 +1,7 @@ /** * MLP encoder * Copyright (c) 2008 Ramiro Polla + * Copyright (c) 2016-2019 Jai Luthra * * This file is part of FFmpeg. * @@ -86,15 +87,15 @@ typedef struct { } DecodingParams; typedef struct BestOffset { - int16_t offset; + int32_t offset; int bitcount; int lsb_bits; - int16_t min; - int16_t max; + int32_t min; + int32_t max; } BestOffset; -#define HUFF_OFFSET_MIN -16384 -#define HUFF_OFFSET_MAX 16383 +#define HUFF_OFFSET_MIN (-16384) +#define HUFF_OFFSET_MAX ( 16383) /** Number of possible codebooks (counting "no codebooks") */ #define NUM_CODEBOOKS 4 @@ -466,7 +467,7 @@ static void default_decoding_params(MLPEncodeContext *ctx, */ static int inline number_sbits(int number) { - if (number < 0) + if (number < -1) number++; return av_log2(FFABS(number)) + 1 + !!number; @@ -807,7 +808,7 @@ static void write_major_sync(MLPEncodeContext *ctx, uint8_t *buf, int buf_size) static void write_restart_header(MLPEncodeContext *ctx, PutBitContext *pb) { RestartHeader *rh = ctx->cur_restart_header; - int32_t lossless_check = xor_32_to_8(rh->lossless_check_data); + uint8_t lossless_check = xor_32_to_8(rh->lossless_check_data); unsigned int start_count = put_bits_count(pb); PutBitContext tmpb; uint8_t checksum; @@ -986,6 +987,9 @@ static void write_decoding_params(MLPEncodeContext *ctx, PutBitContext *pb, put_bits(pb, 1, 0); } } + if (cp->codebook > 0 && cp->huff_lsbs > 24) { + av_log(ctx->avctx, AV_LOG_ERROR, "Invalid Huff LSBs\n"); + } put_bits(pb, 2, cp->codebook ); put_bits(pb, 5, cp->huff_lsbs); @@ -1016,12 +1020,10 @@ static void write_block_data(MLPEncodeContext *ctx, PutBitContext *pb) codebook_index [ch] = cp->codebook - 1; sign_huff_offset[ch] = cp->huff_offset; - sign_shift = lsb_bits[ch] - 1; + sign_shift = lsb_bits[ch] + (cp->codebook ? 2 - cp->codebook : -1); - if (cp->codebook > 0) { + if (cp->codebook > 0) sign_huff_offset[ch] -= 7 << lsb_bits[ch]; - sign_shift += 3 - cp->codebook; - } /* Unsign if needed. */ if (sign_shift >= 0) @@ -1031,7 +1033,6 @@ static void write_block_data(MLPEncodeContext *ctx, PutBitContext *pb) for (i = 0; i < dp->blocksize; i++) { for (ch = rh->min_channel; ch <= rh->max_channel; ch++) { int32_t sample = *sample_buffer++ >> dp->quant_step_size[ch]; - sample -= sign_huff_offset[ch]; if (codebook_index[ch] >= 0) { @@ -1251,7 +1252,7 @@ static void input_data_internal(MLPEncodeContext *ctx, const uint8_t *samples, uint32_t abs_sample; int32_t sample; - sample = is24 ? *samples_32++ >> 8 : *samples_16++ << 8; + sample = is24 ? *samples_32++ >> 8 : *samples_16++ * 256; /* TODO Find out if number_sbits can be used for negative values. */ abs_sample = FFABS(sample); @@ -1562,7 +1563,7 @@ static void no_codebook_bits_offset(MLPEncodeContext *ctx, BestOffset *bo) { DecodingParams *dp = ctx->cur_decoding_params; - int32_t unsign; + int32_t unsign = 0; int lsb_bits; min -= offset; @@ -1572,7 +1573,8 @@ static void no_codebook_bits_offset(MLPEncodeContext *ctx, lsb_bits += !!lsb_bits; - unsign = 1 << (lsb_bits - 1); + if (lsb_bits > 0) + unsign = 1 << (lsb_bits - 1); bo->offset = offset; bo->lsb_bits = lsb_bits; @@ -1591,7 +1593,7 @@ static void no_codebook_bits(MLPEncodeContext *ctx, { DecodingParams *dp = ctx->cur_decoding_params; int16_t offset; - int32_t unsign; + int32_t unsign = 0; uint32_t diff; int lsb_bits; @@ -1607,7 +1609,8 @@ static void no_codebook_bits(MLPEncodeContext *ctx, lsb_bits = number_sbits(diff) - 1; - unsign = 1 << (lsb_bits - 1); + 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. */ @@ -1699,7 +1702,7 @@ static inline void codebook_bits(MLPEncodeContext *ctx, offset_min = FFMAX(min, HUFF_OFFSET_MIN); offset_max = FFMIN(max, HUFF_OFFSET_MAX); - for (;;) { + while (offset <= offset_max && offset >= offset_min) { BestOffset temp_bo; codebook_bits_offset(ctx, channel, codebook, @@ -1718,12 +1721,8 @@ static inline void codebook_bits(MLPEncodeContext *ctx, if (direction) { offset = temp_bo.max + 1; - if (offset > offset_max) - break; } else { offset = temp_bo.min - 1; - if (offset < offset_min) - break; } } } @@ -1796,24 +1795,24 @@ static void determine_bits(MLPEncodeContext *ctx) #define SAMPLE_MAX(bitdepth) ((1 << (bitdepth - 1)) - 1) #define SAMPLE_MIN(bitdepth) (~SAMPLE_MAX(bitdepth)) -#define MSB_MASK(bits) (-1u << bits) +#define MSB_MASK(bits) (-(int)(1u << (bits))) /** Applies the filter to the current samples, and saves the residual back * into the samples buffer. If the filter is too bad and overflows the - * maximum amount of bits allowed (16 or 24), the samples buffer is left as is and + * 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) { FilterParams *fp[NUM_FILTERS] = { &ctx->cur_channel_params[channel].filter_params[FIR], &ctx->cur_channel_params[channel].filter_params[IIR], }; - int32_t *filter_state_buffer[NUM_FILTERS]; + int32_t *filter_state_buffer[NUM_FILTERS] = { NULL }; 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; unsigned int filter_shift = fp[FIR]->shift; int filter; - int i; + int i, ret = 0; for (i = 0; i < NUM_FILTERS; i++) { unsigned int size = ctx->number_of_samples; @@ -1836,7 +1835,7 @@ static int apply_filter(MLPEncodeContext *ctx, unsigned int channel) int32_t sample = *sample_buffer; unsigned int order; int64_t accum = 0; - int32_t residual; + int64_t residual; for (filter = 0; filter < NUM_FILTERS; filter++) { int32_t *fcoeff = ctx->cur_channel_params[channel].coeff[filter]; @@ -1848,11 +1847,13 @@ static int apply_filter(MLPEncodeContext *ctx, unsigned int channel) accum >>= filter_shift; residual = sample - (accum & mask); - if (residual < SAMPLE_MIN(ctx->wordlength) || residual > SAMPLE_MAX(ctx->wordlength)) - return -1; + if (residual < SAMPLE_MIN(24) || residual > SAMPLE_MAX(24)) { + ret = -1; + goto free_and_return; + } filter_state_buffer[FIR][i] = sample; - filter_state_buffer[IIR][i] = residual; + filter_state_buffer[IIR][i] = (int32_t) residual; sample_buffer += ctx->num_channels; } @@ -1864,11 +1865,12 @@ static int apply_filter(MLPEncodeContext *ctx, unsigned int channel) sample_buffer += ctx->num_channels; } +free_and_return: for (i = 0; i < NUM_FILTERS; i++) { av_freep(&filter_state_buffer[i]); } - return 0; + return ret; } static void apply_filters(MLPEncodeContext *ctx) @@ -1897,8 +1899,8 @@ static void generate_2_noise_channels(MLPEncodeContext *ctx) for (i = 0; i < ctx->number_of_samples; i++) { uint16_t seed_shr7 = seed >> 7; - *sample_buffer++ = ((int8_t)(seed >> 15)) << rh->noise_shift; - *sample_buffer++ = ((int8_t) seed_shr7) << rh->noise_shift; + *sample_buffer++ = ((int8_t)(seed >> 15)) * (1 << rh->noise_shift); + *sample_buffer++ = ((int8_t) seed_shr7) * (1 << rh->noise_shift); seed = (seed << 16) ^ seed_shr7 ^ (seed_shr7 << 5); @@ -2069,9 +2071,9 @@ static void set_best_codebook(MLPEncodeContext *ctx) best_codebook = *best_path++ - ZERO_PATH; cur_bo = &ctx->best_offset[index][channel][best_codebook]; - cp->huff_offset = cur_bo->offset; - cp->huff_lsbs = cur_bo->lsb_bits + dp->quant_step_size[channel]; - cp->codebook = best_codebook; + cp->huff_offset = cur_bo->offset; + cp->huff_lsbs = cur_bo->lsb_bits + dp->quant_step_size[channel]; + cp->codebook = best_codebook; } } } @@ -2199,9 +2201,6 @@ static void process_major_frame(MLPEncodeContext *ctx) ctx->number_of_samples = ctx->major_frame_size; for (substr = 0; substr < ctx->num_substreams; substr++) { - RestartHeader *rh = ctx->cur_restart_header; - unsigned int channel; - ctx->cur_restart_header = &ctx->restart_header[substr]; ctx->cur_decoding_params = &ctx->major_decoding_params[1][substr]; @@ -2210,8 +2209,7 @@ static void process_major_frame(MLPEncodeContext *ctx) generate_2_noise_channels(ctx); rematrix_channels (ctx); - for (channel = rh->min_channel; channel <= rh->max_channel; channel++) - apply_filter(ctx, channel); + apply_filters(ctx); } } @@ -2277,7 +2275,7 @@ static int mlp_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, if (restart_frame) { set_major_params(ctx); if (ctx->min_restart_interval != ctx->max_restart_interval) - process_major_frame(ctx); + process_major_frame(ctx); } if (ctx->min_restart_interval == ctx->max_restart_interval) @@ -2375,6 +2373,7 @@ static av_cold int mlp_encode_close(AVCodecContext *avctx) av_freep(&ctx->decoding_params); av_freep(&ctx->channel_params); av_freep(&ctx->frame_size); + av_freep(&ctx->max_output_bits); ff_af_queue_close(&ctx->afq); return 0; diff --git a/libavcodec/mmaldec.c b/libavcodec/mmaldec.c index 647a22ef7c0..547bece5763 100644 --- a/libavcodec/mmaldec.c +++ b/libavcodec/mmaldec.c @@ -34,7 +34,7 @@ #include #include "avcodec.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "internal.h" #include "libavutil/avassert.h" #include "libavutil/buffer.h" diff --git a/libavcodec/mmvideo.c b/libavcodec/mmvideo.c index 04de6bb4c84..7f650be80ef 100644 --- a/libavcodec/mmvideo.c +++ b/libavcodec/mmvideo.c @@ -201,7 +201,7 @@ static int mm_decode_frame(AVCodecContext *avctx, buf_size -= MM_PREAMBLE_SIZE; bytestream2_init(&s->gb, buf, buf_size); - if ((res = ff_reget_buffer(avctx, s->frame)) < 0) + if ((res = ff_reget_buffer(avctx, s->frame, 0)) < 0) return res; switch(type) { diff --git a/libavcodec/motion_est.c b/libavcodec/motion_est.c index 759eea479d5..02c75fd4702 100644 --- a/libavcodec/motion_est.c +++ b/libavcodec/motion_est.c @@ -971,7 +971,7 @@ void ff_estimate_p_frame_motion(MpegEncContext * s, int i_score= varc-500+(s->lambda2>>FF_LAMBDA_SHIFT)*20; c->scene_change_score+= ff_sqrt(p_score) - ff_sqrt(i_score); - if (vard*2 + 200*256 > varc) + if (vard*2 + 200*256 > varc && !s->intra_penalty) mb_type|= CANDIDATE_MB_TYPE_INTRA; if (varc*2 + 200*256 > vard || s->qscale > 24){ // if (varc*2 + 200*256 + 50*(s->lambda2>>FF_LAMBDA_SHIFT) > vard){ @@ -1040,7 +1040,7 @@ void ff_estimate_p_frame_motion(MpegEncContext * s, intra_score= s->mecc.mb_cmp[0](s, c->scratchpad, pix, s->linesize, 16); } - intra_score += c->mb_penalty_factor*16; + intra_score += c->mb_penalty_factor*16 + s->intra_penalty; if(intra_score < dmin){ mb_type= CANDIDATE_MB_TYPE_INTRA; @@ -1648,7 +1648,7 @@ int ff_get_best_fcode(MpegEncContext * s, int16_t (*mv_table)[2], int type) } } -void ff_fix_long_p_mvs(MpegEncContext * s) +void ff_fix_long_p_mvs(MpegEncContext * s, int type) { MotionEstContext * const c= &s->me; const int f_code= s->f_code; @@ -1682,8 +1682,8 @@ void ff_fix_long_p_mvs(MpegEncContext * s) if( mx >=range || mx <-range || my >=range || my <-range){ s->mb_type[i] &= ~CANDIDATE_MB_TYPE_INTER4V; - s->mb_type[i] |= CANDIDATE_MB_TYPE_INTRA; - s->current_picture.mb_type[i] = CANDIDATE_MB_TYPE_INTRA; + s->mb_type[i] |= type; + s->current_picture.mb_type[i] = type; } } } diff --git a/libavcodec/motion_est.h b/libavcodec/motion_est.h index 3b3a8d7341c..817220f3405 100644 --- a/libavcodec/motion_est.h +++ b/libavcodec/motion_est.h @@ -127,7 +127,7 @@ int ff_get_mb_score(struct MpegEncContext *s, int mx, int my, int src_index, int ff_get_best_fcode(struct MpegEncContext *s, int16_t (*mv_table)[2], int type); -void ff_fix_long_p_mvs(struct MpegEncContext *s); +void ff_fix_long_p_mvs(struct MpegEncContext *s, int type); void ff_fix_long_mvs(struct MpegEncContext *s, uint8_t *field_select_table, int field_select, int16_t (*mv_table)[2], int f_code, int type, int truncate); diff --git a/libavcodec/motion_est_template.c b/libavcodec/motion_est_template.c index 014038e54fc..13e73f2653a 100644 --- a/libavcodec/motion_est_template.c +++ b/libavcodec/motion_est_template.c @@ -157,8 +157,8 @@ static int no_sub_motion_search(MpegEncContext * s, int src_index, int ref_index, int size, int h) { - (*mx_ptr)<<=1; - (*my_ptr)<<=1; + (*mx_ptr) *= 2; + (*my_ptr) *= 2; return dmin; } diff --git a/libavcodec/motionpixels.c b/libavcodec/motionpixels.c index 73977664a51..6cb444a7034 100644 --- a/libavcodec/motionpixels.c +++ b/libavcodec/motionpixels.c @@ -171,7 +171,7 @@ static int mp_read_codes_table(MotionPixelsContext *mp, GetBitContext *gb) return 0; } -static int mp_gradient(MotionPixelsContext *mp, int component, int v) +static av_always_inline int mp_gradient(MotionPixelsContext *mp, int component, int v) { int delta; @@ -196,7 +196,7 @@ static void mp_set_rgb_from_yuv(MotionPixelsContext *mp, int x, int y, const Yuv *(uint16_t *)&mp->frame->data[0][y * mp->frame->linesize[0] + x * 2] = color; } -static int mp_get_vlc(MotionPixelsContext *mp, GetBitContext *gb) +static av_always_inline int mp_get_vlc(MotionPixelsContext *mp, GetBitContext *gb) { int i; @@ -292,7 +292,7 @@ static int mp_decode_frame(AVCodecContext *avctx, GetBitContext gb; int i, count1, count2, sz, ret; - if ((ret = ff_reget_buffer(avctx, mp->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, mp->frame, 0)) < 0) return ret; /* le32 bitstream msb first */ diff --git a/libavcodec/movsub_bsf.c b/libavcodec/movsub_bsf.c index cd48aa7bb8f..6e29fa8e507 100644 --- a/libavcodec/movsub_bsf.c +++ b/libavcodec/movsub_bsf.c @@ -20,8 +20,8 @@ #include "libavutil/common.h" #include "libavutil/intreadwrite.h" -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" static int text2movsub(AVBSFContext *ctx, AVPacket *out) { diff --git a/libavcodec/movtextdec.c b/libavcodec/movtextdec.c index c38c5edce67..4a21dbf36d4 100644 --- a/libavcodec/movtextdec.c +++ b/libavcodec/movtextdec.c @@ -21,6 +21,7 @@ #include "avcodec.h" #include "ass.h" +#include "libavutil/opt.h" #include "libavutil/avstring.h" #include "libavutil/common.h" #include "libavutil/bprint.h" @@ -48,14 +49,19 @@ #define TOP_CENTER 8 #define TOP_RIGHT 9 +#define RGB_TO_BGR(c) (((c) & 0xff) << 16 | ((c) & 0xff00) | (((c) >> 16) & 0xff)) + typedef struct { - char *font; - int fontsize; + uint16_t fontID; + const char *font; + uint8_t fontsize; int color; + uint8_t alpha; int back_color; - int bold; - int italic; - int underline; + uint8_t back_alpha; + uint8_t bold; + uint8_t italic; + uint8_t underline; int alignment; } MovTextDefault; @@ -68,6 +74,11 @@ typedef struct { uint16_t style_start; uint16_t style_end; uint8_t style_flag; + uint8_t bold; + uint8_t italic; + uint8_t underline; + int color; + uint8_t alpha; uint8_t fontsize; uint16_t style_fontID; } StyleBox; @@ -86,6 +97,7 @@ typedef struct { } TextWrapBox; typedef struct { + AVClass *class; StyleBox **s; StyleBox *s_temp; HighlightBox h; @@ -100,6 +112,8 @@ typedef struct { int size_var; int count_s, count_f; int readorder; + int frame_width; + int frame_height; } MovTextContext; typedef struct { @@ -141,7 +155,6 @@ static int mov_text_tx3g(AVCodecContext *avctx, MovTextContext *m) uint8_t *tx3g_ptr = avctx->extradata; int i, box_size, font_length; int8_t v_align, h_align; - int style_fontID; StyleBox s_default; m->count_f = 0; @@ -181,24 +194,28 @@ static int mov_text_tx3g(AVCodecContext *avctx, MovTextContext *m) } // Background Color m->d.back_color = AV_RB24(tx3g_ptr); - tx3g_ptr += 4; + tx3g_ptr += 3; + m->d.back_alpha = AV_RB8(tx3g_ptr); + tx3g_ptr += 1; // BoxRecord tx3g_ptr += 8; // StyleRecord tx3g_ptr += 4; // fontID - style_fontID = AV_RB16(tx3g_ptr); + m->d.fontID = AV_RB16(tx3g_ptr); tx3g_ptr += 2; // face-style-flags s_default.style_flag = *tx3g_ptr++; - m->d.bold = s_default.style_flag & STYLE_FLAG_BOLD; - m->d.italic = s_default.style_flag & STYLE_FLAG_ITALIC; - m->d.underline = s_default.style_flag & STYLE_FLAG_UNDERLINE; + m->d.bold = !!(s_default.style_flag & STYLE_FLAG_BOLD); + m->d.italic = !!(s_default.style_flag & STYLE_FLAG_ITALIC); + m->d.underline = !!(s_default.style_flag & STYLE_FLAG_UNDERLINE); // fontsize m->d.fontsize = *tx3g_ptr++; // Primary color m->d.color = AV_RB24(tx3g_ptr); - tx3g_ptr += 4; + tx3g_ptr += 3; + m->d.alpha = AV_RB8(tx3g_ptr); + tx3g_ptr += 1; // FontRecord // FontRecord Size tx3g_ptr += 4; @@ -246,8 +263,10 @@ static int mov_text_tx3g(AVCodecContext *avctx, MovTextContext *m) m->ftab_temp = NULL; tx3g_ptr = tx3g_ptr + font_length; } + // In case of broken header, init default font + m->d.font = ASS_DEFAULT_FONT; for (i = 0; i < m->ftab_entries; i++) { - if (style_fontID == m->ftab[i]->fontID) + if (m->d.fontID == m->ftab[i]->fontID) m->d.font = m->ftab[i]->font; } return 0; @@ -311,16 +330,21 @@ static int decode_styl(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt) m->s_temp->style_fontID = AV_RB16(tsmb); tsmb += 2; m->s_temp->style_flag = AV_RB8(tsmb); + m->s_temp->bold = !!(m->s_temp->style_flag & STYLE_FLAG_BOLD); + m->s_temp->italic = !!(m->s_temp->style_flag & STYLE_FLAG_ITALIC); + m->s_temp->underline = !!(m->s_temp->style_flag & STYLE_FLAG_UNDERLINE); tsmb++; m->s_temp->fontsize = AV_RB8(tsmb); + tsmb++; + m->s_temp->color = AV_RB24(tsmb); + tsmb += 3; + m->s_temp->alpha = AV_RB8(tsmb); + tsmb++; av_dynarray_add(&m->s, &m->count_s, m->s_temp); if(!m->s) { mov_text_cleanup(m); return AVERROR(ENOMEM); } - tsmb++; - // text-color-rgba - tsmb += 4; } return 0; } @@ -353,8 +377,10 @@ static int text_to_ass(AVBPrint *buf, const char *text, const char *text_end, { MovTextContext *m = avctx->priv_data; int i = 0; - int j = 0; int text_pos = 0; + int style_active = 0; + int entry = 0; + int color = m->d.color; if (text < text_end && m->box_flags & TWRP_BOX) { if (m->w.wrap_flag == 1) { @@ -367,26 +393,36 @@ static int text_to_ass(AVBPrint *buf, const char *text, const char *text_end, while (text < text_end) { int len; - if (m->box_flags & STYL_BOX) { - for (i = 0; i < m->style_entries; i++) { - if (m->s[i]->style_flag && text_pos == m->s[i]->style_end) { - av_bprintf(buf, "{\\r}"); + if ((m->box_flags & STYL_BOX) && entry < m->style_entries) { + if (text_pos == m->s[entry]->style_start) { + style_active = 1; + if (m->s[entry]->bold ^ m->d.bold) + av_bprintf(buf, "{\\b%d}", m->s[entry]->bold); + if (m->s[entry]->italic ^ m->d.italic) + av_bprintf(buf, "{\\i%d}", m->s[entry]->italic); + if (m->s[entry]->underline ^ m->d.underline) + av_bprintf(buf, "{\\u%d}", m->s[entry]->underline); + if (m->s[entry]->fontsize != m->d.fontsize) + av_bprintf(buf, "{\\fs%d}", m->s[entry]->fontsize); + if (m->s[entry]->style_fontID != m->d.fontID) + for (i = 0; i < m->ftab_entries; i++) { + if (m->s[entry]->style_fontID == m->ftab[i]->fontID) + av_bprintf(buf, "{\\fn%s}", m->ftab[i]->font); + } + if (m->d.color != m->s[entry]->color) { + color = m->s[entry]->color; + av_bprintf(buf, "{\\1c&H%X&}", RGB_TO_BGR(color)); } + if (m->d.alpha != m->s[entry]->alpha) + av_bprintf(buf, "{\\1a&H%02X&}", 255 - m->s[entry]->alpha); } - for (i = 0; i < m->style_entries; i++) { - if (m->s[i]->style_flag && text_pos == m->s[i]->style_start) { - if (m->s[i]->style_flag & STYLE_FLAG_BOLD) - av_bprintf(buf, "{\\b1}"); - if (m->s[i]->style_flag & STYLE_FLAG_ITALIC) - av_bprintf(buf, "{\\i1}"); - if (m->s[i]->style_flag & STYLE_FLAG_UNDERLINE) - av_bprintf(buf, "{\\u1}"); - av_bprintf(buf, "{\\fs%d}", m->s[i]->fontsize); - for (j = 0; j < m->ftab_entries; j++) { - if (m->s[i]->style_fontID == m->ftab[j]->fontID) - av_bprintf(buf, "{\\fn%s}", m->ftab[j]->font); - } + if (text_pos == m->s[entry]->style_end) { + if (style_active) { + av_bprintf(buf, "{\\r}"); + style_active = 0; + color = m->d.color; } + entry++; } } if (m->box_flags & HLIT_BOX) { @@ -406,9 +442,10 @@ static int text_to_ass(AVBPrint *buf, const char *text, const char *text_end, } if (text_pos == m->h.hlit_end) { if (m->box_flags & HCLR_BOX) { - av_bprintf(buf, "{\\2c&H000000&}"); + av_bprintf(buf, "{\\2c&H%X&}", RGB_TO_BGR(m->d.color)); } else { - av_bprintf(buf, "{\\1c&HFFFFFF&}{\\2c&H000000&}"); + av_bprintf(buf, "{\\1c&H%X&}{\\2c&H%X&}", + RGB_TO_BGR(color), RGB_TO_BGR(m->d.color)); } } } @@ -448,10 +485,19 @@ static int mov_text_init(AVCodecContext *avctx) { MovTextContext *m = avctx->priv_data; ret = mov_text_tx3g(avctx, m); if (ret == 0) { - return ff_ass_subtitle_header(avctx, m->d.font, m->d.fontsize, m->d.color, - m->d.back_color, m->d.bold, m->d.italic, - m->d.underline, ASS_DEFAULT_BORDERSTYLE, - m->d.alignment); + if (!m->frame_width || !m->frame_height) { + m->frame_width = ASS_DEFAULT_PLAYRESX; + m->frame_height = ASS_DEFAULT_PLAYRESY; + } + return ff_ass_subtitle_header_full(avctx, + m->frame_width, m->frame_height, + m->d.font, m->d.fontsize, + (255U - m->d.alpha) << 24 | RGB_TO_BGR(m->d.color), + (255U - m->d.alpha) << 24 | RGB_TO_BGR(m->d.color), + (255U - m->d.back_alpha) << 24 | RGB_TO_BGR(m->d.back_color), + (255U - m->d.back_alpha) << 24 | RGB_TO_BGR(m->d.back_color), + m->d.bold, m->d.italic, m->d.underline, + ASS_DEFAULT_BORDERSTYLE, m->d.alignment); } else return ff_ass_subtitle_header_default(avctx); } @@ -567,12 +613,28 @@ static void mov_text_flush(AVCodecContext *avctx) m->readorder = 0; } +#define OFFSET(x) offsetof(MovTextContext, x) +#define FLAGS AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_SUBTITLE_PARAM +static const AVOption options[] = { + { "width", "Frame width, usually video width", OFFSET(frame_width), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, + { "height", "Frame height, usually video height", OFFSET(frame_height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, + { NULL }, +}; + +static const AVClass mov_text_decoder_class = { + .class_name = "MOV text decoder", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVCodec ff_movtext_decoder = { .name = "mov_text", .long_name = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"), .type = AVMEDIA_TYPE_SUBTITLE, .id = AV_CODEC_ID_MOV_TEXT, .priv_data_size = sizeof(MovTextContext), + .priv_class = &mov_text_decoder_class, .init = mov_text_init, .decode = mov_text_decode_frame, .close = mov_text_decode_close, diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c index c19ef384bcf..b2368b641bb 100644 --- a/libavcodec/movtextenc.c +++ b/libavcodec/movtextenc.c @@ -21,6 +21,7 @@ #include #include "avcodec.h" +#include "libavutil/opt.h" #include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/intreadwrite.h" @@ -39,12 +40,22 @@ #define HLIT_BOX (1<<1) #define HCLR_BOX (1<<2) +#define DEFAULT_STYLE_FONT_ID 0x01 +#define DEFAULT_STYLE_FONTSIZE 0x12 +#define DEFAULT_STYLE_COLOR 0xffffffff +#define DEFAULT_STYLE_FLAG 0x00 + +#define BGR_TO_RGB(c) (((c) & 0xff) << 16 | ((c) & 0xff00) | (((c) >> 16) & 0xff)) +#define FONTSIZE_SCALE(s,fs) ((fs) * (s)->font_scale_factor + 0.5) #define av_bprint_append_any(buf, data, size) av_bprint_append_data(buf, ((const char*)data), size) typedef struct { uint16_t style_start; uint16_t style_end; uint8_t style_flag; + uint16_t style_fontID; + uint8_t style_fontsize; + uint32_t style_color; } StyleBox; typedef struct { @@ -57,9 +68,11 @@ typedef struct { } HilightcolorBox; typedef struct { + AVClass *class; AVCodecContext *avctx; ASSSplitContext *ass_ctx; + ASSStyle *ass_dialog_style; AVBPrint buffer; StyleBox **style_attributes; StyleBox *style_attributes_temp; @@ -67,12 +80,13 @@ typedef struct { HilightcolorBox hclr; int count; uint8_t box_flags; - uint16_t style_entries; - uint16_t style_fontID; - uint8_t style_fontsize; - uint32_t style_color; + StyleBox d; uint16_t text_pos; uint16_t byte_count; + char ** fonts; + int font_count; + double font_scale_factor; + int frame_height; } MovTextContext; typedef struct { @@ -89,58 +103,71 @@ static void mov_text_cleanup(MovTextContext *s) } av_freep(&s->style_attributes); } + if (s->style_attributes_temp) { + *s->style_attributes_temp = s->d; + } } static void encode_styl(MovTextContext *s, uint32_t tsmb_type) { int j; uint32_t tsmb_size; - if (s->box_flags & STYL_BOX) { + uint16_t style_entries; + if ((s->box_flags & STYL_BOX) && s->count) { tsmb_size = s->count * STYLE_RECORD_SIZE + SIZE_ADD; tsmb_size = AV_RB32(&tsmb_size); - s->style_entries = AV_RB16(&s->count); - s->style_fontID = 0x00 | 0x01<<8; - s->style_fontsize = 0x12; - s->style_color = MKTAG(0xFF, 0xFF, 0xFF, 0xFF); + style_entries = AV_RB16(&s->count); /*The above three attributes are hard coded for now but will come from ASS style in the future*/ av_bprint_append_any(&s->buffer, &tsmb_size, 4); av_bprint_append_any(&s->buffer, &tsmb_type, 4); - av_bprint_append_any(&s->buffer, &s->style_entries, 2); + av_bprint_append_any(&s->buffer, &style_entries, 2); for (j = 0; j < s->count; j++) { - av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_start, 2); - av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_end, 2); - av_bprint_append_any(&s->buffer, &s->style_fontID, 2); + uint16_t style_start, style_end, style_fontID; + uint32_t style_color; + + style_start = AV_RB16(&s->style_attributes[j]->style_start); + style_end = AV_RB16(&s->style_attributes[j]->style_end); + style_color = AV_RB32(&s->style_attributes[j]->style_color); + style_fontID = AV_RB16(&s->style_attributes[j]->style_fontID); + + av_bprint_append_any(&s->buffer, &style_start, 2); + av_bprint_append_any(&s->buffer, &style_end, 2); + av_bprint_append_any(&s->buffer, &style_fontID, 2); av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_flag, 1); - av_bprint_append_any(&s->buffer, &s->style_fontsize, 1); - av_bprint_append_any(&s->buffer, &s->style_color, 4); + av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_fontsize, 1); + av_bprint_append_any(&s->buffer, &style_color, 4); } - mov_text_cleanup(s); } + mov_text_cleanup(s); } static void encode_hlit(MovTextContext *s, uint32_t tsmb_type) { uint32_t tsmb_size; + uint16_t start, end; if (s->box_flags & HLIT_BOX) { tsmb_size = 12; tsmb_size = AV_RB32(&tsmb_size); + start = AV_RB16(&s->hlit.start); + end = AV_RB16(&s->hlit.end); av_bprint_append_any(&s->buffer, &tsmb_size, 4); av_bprint_append_any(&s->buffer, &tsmb_type, 4); - av_bprint_append_any(&s->buffer, &s->hlit.start, 2); - av_bprint_append_any(&s->buffer, &s->hlit.end, 2); + av_bprint_append_any(&s->buffer, &start, 2); + av_bprint_append_any(&s->buffer, &end, 2); } } static void encode_hclr(MovTextContext *s, uint32_t tsmb_type) { - uint32_t tsmb_size; + uint32_t tsmb_size, color; if (s->box_flags & HCLR_BOX) { tsmb_size = 12; tsmb_size = AV_RB32(&tsmb_size); + color = AV_RB32(&s->hclr.color); av_bprint_append_any(&s->buffer, &tsmb_size, 4); av_bprint_append_any(&s->buffer, &tsmb_type, 4); - av_bprint_append_any(&s->buffer, &s->hclr.color, 4); + av_bprint_append_any(&s->buffer, &color, 4); } } @@ -152,157 +179,451 @@ static const Box box_types[] = { const static size_t box_count = FF_ARRAY_ELEMS(box_types); -static av_cold int mov_text_encode_init(AVCodecContext *avctx) +static int mov_text_encode_close(AVCodecContext *avctx) +{ + MovTextContext *s = avctx->priv_data; + int i; + + ff_ass_split_free(s->ass_ctx); + if (s->style_attributes) { + for (i = 0; i < s->count; i++) { + av_freep(&s->style_attributes[i]); + } + av_freep(&s->style_attributes); + } + av_freep(&s->fonts); + av_freep(&s->style_attributes_temp); + av_bprint_finalize(&s->buffer, NULL); + return 0; +} + +static int encode_sample_description(AVCodecContext *avctx) { - /* - * For now, we'll use a fixed default style. When we add styling - * support, this will be generated from the ASS style. - */ - static const uint8_t text_sample_entry[] = { + ASS * ass; + ASSStyle * style; + int i, j; + uint32_t tsmb_size, tsmb_type, back_color, style_color; + uint16_t style_start, style_end, fontID, count; + int font_names_total_len = 0; + MovTextContext *s = avctx->priv_data; + + static const uint8_t display_and_justification[] = { 0x00, 0x00, 0x00, 0x00, // uint32_t displayFlags 0x01, // int8_t horizontal-justification 0xFF, // int8_t vertical-justification - 0x00, 0x00, 0x00, 0x00, // uint8_t background-color-rgba[4] - // BoxRecord { + }; + // 0x00, 0x00, 0x00, 0x00, // uint8_t background-color-rgba[4] + static const uint8_t box_record[] = { + // BoxRecord { 0x00, 0x00, // int16_t top 0x00, 0x00, // int16_t left 0x00, 0x00, // int16_t bottom 0x00, 0x00, // int16_t right - // }; - // StyleRecord { - 0x00, 0x00, // uint16_t startChar - 0x00, 0x00, // uint16_t endChar - 0x00, 0x01, // uint16_t font-ID - 0x00, // uint8_t face-style-flags - 0x12, // uint8_t font-size - 0xFF, 0xFF, 0xFF, 0xFF, // uint8_t text-color-rgba[4] - // }; - // FontTableBox { - 0x00, 0x00, 0x00, 0x12, // uint32_t size - 'f', 't', 'a', 'b', // uint8_t name[4] - 0x00, 0x01, // uint16_t entry-count - // FontRecord { - 0x00, 0x01, // uint16_t font-ID - 0x05, // uint8_t font-name-length - 'S', 'e', 'r', 'i', 'f',// uint8_t font[font-name-length] - // }; - // }; + // }; }; + // StyleRecord { + // 0x00, 0x00, // uint16_t startChar + // 0x00, 0x00, // uint16_t endChar + // 0x00, 0x01, // uint16_t font-ID + // 0x00, // uint8_t face-style-flags + // 0x12, // uint8_t font-size + // 0xFF, 0xFF, 0xFF, 0xFF, // uint8_t text-color-rgba[4] + // }; + // FontTableBox { + // 0x00, 0x00, 0x00, 0x12, // uint32_t size + // 'f', 't', 'a', 'b', // uint8_t name[4] + // 0x00, 0x01, // uint16_t entry-count + // FontRecord { + // 0x00, 0x01, // uint16_t font-ID + // 0x05, // uint8_t font-name-length + // 'S', 'e', 'r', 'i', 'f',// uint8_t font[font-name-length] + // }; + // }; + + // Populate sample description from ASS header + ass = (ASS*)s->ass_ctx; + // Compute font scaling factor based on (optionally) provided + // output video height and ASS script play_res_y + if (s->frame_height && ass->script_info.play_res_y) + s->font_scale_factor = (double)s->frame_height / ass->script_info.play_res_y; + else + s->font_scale_factor = 1; + + style = ff_ass_style_get(s->ass_ctx, "Default"); + if (!style && ass->styles_count) { + style = &ass->styles[0]; + } + s->d.style_fontID = DEFAULT_STYLE_FONT_ID; + s->d.style_fontsize = DEFAULT_STYLE_FONTSIZE; + s->d.style_color = DEFAULT_STYLE_COLOR; + s->d.style_flag = DEFAULT_STYLE_FLAG; + if (style) { + s->d.style_fontsize = FONTSIZE_SCALE(s, style->font_size); + s->d.style_color = BGR_TO_RGB(style->primary_color & 0xffffff) << 8 | + 255 - ((uint32_t)style->primary_color >> 24); + s->d.style_flag = (!!style->bold * STYLE_FLAG_BOLD) | + (!!style->italic * STYLE_FLAG_ITALIC) | + (!!style->underline * STYLE_FLAG_UNDERLINE); + back_color = (BGR_TO_RGB(style->back_color & 0xffffff) << 8) | + (255 - ((uint32_t)style->back_color >> 24)); + } - MovTextContext *s = avctx->priv_data; - s->avctx = avctx; + av_bprint_append_any(&s->buffer, display_and_justification, + sizeof(display_and_justification)); + back_color = AV_RB32(&back_color); + av_bprint_append_any(&s->buffer, &back_color, 4); + // BoxRecord { + av_bprint_append_any(&s->buffer, box_record, sizeof(box_record)); + // }; + // StyleRecord { + style_start = AV_RB16(&s->d.style_start); + style_end = AV_RB16(&s->d.style_end); + fontID = AV_RB16(&s->d.style_fontID); + style_color = AV_RB32(&s->d.style_color); + av_bprint_append_any(&s->buffer, &style_start, 2); + av_bprint_append_any(&s->buffer, &style_end, 2); + av_bprint_append_any(&s->buffer, &fontID, 2); + av_bprint_append_any(&s->buffer, &s->d.style_flag, 1); + av_bprint_append_any(&s->buffer, &s->d.style_fontsize, 1); + av_bprint_append_any(&s->buffer, &style_color, 4); + // }; + + // Build font table + // We can't build a complete font table since that would require + // scanning all dialogs first. But we can at least fill in what + // is avaiable in the ASS header + if (style && ass->styles_count) { + // Find unique font names + av_dynarray_add(&s->fonts, &s->font_count, style->font_name); + font_names_total_len += strlen(style->font_name); + for (i = 0; i < ass->styles_count; i++) { + int found = 0; + for (j = 0; j < s->font_count; j++) { + if (!strcmp(s->fonts[j], ass->styles[i].font_name)) { + found = 1; + break; + } + } + if (!found) { + av_dynarray_add(&s->fonts, &s->font_count, + ass->styles[i].font_name); + font_names_total_len += strlen(ass->styles[i].font_name); + } + } + } else + av_dynarray_add(&s->fonts, &s->font_count, (char*)"Serif"); + + // FontTableBox { + tsmb_size = SIZE_ADD + 3 * s->font_count + font_names_total_len; + tsmb_size = AV_RB32(&tsmb_size); + tsmb_type = MKTAG('f','t','a','b'); + count = AV_RB16(&s->font_count); + av_bprint_append_any(&s->buffer, &tsmb_size, 4); + av_bprint_append_any(&s->buffer, &tsmb_type, 4); + av_bprint_append_any(&s->buffer, &count, 2); + // FontRecord { + for (i = 0; i < s->font_count; i++) { + int len; + fontID = i + 1; + fontID = AV_RB16(&fontID); + av_bprint_append_any(&s->buffer, &fontID, 2); + len = strlen(s->fonts[i]); + av_bprint_append_any(&s->buffer, &len, 1); + av_bprint_append_any(&s->buffer, s->fonts[i], len); + } + // }; + // }; - avctx->extradata_size = sizeof text_sample_entry; - avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); - if (!avctx->extradata) + if (!av_bprint_is_complete(&s->buffer)) { return AVERROR(ENOMEM); + } - av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); + avctx->extradata_size = s->buffer.len; + avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!avctx->extradata) { + return AVERROR(ENOMEM); + } - memcpy(avctx->extradata, text_sample_entry, avctx->extradata_size); + memcpy(avctx->extradata, s->buffer.str, avctx->extradata_size); + av_bprint_clear(&s->buffer); - s->ass_ctx = ff_ass_split(avctx->subtitle_header); - return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; + return 0; } -static void mov_text_style_cb(void *priv, const char style, int close) +static av_cold int mov_text_encode_init(AVCodecContext *avctx) { - MovTextContext *s = priv; - if (!close) { - if (!(s->box_flags & STYL_BOX)) { //first style entry + int ret; + MovTextContext *s = avctx->priv_data; + s->avctx = avctx; - s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp)); + av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); - if (!s->style_attributes_temp) { - av_bprint_clear(&s->buffer); - s->box_flags &= ~STYL_BOX; - return; - } + s->style_attributes_temp = av_mallocz(sizeof(*s->style_attributes_temp)); + if (!s->style_attributes_temp) { + ret = AVERROR(ENOMEM); + goto fail; + } - s->style_attributes_temp->style_flag = 0; - s->style_attributes_temp->style_start = AV_RB16(&s->text_pos); - } else { - if (s->style_attributes_temp->style_flag) { //break the style record here and start a new one - s->style_attributes_temp->style_end = AV_RB16(&s->text_pos); - av_dynarray_add(&s->style_attributes, &s->count, s->style_attributes_temp); - s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp)); - if (!s->style_attributes_temp) { - mov_text_cleanup(s); - av_bprint_clear(&s->buffer); - s->box_flags &= ~STYL_BOX; - return; - } + s->ass_ctx = ff_ass_split(avctx->subtitle_header); + if (!s->ass_ctx) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + ret = encode_sample_description(avctx); + if (ret < 0) + goto fail; - s->style_attributes_temp->style_flag = s->style_attributes[s->count - 1]->style_flag; - s->style_attributes_temp->style_start = AV_RB16(&s->text_pos); - } else { - s->style_attributes_temp->style_flag = 0; - s->style_attributes_temp->style_start = AV_RB16(&s->text_pos); - } - } - switch (style){ - case 'b': - s->style_attributes_temp->style_flag |= STYLE_FLAG_BOLD; - break; - case 'i': - s->style_attributes_temp->style_flag |= STYLE_FLAG_ITALIC; - break; - case 'u': - s->style_attributes_temp->style_flag |= STYLE_FLAG_UNDERLINE; - break; - } - } else if (!s->style_attributes_temp) { - av_log(s->avctx, AV_LOG_WARNING, "Ignoring unmatched close tag\n"); - return; - } else { - s->style_attributes_temp->style_end = AV_RB16(&s->text_pos); - av_dynarray_add(&s->style_attributes, &s->count, s->style_attributes_temp); + return 0; - s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp)); +fail: + mov_text_encode_close(avctx); + return ret; +} +// Start a new style box if needed +static int mov_text_style_start(MovTextContext *s) +{ + // there's an existing style entry + if (s->style_attributes_temp->style_start == s->text_pos) + // Still at same text pos, use same entry + return 1; + if (s->style_attributes_temp->style_flag != s->d.style_flag || + s->style_attributes_temp->style_color != s->d.style_color || + s->style_attributes_temp->style_fontID != s->d.style_fontID || + s->style_attributes_temp->style_fontsize != s->d.style_fontsize) { + // last style != defaults, end the style entry and start a new one + s->box_flags |= STYL_BOX; + s->style_attributes_temp->style_end = s->text_pos; + av_dynarray_add(&s->style_attributes, &s->count, s->style_attributes_temp); + s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp)); if (!s->style_attributes_temp) { mov_text_cleanup(s); av_bprint_clear(&s->buffer); s->box_flags &= ~STYL_BOX; - return; + return 0; } - s->style_attributes_temp->style_flag = s->style_attributes[s->count - 1]->style_flag; - switch (style){ - case 'b': - s->style_attributes_temp->style_flag &= ~STYLE_FLAG_BOLD; - break; - case 'i': - s->style_attributes_temp->style_flag &= ~STYLE_FLAG_ITALIC; - break; - case 'u': - s->style_attributes_temp->style_flag &= ~STYLE_FLAG_UNDERLINE; - break; - } - if (s->style_attributes_temp->style_flag) { //start of new style record - s->style_attributes_temp->style_start = AV_RB16(&s->text_pos); - } + *s->style_attributes_temp = s->d; + s->style_attributes_temp->style_start = s->text_pos; + } else { // style entry matches defaults, drop entry + *s->style_attributes_temp = s->d; + s->style_attributes_temp->style_start = s->text_pos; + } + return 1; +} + +static uint8_t mov_text_style_to_flag(const char style) +{ + uint8_t style_flag = 0; + + switch (style){ + case 'b': + style_flag = STYLE_FLAG_BOLD; + break; + case 'i': + style_flag = STYLE_FLAG_ITALIC; + break; + case 'u': + style_flag = STYLE_FLAG_UNDERLINE; + break; } - s->box_flags |= STYL_BOX; + return style_flag; +} + +static void mov_text_style_set(MovTextContext *s, uint8_t style_flags) +{ + if (!s->style_attributes_temp || + !((s->style_attributes_temp->style_flag & style_flags) ^ style_flags)) { + // setting flags that that are already set + return; + } + if (mov_text_style_start(s)) + s->style_attributes_temp->style_flag |= style_flags; +} + +static void mov_text_style_cb(void *priv, const char style, int close) +{ + MovTextContext *s = priv; + uint8_t style_flag = mov_text_style_to_flag(style); + + if (!s->style_attributes_temp || + !!(s->style_attributes_temp->style_flag & style_flag) != close) { + // setting flag that is already set + return; + } + if (mov_text_style_start(s)) { + if (!close) + s->style_attributes_temp->style_flag |= style_flag; + else + s->style_attributes_temp->style_flag &= ~style_flag; + } +} + +static void mov_text_color_set(MovTextContext *s, uint32_t color) +{ + if (!s->style_attributes_temp || + (s->style_attributes_temp->style_color & 0xffffff00) == color) { + // color hasn't changed + return; + } + if (mov_text_style_start(s)) + s->style_attributes_temp->style_color = (color & 0xffffff00) | + (s->style_attributes_temp->style_color & 0xff); } static void mov_text_color_cb(void *priv, unsigned int color, unsigned int color_id) { MovTextContext *s = priv; - if (color_id == 2) { //secondary color changes - if (s->box_flags & HLIT_BOX) { //close tag - s->hlit.end = AV_RB16(&s->text_pos); - } else { + + color = BGR_TO_RGB(color) << 8; + if (color_id == 1) { //primary color changes + mov_text_color_set(s, color); + } else if (color_id == 2) { //secondary color changes + if (!(s->box_flags & HCLR_BOX)) + // Highlight alpha not set yet, use current primary alpha + s->hclr.color = s->style_attributes_temp->style_color; + if (!(s->box_flags & HLIT_BOX) || s->hlit.start == s->text_pos) { s->box_flags |= HCLR_BOX; s->box_flags |= HLIT_BOX; - s->hlit.start = AV_RB16(&s->text_pos); - s->hclr.color = color | (0xFF << 24); //set alpha value to FF + s->hlit.start = s->text_pos; + s->hclr.color = color | (s->hclr.color & 0xFF); } + else //close tag + s->hlit.end = s->text_pos; + /* If there are more than one secondary color changes in ASS, + take start of first section and end of last section. Movtext + allows only one highlight box per sample. + */ + } + // Movtext does not support changes to other color_id (outline, background) +} + +static void mov_text_alpha_set(MovTextContext *s, uint8_t alpha) +{ + if (!s->style_attributes_temp || + (s->style_attributes_temp->style_color & 0xff) == alpha) { + // color hasn't changed + return; + } + if (mov_text_style_start(s)) + s->style_attributes_temp->style_color = + (s->style_attributes_temp->style_color & 0xffffff00) | alpha; +} + +static void mov_text_alpha_cb(void *priv, int alpha, int alpha_id) +{ + MovTextContext *s = priv; + + alpha = 255 - alpha; + if (alpha_id == 1) // primary alpha changes + mov_text_alpha_set(s, alpha); + else if (alpha_id == 2) { //secondary alpha changes + if (!(s->box_flags & HCLR_BOX)) + // Highlight color not set yet, use current primary color + s->hclr.color = s->style_attributes_temp->style_color; + if (!(s->box_flags & HLIT_BOX) || s->hlit.start == s->text_pos) { + s->box_flags |= HCLR_BOX; + s->box_flags |= HLIT_BOX; + s->hlit.start = s->text_pos; + s->hclr.color = (s->hclr.color & 0xffffff00) | alpha; + } + else //close tag + s->hlit.end = s->text_pos; + } + // Movtext does not support changes to other alpha_id (outline, background) +} + +static uint16_t find_font_id(MovTextContext * s, const char * name) +{ + int i; + for (i = 0; i < s->font_count; i++) { + if (!strcmp(name, s->fonts[i])) + return i + 1; + } + return 1; +} + +static void mov_text_font_name_set(MovTextContext *s, const char *name) +{ + int fontID = find_font_id(s, name); + if (!s->style_attributes_temp || + s->style_attributes_temp->style_fontID == fontID) { + // color hasn't changed + return; + } + if (mov_text_style_start(s)) + s->style_attributes_temp->style_fontID = fontID; +} + +static void mov_text_font_name_cb(void *priv, const char *name) +{ + mov_text_font_name_set((MovTextContext*)priv, name); +} + +static void mov_text_font_size_set(MovTextContext *s, int size) +{ + size = FONTSIZE_SCALE(s, size); + if (!s->style_attributes_temp || + s->style_attributes_temp->style_fontsize == size) { + // color hasn't changed + return; + } + if (mov_text_style_start(s)) + s->style_attributes_temp->style_fontsize = size; +} + +static void mov_text_font_size_cb(void *priv, int size) +{ + mov_text_font_size_set((MovTextContext*)priv, size); +} + +static void mov_text_end_cb(void *priv) +{ + // End of text, close any open style record + mov_text_style_start((MovTextContext*)priv); +} + +static void mov_text_ass_style_set(MovTextContext *s, ASSStyle *style) +{ + uint8_t style_flags, alpha; + uint32_t color; + + if (style) { + style_flags = (!!style->bold * STYLE_FLAG_BOLD) | + (!!style->italic * STYLE_FLAG_ITALIC) | + (!!style->underline * STYLE_FLAG_UNDERLINE); + mov_text_style_set(s, style_flags); + color = BGR_TO_RGB(style->primary_color & 0xffffff) << 8; + mov_text_color_set(s, color); + alpha = 255 - ((uint32_t)style->primary_color >> 24); + mov_text_alpha_set(s, alpha); + mov_text_font_size_set(s, style->font_size); + mov_text_font_name_set(s, style->font_name); + } else { + // End current style record, go back to defaults + mov_text_style_start(s); } - /* If there are more than one secondary color changes in ASS, take start of - first section and end of last section. Movtext allows only one - highlight box per sample. - */ +} + +static void mov_text_dialog(MovTextContext *s, ASSDialog *dialog) +{ + ASSStyle * style = ff_ass_style_get(s->ass_ctx, dialog->style); + + s->ass_dialog_style = style; + mov_text_ass_style_set(s, style); +} + +static void mov_text_cancel_overrides_cb(void *priv, const char * style_name) +{ + MovTextContext *s = priv; + ASSStyle * style; + + if (!style_name || !*style_name) + style = s->ass_dialog_style; + else + style= ff_ass_style_get(s->ass_ctx, style_name); + + mov_text_ass_style_set(s, style); } static uint16_t utf8_strlen(const char *text, int len) @@ -344,10 +665,15 @@ static void mov_text_new_line_cb(void *priv, int forced) } static const ASSCodesCallbacks mov_text_callbacks = { - .text = mov_text_text_cb, - .new_line = mov_text_new_line_cb, - .style = mov_text_style_cb, - .color = mov_text_color_cb, + .text = mov_text_text_cb, + .new_line = mov_text_new_line_cb, + .style = mov_text_style_cb, + .color = mov_text_color_cb, + .alpha = mov_text_alpha_cb, + .font_name = mov_text_font_name_cb, + .font_size = mov_text_font_size_cb, + .cancel_overrides = mov_text_cancel_overrides_cb, + .end = mov_text_end_cb, }; static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, @@ -362,13 +688,12 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, s->text_pos = 0; s->count = 0; s->box_flags = 0; - s->style_entries = 0; for (i = 0; i < sub->num_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(ENOSYS); + return AVERROR(EINVAL); } #if FF_API_ASS_TIMING @@ -376,6 +701,7 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, int num; dialog = ff_ass_split_dialog(s->ass_ctx, ass, 0, &num); for (; dialog && num--; dialog++) { + mov_text_dialog(s, dialog); ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); } } else { @@ -383,6 +709,7 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, dialog = ff_ass_split_dialog2(s->ass_ctx, ass); if (!dialog) return AVERROR(ENOMEM); + mov_text_dialog(s, dialog); ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); ff_ass_free_dialog(&dialog); #if FF_API_ASS_TIMING @@ -409,7 +736,7 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, if (s->buffer.len > bufsize - 3) { av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); - length = AVERROR(EINVAL); + length = AVERROR_BUFFER_TOO_SMALL; goto exit; } @@ -421,13 +748,19 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, return length; } -static int mov_text_encode_close(AVCodecContext *avctx) -{ - MovTextContext *s = avctx->priv_data; - ff_ass_split_free(s->ass_ctx); - av_bprint_finalize(&s->buffer, NULL); - return 0; -} +#define OFFSET(x) offsetof(MovTextContext, x) +#define FLAGS AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_SUBTITLE_PARAM +static const AVOption options[] = { + { "height", "Frame height, usually video height", OFFSET(frame_height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, + { NULL }, +}; + +static const AVClass mov_text_encoder_class = { + .class_name = "MOV text enoder", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; AVCodec ff_movtext_encoder = { .name = "mov_text", @@ -435,6 +768,7 @@ AVCodec ff_movtext_encoder = { .type = AVMEDIA_TYPE_SUBTITLE, .id = AV_CODEC_ID_MOV_TEXT, .priv_data_size = sizeof(MovTextContext), + .priv_class = &mov_text_encoder_class, .init = mov_text_encode_init, .encode_sub = mov_text_encode_frame, .close = mov_text_encode_close, diff --git a/libavcodec/mp3_header_decompress_bsf.c b/libavcodec/mp3_header_decompress_bsf.c index 294858953cb..44c174c21c7 100644 --- a/libavcodec/mp3_header_decompress_bsf.c +++ b/libavcodec/mp3_header_decompress_bsf.c @@ -20,8 +20,8 @@ #include "libavutil/common.h" #include "libavutil/intreadwrite.h" -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "mpegaudiodecheader.h" #include "mpegaudiodata.h" @@ -62,6 +62,11 @@ static int mp3_header_decompress(AVBSFContext *ctx, AVPacket *out) 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= avpriv_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++){ diff --git a/libavcodec/mpc8huff.h b/libavcodec/mpc8huff.h index 8491037aa47..0566c910ca6 100644 --- a/libavcodec/mpc8huff.h +++ b/libavcodec/mpc8huff.h @@ -34,7 +34,7 @@ static const uint8_t mpc8_bands_codes[MPC8_BANDS_SIZE] = { 0x08, 0x09, 0x06, 0x07, 0x05, 0x05, 0x03, 0x03, 0x01, }; -static const int8_t mpc8_bands_bits[MPC8_BANDS_SIZE] = { +static const uint8_t mpc8_bands_bits[MPC8_BANDS_SIZE] = { 1, 3, 5, 6, 7, 8, 8, 9, 10, 11, 12, 12, 12, 13, 12, 12, 12, 12, 12, 13, 12, 12, 12, 11, @@ -48,7 +48,7 @@ static const int8_t mpc8_bands_bits[MPC8_BANDS_SIZE] = { static const uint8_t mpc8_scfi0_codes[MPC8_SCFI0_SIZE] = { 0x00, 0x01, 0x01, 0x01, }; -static const int8_t mpc8_scfi0_bits[MPC8_SCFI0_SIZE] = { +static const uint8_t mpc8_scfi0_bits[MPC8_SCFI0_SIZE] = { 3, 3, 1, 2, }; @@ -60,7 +60,7 @@ static const uint8_t mpc8_scfi1_codes[MPC8_SCFI1_SIZE] = { 0x04, 0x06, 0x02, 0x02, 0x05, 0x07, 0x03, 0x03, }; -static const int8_t mpc8_scfi1_bits[MPC8_SCFI1_SIZE] = { +static const uint8_t mpc8_scfi1_bits[MPC8_SCFI1_SIZE] = { 6, 7, 6, 6, 7, 5, 5, 5, 6, 5, 2, 3, 6, 5, 3, 2, @@ -79,7 +79,7 @@ static const uint8_t mpc8_dscf0_codes[MPC8_DSCF0_SIZE] = { 0x0C, 0x0D, 0x07, 0x08, 0x09, 0x06, 0x07, 0x03, 0x04, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, }; -static const int8_t mpc8_dscf0_bits[MPC8_DSCF0_SIZE] = { +static const uint8_t mpc8_dscf0_bits[MPC8_DSCF0_SIZE] = { 12, 12, 12, 11, 11, 11, 10, 10, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 6, @@ -105,7 +105,7 @@ static const uint8_t mpc8_dscf1_codes[MPC8_DSCF1_SIZE] = { 0x05, 0x06, 0x07, 0x01, 0x02, 0x03, 0x04, 0x05, 0x0D, }; -static const int8_t mpc8_dscf1_bits[MPC8_DSCF1_SIZE] = { +static const uint8_t mpc8_dscf1_bits[MPC8_DSCF1_SIZE] = { 15, 14, 14, 13, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, 8, 8, 7, @@ -132,7 +132,7 @@ static const uint8_t mpc8_res_codes[2][MPC8_RES_SIZE] = { 0x03, } }; -static const int8_t mpc8_res_bits[2][MPC8_RES_SIZE] = { +static const uint8_t mpc8_res_bits[2][MPC8_RES_SIZE] = { { 1, 2, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 16, 8, @@ -153,7 +153,7 @@ static const uint8_t mpc8_q1_codes[MPC8_Q1_SIZE] = { 0x03, 0x04, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, }; -static const int8_t mpc8_q1_bits[MPC8_Q1_SIZE] = { +static const uint8_t mpc8_q1_bits[MPC8_Q1_SIZE] = { 6, 4, 4, 3, 3, 3, 3, 3, 4, 4, 4, 5, 7, 8, 9, 10, 11, 12, 12, @@ -196,7 +196,7 @@ static const uint8_t mpc8_q9up_codes[MPC8_Q9UP_SIZE] = { 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x06, 0x07, 0x08, 0x09, 0x00, 0x01, }; -static const int8_t mpc8_q9up_bits[MPC8_Q9UP_SIZE] = { +static const uint8_t mpc8_q9up_bits[MPC8_Q9UP_SIZE] = { 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, @@ -272,7 +272,7 @@ static const uint8_t mpc8_q2_codes[2][MPC8_Q2_SIZE] = { 0x03, 0x1C, 0x17, 0x1D, 0x05, } }; -static const int8_t mpc8_q2_bits[2][MPC8_Q2_SIZE] = { +static const uint8_t mpc8_q2_bits[2][MPC8_Q2_SIZE] = { { 12, 11, 10, 11, 13, 11, 9, 8, 9, 11, 11, 8, 7, 8, 11, 11, @@ -324,7 +324,7 @@ static const uint8_t mpc8_q3_codes[MPC8_Q3_SIZE] = { 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x01, 0x00, }; -static const int8_t mpc8_q3_bits[MPC8_Q3_SIZE] = { +static const uint8_t mpc8_q3_bits[MPC8_Q3_SIZE] = { 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, @@ -333,7 +333,7 @@ static const int8_t mpc8_q3_bits[MPC8_Q3_SIZE] = { 8, 8, 8, 8, 8, 9, 9, 9, 9, }; -static const int8_t mpc8_q3_syms[MPC8_Q3_SIZE] = { +static const uint8_t mpc8_q3_syms[MPC8_Q3_SIZE] = { 48, 65, 64, 49, 63, 32, 47, 80, 79, 50, 62, 33, 16, 82, 81, 95, 94, 66, 78, 34, 46, 17, 31, 30, @@ -360,7 +360,7 @@ static const uint8_t mpc8_q4_codes[MPC8_Q4_SIZE] = { 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x01, 0x00, }; -static const int8_t mpc8_q4_bits[MPC8_Q4_SIZE] = { +static const uint8_t mpc8_q4_bits[MPC8_Q4_SIZE] = { 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, @@ -373,7 +373,7 @@ static const int8_t mpc8_q4_bits[MPC8_Q4_SIZE] = { 9, 9, 9, 9, 9, 10, 10, 10, 10, }; -static const int8_t mpc8_q4_syms[MPC8_Q4_SIZE] = { +static const uint8_t mpc8_q4_syms[MPC8_Q4_SIZE] = { 64, 96, 81, 80, 95, 66, 65, 79, 78, 49, 48, 63, 32, 113, 112, 98, 97, 111, 110, 83, 82, 94, 93, 67, @@ -401,7 +401,7 @@ static const uint8_t mpc8_q5_codes[2][MPC8_Q5_SIZE] = { 0x06, 0x07, 0x04, 0x05, 0x03, 0x02, 0x03, } }; -static const int8_t mpc8_q5_bits[2][MPC8_Q5_SIZE] = { +static const uint8_t mpc8_q5_bits[2][MPC8_Q5_SIZE] = { { 7, 7, 6, 5, 4, 3, 3, 2, 3, 3, 4, 5, 6, 7, 7, @@ -430,7 +430,7 @@ static const uint8_t mpc8_q6_codes[2][MPC8_Q6_SIZE] = { 0x06, 0x07, 0x05, 0x06, 0x07, 0x02, 0x03, } }; -static const int8_t mpc8_q6_bits[2][MPC8_Q6_SIZE] = { +static const uint8_t mpc8_q6_bits[2][MPC8_Q6_SIZE] = { { 9, 9, 9, 9, 8, 8, 7, 6, 6, 6, 5, 5, 4, 4, 3, 2, @@ -471,7 +471,7 @@ static const uint8_t mpc8_q7_codes[2][MPC8_Q7_SIZE] = { 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x02, 0x03, } }; -static const int8_t mpc8_q7_bits[2][MPC8_Q7_SIZE] = { +static const uint8_t mpc8_q7_bits[2][MPC8_Q7_SIZE] = { { 10, 10, 10, 9, 9, 10, 10, 10, 10, 10, 9, 9, 9, 9, 8, 8, @@ -536,7 +536,7 @@ static const uint8_t mpc8_q8_codes[2][MPC8_Q8_SIZE] = { 0x27, 0x28, 0x29, 0x04, 0x05, 0x06, 0x07, } }; -static const int8_t mpc8_q8_bits[2][MPC8_Q8_SIZE] = { +static const uint8_t mpc8_q8_bits[2][MPC8_Q8_SIZE] = { { 11, 11, 10, 10, 10, 10, 10, 9, 10, 9, 10, 12, 12, 11, 11, 11, diff --git a/libavcodec/mpeg12dec.c b/libavcodec/mpeg12dec.c index 83e537884b6..99e56532a59 100644 --- a/libavcodec/mpeg12dec.c +++ b/libavcodec/mpeg12dec.c @@ -36,7 +36,7 @@ #include "avcodec.h" #include "bytestream.h" #include "error_resilience.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "idctdsp.h" #include "internal.h" #include "mpeg_er.h" @@ -64,6 +64,7 @@ typedef struct Mpeg1Context { int slice_count; AVRational save_aspect; int save_width, save_height, save_progressive_seq; + int rc_buffer_size; AVRational frame_rate_ext; /* MPEG-2 specific framerate modificator */ int sync; /* Did we reach a sync point like a GOP/SEQ/KEYFrame? */ int tmpgexs; @@ -220,7 +221,6 @@ static inline int mpeg1_decode_block_inter(MpegEncContext *s, } /** - * Note: this function can read out of range and crash for corrupt streams. * Changing this would eat up any speed benefits it has. * Do not use "fast" flag if you need the code to be robust. */ @@ -396,7 +396,6 @@ static inline int mpeg2_decode_block_non_intra(MpegEncContext *s, } /** - * Note: this function can read out of range and crash for corrupt streams. * Changing this would eat up any speed benefits it has. * Do not use "fast" flag if you need the code to be robust. */ @@ -558,7 +557,6 @@ static inline int mpeg2_decode_block_intra(MpegEncContext *s, } /** - * Note: this function can read out of range and crash for corrupt streams. * Changing this would eat up any speed benefits it has. * Do not use "fast" flag if you need the code to be robust. */ @@ -586,7 +584,7 @@ static inline int mpeg2_fast_decode_block_intra(MpegEncContext *s, dc = s->last_dc[component]; dc += diff; s->last_dc[component] = dc; - block[0] = dc << (3 - s->intra_dc_precision); + block[0] = dc * (1 << (3 - s->intra_dc_precision)); i = 0; if (s->intra_vlc_format) rl = &ff_rl_mpeg2; @@ -1398,6 +1396,7 @@ 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); @@ -1417,7 +1416,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"); - s->avctx->rc_buffer_size += get_bits(&s->gb, 8) * 1024 * 16 << 10; + s1->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) @@ -1429,11 +1428,17 @@ 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, - s->avctx->rc_buffer_size, s->bit_rate); + s1->rc_buffer_size, s->bit_rate); } static void mpeg_decode_sequence_display_extension(Mpeg1Context *s1) @@ -1596,6 +1601,11 @@ static int mpeg_field_start(MpegEncContext *s, const uint8_t *buf, int buf_size) Mpeg1Context *s1 = (Mpeg1Context *) s; int ret; + if (!(avctx->flags2 & AV_CODEC_FLAG2_CHUNKS)) { + if (s->mb_width * s->mb_height * 11LL / (33 * 2 * 8) > buf_size) + return AVERROR_INVALIDDATA; + } + /* start frame decoding */ if (s->first_field || s->picture_structure == PICT_FRAME) { AVFrameSideData *pan_scan; @@ -1664,8 +1674,7 @@ static int mpeg_field_start(MpegEncContext *s, const uint8_t *buf, int buf_size) return AVERROR_INVALIDDATA; } - if (s->avctx->hwaccel && - (s->avctx->slice_flags & SLICE_FLAG_ALLOW_FIELD)) { + if (s->avctx->hwaccel) { if ((ret = s->avctx->hwaccel->end_frame(s->avctx)) < 0) { av_log(avctx, AV_LOG_ERROR, "hardware accelerator failed to decode first field\n"); @@ -2011,13 +2020,15 @@ static int slice_decode_thread(AVCodecContext *c, void *arg) start_code = -1; buf = avpriv_find_start_code(buf, s->gb.buffer_end, &start_code); + if (start_code < SLICE_MIN_START_CODE || start_code > SLICE_MAX_START_CODE) + return AVERROR_INVALIDDATA; mb_y = start_code - SLICE_MIN_START_CODE; if (s->codec_id != AV_CODEC_ID_MPEG1VIDEO && s->mb_height > 2800/16) mb_y += (*buf&0xE0)<<2; mb_y <<= field_pic; if (s->picture_structure == PICT_BOTTOM_FIELD) mb_y++; - if (mb_y < 0 || mb_y >= s->end_mb_y) + if (mb_y >= s->end_mb_y) return AVERROR_INVALIDDATA; } } @@ -2112,7 +2123,7 @@ static int mpeg1_decode_sequence(AVCodecContext *avctx, return AVERROR_INVALIDDATA; } - s->avctx->rc_buffer_size = get_bits(&s->gb, 10) * 1024 * 16; + s1->rc_buffer_size = get_bits(&s->gb, 10) * 1024 * 16; skip_bits(&s->gb, 1); /* get matrix */ @@ -2161,7 +2172,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", - s->avctx->rc_buffer_size, s->bit_rate, s->aspect_ratio_info); + s1->rc_buffer_size, s->bit_rate, s->aspect_ratio_info); return 0; } @@ -2483,7 +2494,7 @@ static int decode_chunks(AVCodecContext *avctx, AVFrame *picture, return ret; else if (ret) { // FIXME: merge with the stuff in mpeg_decode_slice - if (s2->last_picture_ptr || s2->low_delay) + if (s2->last_picture_ptr || s2->low_delay || s2->pict_type == AV_PICTURE_TYPE_B) *got_output = 1; } } diff --git a/libavcodec/mpeg12enc.c b/libavcodec/mpeg12enc.c index 2bc5289d638..9fbbcef6078 100644 --- a/libavcodec/mpeg12enc.c +++ b/libavcodec/mpeg12enc.c @@ -41,6 +41,7 @@ #include "mpeg12data.h" #include "mpegutils.h" #include "mpegvideo.h" +#include "profiles.h" static const uint8_t svcd_scan_offset_placeholder[] = { 0x10, 0x0E, 0x00, 0x80, 0x81, 0x00, 0x80, @@ -139,16 +140,17 @@ static int find_frame_rate_index(MpegEncContext *s) static av_cold int encode_init(AVCodecContext *avctx) { + int ret; MpegEncContext *s = avctx->priv_data; - if (ff_mpv_encode_init(avctx) < 0) - return -1; + if ((ret = ff_mpv_encode_init(avctx)) < 0) + return ret; if (find_frame_rate_index(s) < 0) { if (s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { av_log(avctx, AV_LOG_ERROR, "MPEG-1/2 does not support %d/%d fps\n", avctx->time_base.den, avctx->time_base.num); - return -1; + return AVERROR(EINVAL); } else { av_log(avctx, AV_LOG_INFO, "MPEG-1/2 does not support %d/%d fps, there may be AV sync issues\n", @@ -159,23 +161,23 @@ static av_cold int encode_init(AVCodecContext *avctx) if (avctx->profile == FF_PROFILE_UNKNOWN) { if (avctx->level != FF_LEVEL_UNKNOWN) { av_log(avctx, AV_LOG_ERROR, "Set profile and level\n"); - return -1; + return AVERROR(EINVAL); } /* Main or 4:2:2 */ - avctx->profile = s->chroma_format == CHROMA_420 ? 4 : 0; + avctx->profile = s->chroma_format == CHROMA_420 ? FF_PROFILE_MPEG2_MAIN : FF_PROFILE_MPEG2_422; } if (avctx->level == FF_LEVEL_UNKNOWN) { - if (avctx->profile == 0) { /* 4:2:2 */ + if (avctx->profile == FF_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 != 1 && s->chroma_format != CHROMA_420) { + if (avctx->profile != FF_PROFILE_MPEG2_HIGH && s->chroma_format != CHROMA_420) { av_log(avctx, AV_LOG_ERROR, "Only High(1) and 4:2:2(0) profiles support 4:2:2 color sampling\n"); - return -1; + return AVERROR(EINVAL); } if (avctx->width <= 720 && avctx->height <= 576) avctx->level = 8; /* Main */ @@ -205,7 +207,7 @@ static av_cold int encode_init(AVCodecContext *avctx) if (s->drop_frame_timecode && s->frame_rate_index != 4) { av_log(avctx, AV_LOG_ERROR, "Drop frame time code only allowed with 1001/30000 fps\n"); - return -1; + return AVERROR(EINVAL); } #if FF_API_PRIVATE_OPT @@ -321,7 +323,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 == 0); // escx 1 for 4:2:2 profile + put_bits(&s->pb, 1, s->avctx->profile == FF_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 @@ -1134,7 +1136,7 @@ av_cold void ff_mpeg1_encode_init(MpegEncContext *s) #define VE AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM #define COMMON_OPTS \ { "gop_timecode", "MPEG GOP Timecode in hh:mm:ss[:;.]ff format. Overrides timecode_frame_start.", \ - OFFSET(tc_opt_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, VE },\ + OFFSET(tc_opt_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, VE },\ { "intra_vlc", "Use MPEG-2 intra VLC table.", \ OFFSET(intra_vlc_format), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, \ { "drop_frame_timecode", "Timecode is in drop frame format.", \ @@ -1166,6 +1168,7 @@ static const AVOption mpeg2_options[] = { { "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" }, FF_MPV_COMMON_OPTS + FF_MPEG2_PROFILE_OPTS { NULL }, }; @@ -1193,6 +1196,7 @@ AVCodec ff_mpeg1video_encoder = { .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .priv_class = &mpeg1_class, }; @@ -1210,5 +1214,6 @@ AVCodec ff_mpeg2video_encoder = { AV_PIX_FMT_YUV422P, AV_PIX_FMT_NONE }, .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .priv_class = &mpeg2_class, }; diff --git a/libavcodec/mpeg2_metadata_bsf.c b/libavcodec/mpeg2_metadata_bsf.c index 6779ffd4c42..b1e2d6128a8 100644 --- a/libavcodec/mpeg2_metadata_bsf.c +++ b/libavcodec/mpeg2_metadata_bsf.c @@ -21,6 +21,7 @@ #include "libavutil/opt.h" #include "bsf.h" +#include "bsf_internal.h" #include "cbs.h" #include "cbs_mpeg2.h" #include "mpeg12.h" @@ -213,6 +214,18 @@ static int mpeg2_metadata_init(AVBSFContext *bsf) CodedBitstreamFragment *frag = &ctx->fragment; int err; +#define VALIDITY_CHECK(name) do { \ + if (!ctx->name) { \ + av_log(bsf, AV_LOG_ERROR, "The value 0 for %s is " \ + "forbidden.\n", #name); \ + return AVERROR(EINVAL); \ + } \ + } while (0) + VALIDITY_CHECK(colour_primaries); + VALIDITY_CHECK(transfer_characteristics); + VALIDITY_CHECK(matrix_coefficients); +#undef VALIDITY_CHECK + err = ff_cbs_init(&ctx->cbc, AV_CODEC_ID_MPEG2VIDEO, bsf); if (err < 0) return err; diff --git a/libavcodec/mpeg4_unpack_bframes_bsf.c b/libavcodec/mpeg4_unpack_bframes_bsf.c index 1daf133ce5e..6f8595713d7 100644 --- a/libavcodec/mpeg4_unpack_bframes_bsf.c +++ b/libavcodec/mpeg4_unpack_bframes_bsf.c @@ -19,13 +19,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "internal.h" #include "mpeg4video.h" typedef struct UnpackBFramesBSFContext { - AVPacket *b_frame; + AVBufferRef *b_frame_ref; } UnpackBFramesBSFContext; /* determine the position of the packed marker in the userdata, @@ -56,32 +56,32 @@ static void scan_buffer(const uint8_t *buf, int buf_size, } } -static int mpeg4_unpack_bframes_filter(AVBSFContext *ctx, AVPacket *out) +static int mpeg4_unpack_bframes_filter(AVBSFContext *ctx, AVPacket *pkt) { UnpackBFramesBSFContext *s = ctx->priv_data; int pos_p = -1, nb_vop = 0, pos_vop2 = -1, ret = 0; - AVPacket *in; - ret = ff_bsf_get_packet(ctx, &in); + ret = ff_bsf_get_packet_ref(ctx, pkt); if (ret < 0) return ret; - scan_buffer(in->data, in->size, &pos_p, &nb_vop, &pos_vop2); + scan_buffer(pkt->data, pkt->size, &pos_p, &nb_vop, &pos_vop2); av_log(ctx, AV_LOG_DEBUG, "Found %d VOP startcode(s) in this packet.\n", nb_vop); if (pos_vop2 >= 0) { - if (s->b_frame->data) { + if (s->b_frame_ref) { av_log(ctx, AV_LOG_WARNING, "Missing one N-VOP packet, discarding one B-frame.\n"); - av_packet_unref(s->b_frame); + av_buffer_unref(&s->b_frame_ref); } - /* store the packed B-frame in the BSFContext */ - ret = av_packet_ref(s->b_frame, in); - if (ret < 0) { + /* store a reference to the packed B-frame's data in the BSFContext */ + s->b_frame_ref = av_buffer_ref(pkt->buf); + if (!s->b_frame_ref) { + ret = AVERROR(ENOMEM); goto fail; } - s->b_frame->size -= pos_vop2; - s->b_frame->data += pos_vop2; + s->b_frame_ref->data = pkt->data + pos_vop2; + s->b_frame_ref->size = pkt->size - pos_vop2; } if (nb_vop > 2) { @@ -89,56 +89,49 @@ static int mpeg4_unpack_bframes_filter(AVBSFContext *ctx, AVPacket *out) "Found %d VOP headers in one packet, only unpacking one.\n", nb_vop); } - if (nb_vop == 1 && s->b_frame->data) { - /* use frame from BSFContext */ - av_packet_move_ref(out, s->b_frame); + if (nb_vop == 1 && s->b_frame_ref) { + AVBufferRef *tmp = pkt->buf; - /* use properties from current input packet */ - ret = av_packet_copy_props(out, in); - if (ret < 0) { - goto fail; - } + /* make tmp accurately reflect the packet's data */ + tmp->data = pkt->data; + tmp->size = pkt->size; + + /* replace data in packet with stored data */ + pkt->buf = s->b_frame_ref; + pkt->data = s->b_frame_ref->data; + pkt->size = s->b_frame_ref->size; - if (in->size <= MAX_NVOP_SIZE) { - /* N-VOP */ + /* store reference to data into BSFContext */ + s->b_frame_ref = tmp; + + if (s->b_frame_ref->size <= MAX_NVOP_SIZE) { + /* N-VOP - discard stored data */ av_log(ctx, AV_LOG_DEBUG, "Skipping N-VOP.\n"); - } else { - /* copy packet into BSFContext */ - av_packet_move_ref(s->b_frame, in); + av_buffer_unref(&s->b_frame_ref); } } else if (nb_vop >= 2) { /* use first frame of the packet */ - av_packet_move_ref(out, in); - out->size = pos_vop2; + pkt->size = pos_vop2; } else if (pos_p >= 0) { - ret = av_packet_make_writable(in); + ret = av_packet_make_writable(pkt); if (ret < 0) goto fail; av_log(ctx, AV_LOG_DEBUG, "Updating DivX userdata (remove trailing 'p').\n"); - av_packet_move_ref(out, in); /* remove 'p' (packed) from the end of the (DivX) userdata string */ - out->data[pos_p] = '\0'; + pkt->data[pos_p] = '\0'; } else { - /* copy packet */ - av_packet_move_ref(out, in); + /* use packet as is */ } fail: if (ret < 0) - av_packet_unref(out); - av_packet_free(&in); + av_packet_unref(pkt); return ret; } static int mpeg4_unpack_bframes_init(AVBSFContext *ctx) { - UnpackBFramesBSFContext *s = ctx->priv_data; - - s->b_frame = av_packet_alloc(); - if (!s->b_frame) - return AVERROR(ENOMEM); - if (ctx->par_in->extradata) { int pos_p_ext = -1; scan_buffer(ctx->par_in->extradata, ctx->par_in->extradata_size, &pos_p_ext, NULL, NULL); @@ -152,16 +145,10 @@ static int mpeg4_unpack_bframes_init(AVBSFContext *ctx) return 0; } -static void mpeg4_unpack_bframes_flush(AVBSFContext *bsfc) -{ - UnpackBFramesBSFContext *ctx = bsfc->priv_data; - av_packet_unref(ctx->b_frame); -} - -static void mpeg4_unpack_bframes_close(AVBSFContext *bsfc) +static void mpeg4_unpack_bframes_close_flush(AVBSFContext *bsfc) { UnpackBFramesBSFContext *ctx = bsfc->priv_data; - av_packet_free(&ctx->b_frame); + av_buffer_unref(&ctx->b_frame_ref); } static const enum AVCodecID codec_ids[] = { @@ -173,7 +160,7 @@ const AVBitStreamFilter ff_mpeg4_unpack_bframes_bsf = { .priv_data_size = sizeof(UnpackBFramesBSFContext), .init = mpeg4_unpack_bframes_init, .filter = mpeg4_unpack_bframes_filter, - .flush = mpeg4_unpack_bframes_flush, - .close = mpeg4_unpack_bframes_close, + .flush = mpeg4_unpack_bframes_close_flush, + .close = mpeg4_unpack_bframes_close_flush, .codec_ids = codec_ids, }; diff --git a/libavcodec/mpeg4audio.c b/libavcodec/mpeg4audio.c index 219714752f9..0d83fb8d259 100644 --- a/libavcodec/mpeg4audio.c +++ b/libavcodec/mpeg4audio.c @@ -84,7 +84,7 @@ static inline int get_sample_rate(GetBitContext *gb, int *index) } int ff_mpeg4audio_get_config_gb(MPEG4AudioConfig *c, GetBitContext *gb, - int sync_extension) + int sync_extension, void *logctx) { int specific_config_bitindex, ret; int start_bit_index = get_bits_count(gb); @@ -93,6 +93,10 @@ int ff_mpeg4audio_get_config_gb(MPEG4AudioConfig *c, GetBitContext *gb, c->chan_config = get_bits(gb, 4); if (c->chan_config < FF_ARRAY_ELEMS(ff_mpeg4audio_channels)) c->channels = ff_mpeg4audio_channels[c->chan_config]; + else { + av_log(logctx, AV_LOG_ERROR, "Invalid chan_config %d\n", c->chan_config); + return AVERROR_INVALIDDATA; + } c->sbr = -1; c->ps = -1; if (c->object_type == AOT_SBR || (c->object_type == AOT_PS && @@ -114,8 +118,8 @@ int ff_mpeg4audio_get_config_gb(MPEG4AudioConfig *c, GetBitContext *gb, if (c->object_type == AOT_ALS) { skip_bits(gb, 5); - if (show_bits_long(gb, 24) != MKBETAG('\0','A','L','S')) - skip_bits_long(gb, 24); + if (show_bits(gb, 24) != MKBETAG('\0','A','L','S')) + skip_bits(gb, 24); specific_config_bitindex = get_bits_count(gb); @@ -152,6 +156,7 @@ int ff_mpeg4audio_get_config_gb(MPEG4AudioConfig *c, GetBitContext *gb, return specific_config_bitindex - start_bit_index; } +#if LIBAVCODEC_VERSION_MAJOR < 59 int avpriv_mpeg4audio_get_config(MPEG4AudioConfig *c, const uint8_t *buf, int bit_size, int sync_extension) { @@ -165,5 +170,22 @@ int avpriv_mpeg4audio_get_config(MPEG4AudioConfig *c, const uint8_t *buf, if (ret < 0) return ret; - return ff_mpeg4audio_get_config_gb(c, &gb, sync_extension); + return ff_mpeg4audio_get_config_gb(c, &gb, sync_extension, NULL); +} +#endif + +int avpriv_mpeg4audio_get_config2(MPEG4AudioConfig *c, const uint8_t *buf, + int size, int sync_extension, void *logctx) +{ + GetBitContext gb; + int ret; + + if (size <= 0) + return AVERROR_INVALIDDATA; + + ret = init_get_bits8(&gb, buf, size); + if (ret < 0) + return ret; + + return ff_mpeg4audio_get_config_gb(c, &gb, sync_extension, logctx); } diff --git a/libavcodec/mpeg4audio.h b/libavcodec/mpeg4audio.h index b9cea8af17f..4b390e0f429 100644 --- a/libavcodec/mpeg4audio.h +++ b/libavcodec/mpeg4audio.h @@ -53,21 +53,36 @@ extern const uint8_t ff_mpeg4audio_channels[8]; * @param[in] c MPEG4AudioConfig structure to fill. * @param[in] gb Extradata from container. * @param[in] sync_extension look for a sync extension after config if true. - * @return On error -1 is returned, on success AudioSpecificConfig bit index in extradata. + * @param[in] logctx opaque struct starting with an AVClass element, used for logging. + * @return negative AVERROR code on error, on success AudioSpecificConfig bit index in extradata. */ int ff_mpeg4audio_get_config_gb(MPEG4AudioConfig *c, GetBitContext *gb, - int sync_extension); + int sync_extension, void *logctx); +#if LIBAVCODEC_VERSION_MAJOR < 59 /** * Parse MPEG-4 systems extradata from a raw buffer to retrieve audio configuration. * @param[in] c MPEG4AudioConfig structure to fill. * @param[in] buf Extradata from container. * @param[in] bit_size Extradata size in bits. * @param[in] sync_extension look for a sync extension after config if true. - * @return On error -1 is returned, on success AudioSpecificConfig bit index in extradata. + * @return negative AVERROR code on error, on success AudioSpecificConfig bit index in extradata. */ int avpriv_mpeg4audio_get_config(MPEG4AudioConfig *c, const uint8_t *buf, int bit_size, int sync_extension); +#endif + +/** + * Parse MPEG-4 systems extradata from a raw buffer to retrieve audio configuration. + * @param[in] c MPEG4AudioConfig structure to fill. + * @param[in] buf Extradata from container. + * @param[in] size Extradata size in bytes. + * @param[in] sync_extension look for a sync extension after config if true. + * @param[in] logctx opaque struct starting with an AVClass element, used for logging. + * @return negative AVERROR code on error, AudioSpecificConfig bit index in extradata on success. + */ +int avpriv_mpeg4audio_get_config2(MPEG4AudioConfig *c, const uint8_t *buf, + int size, int sync_extension, void *logctx); enum AudioObjectType { AOT_NULL, diff --git a/libavcodec/mpeg4videodec.c b/libavcodec/mpeg4videodec.c index b6f2ae7b7b5..610e365c361 100644 --- a/libavcodec/mpeg4videodec.c +++ b/libavcodec/mpeg4videodec.c @@ -26,7 +26,7 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "error_resilience.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "idctdsp.h" #include "internal.h" #include "mpegutils.h" @@ -610,7 +610,7 @@ static inline int get_amv(Mpeg4DecContext *ctx, int n) dy -= 1 << (shift + a + 1); else dx -= 1 << (shift + a + 1); - mb_v = s->sprite_offset[0][n] + dx * s->mb_x * 16 + dy * s->mb_y * 16; + mb_v = s->sprite_offset[0][n] + dx * s->mb_x * 16U + dy * s->mb_y * 16U; sum = 0; for (y = 0; y < 16; y++) { @@ -711,7 +711,7 @@ static int mpeg4_decode_partition_a(Mpeg4DecContext *ctx) int i; do { - if (show_bits_long(&s->gb, 19) == DC_MARKER) + 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); @@ -1001,7 +1001,7 @@ int ff_mpeg4_decode_partitions(Mpeg4DecContext *ctx) if (s->pict_type == AV_PICTURE_TYPE_I) { while (show_bits(&s->gb, 9) == 1) skip_bits(&s->gb, 9); - if (get_bits_long(&s->gb, 19) != DC_MARKER) { + if (get_bits(&s->gb, 19) != DC_MARKER) { av_log(s->avctx, AV_LOG_ERROR, "marker missing after first I partition at %d %d\n", s->mb_x, s->mb_y); @@ -1782,7 +1782,7 @@ static void next_start_code_studio(GetBitContext *gb) { align_get_bits(gb); - while (get_bits_left(gb) >= 24 && show_bits_long(gb, 24) != 0x1) { + while (get_bits_left(gb) >= 24 && show_bits(gb, 24) != 0x1) { get_bits(gb, 8); } } @@ -1826,6 +1826,7 @@ static int mpeg4_decode_studio_block(MpegEncContext *s, int32_t block[64], int n uint32_t flc; const int min = -1 * (1 << (s->avctx->bits_per_raw_sample + 6)); const int max = ((1 << (s->avctx->bits_per_raw_sample + 6)) - 1); + int shift = 3 - s->dct_precision; mismatch = 1; @@ -1921,7 +1922,7 @@ static int mpeg4_decode_studio_block(MpegEncContext *s, int32_t block[64], int n else block[j] = flc; } - block[j] = ((8 * 2 * block[j] * quant_matrix[j] * s->qscale) >> s->dct_precision) / 32; + block[j] = ((block[j] * quant_matrix[j] * s->qscale) * (1 << shift)) / 16; block[j] = av_clip(block[j], min, max); mismatch ^= block[j]; } @@ -3133,6 +3134,7 @@ static int decode_studio_vol_header(Mpeg4DecContext *ctx, GetBitContext *gb) MpegEncContext *s = &ctx->m; int width, height; int bits_per_raw_sample; + int rgb, chroma_format; // random_accessible_vol and video_object_type_indication have already // been read by the caller decode_vol_header() @@ -3140,28 +3142,36 @@ static int decode_studio_vol_header(Mpeg4DecContext *ctx, GetBitContext *gb) ctx->shape = get_bits(gb, 2); /* video_object_layer_shape */ skip_bits(gb, 4); /* video_object_layer_shape_extension */ skip_bits1(gb); /* progressive_sequence */ + if (ctx->shape != RECT_SHAPE) { + avpriv_request_sample(s->avctx, "MPEG-4 Studio profile non rectangular shape"); + return AVERROR_PATCHWELCOME; + } if (ctx->shape != BIN_ONLY_SHAPE) { - ctx->rgb = get_bits1(gb); /* rgb_components */ - s->chroma_format = get_bits(gb, 2); /* chroma_format */ - if (!s->chroma_format) { + rgb = get_bits1(gb); /* rgb_components */ + chroma_format = get_bits(gb, 2); /* chroma_format */ + if (!chroma_format || chroma_format == CHROMA_420 || (rgb && chroma_format == CHROMA_422)) { av_log(s->avctx, AV_LOG_ERROR, "illegal chroma format\n"); return AVERROR_INVALIDDATA; } bits_per_raw_sample = get_bits(gb, 4); /* bit_depth */ if (bits_per_raw_sample == 10) { - if (ctx->rgb) { + if (rgb) { s->avctx->pix_fmt = AV_PIX_FMT_GBRP10; } else { - s->avctx->pix_fmt = s->chroma_format == CHROMA_422 ? AV_PIX_FMT_YUV422P10 : AV_PIX_FMT_YUV444P10; + s->avctx->pix_fmt = chroma_format == CHROMA_422 ? AV_PIX_FMT_YUV422P10 : AV_PIX_FMT_YUV444P10; } } else { avpriv_request_sample(s->avctx, "MPEG-4 Studio profile bit-depth %u", bits_per_raw_sample); return AVERROR_PATCHWELCOME; } + if (rgb != ctx->rgb || s->chroma_format != chroma_format) + s->context_reinit = 1; s->avctx->bits_per_raw_sample = bits_per_raw_sample; + ctx->rgb = rgb; + s->chroma_format = chroma_format; } if (ctx->shape == RECT_SHAPE) { check_marker(s->avctx, gb, "before video_object_layer_width"); @@ -3211,7 +3221,7 @@ static int decode_studio_vol_header(Mpeg4DecContext *ctx, GetBitContext *gb) * Decode MPEG-4 headers. * * @param header If set the absence of a VOP is not treated as error; otherwise, it is treated as such. - * @return <0 if an error occured + * @return <0 if an error occurred * FRAME_SKIPPED if a not coded VOP is found * 0 else */ @@ -3459,7 +3469,33 @@ static int mpeg4_update_thread_context(AVCodecContext *dst, if (ret < 0) return ret; - memcpy(((uint8_t*)s) + sizeof(MpegEncContext), ((uint8_t*)s1) + sizeof(MpegEncContext), sizeof(Mpeg4DecContext) - sizeof(MpegEncContext)); + // copy all the necessary fields explicitly + s->time_increment_bits = s1->time_increment_bits; + s->shape = s1->shape; + s->vol_sprite_usage = s1->vol_sprite_usage; + s->sprite_brightness_change = s1->sprite_brightness_change; + s->num_sprite_warping_points = s1->num_sprite_warping_points; + s->rvlc = s1->rvlc; + s->resync_marker = s1->resync_marker; + s->t_frame = s1->t_frame; + s->new_pred = s1->new_pred; + s->enhancement_type = s1->enhancement_type; + s->scalability = s1->scalability; + s->use_intra_dc_vlc = s1->use_intra_dc_vlc; + s->intra_dc_threshold = s1->intra_dc_threshold; + s->divx_version = s1->divx_version; + s->divx_build = s1->divx_build; + s->xvid_build = s1->xvid_build; + s->lavc_build = s1->lavc_build; + s->showed_packed_warning = s1->showed_packed_warning; + s->vol_control_parameters = s1->vol_control_parameters; + s->cplx_estimation_trash_i = s1->cplx_estimation_trash_i; + s->cplx_estimation_trash_p = s1->cplx_estimation_trash_p; + s->cplx_estimation_trash_b = s1->cplx_estimation_trash_b; + s->rgb = s1->rgb; + + memcpy(s->sprite_shift, s1->sprite_shift, sizeof(s1->sprite_shift)); + memcpy(s->sprite_traj, s1->sprite_traj, sizeof(s1->sprite_traj)); if (CONFIG_MPEG4_DECODER && !init && s1->xvid_build >= 0) ff_xvid_idct_init(&s->m.idsp, dst); @@ -3523,7 +3559,6 @@ static av_cold int decode_init(AVCodecContext *avctx) ctx->time_increment_bits = 4; /* default value for broken headers */ avctx->chroma_sample_location = AVCHROMA_LOC_LEFT; - avctx->internal->allocate_progress = 1; return 0; } @@ -3533,13 +3568,11 @@ static av_cold int decode_end(AVCodecContext *avctx) Mpeg4DecContext *ctx = avctx->priv_data; int i; - if (!avctx->internal->is_copy) { - for (i = 0; i < 12; i++) - ff_free_vlc(&ctx->studio_intra_tab[i]); + for (i = 0; i < 12; i++) + ff_free_vlc(&ctx->studio_intra_tab[i]); - ff_free_vlc(&ctx->studio_luma_dc); - ff_free_vlc(&ctx->studio_chroma_dc); - } + ff_free_vlc(&ctx->studio_luma_dc); + ff_free_vlc(&ctx->studio_chroma_dc); return ff_h263_decode_end(avctx); } @@ -3569,7 +3602,8 @@ AVCodec ff_mpeg4_decoder = { .capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_TRUNCATED | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_FRAME_THREADS, - .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, + .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | + FF_CODEC_CAP_ALLOCATE_PROGRESS, .flush = ff_mpeg_flush, .max_lowres = 3, .pix_fmts = ff_h263_hwaccel_pixfmt_list_420, diff --git a/libavcodec/mpeg4videoenc.c b/libavcodec/mpeg4videoenc.c index f6a5992df77..a6a15e302c4 100644 --- a/libavcodec/mpeg4videoenc.c +++ b/libavcodec/mpeg4videoenc.c @@ -27,6 +27,7 @@ #include "mpegvideo.h" #include "h263.h" #include "mpeg4video.h" +#include "profiles.h" /* The uni_DCtab_* tables below contain unified bits+length tables to encode DC * differences in MPEG-4. Unified in the sense that the specification specifies @@ -104,7 +105,7 @@ static inline void restore_ac_coeffs(MpegEncContext *s, int16_t block[6][64], memcpy(s->block_last_index, zigzag_last_index, sizeof(int) * 6); for (n = 0; n < 6; n++) { - int16_t *ac_val = s->ac_val[0][0] + s->block_index[n] * 16; + int16_t *ac_val = &s->ac_val[0][0][0] + s->block_index[n] * 16; st[n] = s->intra_scantable.permutated; if (dir[n]) { @@ -143,7 +144,7 @@ static inline int decide_ac_pred(MpegEncContext *s, int16_t block[6][64], score -= get_block_rate(s, block[n], s->block_last_index[n], s->intra_scantable.permutated); - ac_val = s->ac_val[0][0] + s->block_index[n] * 16; + ac_val = &s->ac_val[0][0][0] + s->block_index[n] * 16; ac_val1 = ac_val; if (dir[n]) { const int xy = s->mb_x + s->mb_y * s->mb_stride - s->mb_stride; @@ -1376,6 +1377,7 @@ static const AVOption options[] = { { "data_partitioning", "Use data partitioning.", OFFSET(data_partitioning), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "alternate_scan", "Enable alternate scantable.", OFFSET(alternate_scan), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, FF_MPV_COMMON_OPTS + FF_MPEG4_PROFILE_OPTS { NULL }, }; @@ -1397,5 +1399,6 @@ AVCodec ff_mpeg4_encoder = { .close = ff_mpv_encode_end, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .priv_class = &mpeg4enc_class, }; diff --git a/libavcodec/mpegaudiodec_template.c b/libavcodec/mpegaudiodec_template.c index 9cce88e263b..3d7e3ba4f2e 100644 --- a/libavcodec/mpegaudiodec_template.c +++ b/libavcodec/mpegaudiodec_template.c @@ -27,6 +27,7 @@ #include "libavutil/attributes.h" #include "libavutil/avassert.h" #include "libavutil/channel_layout.h" +#include "libavutil/crc.h" #include "libavutil/float_dsp.h" #include "libavutil/libm.h" #include "avcodec.h" @@ -1565,9 +1566,22 @@ static int mp_decode_frame(MPADecodeContext *s, OUT_INT **samples, init_get_bits(&s->gb, buf + HEADER_SIZE, (buf_size - HEADER_SIZE) * 8); - /* skip error protection field */ - if (s->error_protection) - skip_bits(&s->gb, 16); + if (s->error_protection) { + uint16_t crc = get_bits(&s->gb, 16); + if (s->err_recognition & AV_EF_CRCCHECK) { + const int sec_len = s->lsf ? ((s->nb_channels == 1) ? 9 : 17) : + ((s->nb_channels == 1) ? 17 : 32); + const AVCRC *crc_tab = av_crc_get_table(AV_CRC_16_ANSI); + uint32_t crc_cal = av_crc(crc_tab, UINT16_MAX, &buf[2], 2); + crc_cal = av_crc(crc_tab, crc_cal, &buf[6], sec_len); + + if (av_bswap16(crc) ^ crc_cal) { + av_log(s->avctx, AV_LOG_ERROR, "CRC mismatch!\n"); + if (s->err_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + } + } + } switch(s->layer) { case 1: @@ -1851,8 +1865,8 @@ static av_cold int decode_init_mp3on4(AVCodecContext * avctx) return AVERROR_INVALIDDATA; } - avpriv_mpeg4audio_get_config(&cfg, avctx->extradata, - avctx->extradata_size * 8, 1); + avpriv_mpeg4audio_get_config2(&cfg, avctx->extradata, + avctx->extradata_size, 1, avctx); if (!cfg.chan_config || cfg.chan_config > 7) { av_log(avctx, AV_LOG_ERROR, "Invalid channel config number.\n"); return AVERROR_INVALIDDATA; diff --git a/libavcodec/mpegaudioenc_template.c b/libavcodec/mpegaudioenc_template.c index 93363fe1d25..12f7a098e6a 100644 --- a/libavcodec/mpegaudioenc_template.c +++ b/libavcodec/mpegaudioenc_template.c @@ -701,7 +701,7 @@ static void encode_frame(MpegAudioContext *s, /* normalize to P bits */ if (shift < 0) - q1 = sample << (-shift); + q1 = sample * (1 << -shift); else q1 = sample >> shift; q1 = (q1 * mult) >> P; diff --git a/libavcodec/mpegpicture.c b/libavcodec/mpegpicture.c index ecbd77d50e4..5fce25ec6e5 100644 --- a/libavcodec/mpegpicture.c +++ b/libavcodec/mpegpicture.c @@ -211,7 +211,7 @@ static int alloc_picture_tables(AVCodecContext *avctx, Picture *pic, int encodin #if FF_API_DEBUG_MV avctx->debug_mv || #endif - (avctx->flags2 & AV_CODEC_FLAG2_EXPORT_MVS)) { + (avctx->export_side_data & AV_CODEC_EXPORT_DATA_MVS)) { int mv_size = 2 * (b8_array_size + 4) * sizeof(int16_t); int ref_index_size = 4 * mb_array_size; diff --git a/libavcodec/mpegutils.c b/libavcodec/mpegutils.c index 3f945406162..c0ee3aae854 100644 --- a/libavcodec/mpegutils.c +++ b/libavcodec/mpegutils.c @@ -105,7 +105,7 @@ void ff_print_debug_info2(AVCodecContext *avctx, AVFrame *pict, uint8_t *mbskip_ int *low_delay, int mb_width, int mb_height, int mb_stride, int quarter_sample) { - if ((avctx->flags2 & AV_CODEC_FLAG2_EXPORT_MVS) && mbtype_table && motion_val[0]) { + if ((avctx->export_side_data & AV_CODEC_EXPORT_DATA_MVS) && mbtype_table && motion_val[0]) { const int shift = 1 + quarter_sample; const int scale = 1 << shift; const int mv_sample_log2 = avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_SVQ3 ? 2 : 1; diff --git a/libavcodec/mpegvideo.c b/libavcodec/mpegvideo.c index dbb6ab9b393..49fd1c999d1 100644 --- a/libavcodec/mpegvideo.c +++ b/libavcodec/mpegvideo.c @@ -32,7 +32,6 @@ #include "libavutil/imgutils.h" #include "libavutil/internal.h" #include "libavutil/motion_vector.h" -#include "libavutil/timer.h" #include "avcodec.h" #include "blockdsp.h" #include "h264chroma.h" @@ -347,9 +346,9 @@ av_cold void ff_mpv_idct_init(MpegEncContext *s) ff_init_scantable(s->idsp.idct_permutation, &s->intra_v_scantable, ff_alternate_vertical_scan); } -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, 0, + return ff_alloc_picture(s->avctx, pic, &s->me, &s->sc, 0, 0, s->chroma_x_shift, s->chroma_y_shift, s->out_format, s->mb_stride, s->mb_width, s->mb_height, s->b8_stride, &s->linesize, &s->uvlinesize); @@ -409,7 +408,7 @@ static int init_duplicate_context(MpegEncContext *s) return 0; fail: - return -1; // free() through ff_mpv_common_end() + return AVERROR(ENOMEM); // free() through ff_mpv_common_end() } static void free_duplicate_context(MpegEncContext *s) @@ -469,7 +468,6 @@ int ff_update_duplicate_context(MpegEncContext *dst, MpegEncContext *src) MpegEncContext bak; int i, ret; // FIXME copy only needed parts - // START_TIMER backup_duplicate_context(&bak, dst); memcpy(dst, src, sizeof(MpegEncContext)); backup_duplicate_context(dst, &bak); @@ -487,8 +485,6 @@ int ff_update_duplicate_context(MpegEncContext *dst, MpegEncContext *src) "scratch buffers.\n"); return ret; } - // STOP_TIMER("update_duplicate_context") - // about 10k cycles / 0.01 sec for 1000frames on 1ghz with 2 threads return 0; } @@ -911,7 +907,7 @@ av_cold int ff_mpv_common_init(MpegEncContext *s) if (s->avctx->pix_fmt == AV_PIX_FMT_NONE) { av_log(s->avctx, AV_LOG_ERROR, "decoding to AV_PIX_FMT_NONE is not supported.\n"); - return -1; + return AVERROR(EINVAL); } if (nb_slices > MAX_THREADS || (nb_slices > s->mb_height && s->mb_height)) { @@ -927,7 +923,7 @@ av_cold int ff_mpv_common_init(MpegEncContext *s) if ((s->width || s->height) && av_image_check_size(s->width, s->height, 0, s->avctx)) - return -1; + return AVERROR(EINVAL); dct_init(s); @@ -939,27 +935,27 @@ av_cold int ff_mpv_common_init(MpegEncContext *s) return ret; FF_ALLOCZ_OR_GOTO(s->avctx, s->picture, - MAX_PICTURE_COUNT * sizeof(Picture), fail); + MAX_PICTURE_COUNT * sizeof(Picture), fail_nomem); for (i = 0; i < MAX_PICTURE_COUNT; i++) { s->picture[i].f = av_frame_alloc(); if (!s->picture[i].f) - goto fail; + goto fail_nomem; } s->next_picture.f = av_frame_alloc(); if (!s->next_picture.f) - goto fail; + goto fail_nomem; s->last_picture.f = av_frame_alloc(); if (!s->last_picture.f) - goto fail; + goto fail_nomem; s->current_picture.f = av_frame_alloc(); if (!s->current_picture.f) - goto fail; + goto fail_nomem; s->new_picture.f = av_frame_alloc(); if (!s->new_picture.f) - goto fail; + goto fail_nomem; - if (init_context_frame(s)) - goto fail; + if ((ret = init_context_frame(s))) + goto fail_nomem; s->parse_context.state = -1; @@ -973,9 +969,9 @@ av_cold int ff_mpv_common_init(MpegEncContext *s) if (i) { s->thread_context[i] = av_memdup(s, sizeof(MpegEncContext)); if (!s->thread_context[i]) - goto fail; + goto fail_nomem; } - if (init_duplicate_context(s->thread_context[i]) < 0) + if ((ret = init_duplicate_context(s->thread_context[i])) < 0) goto fail; s->thread_context[i]->start_mb_y = (s->mb_height * (i) + nb_slices / 2) / nb_slices; @@ -983,7 +979,7 @@ av_cold int ff_mpv_common_init(MpegEncContext *s) (s->mb_height * (i + 1) + nb_slices / 2) / nb_slices; } } else { - if (init_duplicate_context(s) < 0) + if ((ret = init_duplicate_context(s)) < 0) goto fail; s->start_mb_y = 0; s->end_mb_y = s->mb_height; @@ -992,9 +988,11 @@ av_cold int ff_mpv_common_init(MpegEncContext *s) // } return 0; + fail_nomem: + ret = AVERROR(ENOMEM); fail: ff_mpv_common_end(s); - return -1; + return ret; } /** @@ -1263,7 +1261,7 @@ int ff_mpv_frame_start(MpegEncContext *s, AVCodecContext *avctx) pic->f->coded_picture_number = s->coded_picture_number++; - if (alloc_picture(s, pic, 0) < 0) + if (alloc_picture(s, pic) < 0) return -1; s->current_picture_ptr = pic; @@ -1324,7 +1322,7 @@ int ff_mpv_frame_start(MpegEncContext *s, AVCodecContext *avctx) s->last_picture_ptr->f->key_frame = 0; s->last_picture_ptr->f->pict_type = AV_PICTURE_TYPE_P; - if (alloc_picture(s, s->last_picture_ptr, 0) < 0) { + if (alloc_picture(s, s->last_picture_ptr) < 0) { s->last_picture_ptr = NULL; return -1; } @@ -1365,7 +1363,7 @@ int ff_mpv_frame_start(MpegEncContext *s, AVCodecContext *avctx) s->next_picture_ptr->f->key_frame = 0; s->next_picture_ptr->f->pict_type = AV_PICTURE_TYPE_P; - if (alloc_picture(s, s->next_picture_ptr, 0) < 0) { + if (alloc_picture(s, s->next_picture_ptr) < 0) { s->next_picture_ptr = NULL; return -1; } diff --git a/libavcodec/mpegvideo.h b/libavcodec/mpegvideo.h index e1ff5f97dcb..29e692f2457 100644 --- a/libavcodec/mpegvideo.h +++ b/libavcodec/mpegvideo.h @@ -580,6 +580,8 @@ typedef struct MpegEncContext { int scenechange_threshold; int noise_reduction; + + int intra_penalty; } MpegEncContext; /* mpegvideo_enc common options */ @@ -664,6 +666,7 @@ FF_MPV_OPT_CMP_FUNC, \ {"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 }, \ {"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 }, \ {"a53cc", "Use A53 Closed Captions (if available)", FF_MPV_OFFSET(a53_cc), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FF_MPV_OPT_FLAGS }, \ extern const AVOption ff_mpv_generic_options[]; diff --git a/libavcodec/mpegvideo_enc.c b/libavcodec/mpegvideo_enc.c index ae3b131229f..d1e2bd99754 100644 --- a/libavcodec/mpegvideo_enc.c +++ b/libavcodec/mpegvideo_enc.c @@ -38,7 +38,6 @@ #include "libavutil/mathematics.h" #include "libavutil/pixdesc.h" #include "libavutil/opt.h" -#include "libavutil/timer.h" #include "avcodec.h" #include "dct.h" #include "idctdsp.h" @@ -64,6 +63,7 @@ #include "bytestream.h" #include "wmv2.h" #include "rv10.h" +#include "packet_internal.h" #include "libxvid.h" #include #include "sp5x.h" @@ -165,7 +165,7 @@ void ff_convert_matrix(MpegEncContext *s, int (*qmat)[64], } } if (shift) { - av_log(NULL, AV_LOG_INFO, + av_log(s->avctx, AV_LOG_INFO, "Warning, QMAT_SHIFT is larger than %d, overflows possible\n", QMAT_SHIFT - shift); } @@ -299,7 +299,7 @@ av_cold int ff_mpv_encode_init(AVCodecContext *avctx) avctx->pix_fmt != AV_PIX_FMT_YUV422P) { av_log(avctx, AV_LOG_ERROR, "only YUV420 and YUV422 are supported\n"); - return -1; + return AVERROR(EINVAL); } break; case AV_CODEC_ID_MJPEG: @@ -323,13 +323,13 @@ av_cold int ff_mpv_encode_init(AVCodecContext *avctx) if (!format_supported) { av_log(avctx, AV_LOG_ERROR, "colorspace not supported in jpeg\n"); - return -1; + return AVERROR(EINVAL); } break; default: if (avctx->pix_fmt != AV_PIX_FMT_YUV420P) { av_log(avctx, AV_LOG_ERROR, "only YUV420 is supported\n"); - return -1; + return AVERROR(EINVAL); } } @@ -457,7 +457,7 @@ FF_ENABLE_DEPRECATION_WARNINGS if ((!avctx->rc_max_rate) != (!avctx->rc_buffer_size)) { av_log(avctx, AV_LOG_ERROR, "Either both buffer size and max rate or neither must be specified\n"); - return -1; + return AVERROR(EINVAL); } if (avctx->rc_min_rate && avctx->rc_max_rate != avctx->rc_min_rate) { @@ -467,12 +467,12 @@ FF_ENABLE_DEPRECATION_WARNINGS if (avctx->rc_min_rate && avctx->rc_min_rate > avctx->bit_rate) { av_log(avctx, AV_LOG_ERROR, "bitrate below min bitrate\n"); - return -1; + return AVERROR(EINVAL); } if (avctx->rc_max_rate && avctx->rc_max_rate < avctx->bit_rate) { av_log(avctx, AV_LOG_ERROR, "bitrate above max bitrate\n"); - return -1; + return AVERROR(EINVAL); } if (avctx->rc_max_rate && @@ -486,7 +486,7 @@ FF_ENABLE_DEPRECATION_WARNINGS avctx->bit_rate * (int64_t)avctx->time_base.num > avctx->rc_buffer_size * (int64_t)avctx->time_base.den) { av_log(avctx, AV_LOG_ERROR, "VBV buffer too small for bitrate\n"); - return -1; + return AVERROR(EINVAL); } if (!s->fixed_qscale && @@ -512,18 +512,18 @@ FF_ENABLE_DEPRECATION_WARNINGS s->codec_id != AV_CODEC_ID_H263 && s->codec_id != AV_CODEC_ID_H263P && s->codec_id != AV_CODEC_ID_FLV1) { av_log(avctx, AV_LOG_ERROR, "4MV not supported by codec\n"); - return -1; + return AVERROR(EINVAL); } if (s->obmc && s->avctx->mb_decision != FF_MB_DECISION_SIMPLE) { av_log(avctx, AV_LOG_ERROR, "OBMC is only supported with simple mb decision\n"); - return -1; + return AVERROR(EINVAL); } if (s->quarter_sample && s->codec_id != AV_CODEC_ID_MPEG4) { av_log(avctx, AV_LOG_ERROR, "qpel not supported by codec\n"); - return -1; + return AVERROR(EINVAL); } if (s->max_b_frames && @@ -531,12 +531,12 @@ FF_ENABLE_DEPRECATION_WARNINGS s->codec_id != AV_CODEC_ID_MPEG1VIDEO && s->codec_id != AV_CODEC_ID_MPEG2VIDEO) { av_log(avctx, AV_LOG_ERROR, "B-frames not supported by codec\n"); - return -1; + return AVERROR(EINVAL); } if (s->max_b_frames < 0) { av_log(avctx, AV_LOG_ERROR, "max b frames must be 0 or positive for mpegvideo based encoders\n"); - return -1; + return AVERROR(EINVAL); } if ((s->codec_id == AV_CODEC_ID_MPEG4 || @@ -556,28 +556,28 @@ FF_ENABLE_DEPRECATION_WARNINGS (avctx->width > 2048 || avctx->height > 1152 )) { av_log(avctx, AV_LOG_ERROR, "H.263 does not support resolutions above 2048x1152\n"); - return -1; + return AVERROR(EINVAL); } if ((s->codec_id == AV_CODEC_ID_H263 || s->codec_id == AV_CODEC_ID_H263P) && ((avctx->width &3) || (avctx->height&3) )) { av_log(avctx, AV_LOG_ERROR, "w/h must be a multiple of 4\n"); - return -1; + return AVERROR(EINVAL); } if (s->codec_id == AV_CODEC_ID_MPEG1VIDEO && (avctx->width > 4095 || avctx->height > 4095 )) { av_log(avctx, AV_LOG_ERROR, "MPEG-1 does not support resolutions above 4095x4095\n"); - return -1; + return AVERROR(EINVAL); } if (s->codec_id == AV_CODEC_ID_MPEG2VIDEO && (avctx->width > 16383 || avctx->height > 16383 )) { av_log(avctx, AV_LOG_ERROR, "MPEG-2 does not support resolutions above 16383x16383\n"); - return -1; + return AVERROR(EINVAL); } if (s->codec_id == AV_CODEC_ID_RV10 && @@ -597,14 +597,14 @@ FF_ENABLE_DEPRECATION_WARNINGS if ((s->codec_id == AV_CODEC_ID_WMV1 || s->codec_id == AV_CODEC_ID_WMV2) && avctx->width & 1) { - av_log(avctx, AV_LOG_ERROR, "width must be multiple of 2\n"); - return -1; + av_log(avctx, AV_LOG_ERROR, "width must be multiple of 2\n"); + return AVERROR(EINVAL); } if ((s->avctx->flags & (AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME)) && s->codec_id != AV_CODEC_ID_MPEG4 && s->codec_id != AV_CODEC_ID_MPEG2VIDEO) { av_log(avctx, AV_LOG_ERROR, "interlacing not supported by codec\n"); - return -1; + return AVERROR(EINVAL); } #if FF_API_PRIVATE_OPT @@ -619,18 +619,18 @@ FF_ENABLE_DEPRECATION_WARNINGS && s->codec_id != AV_CODEC_ID_MPEG2VIDEO)) { av_log(avctx, AV_LOG_ERROR, "mpeg2 style quantization not supported by codec\n"); - return -1; + return AVERROR(EINVAL); } if ((s->mpv_flags & FF_MPV_FLAG_CBP_RD) && !avctx->trellis) { av_log(avctx, AV_LOG_ERROR, "CBP RD needs trellis quant\n"); - return -1; + return AVERROR(EINVAL); } if ((s->mpv_flags & FF_MPV_FLAG_QP_RD) && s->avctx->mb_decision != FF_MB_DECISION_RD) { av_log(avctx, AV_LOG_ERROR, "QP RD needs mbd=2\n"); - return -1; + return AVERROR(EINVAL); } if ((s->mpv_flags & FF_MPV_FLAG_QP_RD) && @@ -639,7 +639,7 @@ FF_ENABLE_DEPRECATION_WARNINGS // Used to produce garbage with MJPEG. av_log(avctx, AV_LOG_ERROR, "QP RD is no longer compatible with MJPEG or AMV\n"); - return -1; + return AVERROR(EINVAL); } #if FF_API_PRIVATE_OPT @@ -654,7 +654,7 @@ FF_ENABLE_DEPRECATION_WARNINGS av_log(avctx, AV_LOG_ERROR, "closed gop with scene change detection are not supported yet, " "set threshold to 1000000000\n"); - return -1; + return AVERROR_PATCHWELCOME; } if (s->avctx->flags & AV_CODEC_FLAG_LOW_DELAY) { @@ -663,12 +663,12 @@ FF_ENABLE_DEPRECATION_WARNINGS av_log(avctx, AV_LOG_ERROR, "low delay forcing is only available for mpeg2, " "set strict_std_compliance to 'unofficial' or lower in order to allow it\n"); - return -1; + return AVERROR(EINVAL); } if (s->max_b_frames != 0) { av_log(avctx, AV_LOG_ERROR, "B-frames cannot be used with low delay\n"); - return -1; + return AVERROR(EINVAL); } } @@ -676,7 +676,7 @@ FF_ENABLE_DEPRECATION_WARNINGS if (avctx->qmax > 28) { av_log(avctx, AV_LOG_ERROR, "non linear quant only supports qmax <= 28 currently\n"); - return -1; + return AVERROR_PATCHWELCOME; } } @@ -694,19 +694,19 @@ FF_ENABLE_DEPRECATION_WARNINGS (s->codec_id != AV_CODEC_ID_H263P)) { av_log(avctx, AV_LOG_ERROR, "multi threaded encoding not supported by codec\n"); - return -1; + return AVERROR_PATCHWELCOME; } if (s->avctx->thread_count < 1) { av_log(avctx, AV_LOG_ERROR, "automatic thread number detection not supported by codec, " "patch welcome\n"); - return -1; + return AVERROR_PATCHWELCOME; } if (!avctx->time_base.den || !avctx->time_base.num) { av_log(avctx, AV_LOG_ERROR, "framerate not set\n"); - return -1; + return AVERROR(EINVAL); } #if FF_API_PRIVATE_OPT @@ -756,7 +756,7 @@ FF_ENABLE_DEPRECATION_WARNINGS "the maximum admitted value for the timebase denominator " "is %d\n", s->avctx->time_base.num, s->avctx->time_base.den, (1 << 16) - 1); - return -1; + return AVERROR(EINVAL); } s->time_increment_bits = av_log2(s->avctx->time_base.den - 1) + 1; @@ -776,21 +776,22 @@ FF_ENABLE_DEPRECATION_WARNINGS case AV_CODEC_ID_AMV: s->out_format = FMT_MJPEG; s->intra_only = 1; /* force intra only for jpeg */ - if (!CONFIG_MJPEG_ENCODER || - ff_mjpeg_encode_init(s) < 0) - return -1; + if (!CONFIG_MJPEG_ENCODER) + return AVERROR_ENCODER_NOT_FOUND; + if ((ret = ff_mjpeg_encode_init(s)) < 0) + return ret; avctx->delay = 0; s->low_delay = 1; break; case AV_CODEC_ID_H261: if (!CONFIG_H261_ENCODER) - return -1; + return AVERROR_ENCODER_NOT_FOUND; if (ff_h261_get_picture_format(s->width, s->height) < 0) { av_log(avctx, AV_LOG_ERROR, "The specified picture size of %dx%d is not valid for the " "H.261 codec.\nValid sizes are 176x144, 352x288\n", s->width, s->height); - return -1; + return AVERROR(EINVAL); } s->out_format = FMT_H261; avctx->delay = 0; @@ -799,7 +800,7 @@ FF_ENABLE_DEPRECATION_WARNINGS break; case AV_CODEC_ID_H263: if (!CONFIG_H263_ENCODER) - return -1; + return AVERROR_ENCODER_NOT_FOUND; if (ff_match_2uint16(ff_h263_format, FF_ARRAY_ELEMS(ff_h263_format), s->width, s->height) == 8) { av_log(avctx, AV_LOG_ERROR, @@ -807,7 +808,7 @@ FF_ENABLE_DEPRECATION_WARNINGS "the H.263 codec.\nValid sizes are 128x96, 176x144, " "352x288, 704x576, and 1408x1152. " "Try H.263+.\n", s->width, s->height); - return -1; + return AVERROR(EINVAL); } s->out_format = FMT_H263; avctx->delay = 0; @@ -893,7 +894,7 @@ FF_ENABLE_DEPRECATION_WARNINGS s->low_delay = 1; break; default: - return -1; + return AVERROR(EINVAL); } #if FF_API_PRIVATE_OPT @@ -914,8 +915,8 @@ FF_ENABLE_DEPRECATION_WARNINGS /* init */ ff_mpv_idct_init(s); - if (ff_mpv_common_init(s) < 0) - return -1; + if ((ret = ff_mpv_common_init(s)) < 0) + return ret; ff_fdctdsp_init(&s->fdsp, avctx); ff_me_cmp_init(&s->mecc, avctx); @@ -1021,8 +1022,8 @@ FF_ENABLE_DEPRECATION_WARNINGS 31, 0); } - if (ff_rate_control_init(s) < 0) - return -1; + if ((ret = ff_rate_control_init(s)) < 0) + return ret; #if FF_API_PRIVATE_OPT FF_DISABLE_DEPRECATION_WARNINGS @@ -1044,7 +1045,7 @@ FF_ENABLE_DEPRECATION_WARNINGS s->tmp_frames[i]->width = s->width >> s->brd_scale; s->tmp_frames[i]->height = s->height >> s->brd_scale; - ret = av_frame_get_buffer(s->tmp_frames[i], 32); + ret = av_frame_get_buffer(s->tmp_frames[i], 0); if (ret < 0) return ret; } @@ -1060,7 +1061,6 @@ FF_ENABLE_DEPRECATION_WARNINGS return 0; fail: - ff_mpv_encode_end(avctx); return AVERROR_UNKNOWN; } @@ -3761,14 +3761,14 @@ static int encode_picture(MpegEncContext *s, int picture_number) s->f_code= FFMAX3(s->f_code, a, b); } - ff_fix_long_p_mvs(s); - ff_fix_long_mvs(s, NULL, 0, s->p_mv_table, s->f_code, CANDIDATE_MB_TYPE_INTER, 0); + ff_fix_long_p_mvs(s, s->intra_penalty ? CANDIDATE_MB_TYPE_INTER : CANDIDATE_MB_TYPE_INTRA); + ff_fix_long_mvs(s, NULL, 0, s->p_mv_table, s->f_code, CANDIDATE_MB_TYPE_INTER, !!s->intra_penalty); if (s->avctx->flags & AV_CODEC_FLAG_INTERLACED_ME) { int j; for(i=0; i<2; i++){ for(j=0; j<2; j++) ff_fix_long_mvs(s, s->p_field_select_table[i], j, - s->p_field_mv_table[i][j], s->f_code, CANDIDATE_MB_TYPE_INTER_I, 0); + s->p_field_mv_table[i][j], s->f_code, CANDIDATE_MB_TYPE_INTER_I, !!s->intra_penalty); } } } @@ -4262,7 +4262,6 @@ static int dct_quantize_trellis_c(MpegEncContext *s, return last_non_zero; } -//#define REFINE_STATS 1 static int16_t basis[64][64]; static void build_basis(uint8_t *perm){ @@ -4301,15 +4300,6 @@ static int dct_quantize_refine(MpegEncContext *s, //FIXME breaks denoise? uint8_t * last_length; int lambda; int rle_index, run, q = 1, sum; //q is only used when s->mb_intra is true -#ifdef REFINE_STATS -static int count=0; -static int after_last=0; -static int to_zero=0; -static int from_zero=0; -static int raise=0; -static int lower=0; -static int messed_sign=0; -#endif if(basis[0][0] == 0) build_basis(s->idsp.idct_permutation); @@ -4353,16 +4343,11 @@ static int messed_sign=0; } last_non_zero = s->block_last_index[n]; -#ifdef REFINE_STATS -{START_TIMER -#endif dc += (1<<(RECON_SHIFT-1)); for(i=0; i<64; i++){ rem[i] = dc - (orig[i] << RECON_SHIFT); // FIXME use orig directly instead of copying to rem[] } -#ifdef REFINE_STATS -STOP_TIMER("memset rem[]")} -#endif + sum=0; for(i=0; i<64; i++){ int one= 36; @@ -4380,9 +4365,7 @@ STOP_TIMER("memset rem[]")} sum += w*w; } lambda= sum*(uint64_t)s->lambda2 >> (FF_LAMBDA_SHIFT - 6 + 6 + 6 + 6); -#ifdef REFINE_STATS -{START_TIMER -#endif + run=0; rle_index=0; for(i=start_i; i<=last_non_zero; i++){ @@ -4401,41 +4384,21 @@ STOP_TIMER("memset rem[]")} run++; } } -#ifdef REFINE_STATS -if(last_non_zero>0){ -STOP_TIMER("init rem[]") -} -} -{START_TIMER -#endif for(;;){ int best_score = s->mpvencdsp.try_8x8basis(rem, weight, basis[0], 0); int best_coeff=0; int best_change=0; int run2, best_unquant_change=0, analyze_gradient; -#ifdef REFINE_STATS -{START_TIMER -#endif analyze_gradient = last_non_zero > 2 || s->quantizer_noise_shaping >= 3; if(analyze_gradient){ -#ifdef REFINE_STATS -{START_TIMER -#endif for(i=0; i<64; i++){ int w= weight[i]; d1[i] = (rem[i]*w*w + (1<<(RECON_SHIFT+12-1)))>>(RECON_SHIFT+12); } -#ifdef REFINE_STATS -STOP_TIMER("rem*w*w")} -{START_TIMER -#endif s->fdsp.fdct(d1); -#ifdef REFINE_STATS -STOP_TIMER("dct")} -#endif } if(start_i){ @@ -4597,9 +4560,6 @@ STOP_TIMER("dct")} run++; } } -#ifdef REFINE_STATS -STOP_TIMER("iterative step")} -#endif if(best_change){ int j= perm_scantable[ best_coeff ]; @@ -4609,36 +4569,13 @@ STOP_TIMER("iterative step")} if(best_coeff > last_non_zero){ last_non_zero= best_coeff; av_assert2(block[j]); -#ifdef REFINE_STATS -after_last++; -#endif }else{ -#ifdef REFINE_STATS -if(block[j]){ - if(block[j] - best_change){ - if(FFABS(block[j]) > FFABS(block[j] - best_change)){ - raise++; - }else{ - lower++; - } - }else{ - from_zero++; - } -}else{ - to_zero++; -} -#endif for(; last_non_zero>=start_i; last_non_zero--){ if(block[perm_scantable[last_non_zero]]) break; } } -#ifdef REFINE_STATS -count++; -if(256*256*256*64 % count == 0){ - av_log(s->avctx, AV_LOG_DEBUG, "after_last:%d to_zero:%d from_zero:%d raise:%d lower:%d sign:%d xyp:%d/%d/%d\n", after_last, to_zero, from_zero, raise, lower, messed_sign, s->mb_x, s->mb_y, s->picture_number); -} -#endif + run=0; rle_index=0; for(i=start_i; i<=last_non_zero; i++){ @@ -4658,12 +4595,6 @@ if(256*256*256*64 % count == 0){ break; } } -#ifdef REFINE_STATS -if(last_non_zero>0){ -STOP_TIMER("iterative search") -} -} -#endif return last_non_zero; } @@ -4812,6 +4743,7 @@ AVCodec ff_h263_encoder = { .init = ff_mpv_encode_init, .encode2 = ff_mpv_encode_picture, .close = ff_mpv_encode_end, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts= (const enum AVPixelFormat[]){AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE}, .priv_class = &h263_class, }; @@ -4841,6 +4773,7 @@ AVCodec ff_h263p_encoder = { .encode2 = ff_mpv_encode_picture, .close = ff_mpv_encode_end, .capabilities = AV_CODEC_CAP_SLICE_THREADS, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, .priv_class = &h263p_class, }; @@ -4861,6 +4794,7 @@ AVCodec ff_msmpeg4v2_encoder = { .init = ff_mpv_encode_init, .encode2 = ff_mpv_encode_picture, .close = ff_mpv_encode_end, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, .priv_class = &msmpeg4v2_class, }; @@ -4881,6 +4815,7 @@ AVCodec ff_msmpeg4v3_encoder = { .init = ff_mpv_encode_init, .encode2 = ff_mpv_encode_picture, .close = ff_mpv_encode_end, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, .priv_class = &msmpeg4v3_class, }; @@ -4901,6 +4836,7 @@ AVCodec ff_wmv1_encoder = { .init = ff_mpv_encode_init, .encode2 = ff_mpv_encode_picture, .close = ff_mpv_encode_end, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, .priv_class = &wmv1_class, }; diff --git a/libavcodec/mpegvideo_parser.c b/libavcodec/mpegvideo_parser.c index 7a3c7abdb24..c02cd7f92f5 100644 --- a/libavcodec/mpegvideo_parser.c +++ b/libavcodec/mpegvideo_parser.c @@ -154,7 +154,7 @@ static void mpegvideo_extract_headers(AVCodecParserContext *s, break; } } - the_end: ; + the_end: if (set_dim_ret < 0) av_log(avctx, AV_LOG_ERROR, "Failed to set dimensions\n"); diff --git a/libavcodec/mpegvideodata.c b/libavcodec/mpegvideodata.c index 5f1d8f7c00d..da0638e0529 100644 --- a/libavcodec/mpegvideodata.c +++ b/libavcodec/mpegvideodata.c @@ -19,7 +19,6 @@ #include const uint8_t ff_default_chroma_qscale_table[32] = { -// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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,7 +31,6 @@ const uint8_t ff_mpeg2_non_linear_qscale[32] = { }; const uint8_t ff_mpeg1_dc_scale_table[128] = { -// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, @@ -44,7 +42,6 @@ const uint8_t ff_mpeg1_dc_scale_table[128] = { }; static const uint8_t mpeg2_dc_scale_table1[128] = { -// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, @@ -56,7 +53,6 @@ static const uint8_t mpeg2_dc_scale_table1[128] = { }; static const uint8_t mpeg2_dc_scale_table2[128] = { -// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -68,7 +64,6 @@ static const uint8_t mpeg2_dc_scale_table2[128] = { }; static const uint8_t mpeg2_dc_scale_table3[128] = { -// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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, diff --git a/libavcodec/msrle.c b/libavcodec/msrle.c index 1ab8a419851..9233b34cc54 100644 --- a/libavcodec/msrle.c +++ b/libavcodec/msrle.c @@ -42,8 +42,6 @@ typedef struct MsrleContext { AVFrame *frame; GetByteContext gb; - const unsigned char *buf; - int size; uint32_t pal[256]; } MsrleContext; @@ -92,13 +90,10 @@ static int msrle_decode_frame(AVCodecContext *avctx, int istride = FFALIGN(avctx->width*avctx->bits_per_coded_sample, 32) / 8; int ret; - s->buf = buf; - s->size = buf_size; - if (buf_size < 2) //Minimally a end of picture code should be there return AVERROR_INVALIDDATA; - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; if (avctx->bits_per_coded_sample > 1 && avctx->bits_per_coded_sample <= 8) { @@ -153,6 +148,13 @@ static int msrle_decode_frame(AVCodecContext *avctx, return buf_size; } +static void msrle_decode_flush(AVCodecContext *avctx) +{ + MsrleContext *s = avctx->priv_data; + + av_frame_unref(s->frame); +} + static av_cold int msrle_decode_end(AVCodecContext *avctx) { MsrleContext *s = avctx->priv_data; @@ -172,5 +174,6 @@ AVCodec ff_msrle_decoder = { .init = msrle_decode_init, .close = msrle_decode_end, .decode = msrle_decode_frame, + .flush = msrle_decode_flush, .capabilities = AV_CODEC_CAP_DR1, }; diff --git a/libavcodec/mss1.c b/libavcodec/mss1.c index 84b7a37007d..7af8024d63d 100644 --- a/libavcodec/mss1.c +++ b/libavcodec/mss1.c @@ -154,7 +154,7 @@ static int mss1_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, arith_init(&acoder, &gb); - if ((ret = ff_reget_buffer(avctx, ctx->pic)) < 0) + if ((ret = ff_reget_buffer(avctx, ctx->pic, 0)) < 0) return ret; c->pal_pic = ctx->pic->data[0] + ctx->pic->linesize[0] * (avctx->height - 1); diff --git a/libavcodec/mss2.c b/libavcodec/mss2.c index 29897cea2eb..9434a740a7c 100644 --- a/libavcodec/mss2.c +++ b/libavcodec/mss2.c @@ -412,8 +412,6 @@ static int decode_wmv9(AVCodecContext *avctx, const uint8_t *buf, int buf_size, ff_mpeg_er_frame_start(s); - v->bits = buf_size * 8; - v->end_mb_x = (w + 15) >> 4; s->end_mb_y = (h + 15) >> 4; if (v->respic & 1) @@ -618,7 +616,7 @@ static int mss2_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, return AVERROR_INVALIDDATA; } } else { - if ((ret = ff_reget_buffer(avctx, ctx->last_pic)) < 0) + if ((ret = ff_reget_buffer(avctx, ctx->last_pic, 0)) < 0) return ret; if ((ret = av_frame_ref(frame, ctx->last_pic)) < 0) return ret; diff --git a/libavcodec/mss3.c b/libavcodec/mss3.c index 02bd3609962..113af5ba37b 100644 --- a/libavcodec/mss3.c +++ b/libavcodec/mss3.c @@ -737,7 +737,7 @@ static int mss3_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, return buf_size; c->got_error = 0; - if ((ret = ff_reget_buffer(avctx, c->pic)) < 0) + if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; c->pic->key_frame = keyframe; c->pic->pict_type = keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; diff --git a/libavcodec/mss4.c b/libavcodec/mss4.c index 76c746a2d52..432df294d69 100644 --- a/libavcodec/mss4.c +++ b/libavcodec/mss4.c @@ -558,7 +558,7 @@ static int mss4_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, if (frame_type != SKIP_FRAME && 8*buf_size < 8*HEADER_SIZE + mb_width*mb_height) return AVERROR_INVALIDDATA; - if ((ret = ff_reget_buffer(avctx, c->pic)) < 0) + if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; c->pic->key_frame = (frame_type == INTRA_FRAME); c->pic->pict_type = (frame_type == INTRA_FRAME) ? AV_PICTURE_TYPE_I diff --git a/libavcodec/msvideo1.c b/libavcodec/msvideo1.c index de048d8b6fb..c9bcce552d0 100644 --- a/libavcodec/msvideo1.c +++ b/libavcodec/msvideo1.c @@ -310,7 +310,7 @@ static int msvideo1_decode_frame(AVCodecContext *avctx, return AVERROR_INVALIDDATA; } - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; if (s->mode_8bit) { diff --git a/libavcodec/mv30.c b/libavcodec/mv30.c new file mode 100644 index 00000000000..c83ba7ffbdb --- /dev/null +++ b/libavcodec/mv30.c @@ -0,0 +1,718 @@ +/* + * MidiVid MV30 decoder + * + * Copyright (c) 2020 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/thread.h" + +#include "avcodec.h" +#include "bytestream.h" +#include "copy_block.h" +#include "mathops.h" +#include "blockdsp.h" +#include "get_bits.h" +#include "internal.h" +#include "aandcttab.h" + +typedef struct MV30Context { + GetBitContext gb; + + int intra_quant; + int inter_quant; + int is_inter; + int mode_size; + int nb_mvectors; + + int block[6][64]; + int16_t *mvectors; + unsigned int mvectors_size; + int16_t *coeffs; + unsigned int coeffs_size; + + int16_t intraq_tab[2][64]; + int16_t interq_tab[2][64]; + + BlockDSPContext bdsp; + AVFrame *prev_frame; +} MV30Context; + +static VLC cbp_tab; + +static const uint8_t luma_tab[] = { + 12, 12, 15, 19, 25, 34, 40, 48, + 12, 12, 18, 22, 27, 44, 47, 46, + 17, 18, 21, 26, 35, 46, 52, 47, + 18, 20, 24, 28, 40, 61, 59, 51, + 20, 24, 32, 43, 50, 72, 72, 63, + 25, 31, 42, 48, 58, 72, 81, 75, + 38, 46, 54, 61, 71, 84, 88, 85, + 50, 61, 65, 68, 79, 78, 86, 91, +}; + +static const uint8_t chroma_tab[] = { + 12, 16, 24, 47, 99, 99, 99, 99, + 16, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, +}; + +static const uint8_t zigzag[] = { + 0, 1, 8, 9, 16, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, +}; + +static void get_qtable(int16_t *table, int quant, const uint8_t *quant_tab) +{ + int factor = quant < 50 ? 5000 / FFMAX(quant, 1) : 200 - FFMIN(quant, 100) * 2; + + for (int i = 0; i < 64; i++) { + table[i] = av_clip((quant_tab[i] * factor + 0x32) / 100, 1, 0x7fff); + table[i] = ((int)ff_aanscales[i] * (int)table[i] + 0x800) >> 12; + } +} + +static inline void idct_1d(int *blk, int step) +{ + const int t0 = blk[0 * step] + blk[4 * step]; + const int t1 = blk[0 * step] - blk[4 * step]; + const int t2 = blk[2 * step] + blk[6 * step]; + const int t3 = ((int)((blk[2 * step] - blk[6 * step]) * 362U) >> 8) - t2; + const int t4 = t0 + t2; + const int t5 = t0 - t2; + const int t6 = t1 + t3; + const int t7 = t1 - t3; + const int t8 = blk[5 * step] + blk[3 * step]; + const int t9 = blk[5 * step] - blk[3 * step]; + const int tA = blk[1 * step] + blk[7 * step]; + const int tB = blk[1 * step] - blk[7 * step]; + const int tC = t8 + tA; + const int tD = (int)((tB + t9) * 473U) >> 8; + const int tE = (((int)(t9 * -669U) >> 8) - tC) + tD; + const int tF = ((int)((tA - t8) * 362U) >> 8) - tE; + const int t10 = (((int)(tB * 277U) >> 8) - tD) + tF; + + blk[0 * step] = t4 + tC; + blk[1 * step] = t6 + tE; + blk[2 * step] = t7 + tF; + blk[3 * step] = t5 - t10; + blk[4 * step] = t5 + t10; + blk[5 * step] = t7 - tF; + blk[6 * step] = t6 - tE; + blk[7 * step] = t4 - tC; +} + +static void idct_put(uint8_t *dst, int stride, int *block) +{ + for (int i = 0; i < 8; i++) { + if ((block[0x08 + i] | + block[0x10 + i] | + block[0x18 + i] | + block[0x20 + i] | + block[0x28 + i] | + block[0x30 + i] | + block[0x38 + i]) == 0) { + block[0x08 + i] = block[i]; + block[0x10 + i] = block[i]; + block[0x18 + i] = block[i]; + block[0x20 + i] = block[i]; + block[0x28 + i] = block[i]; + block[0x30 + i] = block[i]; + block[0x38 + i] = block[i]; + } else { + idct_1d(block + i, 8); + } + } + + for (int i = 0; i < 8; i++) { + idct_1d(block, 1); + for (int j = 0; j < 8; j++) + dst[j] = av_clip_uint8((block[j] >> 5) + 128); + block += 8; + dst += stride; + } +} + +static void idct_add(uint8_t *dst, int stride, + const uint8_t *src, int in_linesize, int *block) +{ + for (int i = 0; i < 8; i++) { + if ((block[0x08 + i] | + block[0x10 + i] | + block[0x18 + i] | + block[0x20 + i] | + block[0x28 + i] | + block[0x30 + i] | + block[0x38 + i]) == 0) { + block[0x08 + i] = block[i]; + block[0x10 + i] = block[i]; + block[0x18 + i] = block[i]; + block[0x20 + i] = block[i]; + block[0x28 + i] = block[i]; + block[0x30 + i] = block[i]; + block[0x38 + i] = block[i]; + } else { + idct_1d(block + i, 8); + } + } + + for (int i = 0; i < 8; i++) { + idct_1d(block, 1); + for (int j = 0; j < 8; j++) + dst[j] = av_clip_uint8((block[j] >> 5) + src[j]); + block += 8; + dst += stride; + src += in_linesize; + } +} + +static inline void idct2_1d(int *blk, int step) +{ + const int t0 = blk[0 * step]; + const int t1 = blk[1 * step]; + const int t2 = (int)(t1 * 473U) >> 8; + const int t3 = t2 - t1; + const int t4 = ((int)(t1 * 362U) >> 8) - t3; + const int t5 = (((int)(t1 * 277U) >> 8) - t2) + t4; + + blk[0 * step] = t1 + t0; + blk[1 * step] = t0 + t3; + blk[2 * step] = t4 + t0; + blk[3 * step] = t0 - t5; + blk[4 * step] = t5 + t0; + blk[5 * step] = t0 - t4; + blk[6 * step] = t0 - t3; + blk[7 * step] = t0 - t1; +} + +static void idct2_put(uint8_t *dst, int stride, int *block) +{ + for (int i = 0; i < 2; i++) { + if ((block[0x08 + i]) == 0) { + block[0x08 + i] = block[i]; + block[0x10 + i] = block[i]; + block[0x18 + i] = block[i]; + block[0x20 + i] = block[i]; + block[0x28 + i] = block[i]; + block[0x30 + i] = block[i]; + block[0x38 + i] = block[i]; + } else { + idct2_1d(block + i, 8); + } + } + + for (int i = 0; i < 8; i++) { + if (block[1] == 0) { + for (int j = 0; j < 8; j++) + dst[j] = av_clip_uint8((block[0] >> 5) + 128); + } else { + idct2_1d(block, 1); + for (int j = 0; j < 8; j++) + dst[j] = av_clip_uint8((block[j] >> 5) + 128); + } + block += 8; + dst += stride; + } +} + +static void idct2_add(uint8_t *dst, int stride, + const uint8_t *src, int in_linesize, + int *block) +{ + for (int i = 0; i < 2; i++) { + if ((block[0x08 + i]) == 0) { + block[0x08 + i] = block[i]; + block[0x10 + i] = block[i]; + block[0x18 + i] = block[i]; + block[0x20 + i] = block[i]; + block[0x28 + i] = block[i]; + block[0x30 + i] = block[i]; + block[0x38 + i] = block[i]; + } else { + idct2_1d(block + i, 8); + } + } + + for (int i = 0; i < 8; i++) { + if (block[1] == 0) { + for (int j = 0; j < 8; j++) + dst[j] = av_clip_uint8((block[0] >> 5) + src[j]); + } else { + idct2_1d(block, 1); + for (int j = 0; j < 8; j++) + dst[j] = av_clip_uint8((block[j] >> 5) + src[j]); + } + block += 8; + dst += stride; + src += in_linesize; + } +} + +static void update_inter_block(uint8_t *dst, int stride, + const uint8_t *src, int in_linesize, + int block) +{ + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) + dst[j] = av_clip_uint8(block + src[j]); + dst += stride; + src += in_linesize; + } +} + +static int decode_intra_block(AVCodecContext *avctx, int mode, + GetByteContext *gbyte, int16_t *qtab, + int *block, int *pfill, + uint8_t *dst, int linesize) +{ + MV30Context *s = avctx->priv_data; + int fill; + + switch (mode) { + case 0: + s->bdsp.fill_block_tab[1](dst, 128, linesize, 8); + break; + case 1: + fill = sign_extend(bytestream2_get_ne16(gbyte), 16); + pfill[0] += fill; + block[0] = ((pfill[0] * qtab[0]) >> 5) + 128; + s->bdsp.fill_block_tab[1](dst, block[0], linesize, 8); + break; + case 2: + memset(block, 0, sizeof(*block) * 64); + fill = sign_extend(bytestream2_get_ne16(gbyte), 16); + pfill[0] += fill; + block[0] = pfill[0] * qtab[0]; + block[1] = sign_extend(bytestream2_get_ne16(gbyte), 16) * qtab[1]; + block[8] = sign_extend(bytestream2_get_ne16(gbyte), 16) * qtab[8]; + block[9] = sign_extend(bytestream2_get_ne16(gbyte), 16) * qtab[9]; + idct2_put(dst, linesize, block); + break; + case 3: + fill = sign_extend(bytestream2_get_ne16(gbyte), 16); + pfill[0] += fill; + block[0] = pfill[0] * qtab[0]; + for (int i = 1; i < 64; i++) + block[zigzag[i]] = sign_extend(bytestream2_get_ne16(gbyte), 16) * qtab[zigzag[i]]; + idct_put(dst, linesize, block); + break; + } + + return 0; +} + +static int decode_inter_block(AVCodecContext *avctx, int mode, + GetByteContext *gbyte, int16_t *qtab, + int *block, int *pfill, + uint8_t *dst, int linesize, + const uint8_t *src, int in_linesize) +{ + int fill; + + switch (mode) { + case 0: + copy_block8(dst, src, linesize, in_linesize, 8); + break; + case 1: + fill = sign_extend(bytestream2_get_ne16(gbyte), 16); + pfill[0] += fill; + block[0] = (pfill[0] * qtab[0]) >> 5; + update_inter_block(dst, linesize, src, in_linesize, block[0]); + break; + case 2: + memset(block, 0, sizeof(*block) * 64); + fill = sign_extend(bytestream2_get_ne16(gbyte), 16); + pfill[0] += fill; + block[0] = pfill[0] * qtab[0]; + block[1] = sign_extend(bytestream2_get_ne16(gbyte), 16) * qtab[1]; + block[8] = sign_extend(bytestream2_get_ne16(gbyte), 16) * qtab[8]; + block[9] = sign_extend(bytestream2_get_ne16(gbyte), 16) * qtab[9]; + idct2_add(dst, linesize, src, in_linesize, block); + break; + case 3: + fill = sign_extend(bytestream2_get_ne16(gbyte), 16); + pfill[0] += fill; + block[0] = pfill[0] * qtab[0]; + for (int i = 1; i < 64; i++) + block[zigzag[i]] = sign_extend(bytestream2_get_ne16(gbyte), 16) * qtab[zigzag[i]]; + idct_add(dst, linesize, src, in_linesize, block); + break; + } + + return 0; +} + +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_tab.bits, 1); + + if (value < 0) + return AVERROR_INVALIDDATA; + + if (value > 0) { + int x = get_bits(gb, value); + + if (x < (1 << value) / 2) { + x = (1 << (value - 1)) + (x & ((1 << value) - 1 >> 1)); + } else { + x = -(1 << (value - 1)) - (x & ((1 << value) - 1 >> 1)); + } + coeffs[i++] = x; + } else { + int flag = get_bits1(gb); + + i += get_bits(gb, 3 + flag * 3) + 1 + flag * 8; + } + } + + return 0; +} + +static int decode_intra(AVCodecContext *avctx, GetBitContext *gb, AVFrame *frame) +{ + MV30Context *s = avctx->priv_data; + GetBitContext mgb; + uint8_t *dst[6]; + int linesize[6]; + int ret; + + mgb = *gb; + if (get_bits_left(gb) < s->mode_size * 8) + return AVERROR_INVALIDDATA; + + skip_bits_long(gb, s->mode_size * 8); + + linesize[0] = frame->linesize[0]; + linesize[1] = frame->linesize[0]; + linesize[2] = frame->linesize[0]; + linesize[3] = frame->linesize[0]; + linesize[4] = frame->linesize[1]; + linesize[5] = frame->linesize[2]; + + for (int y = 0; y < avctx->height; y += 16) { + GetByteContext gbyte; + int pfill[3][1] = { {0} }; + int nb_codes = get_bits(gb, 16); + + av_fast_padded_malloc(&s->coeffs, &s->coeffs_size, nb_codes * sizeof(*s->coeffs)); + if (!s->coeffs) + return AVERROR(ENOMEM); + ret = decode_coeffs(gb, s->coeffs, nb_codes); + if (ret < 0) + return ret; + + bytestream2_init(&gbyte, (uint8_t *)s->coeffs, nb_codes * sizeof(*s->coeffs)); + + for (int x = 0; x < avctx->width; x += 16) { + dst[0] = frame->data[0] + linesize[0] * y + x; + dst[1] = frame->data[0] + linesize[0] * y + x + 8; + dst[2] = frame->data[0] + linesize[0] * (y + 8) + x; + dst[3] = frame->data[0] + linesize[0] * (y + 8) + x + 8; + dst[4] = frame->data[1] + linesize[4] * (y >> 1) + (x >> 1); + dst[5] = frame->data[2] + linesize[5] * (y >> 1) + (x >> 1); + + for (int b = 0; b < 6; b++) { + int mode = get_bits_le(&mgb, 2); + + ret = decode_intra_block(avctx, mode, &gbyte, s->intraq_tab[b >= 4], + s->block[b], + pfill[(b >= 4) + (b >= 5)], + dst[b], linesize[b]); + if (ret < 0) + return ret; + } + } + } + + return 0; +} + +static int decode_inter(AVCodecContext *avctx, GetBitContext *gb, + AVFrame *frame, AVFrame *prev) +{ + MV30Context *s = avctx->priv_data; + GetBitContext mask; + GetBitContext mgb; + GetByteContext mv; + const int mask_size = ((avctx->height >> 4) * (avctx->width >> 4) * 2 + 7) / 8; + uint8_t *dst[6], *src[6]; + int in_linesize[6]; + int linesize[6]; + int ret, cnt = 0; + int flags = 0; + + in_linesize[0] = prev->linesize[0]; + in_linesize[1] = prev->linesize[0]; + in_linesize[2] = prev->linesize[0]; + in_linesize[3] = prev->linesize[0]; + in_linesize[4] = prev->linesize[1]; + in_linesize[5] = prev->linesize[2]; + + linesize[0] = frame->linesize[0]; + linesize[1] = frame->linesize[0]; + linesize[2] = frame->linesize[0]; + linesize[3] = frame->linesize[0]; + linesize[4] = frame->linesize[1]; + linesize[5] = frame->linesize[2]; + + av_fast_padded_malloc(&s->mvectors, &s->mvectors_size, 2 * s->nb_mvectors * sizeof(*s->mvectors)); + if (!s->mvectors) { + ret = AVERROR(ENOMEM); + goto fail; + } + + mask = *gb; + skip_bits_long(gb, mask_size * 8); + mgb = *gb; + skip_bits_long(gb, s->mode_size * 8); + + ret = decode_coeffs(gb, s->mvectors, 2 * s->nb_mvectors); + if (ret < 0) + goto fail; + + bytestream2_init(&mv, (uint8_t *)s->mvectors, 2 * s->nb_mvectors * sizeof(*s->mvectors)); + + for (int y = 0; y < avctx->height; y += 16) { + GetByteContext gbyte; + int pfill[3][1] = { {0} }; + int nb_codes = get_bits(gb, 16); + + skip_bits(gb, 8); + if (get_bits_left(gb) < 0) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + av_fast_padded_malloc(&s->coeffs, &s->coeffs_size, nb_codes * sizeof(*s->coeffs)); + if (!s->coeffs) { + ret = AVERROR(ENOMEM); + goto fail; + } + + ret = decode_coeffs(gb, s->coeffs, nb_codes); + if (ret < 0) + goto fail; + + bytestream2_init(&gbyte, (uint8_t *)s->coeffs, nb_codes * sizeof(*s->coeffs)); + + for (int x = 0; x < avctx->width; x += 16) { + if (cnt >= 4) + cnt = 0; + if (cnt == 0) + flags = get_bits(&mask, 8); + + dst[0] = frame->data[0] + linesize[0] * y + x; + dst[1] = frame->data[0] + linesize[0] * y + x + 8; + dst[2] = frame->data[0] + linesize[0] * (y + 8) + x; + dst[3] = frame->data[0] + linesize[0] * (y + 8) + x + 8; + dst[4] = frame->data[1] + linesize[4] * (y >> 1) + (x >> 1); + dst[5] = frame->data[2] + linesize[5] * (y >> 1) + (x >> 1); + + if ((flags >> (cnt)) & 1) { + int mv_x = sign_extend(bytestream2_get_ne16(&mv), 16); + int mv_y = sign_extend(bytestream2_get_ne16(&mv), 16); + + int px = x + mv_x; + int py = y + mv_y; + + if (px < 0 || px > FFALIGN(avctx->width , 16) - 16 || + py < 0 || py > FFALIGN(avctx->height, 16) - 16) + return AVERROR_INVALIDDATA; + + src[0] = prev->data[0] + in_linesize[0] * py + px; + src[1] = prev->data[0] + in_linesize[0] * py + px + 8; + src[2] = prev->data[0] + in_linesize[0] * (py + 8) + px; + src[3] = prev->data[0] + in_linesize[0] * (py + 8) + px + 8; + src[4] = prev->data[1] + in_linesize[4] * (py >> 1) + (px >> 1); + src[5] = prev->data[2] + in_linesize[5] * (py >> 1) + (px >> 1); + + if ((flags >> (cnt + 4)) & 1) { + for (int b = 0; b < 6; b++) + copy_block8(dst[b], src[b], linesize[b], in_linesize[b], 8); + } else { + for (int b = 0; b < 6; b++) { + int mode = get_bits_le(&mgb, 2); + + ret = decode_inter_block(avctx, mode, &gbyte, s->interq_tab[b >= 4], + s->block[b], + pfill[(b >= 4) + (b >= 5)], + dst[b], linesize[b], + src[b], in_linesize[b]); + if (ret < 0) + goto fail; + } + } + } else { + for (int b = 0; b < 6; b++) { + int mode = get_bits_le(&mgb, 2); + + ret = decode_intra_block(avctx, mode, &gbyte, s->intraq_tab[b >= 4], + s->block[b], + pfill[(b >= 4) + (b >= 5)], + dst[b], linesize[b]); + if (ret < 0) + goto fail; + } + } + + cnt++; + } + } + +fail: + return ret; +} + +static int decode_frame(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *avpkt) +{ + MV30Context *s = avctx->priv_data; + GetBitContext *gb = &s->gb; + AVFrame *frame = data; + int ret; + + if ((ret = init_get_bits8(gb, avpkt->data, avpkt->size)) < 0) + return ret; + + if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0) + return ret; + + s->intra_quant = get_bits(gb, 8); + s->inter_quant = s->intra_quant + get_sbits(gb, 8); + s->is_inter = get_bits_le(gb, 16); + s->mode_size = get_bits_le(gb, 16); + if (s->is_inter) + s->nb_mvectors = get_bits_le(gb, 16); + + 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) { + ret = decode_intra(avctx, gb, frame); + if (ret < 0) + return ret; + } else { + get_qtable(s->interq_tab[0], s->inter_quant, luma_tab); + get_qtable(s->interq_tab[1], s->inter_quant, chroma_tab); + + if (!s->prev_frame->data[0]) { + av_log(avctx, AV_LOG_ERROR, "Missing reference frame.\n"); + return AVERROR_INVALIDDATA; + } + + 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) + return ret; + + *got_frame = 1; + + return avpkt->size; +} + +static const uint16_t cbp_codes[] = { + 0, 1, 4, 5, 6, 0xE, 0x1E, 0x3E, 0x7E, 0xFE, 0x1FE, 0x1FF, +}; + +static const uint8_t cbp_bits[] = { + 2, 2, 3, 3, 3, 4, 5, 6, 7, 8, 9, 9, +}; + +static av_cold void init_static_data(void) +{ + INIT_VLC_SPARSE_STATIC(&cbp_tab, 9, FF_ARRAY_ELEMS(cbp_bits), + cbp_bits, 1, 1, cbp_codes, 2, 2, NULL, 0, 0, 512); +} + +static av_cold int decode_init(AVCodecContext *avctx) +{ + MV30Context *s = avctx->priv_data; + static AVOnce init_static_once = AV_ONCE_INIT; + + avctx->pix_fmt = AV_PIX_FMT_YUV420P; + avctx->color_range = AVCOL_RANGE_JPEG; + + ff_blockdsp_init(&s->bdsp, avctx); + + s->prev_frame = av_frame_alloc(); + if (!s->prev_frame) + return AVERROR(ENOMEM); + + ff_thread_once(&init_static_once, init_static_data); + + return 0; +} + +static void decode_flush(AVCodecContext *avctx) +{ + MV30Context *s = avctx->priv_data; + + av_frame_unref(s->prev_frame); +} + +static av_cold int decode_close(AVCodecContext *avctx) +{ + MV30Context *s = avctx->priv_data; + + av_frame_free(&s->prev_frame); + av_freep(&s->coeffs); + s->coeffs_size = 0; + av_freep(&s->mvectors); + s->mvectors_size = 0; + + return 0; +} + +AVCodec ff_mv30_decoder = { + .name = "mv30", + .long_name = NULL_IF_CONFIG_SMALL("MidiVid 3.0"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_MV30, + .priv_data_size = sizeof(MV30Context), + .init = decode_init, + .close = decode_close, + .decode = decode_frame, + .flush = decode_flush, + .capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | + FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/libavcodec/mvha.c b/libavcodec/mvha.c new file mode 100644 index 00000000000..c603ef69755 --- /dev/null +++ b/libavcodec/mvha.c @@ -0,0 +1,322 @@ +/* + * MidiVid Archive codec + * + * 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 +#include +#include + +#define CACHED_BITSTREAM_READER !ARCH_X86_32 +#include "libavutil/intreadwrite.h" + +#include "avcodec.h" +#include "bytestream.h" +#include "get_bits.h" +#include "internal.h" +#include "lossless_videodsp.h" + +#include + +typedef struct MVHAContext { + GetBitContext gb; + int nb_symbols; + + uint8_t symb[256]; + uint32_t prob[256]; + VLC vlc; + + z_stream zstream; + LLVidDSPContext llviddsp; +} MVHAContext; + +typedef struct Node { + int16_t sym; + int16_t n0; + int16_t l, r; + uint32_t count; +} Node; + +static void get_tree_codes(uint32_t *bits, int16_t *lens, uint8_t *xlat, + Node *nodes, int node, + uint32_t pfx, int pl, int *pos) +{ + int s; + + s = nodes[node].sym; + if (s != -1) { + bits[*pos] = (~pfx) & ((1ULL << FFMAX(pl, 1)) - 1); + lens[*pos] = FFMAX(pl, 1); + xlat[*pos] = s + (pl == 0); + (*pos)++; + } else { + pfx <<= 1; + pl++; + get_tree_codes(bits, lens, xlat, nodes, nodes[node].l, pfx, pl, + pos); + pfx |= 1; + get_tree_codes(bits, lens, xlat, nodes, nodes[node].r, pfx, pl, + pos); + } +} + +static int build_vlc(AVCodecContext *avctx, VLC *vlc) +{ + MVHAContext *s = avctx->priv_data; + Node nodes[512]; + uint32_t bits[256]; + int16_t lens[256]; + uint8_t xlat[256]; + int cur_node, i, j, pos = 0; + + ff_free_vlc(vlc); + + for (i = 0; i < s->nb_symbols; i++) { + nodes[i].count = s->prob[i]; + nodes[i].sym = s->symb[i]; + nodes[i].n0 = -2; + nodes[i].l = i; + nodes[i].r = i; + } + + cur_node = s->nb_symbols; + j = 0; + do { + for (i = 0; ; i++) { + int new_node = j; + int first_node = cur_node; + int second_node = cur_node; + unsigned nd, st; + + nodes[cur_node].count = -1; + + do { + int val = nodes[new_node].count; + if (val && (val < nodes[first_node].count)) { + if (val >= nodes[second_node].count) { + first_node = new_node; + } else { + first_node = second_node; + second_node = new_node; + } + } + new_node += 1; + } while (new_node != cur_node); + + if (first_node == cur_node) + break; + + nd = nodes[second_node].count; + st = nodes[first_node].count; + nodes[second_node].count = 0; + nodes[first_node].count = 0; + if (nd >= UINT32_MAX - st) { + av_log(avctx, AV_LOG_ERROR, "count overflow\n"); + return AVERROR_INVALIDDATA; + } + nodes[cur_node].count = nd + st; + nodes[cur_node].sym = -1; + nodes[cur_node].n0 = cur_node; + nodes[cur_node].l = first_node; + nodes[cur_node].r = second_node; + cur_node++; + } + j++; + } while (cur_node - s->nb_symbols == j); + + 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); +} + +static int decode_frame(AVCodecContext *avctx, + void *data, int *got_frame, + AVPacket *avpkt) +{ + MVHAContext *s = avctx->priv_data; + AVFrame *frame = data; + uint32_t type, size; + int ret; + + if (avpkt->size <= 8) + return AVERROR_INVALIDDATA; + + type = AV_RB32(avpkt->data); + size = AV_RL32(avpkt->data + 4); + + if (size < 1 || size >= avpkt->size) + return AVERROR_INVALIDDATA; + + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + return ret; + + if (type == MKTAG('L','Z','Y','V')) { + ret = inflateReset(&s->zstream); + if (ret != Z_OK) { + av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret); + return AVERROR_EXTERNAL; + } + + s->zstream.next_in = avpkt->data + 8; + s->zstream.avail_in = avpkt->size - 8; + + for (int p = 0; p < 3; p++) { + for (int y = 0; y < avctx->height; y++) { + s->zstream.next_out = frame->data[p] + (avctx->height - y - 1) * frame->linesize[p]; + s->zstream.avail_out = avctx->width >> (p > 0); + + ret = inflate(&s->zstream, Z_SYNC_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) { + av_log(avctx, AV_LOG_ERROR, "Inflate error: %d\n", ret); + return AVERROR_EXTERNAL; + } + } + } + } else if (type == MKTAG('H','U','F','Y')) { + GetBitContext *gb = &s->gb; + int first_symbol, symbol; + + ret = init_get_bits8(gb, avpkt->data + 8, avpkt->size - 8); + if (ret < 0) + return ret; + + skip_bits(gb, 24); + + first_symbol = get_bits(gb, 8); + s->nb_symbols = get_bits(gb, 8) + 1; + + symbol = first_symbol; + for (int i = 0; i < s->nb_symbols; symbol++) { + int prob; + + if (get_bits_left(gb) < 4) + return AVERROR_INVALIDDATA; + + if (get_bits1(gb)) { + prob = get_bits(gb, 12); + } else { + prob = get_bits(gb, 3); + } + + if (prob) { + s->symb[i] = symbol; + s->prob[i] = prob; + i++; + } + } + + ret = build_vlc(avctx, &s->vlc); + if (ret < 0) + return ret; + + for (int p = 0; p < 3; p++) { + int width = avctx->width >> (p > 0); + ptrdiff_t stride = frame->linesize[p]; + uint8_t *dst; + + dst = frame->data[p] + (avctx->height - 1) * frame->linesize[p]; + for (int y = 0; y < avctx->height; y++) { + if (get_bits_left(gb) < width) + return AVERROR_INVALIDDATA; + for (int x = 0; x < width; x++) { + int v = get_vlc2(gb, s->vlc.table, s->vlc.bits, 3); + + if (v < 0) + return AVERROR_INVALIDDATA; + + dst[x] = v; + } + dst -= stride; + } + } + } else { + return AVERROR_INVALIDDATA; + } + + for (int p = 0; p < 3; p++) { + int left, lefttop; + int width = avctx->width >> (p > 0); + ptrdiff_t stride = frame->linesize[p]; + uint8_t *dst; + + dst = frame->data[p] + (avctx->height - 1) * frame->linesize[p]; + s->llviddsp.add_left_pred(dst, dst, width, 0); + if (avctx->height > 1) { + dst -= stride; + lefttop = left = dst[0]; + for (int y = 1; y < avctx->height; y++) { + s->llviddsp.add_median_pred(dst, dst + stride, dst, width, &left, &lefttop); + lefttop = left = dst[0]; + dst -= stride; + } + } + } + + frame->pict_type = AV_PICTURE_TYPE_I; + frame->key_frame = 1; + *got_frame = 1; + + return avpkt->size; +} + +static av_cold int decode_init(AVCodecContext *avctx) +{ + MVHAContext *s = avctx->priv_data; + int zret; + + avctx->pix_fmt = AV_PIX_FMT_YUV422P; + + s->zstream.zalloc = Z_NULL; + s->zstream.zfree = Z_NULL; + s->zstream.opaque = Z_NULL; + zret = inflateInit(&s->zstream); + if (zret != Z_OK) { + av_log(avctx, AV_LOG_ERROR, "Inflate init error: %d\n", zret); + return AVERROR_EXTERNAL; + } + + ff_llviddsp_init(&s->llviddsp); + + return 0; +} + +static av_cold int decode_close(AVCodecContext *avctx) +{ + MVHAContext *s = avctx->priv_data; + + inflateEnd(&s->zstream); + ff_free_vlc(&s->vlc); + + return 0; +} + +AVCodec ff_mvha_decoder = { + .name = "mvha", + .long_name = NULL_IF_CONFIG_SMALL("MidiVid Archive Codec"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_MVHA, + .priv_data_size = sizeof(MVHAContext), + .init = decode_init, + .close = decode_close, + .decode = decode_frame, + .capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | + FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/libavcodec/mxpegdec.c b/libavcodec/mxpegdec.c index 2e3ebe6e70e..55ec6e928e2 100644 --- a/libavcodec/mxpegdec.c +++ b/libavcodec/mxpegdec.c @@ -199,6 +199,7 @@ static int mxpeg_decode_frame(AVCodecContext *avctx, buf_end = buf + buf_size; jpg->got_picture = 0; s->got_mxm_bitmask = 0; + s->got_sof_data = !!s->got_sof_data; while (buf_ptr < buf_end) { start_code = ff_mjpeg_find_marker(jpg, &buf_ptr, buf_end, &unescaped_buf_ptr, &unescaped_buf_size); @@ -241,6 +242,11 @@ static int mxpeg_decode_frame(AVCodecContext *avctx, return ret; break; case SOF0: + if (s->got_sof_data > 1) { + av_log(avctx, AV_LOG_ERROR, + "Multiple SOF in a frame\n"); + return AVERROR_INVALIDDATA; + } s->got_sof_data = 0; ret = ff_mjpeg_decode_sof(jpg); if (ret < 0) { @@ -253,7 +259,7 @@ static int mxpeg_decode_frame(AVCodecContext *avctx, "Interlaced mode not supported in MxPEG\n"); return AVERROR(EINVAL); } - s->got_sof_data = 1; + s->got_sof_data ++; break; case SOS: if (!s->got_sof_data) { diff --git a/libavcodec/noise_bsf.c b/libavcodec/noise_bsf.c index d79f63b7779..6ebd3696332 100644 --- a/libavcodec/noise_bsf.c +++ b/libavcodec/noise_bsf.c @@ -19,13 +19,11 @@ */ #include -#include -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "libavutil/log.h" -#include "libavutil/mem.h" #include "libavutil/opt.h" typedef struct NoiseContext { @@ -39,7 +37,7 @@ static int noise(AVBSFContext *ctx, AVPacket *pkt) { NoiseContext *s = ctx->priv_data; int amount = s->amount > 0 ? s->amount : (s->state % 10001 + 1); - int i, ret = 0; + int i, ret; if (amount <= 0) return AVERROR(EINVAL); @@ -55,19 +53,18 @@ static int noise(AVBSFContext *ctx, AVPacket *pkt) } ret = av_packet_make_writable(pkt); - if (ret < 0) - goto fail; + if (ret < 0) { + av_packet_unref(pkt); + return ret; + } for (i = 0; i < pkt->size; i++) { s->state += pkt->data[i] + 1; if (s->state % amount == 0) pkt->data[i] = s->state; } -fail: - if (ret < 0) - av_packet_unref(pkt); - return ret; + return 0; } #define OFFSET(x) offsetof(NoiseContext, x) diff --git a/libavcodec/notchlc.c b/libavcodec/notchlc.c new file mode 100644 index 00000000000..9e6534339f1 --- /dev/null +++ b/libavcodec/notchlc.c @@ -0,0 +1,479 @@ +/* + * NotchLC decoder + * Copyright (c) 2020 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 + +#define BITSTREAM_READER_LE +#include "libavutil/intreadwrite.h" +#include "avcodec.h" +#include "bytestream.h" +#include "get_bits.h" +#include "internal.h" +#include "lzf.h" +#include "thread.h" + +typedef struct NotchLCContext { + unsigned compressed_size; + unsigned format; + + uint8_t *uncompressed_buffer; + unsigned uncompressed_size; + + uint8_t *lzf_buffer; + int64_t lzf_size; + + unsigned texture_size_x; + unsigned texture_size_y; + unsigned y_data_row_offsets; + unsigned uv_offset_data_offset; + unsigned y_control_data_offset; + unsigned a_control_word_offset; + unsigned y_data_offset; + unsigned uv_data_offset; + unsigned y_data_size; + unsigned uv_count_size; + unsigned uv_count_offset; + unsigned a_count_size; + unsigned data_end; + + GetByteContext gb; + PutByteContext pb; +} NotchLCContext; + +static av_cold int decode_init(AVCodecContext *avctx) +{ + avctx->pix_fmt = AV_PIX_FMT_YUV444P12; + avctx->color_range = AVCOL_RANGE_JPEG; + avctx->colorspace = AVCOL_SPC_RGB; + avctx->color_primaries = AVCOL_PRI_BT709; + avctx->color_trc = AVCOL_TRC_IEC61966_2_1; + + return 0; +} + +#define HISTORY_SIZE (64 * 1024) + +static int lz4_decompress(AVCodecContext *avctx, + GetByteContext *gb, + PutByteContext *pb) +{ + unsigned reference_pos, match_length, delta, pos = 0; + uint8_t history[64 * 1024]; + + while (bytestream2_get_bytes_left(gb) > 0) { + uint8_t token = bytestream2_get_byte(gb); + unsigned num_literals = token >> 4; + + if (num_literals == 15) { + unsigned char current; + do { + current = bytestream2_get_byte(gb); + num_literals += current; + } while (current == 255); + } + + if (pos + num_literals < HISTORY_SIZE) { + bytestream2_get_buffer(gb, history + pos, num_literals); + pos += num_literals; + } else { + while (num_literals-- > 0) { + history[pos++] = bytestream2_get_byte(gb); + if (pos == HISTORY_SIZE) { + bytestream2_put_buffer(pb, history, HISTORY_SIZE); + pos = 0; + } + } + } + + if (bytestream2_get_bytes_left(gb) <= 0) + break; + + delta = bytestream2_get_byte(gb); + delta |= (unsigned)bytestream2_get_byte(gb) << 8; + if (delta == 0) + return 0; + match_length = 4 + (token & 0x0F); + if (match_length == 4 + 0x0F) { + uint8_t current; + + do { + current = bytestream2_get_byte(gb); + match_length += current; + } while (current == 255); + } + reference_pos = (pos >= delta) ? (pos - delta) : (HISTORY_SIZE + pos - delta); + if (pos + match_length < HISTORY_SIZE && reference_pos + match_length < HISTORY_SIZE) { + if (pos >= reference_pos + match_length || reference_pos >= pos + match_length) { + memcpy(history + pos, history + reference_pos, match_length); + pos += match_length; + } else { + while (match_length-- > 0) + history[pos++] = history[reference_pos++]; + } + } else { + while (match_length-- > 0) { + history[pos++] = history[reference_pos++]; + if (pos == HISTORY_SIZE) { + bytestream2_put_buffer(pb, history, HISTORY_SIZE); + pos = 0; + } + reference_pos %= HISTORY_SIZE; + } + } + } + + bytestream2_put_buffer(pb, history, pos); + + return bytestream2_tell_p(pb); +} + +static int decode_blocks(AVCodecContext *avctx, AVFrame *p, ThreadFrame *frame, + unsigned uncompressed_size) +{ + NotchLCContext *s = avctx->priv_data; + GetByteContext rgb, dgb, *gb = &s->gb; + GetBitContext bit; + int ylinesize, ulinesize, vlinesize, alinesize; + uint16_t *dsty, *dstu, *dstv, *dsta; + int ret; + + s->texture_size_x = bytestream2_get_le32(gb); + s->texture_size_y = bytestream2_get_le32(gb); + + ret = ff_set_dimensions(avctx, s->texture_size_x, s->texture_size_y); + if (ret < 0) + return ret; + + s->uv_offset_data_offset = bytestream2_get_le32(gb); + if (s->uv_offset_data_offset >= UINT_MAX / 4) + return AVERROR_INVALIDDATA; + s->uv_offset_data_offset *= 4; + if (s->uv_offset_data_offset >= uncompressed_size) + return AVERROR_INVALIDDATA; + + s->y_control_data_offset = bytestream2_get_le32(gb); + if (s->y_control_data_offset >= UINT_MAX / 4) + return AVERROR_INVALIDDATA; + s->y_control_data_offset *= 4; + if (s->y_control_data_offset >= uncompressed_size) + return AVERROR_INVALIDDATA; + + s->a_control_word_offset = bytestream2_get_le32(gb); + if (s->a_control_word_offset >= UINT_MAX / 4) + return AVERROR_INVALIDDATA; + s->a_control_word_offset *= 4; + if (s->a_control_word_offset >= uncompressed_size) + return AVERROR_INVALIDDATA; + + s->uv_data_offset = bytestream2_get_le32(gb); + if (s->uv_data_offset >= UINT_MAX / 4) + return AVERROR_INVALIDDATA; + s->uv_data_offset *= 4; + if (s->uv_data_offset >= uncompressed_size) + return AVERROR_INVALIDDATA; + + s->y_data_size = bytestream2_get_le32(gb); + if (s->y_data_size >= UINT_MAX / 4) + return AVERROR_INVALIDDATA; + + s->uv_count_size = bytestream2_get_le32(gb); + if (s->uv_count_size >= UINT_MAX / 4) + return AVERROR_INVALIDDATA; + s->uv_count_size *= 4; + if (s->uv_count_size >= uncompressed_size) + return AVERROR_INVALIDDATA; + + s->a_count_size = bytestream2_get_le32(gb); + if (s->a_count_size >= UINT_MAX / 4) + return AVERROR_INVALIDDATA; + s->a_count_size *= 4; + if (s->a_count_size >= uncompressed_size) + return AVERROR_INVALIDDATA; + + s->data_end = bytestream2_get_le32(gb); + if (s->data_end > uncompressed_size) + return AVERROR_INVALIDDATA; + + s->y_data_row_offsets = bytestream2_tell(gb); + if (s->data_end <= s->y_data_size) + return AVERROR_INVALIDDATA; + s->y_data_offset = s->data_end - s->y_data_size; + if (s->y_data_offset <= s->uv_count_size) + return AVERROR_INVALIDDATA; + s->uv_count_offset = s->y_data_offset - s->uv_count_size; + + if ((ret = ff_thread_get_buffer(avctx, frame, 0)) < 0) + return ret; + + rgb = *gb; + dgb = *gb; + bytestream2_seek(&rgb, s->y_data_row_offsets, SEEK_SET); + bytestream2_seek(gb, s->y_control_data_offset, SEEK_SET); + + dsty = (uint16_t *)p->data[0]; + dsta = (uint16_t *)p->data[3]; + ylinesize = p->linesize[0] / 2; + alinesize = p->linesize[3] / 2; + + for (int y = 0; y < avctx->height; y += 4) { + const unsigned row_offset = bytestream2_get_le32(&rgb); + + bytestream2_seek(&dgb, s->y_data_offset + row_offset, SEEK_SET); + + init_get_bits8(&bit, dgb.buffer, bytestream2_get_bytes_left(&dgb)); + for (int x = 0; x < avctx->width; x += 4) { + unsigned item = bytestream2_get_le32(gb); + unsigned y_min = item & 4095; + unsigned y_max = (item >> 12) & 4095; + unsigned y_diff = y_max - y_min; + unsigned control[4]; + + control[0] = (item >> 24) & 3; + control[1] = (item >> 26) & 3; + control[2] = (item >> 28) & 3; + control[3] = (item >> 30) & 3; + + for (int i = 0; i < 4; i++) { + const int nb_bits = control[i] + 1; + const int div = (1 << nb_bits) - 1; + const int add = div - 1; + + dsty[x + i * ylinesize + 0] = av_clip_uintp2(y_min + ((y_diff * get_bits(&bit, nb_bits) + add) / div), 12); + dsty[x + i * ylinesize + 1] = av_clip_uintp2(y_min + ((y_diff * get_bits(&bit, nb_bits) + add) / div), 12); + dsty[x + i * ylinesize + 2] = av_clip_uintp2(y_min + ((y_diff * get_bits(&bit, nb_bits) + add) / div), 12); + dsty[x + i * ylinesize + 3] = av_clip_uintp2(y_min + ((y_diff * get_bits(&bit, nb_bits) + add) / div), 12); + } + } + + dsty += 4 * ylinesize; + dsta += 4 * alinesize; + } + + rgb = *gb; + dgb = *gb; + bytestream2_seek(&rgb, s->uv_offset_data_offset, SEEK_SET); + bytestream2_seek(gb, s->a_control_word_offset, SEEK_SET); + + dstu = (uint16_t *)p->data[1]; + dstv = (uint16_t *)p->data[2]; + ulinesize = p->linesize[1] / 2; + vlinesize = p->linesize[2] / 2; + + for (int y = 0; y < avctx->height; y += 16) { + for (int x = 0; x < avctx->width; x += 16) { + unsigned offset = bytestream2_get_le32(&rgb) * 4; + int u[16][16] = { 0 }, v[16][16] = { 0 }; + int u0, v0, u1, v1, udif, vdif; + unsigned escape, is8x8, loc; + + bytestream2_seek(&dgb, s->uv_data_offset + offset, SEEK_SET); + + is8x8 = bytestream2_get_le16(&dgb); + escape = bytestream2_get_le16(&dgb); + + if (escape == 0 && is8x8 == 0) { + u0 = bytestream2_get_byte(&dgb); + v0 = bytestream2_get_byte(&dgb); + u1 = bytestream2_get_byte(&dgb); + v1 = bytestream2_get_byte(&dgb); + loc = bytestream2_get_le32(&dgb); + u0 = (u0 << 4) | (u0 & 0xF); + v0 = (v0 << 4) | (v0 & 0xF); + u1 = (u1 << 4) | (u1 & 0xF); + v1 = (v1 << 4) | (v1 & 0xF); + udif = u1 - u0; + vdif = v1 - v0; + + for (int i = 0; i < 16; i += 4) { + for (int j = 0; j < 16; j += 4) { + for (int ii = 0; ii < 4; ii++) { + for (int jj = 0; jj < 4; jj++) { + u[i + ii][j + jj] = u0 + ((udif * (int)(loc & 3) + 2) / 3); + v[i + ii][j + jj] = v0 + ((vdif * (int)(loc & 3) + 2) / 3); + } + } + + loc >>= 2; + } + } + } else { + for (int i = 0; i < 16; i += 8) { + for (int j = 0; j < 16; j += 8) { + if (is8x8 & 1) { + u0 = bytestream2_get_byte(&dgb); + v0 = bytestream2_get_byte(&dgb); + u1 = bytestream2_get_byte(&dgb); + v1 = bytestream2_get_byte(&dgb); + loc = bytestream2_get_le32(&dgb); + u0 = (u0 << 4) | (u0 & 0xF); + v0 = (v0 << 4) | (v0 & 0xF); + u1 = (u1 << 4) | (u1 & 0xF); + v1 = (v1 << 4) | (v1 & 0xF); + udif = u1 - u0; + vdif = v1 - v0; + + for (int ii = 0; ii < 8; ii += 2) { + for (int jj = 0; jj < 8; jj += 2) { + for (int iii = 0; iii < 2; iii++) { + for (int jjj = 0; jjj < 2; jjj++) { + u[i + ii + iii][j + jj + jjj] = u0 + ((udif * (int)(loc & 3) + 2) / 3); + v[i + ii + iii][j + jj + jjj] = v0 + ((vdif * (int)(loc & 3) + 2) / 3); + } + } + + loc >>= 2; + } + } + } else if (escape) { + for (int ii = 0; ii < 8; ii += 4) { + for (int jj = 0; jj < 8; jj += 4) { + u0 = bytestream2_get_byte(&dgb); + v0 = bytestream2_get_byte(&dgb); + u1 = bytestream2_get_byte(&dgb); + v1 = bytestream2_get_byte(&dgb); + loc = bytestream2_get_le32(&dgb); + u0 = (u0 << 4) | (u0 & 0xF); + v0 = (v0 << 4) | (v0 & 0xF); + u1 = (u1 << 4) | (u1 & 0xF); + v1 = (v1 << 4) | (v1 & 0xF); + udif = u1 - u0; + vdif = v1 - v0; + + for (int iii = 0; iii < 4; iii++) { + for (int jjj = 0; jjj < 4; jjj++) { + u[i + ii + iii][j + jj + jjj] = u0 + ((udif * (int)(loc & 3) + 2) / 3); + v[i + ii + iii][j + jj + jjj] = v0 + ((vdif * (int)(loc & 3) + 2) / 3); + + loc >>= 2; + } + } + } + } + } + + is8x8 >>= 1; + } + } + } + + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + dstu[x + i * ulinesize + j] = u[i][j]; + dstv[x + i * vlinesize + j] = v[i][j]; + } + } + } + + dstu += 16 * ulinesize; + dstv += 16 * vlinesize; + } + + return 0; +} + +static int decode_frame(AVCodecContext *avctx, + void *data, int *got_frame, + AVPacket *avpkt) +{ + NotchLCContext *s = avctx->priv_data; + ThreadFrame frame = { .f = data }; + GetByteContext *gb = &s->gb; + PutByteContext *pb = &s->pb; + unsigned uncompressed_size; + AVFrame *p = data; + int ret; + + if (avpkt->size <= 40) + return AVERROR_INVALIDDATA; + + bytestream2_init(gb, avpkt->data, avpkt->size); + + if (bytestream2_get_le32(gb) != MKBETAG('N','L','C','1')) + return AVERROR_INVALIDDATA; + + uncompressed_size = bytestream2_get_le32(gb); + s->compressed_size = bytestream2_get_le32(gb); + s->format = bytestream2_get_le32(gb); + + if (s->format > 2) + return AVERROR_PATCHWELCOME; + + if (s->format == 0) { + ret = ff_lzf_uncompress(gb, &s->lzf_buffer, &s->lzf_size); + if (ret < 0) + return ret; + + if (uncompressed_size > s->lzf_size) + return AVERROR_INVALIDDATA; + + bytestream2_init(gb, s->lzf_buffer, uncompressed_size); + } else if (s->format == 1) { + av_fast_padded_malloc(&s->uncompressed_buffer, &s->uncompressed_size, + uncompressed_size); + if (!s->uncompressed_buffer) + return AVERROR(ENOMEM); + + bytestream2_init_writer(pb, s->uncompressed_buffer, s->uncompressed_size); + + ret = lz4_decompress(avctx, gb, pb); + if (ret != uncompressed_size) + return AVERROR_INVALIDDATA; + + bytestream2_init(gb, s->uncompressed_buffer, uncompressed_size); + } + + ret = decode_blocks(avctx, p, &frame, uncompressed_size); + if (ret < 0) + return ret; + + p->pict_type = AV_PICTURE_TYPE_I; + p->key_frame = 1; + + *got_frame = 1; + + return avpkt->size; +} + +static av_cold int decode_end(AVCodecContext *avctx) +{ + NotchLCContext *s = avctx->priv_data; + + av_freep(&s->uncompressed_buffer); + s->uncompressed_size = 0; + av_freep(&s->lzf_buffer); + s->lzf_size = 0; + + return 0; +} + +AVCodec ff_notchlc_decoder = { + .name = "notchlc", + .long_name = NULL_IF_CONFIG_SMALL("NotchLC"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_NOTCHLC, + .priv_data_size = sizeof(NotchLCContext), + .init = decode_init, + .close = decode_end, + .decode = decode_frame, + .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, +}; diff --git a/libavcodec/null_bsf.c b/libavcodec/null_bsf.c index 24d26dfb1aa..595945027d0 100644 --- a/libavcodec/null_bsf.c +++ b/libavcodec/null_bsf.c @@ -21,15 +21,10 @@ * Null bitstream filter -- pass the input through unchanged. */ -#include "avcodec.h" #include "bsf.h" - -static int null_filter(AVBSFContext *ctx, AVPacket *pkt) -{ - return ff_bsf_get_packet_ref(ctx, pkt); -} +#include "bsf_internal.h" const AVBitStreamFilter ff_null_bsf = { .name = "null", - .filter = null_filter, + .filter = ff_bsf_get_packet_ref, }; diff --git a/libavcodec/nuv.c b/libavcodec/nuv.c index 75b14bce5b0..3ceaaac4e9d 100644 --- a/libavcodec/nuv.c +++ b/libavcodec/nuv.c @@ -126,15 +126,15 @@ static int codec_reinit(AVCodecContext *avctx, int width, int height, get_quant_quality(c, quality); if (width != c->width || height != c->height) { // also reserve space for a possible additional header - int buf_size = height * width * 3 / 2 + int64_t buf_size = height * (int64_t)width * 3 / 2 + FFMAX(AV_LZO_OUTPUT_PADDING, AV_INPUT_BUFFER_PADDING_SIZE) + RTJPEG_HEADER_SIZE; if (buf_size > INT_MAX/8) return -1; - if ((ret = av_image_check_size(height, width, 0, avctx)) < 0) + if ((ret = ff_set_dimensions(avctx, width, height)) < 0) return ret; - avctx->width = c->width = width; - avctx->height = c->height = height; + c->width = width; + c->height = height; av_fast_malloc(&c->decomp_buf, &c->decomp_size, buf_size); if (!c->decomp_buf) { @@ -162,6 +162,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, int keyframe, ret; int size_change = 0; int minsize = 0; + int flags = 0; int result, init_frame = !avctx->frame_number; enum { NUV_UNCOMPRESSED = '0', @@ -204,6 +205,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, } break; case NUV_COPY_LAST: + flags |= FF_REGET_BUFFER_FLAG_READONLY; keyframe = 0; break; default: @@ -217,6 +219,14 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, case NUV_RTJPEG: minsize = c->width/16 * (c->height/16) * 6; break; + case NUV_BLACK: + case NUV_COPY_LAST: + case NUV_LZO: + case NUV_RTJPEG_IN_LZO: + break; + default: + av_log(avctx, AV_LOG_ERROR, "unknown compression\n"); + return AVERROR_INVALIDDATA; } if (buf_size < minsize / 4) return AVERROR_INVALIDDATA; @@ -268,7 +278,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, init_frame = 1; } - if ((result = ff_reget_buffer(avctx, c->pic)) < 0) + if ((result = ff_reget_buffer(avctx, c->pic, flags)) < 0) return result; if (init_frame) { memset(c->pic->data[0], 0, avctx->height * c->pic->linesize[0]); @@ -305,9 +315,6 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, case NUV_COPY_LAST: /* nothing more to do here */ break; - default: - av_log(avctx, AV_LOG_ERROR, "unknown compression\n"); - return AVERROR_INVALIDDATA; } if ((result = av_frame_ref(picture, c->pic)) < 0) diff --git a/libavcodec/nvdec.c b/libavcodec/nvdec.c index b60da24301d..6168a7e3f1d 100644 --- a/libavcodec/nvdec.c +++ b/libavcodec/nvdec.c @@ -44,6 +44,7 @@ typedef struct NVDECDecoder { CUvideodecoder decoder; AVBufferRef *hw_device_ref; + AVBufferRef *real_hw_frames_ref; CUcontext cuda_ctx; CUstream stream; @@ -163,6 +164,7 @@ static void nvdec_decoder_free(void *opaque, uint8_t *data) CHECK_CU(decoder->cudl->cuCtxPopCurrent(&dummy)); } + av_buffer_unref(&decoder->real_hw_frames_ref); av_buffer_unref(&decoder->hw_device_ref); cuvid_free_functions(&decoder->cvdl); @@ -269,10 +271,61 @@ int ff_nvdec_decode_uninit(AVCodecContext *avctx) return 0; } +static void nvdec_free_dummy(struct AVHWFramesContext *ctx) +{ + av_buffer_pool_uninit(&ctx->pool); +} + +static AVBufferRef *nvdec_alloc_dummy(int size) +{ + return av_buffer_create(NULL, 0, NULL, NULL, 0); +} + +static int nvdec_init_hwframes(AVCodecContext *avctx, AVBufferRef **out_frames_ref, int dummy) +{ + AVHWFramesContext *frames_ctx; + int ret; + + ret = avcodec_get_hw_frames_parameters(avctx, + avctx->hw_device_ctx, + avctx->hwaccel->pix_fmt, + out_frames_ref); + if (ret < 0) + return ret; + + frames_ctx = (AVHWFramesContext*)(*out_frames_ref)->data; + + if (dummy) { + // Copied from ff_decode_get_hw_frames_ctx for compatibility + frames_ctx->initial_pool_size += 3; + + frames_ctx->free = nvdec_free_dummy; + frames_ctx->pool = av_buffer_pool_init(0, nvdec_alloc_dummy); + + if (!frames_ctx->pool) { + av_buffer_unref(out_frames_ref); + return AVERROR(ENOMEM); + } + } else { + // This is normally not used to actually allocate frames from + frames_ctx->initial_pool_size = 0; + } + + ret = av_hwframe_ctx_init(*out_frames_ref); + if (ret < 0) { + av_buffer_unref(out_frames_ref); + return ret; + } + + return 0; +} + int ff_nvdec_decode_init(AVCodecContext *avctx) { NVDECContext *ctx = avctx->internal->hwaccel_priv_data; + NVDECDecoder *decoder; + AVBufferRef *real_hw_frames_ref; NVDECFramePool *pool; AVHWFramesContext *frames_ctx; const AVPixFmtDescriptor *sw_desc; @@ -301,9 +354,17 @@ int ff_nvdec_decode_init(AVCodecContext *avctx) chroma_444 = ctx->supports_444 && cuvid_chroma_format == cudaVideoChromaFormat_444; if (!avctx->hw_frames_ctx) { - ret = ff_decode_get_hw_frames_ctx(avctx, AV_HWDEVICE_TYPE_CUDA); + ret = nvdec_init_hwframes(avctx, &avctx->hw_frames_ctx, 1); + if (ret < 0) + return ret; + + ret = nvdec_init_hwframes(avctx, &real_hw_frames_ref, 0); if (ret < 0) return ret; + } else { + real_hw_frames_ref = av_buffer_ref(avctx->hw_frames_ctx); + if (!real_hw_frames_ref) + return AVERROR(ENOMEM); } switch (sw_desc->comp[0].depth) { @@ -318,6 +379,7 @@ int ff_nvdec_decode_init(AVCodecContext *avctx) break; default: av_log(avctx, AV_LOG_ERROR, "Unsupported bit depth\n"); + av_buffer_unref(&real_hw_frames_ref); return AVERROR(ENOSYS); } @@ -342,9 +404,14 @@ int ff_nvdec_decode_init(AVCodecContext *avctx) av_log(avctx, AV_LOG_WARNING, "Try lowering the amount of threads. Using %d right now.\n", avctx->thread_count); } + av_buffer_unref(&real_hw_frames_ref); return ret; } + decoder = (NVDECDecoder*)ctx->decoder_ref->data; + decoder->real_hw_frames_ref = real_hw_frames_ref; + real_hw_frames_ref = NULL; + pool = av_mallocz(sizeof(*pool)); if (!pool) { ret = AVERROR(ENOMEM); @@ -447,6 +514,13 @@ static int nvdec_retrieve_data(void *logctx, AVFrame *frame) goto copy_fail; } + av_buffer_unref(&frame->hw_frames_ctx); + frame->hw_frames_ctx = av_buffer_ref(decoder->real_hw_frames_ref); + if (!frame->hw_frames_ctx) { + ret = AVERROR(ENOMEM); + goto copy_fail; + } + unmap_data->idx = cf->idx; unmap_data->idx_ref = av_buffer_ref(cf->idx_ref); unmap_data->decoder_ref = av_buffer_ref(cf->decoder_ref); @@ -575,16 +649,6 @@ int ff_nvdec_simple_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, return 0; } -static void nvdec_free_dummy(struct AVHWFramesContext *ctx) -{ - av_buffer_pool_uninit(&ctx->pool); -} - -static AVBufferRef *nvdec_alloc_dummy(int size) -{ - return av_buffer_create(NULL, 0, NULL, NULL, 0); -} - int ff_nvdec_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx, int dpb_size, @@ -620,12 +684,6 @@ int ff_nvdec_frame_params(AVCodecContext *avctx, */ frames_ctx->initial_pool_size = dpb_size + 2; - frames_ctx->free = nvdec_free_dummy; - frames_ctx->pool = av_buffer_pool_init(0, nvdec_alloc_dummy); - - if (!frames_ctx->pool) - return AVERROR(ENOMEM); - switch (sw_desc->comp[0].depth) { case 8: frames_ctx->sw_format = chroma_444 ? AV_PIX_FMT_YUV444P : AV_PIX_FMT_NV12; diff --git a/libavcodec/nvdec_mpeg12.c b/libavcodec/nvdec_mpeg12.c index 300e1d3d88d..9a9030d8d35 100644 --- a/libavcodec/nvdec_mpeg12.c +++ b/libavcodec/nvdec_mpeg12.c @@ -50,6 +50,10 @@ static int nvdec_mpeg12_start_frame(AVCodecContext *avctx, const uint8_t *buffer .FrameHeightInMbs = (cur_frame->height + 15) / 16, .CurrPicIdx = cf->idx, + .field_pic_flag = s->picture_structure != PICT_FRAME, + .bottom_field_flag = s->picture_structure == PICT_BOTTOM_FIELD, + .second_field = s->picture_structure != PICT_FRAME && !s->first_field, + .intra_pic_flag = s->pict_type == AV_PICTURE_TYPE_I, .ref_pic_flag = s->pict_type == AV_PICTURE_TYPE_I || s->pict_type == AV_PICTURE_TYPE_P, diff --git a/libavcodec/nvenc.c b/libavcodec/nvenc.c index 3fe64bba8bd..e269c716a4d 100644 --- a/libavcodec/nvenc.c +++ b/libavcodec/nvenc.c @@ -31,6 +31,7 @@ #include "libavutil/mem.h" #include "libavutil/pixdesc.h" #include "internal.h" +#include "packet_internal.h" #define CHECK_CU(x) FF_CUDA_CHECK_DL(avctx, dl_fn->cuda_dl, x) @@ -55,6 +56,16 @@ const enum AVPixelFormat ff_nvenc_pix_fmts[] = { AV_PIX_FMT_NONE }; +const AVCodecHWConfigInternal *ff_nvenc_hw_configs[] = { + HW_CONFIG_ENCODER_FRAMES(CUDA, CUDA), + HW_CONFIG_ENCODER_DEVICE(NONE, CUDA), +#if CONFIG_D3D11VA + HW_CONFIG_ENCODER_FRAMES(D3D11, D3D11VA), + HW_CONFIG_ENCODER_DEVICE(NONE, D3D11VA), +#endif + NULL, +}; + #define IS_10BIT(pix_fmt) (pix_fmt == AV_PIX_FMT_P010 || \ pix_fmt == AV_PIX_FMT_P016 || \ pix_fmt == AV_PIX_FMT_YUV444P16) @@ -110,19 +121,37 @@ static int nvenc_map_error(NVENCSTATUS err, const char **desc) return AVERROR_UNKNOWN; } -static int nvenc_print_error(void *log_ctx, NVENCSTATUS err, +static int nvenc_print_error(AVCodecContext *avctx, NVENCSTATUS err, const char *error_string) { const char *desc; - int ret; - ret = nvenc_map_error(err, &desc); - av_log(log_ctx, AV_LOG_ERROR, "%s: %s (%d)\n", error_string, desc, err); + const char *details = "(no details)"; + int ret = nvenc_map_error(err, &desc); + +#ifdef NVENC_HAVE_GETLASTERRORSTRING + NvencContext *ctx = avctx->priv_data; + NV_ENCODE_API_FUNCTION_LIST *p_nvenc = &ctx->nvenc_dload_funcs.nvenc_funcs; + + if (p_nvenc && ctx->nvencoder) + details = p_nvenc->nvEncGetLastErrorString(ctx->nvencoder); +#endif + + av_log(avctx, AV_LOG_ERROR, "%s: %s (%d): %s\n", error_string, desc, err, details); + return ret; } static void nvenc_print_driver_requirement(AVCodecContext *avctx, int level) { -#if NVENCAPI_CHECK_VERSION(9, 0) +#if NVENCAPI_CHECK_VERSION(9, 2) + const char *minver = "(unknown)"; +#elif NVENCAPI_CHECK_VERSION(9, 1) +# if defined(_WIN32) || defined(__CYGWIN__) + const char *minver = "436.15"; +# else + const char *minver = "435.21"; +# endif +#elif NVENCAPI_CHECK_VERSION(9, 0) # if defined(_WIN32) || defined(__CYGWIN__) const char *minver = "418.81"; # else @@ -303,39 +332,39 @@ static int nvenc_check_capabilities(AVCodecContext *avctx) ret = nvenc_check_codec_support(avctx); if (ret < 0) { - av_log(avctx, AV_LOG_VERBOSE, "Codec not supported\n"); + av_log(avctx, AV_LOG_WARNING, "Codec not supported\n"); return ret; } ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_YUV444_ENCODE); if (IS_YUV444(ctx->data_pix_fmt) && ret <= 0) { - av_log(avctx, AV_LOG_VERBOSE, "YUV444P not supported\n"); + av_log(avctx, AV_LOG_WARNING, "YUV444P not supported\n"); return AVERROR(ENOSYS); } ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE); if (ctx->preset >= PRESET_LOSSLESS_DEFAULT && ret <= 0) { - av_log(avctx, AV_LOG_VERBOSE, "Lossless encoding not supported\n"); + av_log(avctx, AV_LOG_WARNING, "Lossless encoding not supported\n"); return AVERROR(ENOSYS); } ret = nvenc_check_cap(avctx, NV_ENC_CAPS_WIDTH_MAX); if (ret < avctx->width) { - av_log(avctx, AV_LOG_VERBOSE, "Width %d exceeds %d\n", + av_log(avctx, AV_LOG_WARNING, "Width %d exceeds %d\n", avctx->width, ret); return AVERROR(ENOSYS); } ret = nvenc_check_cap(avctx, NV_ENC_CAPS_HEIGHT_MAX); if (ret < avctx->height) { - av_log(avctx, AV_LOG_VERBOSE, "Height %d exceeds %d\n", + av_log(avctx, AV_LOG_WARNING, "Height %d exceeds %d\n", avctx->height, ret); return AVERROR(ENOSYS); } ret = nvenc_check_cap(avctx, NV_ENC_CAPS_NUM_MAX_BFRAMES); if (ret < avctx->max_b_frames) { - av_log(avctx, AV_LOG_VERBOSE, "Max B-frames %d exceed %d\n", + av_log(avctx, AV_LOG_WARNING, "Max B-frames %d exceed %d\n", avctx->max_b_frames, ret); return AVERROR(ENOSYS); @@ -343,7 +372,7 @@ static int nvenc_check_capabilities(AVCodecContext *avctx) ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_FIELD_ENCODING); if (ret < 1 && avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT) { - av_log(avctx, AV_LOG_VERBOSE, + av_log(avctx, AV_LOG_WARNING, "Interlaced encoding is not supported. Supported level: %d\n", ret); return AVERROR(ENOSYS); @@ -351,46 +380,59 @@ static int nvenc_check_capabilities(AVCodecContext *avctx) ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_10BIT_ENCODE); if (IS_10BIT(ctx->data_pix_fmt) && ret <= 0) { - av_log(avctx, AV_LOG_VERBOSE, "10 bit encode not supported\n"); + av_log(avctx, AV_LOG_WARNING, "10 bit encode not supported\n"); return AVERROR(ENOSYS); } ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_LOOKAHEAD); if (ctx->rc_lookahead > 0 && ret <= 0) { - av_log(avctx, AV_LOG_VERBOSE, "RC lookahead not supported\n"); + av_log(avctx, AV_LOG_WARNING, "RC lookahead not supported\n"); return AVERROR(ENOSYS); } ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ); if (ctx->temporal_aq > 0 && ret <= 0) { - av_log(avctx, AV_LOG_VERBOSE, "Temporal AQ not supported\n"); + av_log(avctx, AV_LOG_WARNING, "Temporal AQ not supported\n"); return AVERROR(ENOSYS); } ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION); if (ctx->weighted_pred > 0 && ret <= 0) { - av_log (avctx, AV_LOG_VERBOSE, "Weighted Prediction not supported\n"); + av_log (avctx, AV_LOG_WARNING, "Weighted Prediction not supported\n"); return AVERROR(ENOSYS); } ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_CABAC); if (ctx->coder == NV_ENC_H264_ENTROPY_CODING_MODE_CABAC && ret <= 0) { - av_log(avctx, AV_LOG_VERBOSE, "CABAC entropy coding not supported\n"); + av_log(avctx, AV_LOG_WARNING, "CABAC entropy coding not supported\n"); return AVERROR(ENOSYS); } #ifdef NVENC_HAVE_BFRAME_REF_MODE ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE); if (ctx->b_ref_mode == NV_ENC_BFRAME_REF_MODE_EACH && ret != 1) { - av_log(avctx, AV_LOG_VERBOSE, "Each B frame as reference is not supported\n"); + av_log(avctx, AV_LOG_WARNING, "Each B frame as reference is not supported\n"); return AVERROR(ENOSYS); } else if (ctx->b_ref_mode != NV_ENC_BFRAME_REF_MODE_DISABLED && ret == 0) { - av_log(avctx, AV_LOG_VERBOSE, "B frames as references are not supported\n"); + av_log(avctx, AV_LOG_WARNING, "B frames as references are not supported\n"); return AVERROR(ENOSYS); } #else if (ctx->b_ref_mode != 0) { - av_log(avctx, AV_LOG_VERBOSE, "B frames as references need SDK 8.1 at build time\n"); + av_log(avctx, AV_LOG_WARNING, "B frames as references need SDK 8.1 at build time\n"); + return AVERROR(ENOSYS); + } +#endif + +#ifdef NVENC_HAVE_MULTIPLE_REF_FRAMES + ret = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES); + if(avctx->refs != NV_ENC_NUM_REF_FRAMES_AUTOSELECT && ret <= 0) { + av_log(avctx, AV_LOG_WARNING, "Multiple reference frames are not supported by the device\n"); + return AVERROR(ENOSYS); + } +#else + if(avctx->refs != 0) { + av_log(avctx, AV_LOG_WARNING, "Multiple reference frames need SDK 9.1 at build time\n"); return AVERROR(ENOSYS); } #endif @@ -439,6 +481,7 @@ static av_cold int nvenc_check_device(AVCodecContext *avctx, int idx) goto fail; ctx->cu_context = ctx->cu_context_internal; + ctx->cu_stream = NULL; if ((ret = nvenc_pop_context(avctx)) < 0) goto fail2; @@ -525,6 +568,7 @@ static av_cold int nvenc_setup_device(AVCodecContext *avctx) if (cuda_device_hwctx) { ctx->cu_context = cuda_device_hwctx->cuda_ctx; + ctx->cu_stream = cuda_device_hwctx->stream; } #if CONFIG_D3D11VA else if (d3d11_device_hwctx) { @@ -568,7 +612,7 @@ static av_cold int nvenc_setup_device(AVCodecContext *avctx) return AVERROR_EXIT; if (!dl_fn->nvenc_device_count) { - av_log(avctx, AV_LOG_FATAL, "No NVENC capable devices found\n"); + av_log(avctx, AV_LOG_FATAL, "No capable devices found\n"); return AVERROR_EXTERNAL; } @@ -904,12 +948,17 @@ static av_cold void nvenc_setup_rate_control(AVCodecContext *avctx) if (ctx->zerolatency) ctx->encode_config.rcParams.zeroReorderDelay = 1; - if (ctx->quality) - { + if (ctx->quality) { //convert from float to fixed point 8.8 int tmp_quality = (int)(ctx->quality * 256.0f); ctx->encode_config.rcParams.targetQuality = (uint8_t)(tmp_quality >> 8); ctx->encode_config.rcParams.targetQualityLSB = (uint8_t)(tmp_quality & 0xff); + + av_log(avctx, AV_LOG_VERBOSE, "CQ(%d) mode enabled.\n", tmp_quality); + + //CQ mode shall discard avg bitrate & honor max bitrate; + ctx->encode_config.rcParams.averageBitRate = avctx->bit_rate = 0; + ctx->encode_config.rcParams.maxBitRate = avctx->rc_max_rate; } } @@ -941,9 +990,9 @@ static av_cold int nvenc_setup_h264_config(AVCodecContext *avctx) h264->repeatSPSPPS = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) ? 0 : 1; h264->outputAUD = ctx->aud; - if (avctx->refs >= 0) { + if (ctx->dpb_size >= 0) { /* 0 means "let the hardware decide" */ - h264->maxNumRefFrames = avctx->refs; + h264->maxNumRefFrames = ctx->dpb_size; } if (avctx->gop_size >= 0) { h264->idrPeriod = cc->gopLength; @@ -1002,6 +1051,11 @@ static av_cold int nvenc_setup_h264_config(AVCodecContext *avctx) h264->useBFramesAsRef = ctx->b_ref_mode; #endif +#ifdef NVENC_HAVE_MULTIPLE_REF_FRAMES + h264->numRefL0 = avctx->refs; + h264->numRefL1 = avctx->refs; +#endif + return 0; } @@ -1033,9 +1087,9 @@ static av_cold int nvenc_setup_hevc_config(AVCodecContext *avctx) hevc->repeatSPSPPS = (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) ? 0 : 1; hevc->outputAUD = ctx->aud; - if (avctx->refs >= 0) { + if (ctx->dpb_size >= 0) { /* 0 means "let the hardware decide" */ - hevc->maxNumRefFramesInDPB = avctx->refs; + hevc->maxNumRefFramesInDPB = ctx->dpb_size; } if (avctx->gop_size >= 0) { hevc->idrPeriod = cc->gopLength; @@ -1086,6 +1140,11 @@ static av_cold int nvenc_setup_hevc_config(AVCodecContext *avctx) hevc->useBFramesAsRef = ctx->b_ref_mode; #endif +#ifdef NVENC_HAVE_MULTIPLE_REF_FRAMES + hevc->numRefL0 = avctx->refs; + hevc->numRefL1 = avctx->refs; +#endif + return 0; } @@ -1156,8 +1215,13 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx) ctx->init_encode_params.darHeight = dh; ctx->init_encode_params.darWidth = dw; - ctx->init_encode_params.frameRateNum = avctx->time_base.den; - ctx->init_encode_params.frameRateDen = avctx->time_base.num * avctx->ticks_per_frame; + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { + ctx->init_encode_params.frameRateNum = avctx->framerate.num; + 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; + } ctx->init_encode_params.enableEncodeAsync = 0; ctx->init_encode_params.enablePTD = 1; @@ -1167,7 +1231,7 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx) if (ctx->bluray_compat) { ctx->aud = 1; - avctx->refs = FFMIN(FFMAX(avctx->refs, 0), 6); + ctx->dpb_size = FFMIN(FFMAX(avctx->refs, 0), 6); avctx->max_b_frames = FFMIN(avctx->max_b_frames, 3); switch (avctx->codec->id) { case AV_CODEC_ID_H264: @@ -1192,9 +1256,6 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx) ctx->encode_config.gopLength = 1; } - ctx->initial_pts[0] = AV_NOPTS_VALUE; - ctx->initial_pts[1] = AV_NOPTS_VALUE; - nvenc_recalc_surfaces(avctx); nvenc_setup_rate_control(avctx); @@ -1214,15 +1275,25 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx) return res; nv_status = p_nvenc->nvEncInitializeEncoder(ctx->nvencoder, &ctx->init_encode_params); + if (nv_status != NV_ENC_SUCCESS) { + nvenc_pop_context(avctx); + return nvenc_print_error(avctx, nv_status, "InitializeEncoder failed"); + } + +#ifdef NVENC_HAVE_CUSTREAM_PTR + if (ctx->cu_context) { + nv_status = p_nvenc->nvEncSetIOCudaStreams(ctx->nvencoder, &ctx->cu_stream, &ctx->cu_stream); + if (nv_status != NV_ENC_SUCCESS) { + nvenc_pop_context(avctx); + return nvenc_print_error(avctx, nv_status, "SetIOCudaStreams failed"); + } + } +#endif res = nvenc_pop_context(avctx); if (res < 0) return res; - if (nv_status != NV_ENC_SUCCESS) { - return nvenc_print_error(avctx, nv_status, "InitializeEncoder failed"); - } - if (ctx->encode_config.frameIntervalP > 1) avctx->has_b_frames = 2; @@ -1756,30 +1827,10 @@ static int nvenc_set_timestamp(AVCodecContext *avctx, NvencContext *ctx = avctx->priv_data; pkt->pts = params->outputTimeStamp; - - /* generate the first dts by linearly extrapolating the - * first two pts values to the past */ - if (avctx->max_b_frames > 0 && !ctx->first_packet_output && - ctx->initial_pts[1] != AV_NOPTS_VALUE) { - int64_t ts0 = ctx->initial_pts[0], ts1 = ctx->initial_pts[1]; - int64_t delta; - - if ((ts0 < 0 && ts1 > INT64_MAX + ts0) || - (ts0 > 0 && ts1 < INT64_MIN + ts0)) - return AVERROR(ERANGE); - delta = ts1 - ts0; - - if ((delta < 0 && ts0 > INT64_MAX + delta) || - (delta > 0 && ts0 < INT64_MIN + delta)) - return AVERROR(ERANGE); - pkt->dts = ts0 - delta; - - ctx->first_packet_output = 1; - return 0; - } - pkt->dts = timestamp_queue_dequeue(ctx->timestamp_list); + pkt->dts -= FFMAX(avctx->max_b_frames, 0) * FFMIN(avctx->ticks_per_frame, 1); + return 0; } @@ -1828,7 +1879,11 @@ static int process_output_surface(AVCodecContext *avctx, AVPacket *pkt, NvencSur goto error; } - if (res = ff_alloc_packet2(avctx, pkt, lock_params.bitstreamSizeInBytes,0)) { + res = pkt->data ? + ff_alloc_packet2(avctx, pkt, lock_params.bitstreamSizeInBytes, lock_params.bitstreamSizeInBytes) : + av_new_packet(pkt, lock_params.bitstreamSizeInBytes); + + if (res < 0) { p_nvenc->nvEncUnlockBitstream(ctx->nvencoder, tmpoutsurf->output_surface); goto error; } @@ -1913,12 +1968,6 @@ static int output_ready(AVCodecContext *avctx, int flush) NvencContext *ctx = avctx->priv_data; int nb_ready, nb_pending; - /* when B-frames are enabled, we wait for two initial timestamps to - * calculate the first dts */ - if (!flush && avctx->max_b_frames > 0 && - (ctx->initial_pts[0] == AV_NOPTS_VALUE || ctx->initial_pts[1] == AV_NOPTS_VALUE)) - return 0; - nb_ready = av_fifo_size(ctx->output_surface_ready_queue) / sizeof(NvencSurface*); nb_pending = av_fifo_size(ctx->output_surface_queue) / sizeof(NvencSurface*); if (flush) @@ -2041,9 +2090,6 @@ int ff_nvenc_send_frame(AVCodecContext *avctx, const AVFrame *frame) return AVERROR_EOF; ctx->encoder_flushing = 0; - ctx->first_packet_output = 0; - ctx->initial_pts[0] = AV_NOPTS_VALUE; - ctx->initial_pts[1] = AV_NOPTS_VALUE; av_fifo_reset(ctx->timestamp_list); } @@ -2128,11 +2174,6 @@ int ff_nvenc_send_frame(AVCodecContext *avctx, const AVFrame *frame) if (frame) { av_fifo_generic_write(ctx->output_surface_queue, &in_surf, sizeof(in_surf), NULL); timestamp_queue_enqueue(ctx->timestamp_list, frame->pts); - - if (ctx->initial_pts[0] == AV_NOPTS_VALUE) - ctx->initial_pts[0] = frame->pts; - else if (ctx->initial_pts[1] == AV_NOPTS_VALUE) - ctx->initial_pts[1] = frame->pts; } /* all the pending buffers are now ready for output */ @@ -2205,3 +2246,8 @@ int ff_nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt, return 0; } + +av_cold void ff_nvenc_encode_flush(AVCodecContext *avctx) +{ + ff_nvenc_send_frame(avctx, NULL); +} diff --git a/libavcodec/nvenc.h b/libavcodec/nvenc.h index ddd61684090..7a415a48355 100644 --- a/libavcodec/nvenc.h +++ b/libavcodec/nvenc.h @@ -33,6 +33,7 @@ typedef void ID3D11Device; #include "compat/cuda/dynlink_loader.h" #include "libavutil/fifo.h" #include "libavutil/opt.h" +#include "hwconfig.h" #include "avcodec.h" @@ -54,6 +55,13 @@ typedef void ID3D11Device; #define NVENC_HAVE_HEVC_BFRAME_REF_MODE #endif +// SDK 9.1 compile time feature checks +#if NVENCAPI_CHECK_VERSION(9, 1) +#define NVENC_HAVE_MULTIPLE_REF_FRAMES +#define NVENC_HAVE_CUSTREAM_PTR +#define NVENC_HAVE_GETLASTERRORSTRING +#endif + typedef struct NvencSurface { NV_ENC_INPUT_PTR input_surface; @@ -127,6 +135,7 @@ typedef struct NvencContext NV_ENC_CONFIG encode_config; CUcontext cu_context; CUcontext cu_context_internal; + CUstream cu_stream; ID3D11Device *d3d11_device; int nb_surfaces; @@ -152,11 +161,6 @@ typedef struct NvencContext * AVCodecContext.pix_fmt when using hwaccel frames on input */ enum AVPixelFormat data_pix_fmt; - /* timestamps of the first two frames, for computing the first dts - * when B-frames are present */ - int64_t initial_pts[2]; - int first_packet_output; - int support_dyn_bitrate; void *nvencoder; @@ -192,6 +196,7 @@ typedef struct NvencContext int coder; int b_ref_mode; int a53_cc; + int dpb_size; } NvencContext; int ff_nvenc_encode_init(AVCodecContext *avctx); @@ -205,6 +210,9 @@ int ff_nvenc_receive_packet(AVCodecContext *avctx, AVPacket *pkt); int ff_nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet); +void ff_nvenc_encode_flush(AVCodecContext *avctx); + extern const enum AVPixelFormat ff_nvenc_pix_fmts[]; +extern const AVCodecHWConfigInternal *ff_nvenc_hw_configs[]; #endif /* AVCODEC_NVENC_H */ diff --git a/libavcodec/nvenc_h264.c b/libavcodec/nvenc_h264.c index a6623f5f35a..c877cf4a303 100644 --- a/libavcodec/nvenc_h264.c +++ b/libavcodec/nvenc_h264.c @@ -99,7 +99,9 @@ static const AVOption options[] = { { "b_adapt", "When lookahead is enabled, set this to 0 to disable adaptive B-frame decision", OFFSET(b_adapt), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, { "spatial-aq", "set to 1 to enable Spatial AQ", OFFSET(aq), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, + { "spatial_aq", "set to 1 to enable Spatial AQ", OFFSET(aq), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "temporal-aq", "set to 1 to enable Temporal AQ", OFFSET(temporal_aq), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, + { "temporal_aq", "set to 1 to enable Temporal AQ", OFFSET(temporal_aq), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "zerolatency", "Set 1 to indicate zero latency operation (no reordering delay)", OFFSET(zerolatency), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "nonref_p", "Set this to 1 to enable automatic insertion of non-reference P-frames", @@ -138,6 +140,8 @@ static const AVOption options[] = { { "middle", "", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, VE, "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 }, { NULL } }; @@ -180,13 +184,16 @@ AVCodec ff_nvenc_encoder = { .receive_packet = ff_nvenc_receive_packet, .encode2 = ff_nvenc_encode_frame, .close = ff_nvenc_encode_close, + .flush = ff_nvenc_encode_flush, .priv_data_size = sizeof(NvencContext), .priv_class = &nvenc_class, .defaults = defaults, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | + AV_CODEC_CAP_ENCODER_FLUSH, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = ff_nvenc_pix_fmts, .wrapper_name = "nvenc", + .hw_configs = ff_nvenc_hw_configs, }; #endif @@ -209,13 +216,16 @@ AVCodec ff_nvenc_h264_encoder = { .receive_packet = ff_nvenc_receive_packet, .encode2 = ff_nvenc_encode_frame, .close = ff_nvenc_encode_close, + .flush = ff_nvenc_encode_flush, .priv_data_size = sizeof(NvencContext), .priv_class = &nvenc_h264_class, .defaults = defaults, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | + AV_CODEC_CAP_ENCODER_FLUSH, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = ff_nvenc_pix_fmts, .wrapper_name = "nvenc", + .hw_configs = ff_nvenc_hw_configs, }; #endif @@ -238,11 +248,14 @@ AVCodec ff_h264_nvenc_encoder = { .receive_packet = ff_nvenc_receive_packet, .encode2 = ff_nvenc_encode_frame, .close = ff_nvenc_encode_close, + .flush = ff_nvenc_encode_flush, .priv_data_size = sizeof(NvencContext), .priv_class = &h264_nvenc_class, .defaults = defaults, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | + AV_CODEC_CAP_ENCODER_FLUSH, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = ff_nvenc_pix_fmts, .wrapper_name = "nvenc", + .hw_configs = ff_nvenc_hw_configs, }; diff --git a/libavcodec/nvenc_hevc.c b/libavcodec/nvenc_hevc.c index d567d960baa..7f12b56a461 100644 --- a/libavcodec/nvenc_hevc.c +++ b/libavcodec/nvenc_hevc.c @@ -96,7 +96,9 @@ static const AVOption options[] = { { "forced-idr", "If forcing keyframes, force them as IDR frames.", OFFSET(forced_idr), AV_OPT_TYPE_BOOL, { .i64 = 0 }, -1, 1, VE }, { "spatial_aq", "set to 1 to enable Spatial AQ", OFFSET(aq), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, + { "spatial-aq", "set to 1 to enable Spatial AQ", OFFSET(aq), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "temporal_aq", "set to 1 to enable Temporal AQ", OFFSET(temporal_aq), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, + { "temporal-aq", "set to 1 to enable Temporal AQ", OFFSET(temporal_aq), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "zerolatency", "Set 1 to indicate zero latency operation (no reordering delay)", OFFSET(zerolatency), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "nonref_p", "Set this to 1 to enable automatic insertion of non-reference P-frames", @@ -127,6 +129,8 @@ static const AVOption options[] = { { "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" }, #endif + { "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 }, { NULL } }; @@ -172,9 +176,11 @@ AVCodec ff_nvenc_hevc_encoder = { .priv_class = &nvenc_hevc_class, .defaults = defaults, .pix_fmts = ff_nvenc_pix_fmts, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | + AV_CODEC_CAP_ENCODER_FLUSH, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .wrapper_name = "nvenc", + .hw_configs = ff_nvenc_hw_configs, }; #endif @@ -196,11 +202,14 @@ AVCodec ff_hevc_nvenc_encoder = { .receive_packet = ff_nvenc_receive_packet, .encode2 = ff_nvenc_encode_frame, .close = ff_nvenc_encode_close, + .flush = ff_nvenc_encode_flush, .priv_data_size = sizeof(NvencContext), .priv_class = &hevc_nvenc_class, .defaults = defaults, .pix_fmts = ff_nvenc_pix_fmts, - .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | + AV_CODEC_CAP_ENCODER_FLUSH, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .wrapper_name = "nvenc", + .hw_configs = ff_nvenc_hw_configs, }; diff --git a/libavcodec/omx.c b/libavcodec/omx.c index a555c3c07dd..0a6a3083090 100644 --- a/libavcodec/omx.c +++ b/libavcodec/omx.c @@ -799,6 +799,26 @@ static int omx_encode_frame(AVCodecContext *avctx, AVPacket *pkt, // Convert the timestamps to microseconds; some encoders can ignore // the framerate and do VFR bit allocation based on timestamps. buffer->nTimeStamp = to_omx_ticks(av_rescale_q(frame->pts, avctx->time_base, AV_TIME_BASE_Q)); + if (frame->pict_type == AV_PICTURE_TYPE_I) { +#if CONFIG_OMX_RPI + OMX_CONFIG_BOOLEANTYPE config = {0, }; + INIT_STRUCT(config); + config.bEnabled = OMX_TRUE; + err = OMX_SetConfig(s->handle, OMX_IndexConfigBrcmVideoRequestIFrame, &config); + if (err != OMX_ErrorNone) { + av_log(avctx, AV_LOG_ERROR, "OMX_SetConfig(RequestIFrame) failed: %x\n", err); + } +#else + OMX_CONFIG_INTRAREFRESHVOPTYPE config = {0, }; + INIT_STRUCT(config); + config.nPortIndex = s->out_port; + config.IntraRefreshVOP = OMX_TRUE; + err = OMX_SetConfig(s->handle, OMX_IndexConfigVideoIntraVOPRefresh, &config); + if (err != OMX_ErrorNone) { + av_log(avctx, AV_LOG_ERROR, "OMX_SetConfig(IntraVOPRefresh) failed: %x\n", err); + } +#endif + } err = OMX_EmptyThisBuffer(s->handle, buffer); if (err != OMX_ErrorNone) { append_buffer(&s->input_mutex, &s->input_cond, &s->num_free_in_buffers, s->free_in_buffers, buffer); diff --git a/libavcodec/options.c b/libavcodec/options.c index 35e8ac93137..7706a032971 100644 --- a/libavcodec/options.c +++ b/libavcodec/options.c @@ -55,15 +55,16 @@ static void *codec_child_next(void *obj, void *prev) static const AVClass *codec_child_class_next(const AVClass *prev) { - AVCodec *c = NULL; + void *iter = NULL; + const AVCodec *c = NULL; /* find the codec that corresponds to prev */ - while (prev && (c = av_codec_next(c))) + while (prev && (c = av_codec_iterate(&iter))) if (c->priv_class == prev) break; /* find next codec with priv options */ - while (c = av_codec_next(c)) + while (c = av_codec_iterate(&iter)) if (c->priv_class) return c->priv_class; return NULL; diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h index 4a266eca169..8ba137f51eb 100644 --- a/libavcodec/options_table.h +++ b/libavcodec/options_table.h @@ -38,6 +38,7 @@ #define S AV_OPT_FLAG_SUBTITLE_PARAM #define E AV_OPT_FLAG_ENCODING_PARAM #define D AV_OPT_FLAG_DECODING_PARAM +#define CC AV_OPT_FLAG_CHILD_CONSTS #define AV_CODEC_DEFAULT_BITRATE 200*1000 @@ -68,7 +69,7 @@ static const AVOption avcodec_options[] = { {"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, "flags2"}, +{"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"}, @@ -76,8 +77,12 @@ static const AVOption avcodec_options[] = { {"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, 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"}, +{"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"}, {"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}, @@ -141,8 +146,8 @@ static const AVOption avcodec_options[] = { {"explode", "abort decoding on minor error detection", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_EXPLODE }, INT_MIN, INT_MAX, A|V|D, "err_detect"}, {"ignore_err", "ignore errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_IGNORE_ERR }, INT_MIN, INT_MAX, A|V|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, A|V|D, "err_detect"}, -{"compliant", "consider all spec non compliancies as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_COMPLIANT }, INT_MIN, INT_MAX, A|V|D, "err_detect"}, -{"aggressive", "consider things that a sane encoder should not do as an error", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_AGGRESSIVE }, INT_MIN, INT_MAX, A|V|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, A|V|D, "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|D, "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}, #if FF_API_PRIVATE_OPT @@ -256,29 +261,9 @@ static const AVOption avcodec_options[] = { {"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, "profile"}, -{"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, V|A|E, "profile"}, -{"aac_main", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_AAC_MAIN }, INT_MIN, INT_MAX, A|E, "profile"}, -{"aac_low", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_AAC_LOW }, INT_MIN, INT_MAX, A|E, "profile"}, -{"aac_ssr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_AAC_SSR }, INT_MIN, INT_MAX, A|E, "profile"}, -{"aac_ltp", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_AAC_LTP }, INT_MIN, INT_MAX, A|E, "profile"}, -{"aac_he", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_AAC_HE }, INT_MIN, INT_MAX, A|E, "profile"}, -{"aac_he_v2", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_AAC_HE_V2 }, INT_MIN, INT_MAX, A|E, "profile"}, -{"aac_ld", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_AAC_LD }, INT_MIN, INT_MAX, A|E, "profile"}, -{"aac_eld", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_AAC_ELD }, INT_MIN, INT_MAX, A|E, "profile"}, -{"mpeg2_aac_low", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_MPEG2_AAC_LOW }, INT_MIN, INT_MAX, A|E, "profile"}, -{"mpeg2_aac_he", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_MPEG2_AAC_HE }, INT_MIN, INT_MAX, A|E, "profile"}, -{"dts", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_DTS }, INT_MIN, INT_MAX, A|E, "profile"}, -{"dts_es", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_DTS_ES }, INT_MIN, INT_MAX, A|E, "profile"}, -{"dts_96_24", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_DTS_96_24 }, INT_MIN, INT_MAX, A|E, "profile"}, -{"dts_hd_hra", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_DTS_HD_HRA }, INT_MIN, INT_MAX, A|E, "profile"}, -{"dts_hd_ma", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_DTS_HD_MA }, INT_MIN, INT_MAX, A|E, "profile"}, -{"mpeg4_sp", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_MPEG4_SIMPLE }, INT_MIN, INT_MAX, V|E, "profile"}, -{"mpeg4_core", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_MPEG4_CORE }, INT_MIN, INT_MAX, V|E, "profile"}, -{"mpeg4_main", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_MPEG4_MAIN }, INT_MIN, INT_MAX, V|E, "profile"}, -{"mpeg4_asp", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_MPEG4_ADVANCED_SIMPLE }, INT_MIN, INT_MAX, V|E, "profile"}, -{"main10", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_HEVC_MAIN_10 }, INT_MIN, INT_MAX, V|E, "profile"}, -{"msbc", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_SBC_MSBC }, INT_MIN, INT_MAX, A|E, "profile"}, +{"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, "level"}, {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_LEVEL_UNKNOWN }, INT_MIN, INT_MAX, V|A|E, "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}, @@ -366,6 +351,7 @@ static const AVOption avcodec_options[] = { {"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"}, @@ -443,7 +429,7 @@ static const AVOption avcodec_options[] = { {"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"}, {"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}, CHAR_MIN, CHAR_MAX, S|D}, +{"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"}, @@ -470,11 +456,12 @@ static const AVOption avcodec_options[] = { {"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" }, -{"dump_separator", "set information dump field separator", OFFSET(dump_separator), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, 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 }, CHAR_MIN, CHAR_MAX, A|V|S|D }, +{"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"}, @@ -489,6 +476,7 @@ static const AVOption avcodec_options[] = { #undef S #undef E #undef D +#undef CC #undef DEFAULT #undef OFFSET diff --git a/libavcodec/opus.c b/libavcodec/opus.c index f74278a7e3a..64de246720d 100644 --- a/libavcodec/opus.c +++ b/libavcodec/opus.c @@ -613,6 +613,8 @@ void ff_celt_bitalloc(CeltFrame *f, OpusRangeCoder *rc, int encode) } /* Allocation trim */ + if (!encode) + f->alloc_trim = 5; if (opus_rc_tell_frac(rc) + (6 << 3) <= tbits_8ths) if (encode) ff_opus_rc_enc_cdf(rc, f->alloc_trim, ff_celt_model_alloc_trim); diff --git a/libavcodec/opus_celt.c b/libavcodec/opus_celt.c index 4655172b09c..9dbeff1927a 100644 --- a/libavcodec/opus_celt.c +++ b/libavcodec/opus_celt.c @@ -507,7 +507,11 @@ void ff_celt_flush(CeltFrame *f) memset(block->pf_gains_old, 0, sizeof(block->pf_gains_old)); memset(block->pf_gains_new, 0, sizeof(block->pf_gains_new)); - block->emph_coeff = 0.0; + /* libopus uses CELT_EMPH_COEFF on init, but 0 is better since there's + * a lesser discontinuity when seeking. + * The deemphasis functions differ from libopus in that they require + * an initial state divided by the coefficient. */ + block->emph_coeff = 0.0f / CELT_EMPH_COEFF; } f->seed = 0; diff --git a/libavcodec/opus_metadata_bsf.c b/libavcodec/opus_metadata_bsf.c new file mode 100644 index 00000000000..723e31c2437 --- /dev/null +++ b/libavcodec/opus_metadata_bsf.c @@ -0,0 +1,67 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 "bsf_internal.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" + +typedef struct OpusBSFContext { + const AVClass *class; + int gain; +} OpusBSFContext; + +static int opus_metadata_init(AVBSFContext *bsfc) +{ + OpusBSFContext *s = bsfc->priv_data; + + if (bsfc->par_out->extradata_size < 19) + return AVERROR_INVALIDDATA; + + AV_WL16(bsfc->par_out->extradata + 16, s->gain); + + return 0; +} + +#define OFFSET(x) offsetof(OpusBSFContext, x) +#define FLAGS (AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_BSF_PARAM) +static const AVOption opus_metadata_options[] = { + { "gain", "Gain, actual amplification is pow(10, gain/(20.0*256))", OFFSET(gain), + AV_OPT_TYPE_INT, { .i64 = 0 }, -(INT16_MAX + 1), INT16_MAX, .flags = FLAGS }, + + { NULL }, +}; + +static const AVClass opus_metadata_class = { + .class_name = "opus_metadata_bsf", + .item_name = av_default_item_name, + .option = opus_metadata_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static const enum AVCodecID codec_ids[] = { + AV_CODEC_ID_OPUS, AV_CODEC_ID_NONE, +}; + +const AVBitStreamFilter ff_opus_metadata_bsf = { + .name = "opus_metadata", + .priv_data_size = sizeof(OpusBSFContext), + .priv_class = &opus_metadata_class, + .init = &opus_metadata_init, + .filter = &ff_bsf_get_packet_ref, + .codec_ids = codec_ids, +}; diff --git a/libavcodec/opus_pvq.c b/libavcodec/opus_pvq.c index 9c21d672980..a4ab7c46ebb 100644 --- a/libavcodec/opus_pvq.c +++ b/libavcodec/opus_pvq.c @@ -627,7 +627,7 @@ static av_always_inline uint32_t quant_band_template(CeltPVQ *pvq, CeltFrame *f, } } else if (stereo) { if (quant) { - inv = itheta > 8192; + inv = f->apply_phase_inv ? itheta > 8192 : 0; if (inv) { for (i = 0; i < N; i++) Y[i] *= -1; diff --git a/libavcodec/opusdsp.c b/libavcodec/opusdsp.c index 0e179c98c97..08df87ffbe9 100644 --- a/libavcodec/opusdsp.c +++ b/libavcodec/opusdsp.c @@ -43,15 +43,10 @@ static void postfilter_c(float *data, int period, float *gains, int len) static float deemphasis_c(float *y, float *x, float coeff, int len) { - float state = coeff; + for (int i = 0; i < len; i++) + coeff = y[i] = x[i] + coeff*CELT_EMPH_COEFF; - for (int i = 0; i < len; i++) { - const float tmp = x[i] + state; - state = tmp * CELT_EMPH_COEFF; - y[i] = tmp; - } - - return state; + return coeff; } av_cold void ff_opus_dsp_init(OpusDSP *ctx) diff --git a/libavcodec/opusenc.c b/libavcodec/opusenc.c index 3c08ebcf695..953749af3a5 100644 --- a/libavcodec/opusenc.c +++ b/libavcodec/opusenc.c @@ -691,7 +691,7 @@ static av_cold int opus_encode_init(AVCodecContext *avctx) s->frame[i].avctx = s->avctx; s->frame[i].seed = 0; s->frame[i].pvq = s->pvq; - s->frame[i].apply_phase_inv = 1; + s->frame[i].apply_phase_inv = s->options.apply_phase_inv; s->frame[i].block[0].emph_coeff = s->frame[i].block[1].emph_coeff = 0.0f; } @@ -701,6 +701,7 @@ 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" }, { NULL }, }; diff --git a/libavcodec/opusenc.h b/libavcodec/opusenc.h index b9162ebec64..b623b3e948b 100644 --- a/libavcodec/opusenc.h +++ b/libavcodec/opusenc.h @@ -42,6 +42,7 @@ typedef struct OpusEncOptions { float max_delay_ms; + int apply_phase_inv; } OpusEncOptions; typedef struct OpusPacketInfo { diff --git a/libavcodec/packet.h b/libavcodec/packet.h new file mode 100644 index 00000000000..41485f45272 --- /dev/null +++ b/libavcodec/packet.h @@ -0,0 +1,722 @@ +/* + * AVPacket public API + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_PACKET_H +#define AVCODEC_PACKET_H + +#include +#include + +#include "libavutil/attributes.h" +#include "libavutil/buffer.h" +#include "libavutil/dict.h" +#include "libavutil/rational.h" + +#include "libavcodec/version.h" + +/** + * @defgroup lavc_packet AVPacket + * + * Types and functions for working with AVPacket. + * @{ + */ +enum AVPacketSideDataType { + /** + * An AV_PKT_DATA_PALETTE side data packet contains exactly AVPALETTE_SIZE + * bytes worth of palette. This side data signals that a new palette is + * present. + */ + AV_PKT_DATA_PALETTE, + + /** + * The AV_PKT_DATA_NEW_EXTRADATA is used to notify the codec or the format + * that the extradata buffer was changed and the receiving side should + * act upon it appropriately. The new extradata is embedded in the side + * data buffer and should be immediately used for processing the current + * frame or packet. + */ + AV_PKT_DATA_NEW_EXTRADATA, + + /** + * An AV_PKT_DATA_PARAM_CHANGE side data packet is laid out as follows: + * @code + * u32le param_flags + * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT) + * s32le channel_count + * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT) + * u64le channel_layout + * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE) + * s32le sample_rate + * if (param_flags & AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS) + * s32le width + * s32le height + * @endcode + */ + AV_PKT_DATA_PARAM_CHANGE, + + /** + * An AV_PKT_DATA_H263_MB_INFO side data packet contains a number of + * structures with info about macroblocks relevant to splitting the + * packet into smaller packets on macroblock edges (e.g. as for RFC 2190). + * That is, it does not necessarily contain info about all macroblocks, + * as long as the distance between macroblocks in the info is smaller + * than the target payload size. + * Each MB info structure is 12 bytes, and is laid out as follows: + * @code + * u32le bit offset from the start of the packet + * u8 current quantizer at the start of the macroblock + * u8 GOB number + * u16le macroblock address within the GOB + * u8 horizontal MV predictor + * u8 vertical MV predictor + * u8 horizontal MV predictor for block number 3 + * u8 vertical MV predictor for block number 3 + * @endcode + */ + AV_PKT_DATA_H263_MB_INFO, + + /** + * This side data should be associated with an audio stream and contains + * ReplayGain information in form of the AVReplayGain struct. + */ + AV_PKT_DATA_REPLAYGAIN, + + /** + * This side data contains a 3x3 transformation matrix describing an affine + * transformation that needs to be applied to the decoded video frames for + * correct presentation. + * + * See libavutil/display.h for a detailed description of the data. + */ + AV_PKT_DATA_DISPLAYMATRIX, + + /** + * This side data should be associated with a video stream and contains + * Stereoscopic 3D information in form of the AVStereo3D struct. + */ + AV_PKT_DATA_STEREO3D, + + /** + * This side data should be associated with an audio stream and corresponds + * to enum AVAudioServiceType. + */ + AV_PKT_DATA_AUDIO_SERVICE_TYPE, + + /** + * This side data contains quality related information from the encoder. + * @code + * u32le quality factor of the compressed frame. Allowed range is between 1 (good) and FF_LAMBDA_MAX (bad). + * u8 picture type + * u8 error count + * u16 reserved + * u64le[error count] sum of squared differences between encoder in and output + * @endcode + */ + AV_PKT_DATA_QUALITY_STATS, + + /** + * This side data contains an integer value representing the stream index + * of a "fallback" track. A fallback track indicates an alternate + * track to use when the current track can not be decoded for some reason. + * e.g. no decoder available for codec. + */ + AV_PKT_DATA_FALLBACK_TRACK, + + /** + * This side data corresponds to the AVCPBProperties struct. + */ + AV_PKT_DATA_CPB_PROPERTIES, + + /** + * Recommmends skipping the specified number of samples + * @code + * u32le number of samples to skip from start of this packet + * u32le number of samples to skip from end of this packet + * u8 reason for start skip + * u8 reason for end skip (0=padding silence, 1=convergence) + * @endcode + */ + AV_PKT_DATA_SKIP_SAMPLES, + + /** + * An AV_PKT_DATA_JP_DUALMONO side data packet indicates that + * the packet may contain "dual mono" audio specific to Japanese DTV + * and if it is true, recommends only the selected channel to be used. + * @code + * u8 selected channels (0=mail/left, 1=sub/right, 2=both) + * @endcode + */ + AV_PKT_DATA_JP_DUALMONO, + + /** + * A list of zero terminated key/value strings. There is no end marker for + * the list, so it is required to rely on the side data size to stop. + */ + AV_PKT_DATA_STRINGS_METADATA, + + /** + * Subtitle event position + * @code + * u32le x1 + * u32le y1 + * u32le x2 + * u32le y2 + * @endcode + */ + AV_PKT_DATA_SUBTITLE_POSITION, + + /** + * Data found in BlockAdditional element of matroska container. There is + * no end marker for the data, so it is required to rely on the side data + * size to recognize the end. 8 byte id (as found in BlockAddId) followed + * by data. + */ + AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, + + /** + * The optional first identifier line of a WebVTT cue. + */ + AV_PKT_DATA_WEBVTT_IDENTIFIER, + + /** + * The optional settings (rendering instructions) that immediately + * follow the timestamp specifier of a WebVTT cue. + */ + AV_PKT_DATA_WEBVTT_SETTINGS, + + /** + * A list of zero terminated key/value strings. There is no end marker for + * the list, so it is required to rely on the side data size to stop. This + * side data includes updated metadata which appeared in the stream. + */ + AV_PKT_DATA_METADATA_UPDATE, + + /** + * MPEGTS stream ID as uint8_t, this is required to pass the stream ID + * information from the demuxer to the corresponding muxer. + */ + AV_PKT_DATA_MPEGTS_STREAM_ID, + + /** + * Mastering display metadata (based on SMPTE-2086:2014). This metadata + * should be associated with a video stream and contains data in the form + * of the AVMasteringDisplayMetadata struct. + */ + AV_PKT_DATA_MASTERING_DISPLAY_METADATA, + + /** + * This side data should be associated with a video stream and corresponds + * to the AVSphericalMapping structure. + */ + AV_PKT_DATA_SPHERICAL, + + /** + * Content light level (based on CTA-861.3). This metadata should be + * associated with a video stream and contains data in the form of the + * AVContentLightMetadata struct. + */ + AV_PKT_DATA_CONTENT_LIGHT_LEVEL, + + /** + * ATSC A53 Part 4 Closed Captions. This metadata should be associated with + * a video stream. A53 CC bitstream is stored as uint8_t in AVPacketSideData.data. + * The number of bytes of CC data is AVPacketSideData.size. + */ + AV_PKT_DATA_A53_CC, + + /** + * This side data is encryption initialization data. + * The format is not part of ABI, use av_encryption_init_info_* methods to + * access. + */ + AV_PKT_DATA_ENCRYPTION_INIT_INFO, + + /** + * This side data contains encryption info for how to decrypt the packet. + * The format is not part of ABI, use av_encryption_info_* methods to access. + */ + AV_PKT_DATA_ENCRYPTION_INFO, + + /** + * Active Format Description data consisting of a single byte as specified + * in ETSI TS 101 154 using AVActiveFormatDescription enum. + */ + AV_PKT_DATA_AFD, + + /** + * Producer Reference Time data corresponding to the AVProducerReferenceTime struct, + * usually exported by some encoders (on demand through the prft flag set in the + * AVCodecContext export_side_data field). + */ + AV_PKT_DATA_PRFT, + + /** + * ICC profile data consisting of an opaque octet buffer following the + * format described by ISO 15076-1. + */ + AV_PKT_DATA_ICC_PROFILE, + + /** + * DOVI configuration + * ref: + * dolby-vision-bitstreams-within-the-iso-base-media-file-format-v2.1.2, section 2.2 + * dolby-vision-bitstreams-in-mpeg-2-transport-stream-multiplex-v1.2, section 3.3 + * Tags are stored in struct AVDOVIDecoderConfigurationRecord. + */ + AV_PKT_DATA_DOVI_CONF, + + /** + * The number of side data types. + * This is not part of the public API/ABI in the sense that it may + * change when new side data types are added. + * This must stay the last enum value. + * If its value becomes huge, some code using it + * needs to be updated as it assumes it to be smaller than other limits. + */ + AV_PKT_DATA_NB +}; + +#define AV_PKT_DATA_QUALITY_FACTOR AV_PKT_DATA_QUALITY_STATS //DEPRECATED + +typedef struct AVPacketSideData { + uint8_t *data; + int size; + enum AVPacketSideDataType type; +} AVPacketSideData; + +/** + * 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 + * then passed to muxers. + * + * For video, it should typically contain one compressed frame. For audio it may + * contain several compressed frames. Encoders are allowed to output empty + * packets, with no compressed data, containing only side data + * (e.g. to update some stream parameters at the end of encoding). + * + * AVPacket is one of the few structs in FFmpeg, whose size is a part of public + * ABI. Thus it may be allocated on stack and no new fields can be added to it + * without libavcodec and libavformat major bump. + * + * The semantics of data ownership depends on the buf field. + * If it is set, the packet data is dynamically allocated and is + * valid indefinitely until a call to av_packet_unref() reduces the + * reference count to 0. + * + * If the buf field is not set av_packet_ref() would make a copy instead + * of increasing the reference count. + * + * The side data is always allocated with av_malloc(), copied by + * av_packet_ref() and freed by av_packet_unref(). + * + * @see av_packet_ref + * @see av_packet_unref + */ +typedef struct AVPacket { + /** + * A reference to the reference-counted buffer where the packet data is + * stored. + * May be NULL, then the packet data is not reference-counted. + */ + AVBufferRef *buf; + /** + * Presentation timestamp in AVStream->time_base units; the time at which + * the decompressed packet will be presented to the user. + * Can be AV_NOPTS_VALUE if it is not stored in the file. + * pts MUST be larger or equal to dts as presentation cannot happen before + * decompression, unless one wants to view hex dumps. Some formats misuse + * the terms dts and pts/cts to mean something different. Such timestamps + * must be converted to true pts/dts before they are stored in AVPacket. + */ + int64_t pts; + /** + * Decompression timestamp in AVStream->time_base units; the time at which + * the packet is decompressed. + * Can be AV_NOPTS_VALUE if it is not stored in the file. + */ + int64_t dts; + uint8_t *data; + int size; + int stream_index; + /** + * A combination of AV_PKT_FLAG values + */ + int flags; + /** + * Additional packet data that can be provided by the container. + * Packet can contain several types of side information. + */ + AVPacketSideData *side_data; + int side_data_elems; + + /** + * Duration of this packet in AVStream->time_base units, 0 if unknown. + * Equals next_pts - this_pts in presentation order. + */ + int64_t duration; + + int64_t pos; ///< byte position in stream, -1 if unknown + +#if FF_API_CONVERGENCE_DURATION + /** + * @deprecated Same as the duration field, but as int64_t. This was required + * for Matroska subtitles, whose duration values could overflow when the + * duration field was still an int. + */ + attribute_deprecated + int64_t convergence_duration; +#endif +} AVPacket; + +#define AV_PKT_FLAG_KEY 0x0001 ///< The packet contains a keyframe +#define AV_PKT_FLAG_CORRUPT 0x0002 ///< The packet content is corrupted +/** + * Flag is used to discard packets which are required to maintain valid + * decoder state but are not required for output and should be dropped + * after decoding. + **/ +#define AV_PKT_FLAG_DISCARD 0x0004 +/** + * The packet comes from a trusted source. + * + * Otherwise-unsafe constructs such as arbitrary pointers to data + * outside the packet may be followed. + */ +#define AV_PKT_FLAG_TRUSTED 0x0008 +/** + * Flag is used to indicate packets that contain frames that can + * be discarded by the decoder. I.e. Non-reference frames. + */ +#define AV_PKT_FLAG_DISPOSABLE 0x0010 + +enum AVSideDataParamChangeFlags { + AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT = 0x0001, + AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT = 0x0002, + AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE = 0x0004, + AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS = 0x0008, +}; + +/** + * Allocate an AVPacket and set its fields to default values. The resulting + * struct must be freed using av_packet_free(). + * + * @return An AVPacket filled with default values or NULL on failure. + * + * @note this only allocates the AVPacket itself, not the data buffers. Those + * must be allocated through other means such as av_new_packet. + * + * @see av_new_packet + */ +AVPacket *av_packet_alloc(void); + +/** + * Create a new packet that references the same data as src. + * + * This is a shortcut for av_packet_alloc()+av_packet_ref(). + * + * @return newly created AVPacket on success, NULL on error. + * + * @see av_packet_alloc + * @see av_packet_ref + */ +AVPacket *av_packet_clone(const AVPacket *src); + +/** + * Free the packet, if the packet is reference counted, it will be + * unreferenced first. + * + * @param pkt packet to be freed. The pointer will be set to NULL. + * @note passing NULL is a no-op. + */ +void av_packet_free(AVPacket **pkt); + +/** + * Initialize optional fields of a packet with default values. + * + * Note, this does not touch the data and size members, which have to be + * initialized separately. + * + * @param pkt packet + */ +void av_init_packet(AVPacket *pkt); + +/** + * Allocate the payload of a packet and initialize its fields with + * default values. + * + * @param pkt packet + * @param size wanted payload size + * @return 0 if OK, AVERROR_xxx otherwise + */ +int av_new_packet(AVPacket *pkt, int size); + +/** + * Reduce packet size, correctly zeroing padding + * + * @param pkt packet + * @param size new size + */ +void av_shrink_packet(AVPacket *pkt, int size); + +/** + * Increase packet size, correctly zeroing padding + * + * @param pkt packet + * @param grow_by number of bytes by which to increase the size of the packet + */ +int av_grow_packet(AVPacket *pkt, int grow_by); + +/** + * Initialize a reference-counted packet from av_malloc()ed data. + * + * @param pkt packet to be initialized. This function will set the data, size, + * and buf fields, all others are left untouched. + * @param data Data allocated by av_malloc() to be used as packet data. If this + * function returns successfully, the data is owned by the underlying AVBuffer. + * The caller may not access the data through other means. + * @param size size of data in bytes, without the padding. I.e. the full buffer + * size is assumed to be size + AV_INPUT_BUFFER_PADDING_SIZE. + * + * @return 0 on success, a negative AVERROR on error + */ +int av_packet_from_data(AVPacket *pkt, uint8_t *data, int size); + +#if FF_API_AVPACKET_OLD_API +/** + * @warning This is a hack - the packet memory allocation stuff is broken. The + * packet is allocated if it was not really allocated. + * + * @deprecated Use av_packet_ref or av_packet_make_refcounted + */ +attribute_deprecated +int av_dup_packet(AVPacket *pkt); +/** + * Copy packet, including contents + * + * @return 0 on success, negative AVERROR on fail + * + * @deprecated Use av_packet_ref + */ +attribute_deprecated +int av_copy_packet(AVPacket *dst, const AVPacket *src); + +/** + * Copy packet side data + * + * @return 0 on success, negative AVERROR on fail + * + * @deprecated Use av_packet_copy_props + */ +attribute_deprecated +int av_copy_packet_side_data(AVPacket *dst, const AVPacket *src); + +/** + * Free a packet. + * + * @deprecated Use av_packet_unref + * + * @param pkt packet to free + */ +attribute_deprecated +void av_free_packet(AVPacket *pkt); +#endif +/** + * Allocate new information of a packet. + * + * @param pkt packet + * @param type side information type + * @param size side information size + * @return pointer to fresh allocated data or NULL otherwise + */ +uint8_t* av_packet_new_side_data(AVPacket *pkt, enum AVPacketSideDataType type, + int size); + +/** + * Wrap an existing array as a packet side data. + * + * @param pkt packet + * @param type side information type + * @param data the side data array. It must be allocated with the av_malloc() + * family of functions. The ownership of the data is transferred to + * pkt. + * @param size side information size + * @return a non-negative number on success, a negative AVERROR code on + * failure. On failure, the packet is unchanged and the data remains + * owned by the caller. + */ +int av_packet_add_side_data(AVPacket *pkt, enum AVPacketSideDataType type, + uint8_t *data, size_t size); + +/** + * Shrink the already allocated side data buffer + * + * @param pkt packet + * @param type side information type + * @param size new side information size + * @return 0 on success, < 0 on failure + */ +int av_packet_shrink_side_data(AVPacket *pkt, enum AVPacketSideDataType type, + int size); + +/** + * Get side information from packet. + * + * @param pkt packet + * @param type desired side information type + * @param size pointer for side information size to store (optional) + * @return pointer to data if present or NULL otherwise + */ +uint8_t* av_packet_get_side_data(const AVPacket *pkt, enum AVPacketSideDataType type, + int *size); + +#if FF_API_MERGE_SD_API +attribute_deprecated +int av_packet_merge_side_data(AVPacket *pkt); + +attribute_deprecated +int av_packet_split_side_data(AVPacket *pkt); +#endif + +const char *av_packet_side_data_name(enum AVPacketSideDataType type); + +/** + * Pack a dictionary for use in side_data. + * + * @param dict The dictionary to pack. + * @param size pointer to store the size of the returned data + * @return pointer to data if successful, NULL otherwise + */ +uint8_t *av_packet_pack_dictionary(AVDictionary *dict, int *size); +/** + * Unpack a dictionary from side_data. + * + * @param data data from side_data + * @param size size of the data + * @param dict the metadata storage dictionary + * @return 0 on success, < 0 on failure + */ +int av_packet_unpack_dictionary(const uint8_t *data, int size, AVDictionary **dict); + + +/** + * Convenience function to free all the side data stored. + * All the other fields stay untouched. + * + * @param pkt packet + */ +void av_packet_free_side_data(AVPacket *pkt); + +/** + * Setup a new reference to the data described by a given packet + * + * If src is reference-counted, setup dst as a new reference to the + * buffer in src. Otherwise allocate a new buffer in dst and copy the + * data from src into it. + * + * All the other fields are copied from src. + * + * @see av_packet_unref + * + * @param dst Destination packet. Will be completely overwritten. + * @param src Source packet + * + * @return 0 on success, a negative AVERROR on error. On error, dst + * will be blank (as if returned by av_packet_alloc()). + */ +int av_packet_ref(AVPacket *dst, const AVPacket *src); + +/** + * Wipe the packet. + * + * Unreference the buffer referenced by the packet and reset the + * remaining packet fields to their default values. + * + * @param pkt The packet to be unreferenced. + */ +void av_packet_unref(AVPacket *pkt); + +/** + * Move every field in src to dst and reset src. + * + * @see av_packet_unref + * + * @param src Source packet, will be reset + * @param dst Destination packet + */ +void av_packet_move_ref(AVPacket *dst, AVPacket *src); + +/** + * Copy only "properties" fields from src to dst. + * + * Properties for the purpose of this function are all the fields + * beside those related to the packet data (buf, data, size) + * + * @param dst Destination packet + * @param src Source packet + * + * @return 0 on success AVERROR on failure. + */ +int av_packet_copy_props(AVPacket *dst, const AVPacket *src); + +/** + * Ensure the data described by a given packet is reference counted. + * + * @note This function does not ensure that the reference will be writable. + * Use av_packet_make_writable instead for that purpose. + * + * @see av_packet_ref + * @see av_packet_make_writable + * + * @param pkt packet whose data should be made reference counted. + * + * @return 0 on success, a negative AVERROR on error. On failure, the + * packet is unchanged. + */ +int av_packet_make_refcounted(AVPacket *pkt); + +/** + * Create a writable reference for the data described by a given packet, + * avoiding data copy if possible. + * + * @param pkt Packet whose data should be made writable. + * + * @return 0 on success, a negative AVERROR on failure. On failure, the + * packet is unchanged. + */ +int av_packet_make_writable(AVPacket *pkt); + +/** + * Convert valid timing fields (timestamps / durations) in a packet from one + * timebase to another. Timestamps with unknown values (AV_NOPTS_VALUE) will be + * ignored. + * + * @param pkt packet on which the conversion will be performed + * @param tb_src source timebase, in which the timing fields in pkt are + * expressed + * @param tb_dst destination timebase, to which the timing fields will be + * converted + */ +void av_packet_rescale_ts(AVPacket *pkt, AVRational tb_src, AVRational tb_dst); + +/** + * @} + */ + +#endif // AVCODEC_PACKET_H diff --git a/libavcodec/packet_internal.h b/libavcodec/packet_internal.h new file mode 100644 index 00000000000..cdb9a27f2f5 --- /dev/null +++ b/libavcodec/packet_internal.h @@ -0,0 +1,30 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_PACKET_INTERNAL_H +#define AVCODEC_PACKET_INTERNAL_H + +#include + +#include "packet.h" + +int ff_side_data_set_encoder_stats(AVPacket *pkt, int quality, int64_t *error, int error_count, int pict_type); + +int ff_side_data_set_prft(AVPacket *pkt, int64_t timestamp); + +#endif // AVCODEC_PACKET_INTERNAL_H diff --git a/libavcodec/pafvideo.c b/libavcodec/pafvideo.c index 323c662c590..07fa05caf82 100644 --- a/libavcodec/pafvideo.c +++ b/libavcodec/pafvideo.c @@ -55,6 +55,7 @@ typedef struct PAFVideoDecContext { int current_frame; uint8_t *frame[4]; + int dirty[4]; int frame_size; int video_size; @@ -187,6 +188,7 @@ static int decode_0(PAFVideoDecContext *c, uint8_t *pkt, uint8_t code) j = bytestream2_get_le16(&c->gb) + offset; if (bytestream2_get_bytes_left(&c->gb) < (j - offset) * 16) return AVERROR_INVALIDDATA; + c->dirty[page] = 1; do { offset++; if (dst + 3 * c->width + 4 > dend) @@ -289,7 +291,7 @@ static int paf_video_decode(AVCodecContext *avctx, void *data, c->video_size / 32 - (int64_t)bytestream2_get_bytes_left(&c->gb) > c->video_size / 32 * (int64_t)avctx->discard_damaged_percentage / 100) return AVERROR_INVALIDDATA; - if ((ret = ff_reget_buffer(avctx, c->pic)) < 0) + if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; if (code & 0x20) { // frame is keyframe @@ -329,9 +331,13 @@ static int paf_video_decode(AVCodecContext *avctx, void *data, c->pic->palette_has_changed = 1; } + c->dirty[c->current_frame] = 1; if (code & 0x20) - for (i = 0; i < 4; i++) - memset(c->frame[i], 0, c->frame_size); + for (i = 0; i < 4; i++) { + if (c->dirty[i]) + memset(c->frame[i], 0, c->frame_size); + c->dirty[i] = 0; + } switch (code & 0x0F) { case 0: diff --git a/libavcodec/parser.c b/libavcodec/parser.c index 3e19810a949..a63f532c484 100644 --- a/libavcodec/parser.c +++ b/libavcodec/parser.c @@ -295,6 +295,10 @@ int ff_combine_frame(ParseContext *pc, int next, *buf = pc->buffer; } + if (next < -8) { + pc->overread += -8 - next; + next = -8; + } /* store overread bytes */ for (; next < 0; next++) { pc->state = pc->state << 8 | pc->buffer[pc->last_index + next]; diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c index 33a71de8a0f..7d75cea830d 100644 --- a/libavcodec/parsers.c +++ b/libavcodec/parsers.c @@ -48,6 +48,7 @@ extern AVCodecParser ff_h261_parser; extern AVCodecParser ff_h263_parser; extern AVCodecParser ff_h264_parser; extern AVCodecParser ff_hevc_parser; +extern AVCodecParser ff_jpeg2000_parser; extern AVCodecParser ff_mjpeg_parser; extern AVCodecParser ff_mlp_parser; extern AVCodecParser ff_mpeg4video_parser; @@ -66,6 +67,7 @@ extern AVCodecParser ff_vorbis_parser; extern AVCodecParser ff_vp3_parser; extern AVCodecParser ff_vp8_parser; extern AVCodecParser ff_vp9_parser; +extern AVCodecParser ff_webp_parser; extern AVCodecParser ff_xma_parser; #include "libavcodec/parser_list.c" diff --git a/libavcodec/pcm.c b/libavcodec/pcm.c index ffcbccc77db..96a68f7fe81 100644 --- a/libavcodec/pcm.c +++ b/libavcodec/pcm.c @@ -264,6 +264,9 @@ static av_cold int pcm_decode_init(AVCodecContext *avctx) break; case AV_CODEC_ID_PCM_F16LE: case AV_CODEC_ID_PCM_F24LE: + if (avctx->bits_per_coded_sample < 1 || avctx->bits_per_coded_sample > 24) + return AVERROR_INVALIDDATA; + s->scale = 1. / (1 << (avctx->bits_per_coded_sample - 1)); s->fdsp = avpriv_float_dsp_alloc(0); if (!s->fdsp) @@ -300,23 +303,23 @@ static av_cold int pcm_decode_close(AVCodecContext *avctx) * @param shift Bitshift (bits) * @param offset Sample value offset */ -#define DECODE(size, endian, src, dst, n, shift, offset) \ - for (; n > 0; n--) { \ - uint ## size ## _t v = bytestream_get_ ## endian(&src); \ - AV_WN ## size ## A(dst, (v - offset) << shift); \ - dst += size / 8; \ +#define DECODE(size, endian, src, dst, n, shift, offset) \ + for (; n > 0; n--) { \ + uint ## size ## _t v = bytestream_get_ ## endian(&src); \ + AV_WN ## size ## A(dst, (uint ## size ## _t)(v - offset) << shift); \ + dst += size / 8; \ } -#define DECODE_PLANAR(size, endian, src, dst, n, shift, offset) \ - n /= avctx->channels; \ - for (c = 0; c < avctx->channels; c++) { \ - int i; \ - dst = frame->extended_data[c]; \ - for (i = n; i > 0; i--) { \ - uint ## size ## _t v = bytestream_get_ ## endian(&src); \ - AV_WN ## size ## A(dst, (v - offset) << shift); \ - dst += size / 8; \ - } \ +#define DECODE_PLANAR(size, endian, src, dst, n, shift, offset) \ + n /= avctx->channels; \ + for (c = 0; c < avctx->channels; c++) { \ + int i; \ + dst = frame->extended_data[c]; \ + for (i = n; i > 0; i--) { \ + uint ## size ## _t v = bytestream_get_ ## endian(&src); \ + AV_WN ## size ## A(dst, (uint ## size ##_t)(v - offset) << shift); \ + dst += size / 8; \ + } \ } static int pcm_decode_frame(AVCodecContext *avctx, void *data, @@ -488,14 +491,6 @@ static int pcm_decode_frame(AVCodecContext *avctx, void *data, bytestream_get_buffer(&src, samples, n * sample_size); } break; - case AV_CODEC_ID_PCM_ZORK: - for (; n > 0; n--) { - int v = *src++; - if (v < 128) - v = 128 - v; - *samples++ = v; - } - break; case AV_CODEC_ID_PCM_ALAW: case AV_CODEC_ID_PCM_MULAW: case AV_CODEC_ID_PCM_VIDC: @@ -512,13 +507,13 @@ static int pcm_decode_frame(AVCodecContext *avctx, void *data, dst_int32_t = (int32_t *)frame->extended_data[c]; for (i = 0; i < n; i++) { // extract low 20 bits and expand to 32 bits - *dst_int32_t++ = (src[2] << 28) | + *dst_int32_t++ = ((uint32_t)src[2]<<28) | (src[1] << 20) | (src[0] << 12) | ((src[2] & 0x0F) << 8) | src[1]; // extract high 20 bits and expand to 32 bits - *dst_int32_t++ = (src[4] << 24) | + *dst_int32_t++ = ((uint32_t)src[4]<<24) | (src[3] << 16) | ((src[2] & 0xF0) << 8) | (src[4] << 4) | @@ -623,7 +618,6 @@ PCM_CODEC (PCM_U24BE, AV_SAMPLE_FMT_S32, pcm_u24be, "PCM unsigned PCM_CODEC (PCM_U24LE, AV_SAMPLE_FMT_S32, pcm_u24le, "PCM unsigned 24-bit little-endian"); PCM_CODEC (PCM_U32BE, AV_SAMPLE_FMT_S32, pcm_u32be, "PCM unsigned 32-bit big-endian"); PCM_CODEC (PCM_U32LE, AV_SAMPLE_FMT_S32, pcm_u32le, "PCM unsigned 32-bit little-endian"); -PCM_DECODER(PCM_ZORK, AV_SAMPLE_FMT_U8, pcm_zork, "PCM Zork"); PCM_CODEC (PCM_S64BE, AV_SAMPLE_FMT_S64, pcm_s64be, "PCM signed 64-bit big-endian"); PCM_CODEC (PCM_S64LE, AV_SAMPLE_FMT_S64, pcm_s64le, "PCM signed 64-bit little-endian"); PCM_CODEC (PCM_VIDC, AV_SAMPLE_FMT_S16, pcm_vidc, "PCM Archimedes VIDC"); diff --git a/libavcodec/pcm_rechunk_bsf.c b/libavcodec/pcm_rechunk_bsf.c new file mode 100644 index 00000000000..47f44b64672 --- /dev/null +++ b/libavcodec/pcm_rechunk_bsf.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2020 Marton Balint + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 "bsf_internal.h" +#include "libavutil/avassert.h" +#include "libavutil/opt.h" + +typedef struct PCMContext { + const AVClass *class; + + int nb_out_samples; + int pad; + AVRational frame_rate; + + AVPacket *in_pkt; + AVPacket *out_pkt; + int sample_size; + int64_t n; +} PCMContext; + +static int init(AVBSFContext *ctx) +{ + PCMContext *s = ctx->priv_data; + AVRational sr = av_make_q(ctx->par_in->sample_rate, 1); + int64_t min_samples; + + if (ctx->par_in->channels <= 0 || ctx->par_in->sample_rate <= 0) + return AVERROR(EINVAL); + + ctx->time_base_out = av_inv_q(sr); + s->sample_size = ctx->par_in->channels * av_get_bits_per_sample(ctx->par_in->codec_id) / 8; + + if (s->frame_rate.num) { + min_samples = av_rescale_q_rnd(1, sr, s->frame_rate, AV_ROUND_DOWN); + } else { + min_samples = s->nb_out_samples; + } + if (min_samples <= 0 || min_samples > INT_MAX / s->sample_size - 1) + return AVERROR(EINVAL); + + s->in_pkt = av_packet_alloc(); + s->out_pkt = av_packet_alloc(); + if (!s->in_pkt || !s->out_pkt) + return AVERROR(ENOMEM); + + return 0; +} + +static void uninit(AVBSFContext *ctx) +{ + PCMContext *s = ctx->priv_data; + av_packet_free(&s->in_pkt); + av_packet_free(&s->out_pkt); +} + +static void flush(AVBSFContext *ctx) +{ + PCMContext *s = ctx->priv_data; + av_packet_unref(s->in_pkt); + av_packet_unref(s->out_pkt); + s->n = 0; +} + +static int send_packet(PCMContext *s, int nb_samples, AVPacket *pkt) +{ + pkt->duration = nb_samples; + s->n++; + return 0; +} + +static void drain_packet(AVPacket *pkt, int drain_data, int drain_samples) +{ + pkt->size -= drain_data; + pkt->data += drain_data; + if (pkt->dts != AV_NOPTS_VALUE) + pkt->dts += drain_samples; + if (pkt->pts != AV_NOPTS_VALUE) + pkt->pts += drain_samples; +} + +static int get_next_nb_samples(AVBSFContext *ctx) +{ + PCMContext *s = ctx->priv_data; + if (s->frame_rate.num) { + AVRational sr = av_make_q(ctx->par_in->sample_rate, 1); + return av_rescale_q(s->n + 1, sr, s->frame_rate) - av_rescale_q(s->n, sr, s->frame_rate); + } else { + return s->nb_out_samples; + } +} + +static int rechunk_filter(AVBSFContext *ctx, AVPacket *pkt) +{ + PCMContext *s = ctx->priv_data; + int nb_samples = get_next_nb_samples(ctx); + int data_size = nb_samples * s->sample_size; + int ret; + + do { + if (s->in_pkt->size) { + if (s->out_pkt->size || s->in_pkt->size < data_size) { + int drain = FFMIN(s->in_pkt->size, data_size - s->out_pkt->size); + if (!s->out_pkt->size) { + ret = av_new_packet(s->out_pkt, data_size); + if (ret < 0) + return ret; + ret = av_packet_copy_props(s->out_pkt, s->in_pkt); + if (ret < 0) { + av_packet_unref(s->out_pkt); + return ret; + } + s->out_pkt->size = 0; + } + memcpy(s->out_pkt->data + s->out_pkt->size, s->in_pkt->data, drain); + s->out_pkt->size += drain; + drain_packet(s->in_pkt, drain, drain / s->sample_size); + if (!s->in_pkt->size) + av_packet_unref(s->in_pkt); + if (s->out_pkt->size == data_size) { + av_packet_move_ref(pkt, s->out_pkt); + return send_packet(s, nb_samples, pkt); + } + } else if (s->in_pkt->size > data_size) { + ret = av_packet_ref(pkt, s->in_pkt); + if (ret < 0) + return ret; + pkt->size = data_size; + drain_packet(s->in_pkt, data_size, nb_samples); + return send_packet(s, nb_samples, pkt); + } else { + av_assert0(s->in_pkt->size == data_size); + av_packet_move_ref(pkt, s->in_pkt); + return send_packet(s, nb_samples, 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); + s->out_pkt->size = data_size; + } else { + nb_samples = s->out_pkt->size / s->sample_size; + } + av_packet_move_ref(pkt, s->out_pkt); + return send_packet(s, nb_samples, pkt); + } + if (ret >= 0) + av_packet_rescale_ts(s->in_pkt, ctx->time_base_in, ctx->time_base_out); + } while (ret >= 0); + + return ret; +} + +#define OFFSET(x) offsetof(PCMContext, x) +#define FLAGS (AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_BSF_PARAM) +static const AVOption options[] = { + { "nb_out_samples", "set the number of per-packet output samples", OFFSET(nb_out_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS }, + { "n", "set the number of per-packet output samples", OFFSET(nb_out_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS }, + { "pad", "pad last packet with zeros", OFFSET(pad), AV_OPT_TYPE_BOOL, {.i64=1} , 0, 1, FLAGS }, + { "p", "pad last packet with zeros", OFFSET(pad), AV_OPT_TYPE_BOOL, {.i64=1} , 0, 1, FLAGS }, + { "frame_rate", "set number of packets per second", OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS }, + { "r", "set number of packets per second", OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS }, + { NULL }, +}; + +static const AVClass pcm_rechunk_class = { + .class_name = "pcm_rechunk_bsf", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +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_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_S64BE, + AV_CODEC_ID_PCM_F16LE, + AV_CODEC_ID_PCM_F24LE, + AV_CODEC_ID_NONE, +}; + +const AVBitStreamFilter ff_pcm_rechunk_bsf = { + .name = "pcm_rechunk", + .priv_data_size = sizeof(PCMContext), + .priv_class = &pcm_rechunk_class, + .filter = rechunk_filter, + .init = init, + .flush = flush, + .close = uninit, + .codec_ids = codec_ids, +}; diff --git a/libavcodec/pcm_tablegen.h b/libavcodec/pcm_tablegen.h index d8763abc40d..7274c3cd172 100644 --- a/libavcodec/pcm_tablegen.h +++ b/libavcodec/pcm_tablegen.h @@ -45,48 +45,48 @@ /* alaw2linear() - Convert an A-law value to 16-bit linear PCM */ static av_cold int alaw2linear(unsigned char a_val) { - int t; - int seg; + int t; + int seg; - a_val ^= 0x55; + a_val ^= 0x55; - t = a_val & QUANT_MASK; - seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; - if(seg) t= (t + t + 1 + 32) << (seg + 2); - else t= (t + t + 1 ) << 3; + t = a_val & QUANT_MASK; + seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; + if(seg) t= (t + t + 1 + 32) << (seg + 2); + else t= (t + t + 1 ) << 3; - return (a_val & SIGN_BIT) ? t : -t; + return (a_val & SIGN_BIT) ? t : -t; } static av_cold int ulaw2linear(unsigned char u_val) { - int t; + int t; - /* Complement to obtain normal u-law value. */ - u_val = ~u_val; + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; - /* - * Extract and bias the quantization bits. Then - * shift up by the segment number and subtract out the bias. - */ - t = ((u_val & QUANT_MASK) << 3) + BIAS; - t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & QUANT_MASK) << 3) + BIAS; + t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; - return (u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS); + return (u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS); } static av_cold int vidc2linear(unsigned char u_val) { - int t; + int t; - /* - * Extract and bias the quantization bits. Then - * shift up by the segment number and subtract out the bias. - */ - t = (((u_val & VIDC_QUANT_MASK) >> VIDC_QUANT_SHIFT) << 3) + BIAS; - t <<= ((unsigned)u_val & VIDC_SEG_MASK) >> VIDC_SEG_SHIFT; + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = (((u_val & VIDC_QUANT_MASK) >> VIDC_QUANT_SHIFT) << 3) + BIAS; + t <<= ((unsigned)u_val & VIDC_SEG_MASK) >> VIDC_SEG_SHIFT; - return (u_val & VIDC_SIGN_BIT) ? (BIAS - t) : (t - BIAS); + return (u_val & VIDC_SIGN_BIT) ? (BIAS - t) : (t - BIAS); } #if CONFIG_HARDCODED_TABLES diff --git a/libavcodec/pgssubdec.c b/libavcodec/pgssubdec.c index 8c10f6d5733..9c59a2297f1 100644 --- a/libavcodec/pgssubdec.c +++ b/libavcodec/pgssubdec.c @@ -614,7 +614,7 @@ FF_ENABLE_DEPRECATION_WARNINGS return 1; } -static int decode(AVCodecContext *avctx, void *data, int *data_size, +static int decode(AVCodecContext *avctx, void *data, int *got_sub_ptr, AVPacket *avpkt) { const uint8_t *buf = avpkt->data; @@ -636,7 +636,7 @@ static int decode(AVCodecContext *avctx, void *data, int *data_size, if (i & 15) ff_dlog(avctx, "\n"); - *data_size = 0; + *got_sub_ptr = 0; /* Ensure that we have received at a least a segment code and segment length */ if (buf_size < 3) @@ -676,14 +676,14 @@ static int decode(AVCodecContext *avctx, void *data, int *data_size, */ break; case DISPLAY_SEGMENT: - if (*data_size) { + if (*got_sub_ptr) { av_log(avctx, AV_LOG_ERROR, "Duplicate display segment\n"); ret = AVERROR_INVALIDDATA; break; } ret = display_end_segment(avctx, data, buf, segment_length); if (ret >= 0) - *data_size = ret; + *got_sub_ptr = ret; break; default: av_log(avctx, AV_LOG_ERROR, "Unknown subtitle segment type 0x%x, length %d\n", @@ -691,8 +691,11 @@ static int decode(AVCodecContext *avctx, void *data, int *data_size, ret = AVERROR_INVALIDDATA; break; } - if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) + if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) { + avsubtitle_free(data); + *got_sub_ptr = 0; return ret; + } buf += segment_length; } diff --git a/libavcodec/pictordec.c b/libavcodec/pictordec.c index 2e6fcdca524..6340902526e 100644 --- a/libavcodec/pictordec.c +++ b/libavcodec/pictordec.c @@ -66,6 +66,7 @@ static void picmemset(PicContext *s, AVFrame *frame, unsigned value, int run, int xl = *x; int yl = *y; int planel = *plane; + int pixels_per_value = 8/bits_per_plane; value <<= shift; d = frame->data[0] + yl * frame->linesize[0]; @@ -74,7 +75,7 @@ static void picmemset(PicContext *s, AVFrame *frame, unsigned value, int run, for (j = 8-bits_per_plane; j >= 0; j -= bits_per_plane) { d[xl] |= (value >> j) & mask; xl += 1; - if (xl == s->width) { + while (xl == s->width) { yl -= 1; xl = 0; if (yl < 0) { @@ -86,6 +87,19 @@ static void picmemset(PicContext *s, AVFrame *frame, unsigned value, int run, mask <<= bits_per_plane; } d = frame->data[0] + yl * frame->linesize[0]; + if (s->nb_planes == 1 && + run*pixels_per_value >= s->width && + pixels_per_value < s->width && + s->width % pixels_per_value == 0 + ) { + for (; xl < pixels_per_value; xl ++) { + j = (j < bits_per_plane ? 8 : j) - bits_per_plane; + d[xl] |= (value >> j) & mask; + } + av_memcpy_backptr(d+xl, pixels_per_value, s->width - xl); + run -= s->width / pixels_per_value; + xl = s->width; + } } } run--; diff --git a/libavcodec/pixblockdsp.c b/libavcodec/pixblockdsp.c index 50e1d1d735e..67393b95931 100644 --- a/libavcodec/pixblockdsp.c +++ b/libavcodec/pixblockdsp.c @@ -90,15 +90,19 @@ av_cold void ff_pixblockdsp_init(PixblockDSPContext *c, AVCodecContext *avctx) case 10: case 12: case 14: + c->get_pixels_unaligned = c->get_pixels = get_pixels_16_c; break; default: if (avctx->bits_per_raw_sample<=8 || avctx->codec_type != AVMEDIA_TYPE_VIDEO) { + c->get_pixels_unaligned = c->get_pixels = get_pixels_8_c; } break; } + if (ARCH_AARCH64) + ff_pixblockdsp_init_aarch64(c, avctx, high_bit_depth); if (ARCH_ALPHA) ff_pixblockdsp_init_alpha(c, avctx, high_bit_depth); if (ARCH_ARM) diff --git a/libavcodec/pixblockdsp.h b/libavcodec/pixblockdsp.h index e036700ff06..07c2ec4f406 100644 --- a/libavcodec/pixblockdsp.h +++ b/libavcodec/pixblockdsp.h @@ -29,6 +29,9 @@ typedef struct PixblockDSPContext { void (*get_pixels)(int16_t *av_restrict block /* align 16 */, const uint8_t *pixels /* align 8 */, ptrdiff_t stride); + void (*get_pixels_unaligned)(int16_t *av_restrict block /* align 16 */, + const uint8_t *pixels, + ptrdiff_t stride); void (*diff_pixels)(int16_t *av_restrict block /* align 16 */, const uint8_t *s1 /* align 8 */, const uint8_t *s2 /* align 8 */, @@ -41,6 +44,8 @@ typedef struct PixblockDSPContext { } PixblockDSPContext; void ff_pixblockdsp_init(PixblockDSPContext *c, AVCodecContext *avctx); +void ff_pixblockdsp_init_aarch64(PixblockDSPContext *c, AVCodecContext *avctx, + unsigned high_bit_depth); void ff_pixblockdsp_init_alpha(PixblockDSPContext *c, AVCodecContext *avctx, unsigned high_bit_depth); void ff_pixblockdsp_init_arm(PixblockDSPContext *c, AVCodecContext *avctx, diff --git a/libavcodec/pixlet.c b/libavcodec/pixlet.c index 03a2cdacc8b..78f571cd5ff 100644 --- a/libavcodec/pixlet.c +++ b/libavcodec/pixlet.c @@ -221,7 +221,7 @@ static int read_high_coeffs(AVCodecContext *avctx, uint8_t *src, int16_t *dst, length = 25 - nbits; while (i < size) { - if (state >> 8 != -3) + if (((state >> 8) + 3) & 0xFFFFFFF) value = ff_clz((state >> 8) + 3) ^ 0x1F; else value = -1; @@ -675,28 +675,12 @@ static int pixlet_decode_frame(AVCodecContext *avctx, void *data, return pktsize; } -#if HAVE_THREADS -static int pixlet_init_thread_copy(AVCodecContext *avctx) -{ - PixletContext *ctx = avctx->priv_data; - - ctx->filter[0] = NULL; - ctx->filter[1] = NULL; - ctx->prediction = NULL; - ctx->w = 0; - ctx->h = 0; - - return 0; -} -#endif /* HAVE_THREADS */ - AVCodec ff_pixlet_decoder = { .name = "pixlet", .long_name = NULL_IF_CONFIG_SMALL("Apple Pixlet"), .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_PIXLET, .init = pixlet_init, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(pixlet_init_thread_copy), .close = pixlet_close, .decode = pixlet_decode_frame, .priv_data_size = sizeof(PixletContext), diff --git a/libavcodec/png_parser.c b/libavcodec/png_parser.c index 74f29641181..9ec8551a1b7 100644 --- a/libavcodec/png_parser.c +++ b/libavcodec/png_parser.c @@ -45,6 +45,7 @@ static int png_parse(AVCodecParserContext *s, AVCodecContext *avctx, s->pict_type = AV_PICTURE_TYPE_NONE; *poutbuf_size = 0; + *poutbuf = NULL; if (!ppc->pc.frame_start_found) { uint64_t state64 = ppc->pc.state64; diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c index cad57965457..647e7f0a74c 100644 --- a/libavcodec/pngdec.c +++ b/libavcodec/pngdec.c @@ -23,7 +23,9 @@ #include "libavutil/avassert.h" #include "libavutil/bprint.h" +#include "libavutil/crc.h" #include "libavutil/imgutils.h" +#include "libavutil/intreadwrite.h" #include "libavutil/stereo3d.h" #include "libavutil/mastering_display_metadata.h" @@ -319,6 +321,15 @@ static void deloco_ ## NAME(TYPE *dst, int size, int alpha) \ YUV2RGB(rgb8, uint8_t) YUV2RGB(rgb16, uint16_t) +static int percent_missing(PNGDecContext *s) +{ + if (s->interlace_type) { + return 100 - 100 * s->pass / (NB_PASSES - 1); + } else { + return 100 - 100 * s->y / s->cur_h; + } +} + /* process exactly one decompressed row */ static void png_handle_row(PNGDecContext *s) { @@ -405,7 +416,7 @@ static int png_decode_idat(PNGDecContext *s, int length) { int ret; s->zstream.avail_in = FFMIN(length, bytestream2_get_bytes_left(&s->gb)); - s->zstream.next_in = (unsigned char *)s->gb.buffer; + s->zstream.next_in = s->gb.buffer; bytestream2_skip(&s->gb, length); /* decode one line if possible */ @@ -423,7 +434,7 @@ static int png_decode_idat(PNGDecContext *s, int length) s->zstream.next_out = s->crow_buf; } if (ret == Z_STREAM_END && s->zstream.avail_in > 0) { - av_log(NULL, AV_LOG_WARNING, + av_log(s->avctx, AV_LOG_WARNING, "%d undecompressed bytes left in buffer\n", s->zstream.avail_in); return 0; } @@ -444,7 +455,7 @@ static int decode_zbuf(AVBPrint *bp, const uint8_t *data, zstream.opaque = NULL; if (inflateInit(&zstream) != Z_OK) return AVERROR_EXTERNAL; - zstream.next_in = (unsigned char *)data; + zstream.next_in = data; zstream.avail_in = data_end - data; av_bprint_init(bp, 0, AV_BPRINT_SIZE_UNLIMITED); @@ -973,6 +984,11 @@ static int decode_fctl_chunk(AVCodecContext *avctx, PNGDecContext *s, return AVERROR_INVALIDDATA; } + if (s->pic_state & PNG_IDAT) { + av_log(avctx, AV_LOG_ERROR, "fctl after IDAT\n"); + return AVERROR_INVALIDDATA; + } + s->last_w = s->cur_w; s->last_h = s->cur_h; s->last_x_offset = s->x_offset; @@ -1169,6 +1185,7 @@ static int handle_p_frame_apng(AVCodecContext *avctx, PNGDecContext *s, static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, AVFrame *p, AVPacket *avpkt) { + const AVCRC *crc_tab = av_crc_get_table(AV_CRC_32_IEEE_LE); AVDictionary **metadatap = NULL; uint32_t tag, length; int decode_next_dat = 0; @@ -1203,6 +1220,21 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, ret = AVERROR_INVALIDDATA; goto fail; } + if (avctx->err_recognition & (AV_EF_CRCCHECK | AV_EF_IGNORE_ERR)) { + uint32_t crc_sig = AV_RB32(s->gb.buffer + length + 4); + uint32_t crc_cal = ~av_crc(crc_tab, UINT32_MAX, s->gb.buffer, length + 4); + if (crc_sig ^ crc_cal) { + av_log(avctx, AV_LOG_ERROR, "CRC mismatch in chunk"); + if (avctx->err_recognition & AV_EF_EXPLODE) { + av_log(avctx, AV_LOG_ERROR, ", quitting\n"); + ret = AVERROR_INVALIDDATA; + goto fail; + } + av_log(avctx, AV_LOG_ERROR, ", skipping\n"); + bytestream2_skip(&s->gb, 4); /* tag */ + goto skip_tag; + } + } tag = bytestream2_get_le32(&s->gb); if (avctx->debug & FF_DEBUG_STARTCODE) av_log(avctx, AV_LOG_DEBUG, "png: tag=%s length=%u\n", @@ -1242,7 +1274,7 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, case MKTAG('f', 'd', 'A', 'T'): if (!CONFIG_APNG_DECODER || avctx->codec_id != AV_CODEC_ID_APNG) goto skip_tag; - if (!decode_next_dat) { + if (!decode_next_dat || length < 4) { ret = AVERROR_INVALIDDATA; goto fail; } @@ -1276,8 +1308,10 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, case MKTAG('s', 'T', 'E', 'R'): { int mode = bytestream2_get_byte(&s->gb); AVStereo3D *stereo3d = av_stereo3d_create_side_data(p); - if (!stereo3d) + if (!stereo3d) { + ret = AVERROR(ENOMEM); goto fail; + } if (mode == 0 || mode == 1) { stereo3d->type = AV_STEREO3D_SIDEBYSIDE; @@ -1290,7 +1324,7 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, break; } case MKTAG('i', 'C', 'C', 'P'): { - if (decode_iccp_chunk(s, length, p) < 0) + if ((ret = decode_iccp_chunk(s, length, p)) < 0) goto fail; break; } @@ -1353,6 +1387,9 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, return 0; } + if (percent_missing(s) > avctx->discard_damaged_percentage) + return AVERROR_INVALIDDATA; + if (s->bits_per_pixel <= 4) handle_small_bpp(s, p); @@ -1367,15 +1404,35 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, for (y = 0; y < s->height; ++y) { uint8_t *row = &s->image_buf[s->image_linesize * y]; - /* since we're updating in-place, we have to go from right to left */ - for (x = s->width; x > 0; --x) { - uint8_t *pixel = &row[s->bpp * (x - 1)]; - memmove(pixel, &row[raw_bpp * (x - 1)], raw_bpp); + if (s->bpp == 2 && byte_depth == 1) { + uint8_t *pixel = &row[2 * s->width - 1]; + uint8_t *rowp = &row[1 * s->width - 1]; + int tcolor = s->transparent_color_be[0]; + for (x = s->width; x > 0; --x) { + *pixel-- = *rowp == tcolor ? 0 : 0xff; + *pixel-- = *rowp--; + } + } else if (s->bpp == 4 && byte_depth == 1) { + uint8_t *pixel = &row[4 * s->width - 1]; + uint8_t *rowp = &row[3 * s->width - 1]; + int tcolor = AV_RL24(s->transparent_color_be); + for (x = s->width; x > 0; --x) { + *pixel-- = AV_RL24(rowp-2) == tcolor ? 0 : 0xff; + *pixel-- = *rowp--; + *pixel-- = *rowp--; + *pixel-- = *rowp--; + } + } else { + /* since we're updating in-place, we have to go from right to left */ + for (x = s->width; x > 0; --x) { + uint8_t *pixel = &row[s->bpp * (x - 1)]; + memmove(pixel, &row[raw_bpp * (x - 1)], raw_bpp); - if (!memcmp(pixel, s->transparent_color_be, raw_bpp)) { - memset(&pixel[raw_bpp], 0, byte_depth); - } else { - memset(&pixel[raw_bpp], 0xff, byte_depth); + if (!memcmp(pixel, s->transparent_color_be, raw_bpp)) { + memset(&pixel[raw_bpp], 0, byte_depth); + } else { + memset(&pixel[raw_bpp], 0xff, byte_depth); + } } } } @@ -1738,10 +1795,7 @@ static av_cold int png_dec_init(AVCodecContext *avctx) return AVERROR(ENOMEM); } - if (!avctx->internal->is_copy) { - avctx->internal->allocate_progress = 1; - ff_pngdsp_init(&s->dsp); - } + ff_pngdsp_init(&s->dsp); return 0; } @@ -1776,10 +1830,10 @@ AVCodec ff_apng_decoder = { .init = png_dec_init, .close = png_dec_end, .decode = decode_frame_apng, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(png_dec_init), .update_thread_context = ONLY_IF_THREADS_ENABLED(update_thread_context), .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS /*| AV_CODEC_CAP_DRAW_HORIZ_BAND*/, - .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | + FF_CODEC_CAP_ALLOCATE_PROGRESS, }; #endif @@ -1793,10 +1847,10 @@ AVCodec ff_png_decoder = { .init = png_dec_init, .close = png_dec_end, .decode = decode_frame_png, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(png_dec_init), .update_thread_context = ONLY_IF_THREADS_ENABLED(update_thread_context), .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS /*| AV_CODEC_CAP_DRAW_HORIZ_BAND*/, - .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | FF_CODEC_CAP_INIT_THREADSAFE, + .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | FF_CODEC_CAP_INIT_THREADSAFE | + FF_CODEC_CAP_ALLOCATE_PROGRESS, }; #endif @@ -1812,6 +1866,7 @@ AVCodec ff_lscr_decoder = { .decode = decode_frame_lscr, .flush = decode_flush, .capabilities = AV_CODEC_CAP_DR1 /*| AV_CODEC_CAP_DRAW_HORIZ_BAND*/, - .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | FF_CODEC_CAP_INIT_THREADSAFE, + .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | FF_CODEC_CAP_INIT_THREADSAFE | + FF_CODEC_CAP_ALLOCATE_PROGRESS, }; #endif diff --git a/libavcodec/pngenc.c b/libavcodec/pngenc.c index d4d8dc8b5ef..efcae8c494a 100644 --- a/libavcodec/pngenc.c +++ b/libavcodec/pngenc.c @@ -741,7 +741,7 @@ static int apng_encode_frame(AVCodecContext *avctx, const AVFrame *pict, diffFrame->format = pict->format; diffFrame->width = pict->width; diffFrame->height = pict->height; - if ((ret = av_frame_get_buffer(diffFrame, 32)) < 0) + if ((ret = av_frame_get_buffer(diffFrame, 0)) < 0) goto fail; original_bytestream = s->bytestream; @@ -956,7 +956,7 @@ static int encode_apng(AVCodecContext *avctx, AVPacket *pkt, s->prev_frame->format = pict->format; s->prev_frame->width = pict->width; s->prev_frame->height = pict->height; - if ((ret = av_frame_get_buffer(s->prev_frame, 32)) < 0) + if ((ret = av_frame_get_buffer(s->prev_frame, 0)) < 0) return ret; } @@ -1146,7 +1146,7 @@ AVCodec ff_png_encoder = { .init = png_enc_init, .close = png_enc_close, .encode2 = encode_png, - .capabilities = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_FRAME_THREADS, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, AV_PIX_FMT_RGB48BE, AV_PIX_FMT_RGBA64BE, diff --git a/libavcodec/pnm.c b/libavcodec/pnm.c index a9771710c2a..aad23c7ae28 100644 --- a/libavcodec/pnm.c +++ b/libavcodec/pnm.c @@ -22,7 +22,9 @@ #include #include +#include "libavutil/avassert.h" #include "libavutil/imgutils.h" +#include "libavutil/avstring.h" #include "avcodec.h" #include "internal.h" #include "pnm.h" @@ -57,8 +59,6 @@ static void pnm_get(PNMContext *sc, char *str, int buf_size) c = *bs++; } *s = '\0'; - while (bs < end && !pnm_space(c)) - c = *bs++; sc->bytestream = bs; } @@ -68,12 +68,21 @@ int ff_pnm_decode_header(AVCodecContext *avctx, PNMContext * const s) int h, w, depth, maxval; int ret; - pnm_get(s, buf1, sizeof(buf1)); - if(buf1[0] != 'P') + if (s->bytestream_end - s->bytestream < 3 || + s->bytestream[0] != 'P' || + (s->bytestream[1] < '1' || + s->bytestream[1] > '7' && + s->bytestream[1] != 'F')) { + s->bytestream += s->bytestream_end > s->bytestream; + s->bytestream += s->bytestream_end > s->bytestream; return AVERROR_INVALIDDATA; + } + pnm_get(s, buf1, sizeof(buf1)); s->type= buf1[1]-'0'; - if (s->type==1 || s->type==4) { + if (buf1[1] == 'F') { + avctx->pix_fmt = AV_PIX_FMT_GBRPF32; + } 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) @@ -112,6 +121,9 @@ int ff_pnm_decode_header(AVCodecContext *avctx, PNMContext * const s) return AVERROR_INVALIDDATA; } } + if (!pnm_space(s->bytestream[-1])) + return AVERROR_INVALIDDATA; + /* check that all tags are present */ if (w <= 0 || h <= 0 || maxval <= 0 || maxval > UINT16_MAX || depth <= 0 || tuple_type[0] == '\0' || av_image_check_size(w, h, 0, avctx) || s->bytestream >= s->bytestream_end) @@ -152,7 +164,7 @@ int ff_pnm_decode_header(AVCodecContext *avctx, PNMContext * const s) } return 0; } else { - return AVERROR_INVALIDDATA; + av_assert0(0); } pnm_get(s, buf1, sizeof(buf1)); w = atoi(buf1); @@ -165,7 +177,16 @@ int ff_pnm_decode_header(AVCodecContext *avctx, PNMContext * const s) if (ret < 0) return ret; - if (avctx->pix_fmt != AV_PIX_FMT_MONOWHITE && avctx->pix_fmt != AV_PIX_FMT_MONOBLACK) { + if (avctx->pix_fmt == AV_PIX_FMT_GBRPF32) { + pnm_get(s, buf1, sizeof(buf1)); + if (av_sscanf(buf1, "%f", &s->scale) != 1 || s->scale == 0.0 || !isfinite(s->scale)) { + av_log(avctx, AV_LOG_ERROR, "Invalid scale.\n"); + return AVERROR_INVALIDDATA; + } + s->endian = s->scale < 0.f; + s->scale = fabsf(s->scale); + s->maxval = (1ULL << 32) - 1; + } else if (avctx->pix_fmt != AV_PIX_FMT_MONOWHITE && avctx->pix_fmt != AV_PIX_FMT_MONOBLACK) { pnm_get(s, buf1, sizeof(buf1)); s->maxval = atoi(buf1); if (s->maxval <= 0 || s->maxval > UINT16_MAX) { @@ -192,6 +213,10 @@ int ff_pnm_decode_header(AVCodecContext *avctx, PNMContext * const s) } }else s->maxval=1; + + if (!pnm_space(s->bytestream[-1])) + return AVERROR_INVALIDDATA; + /* more check if YUV420 */ if (av_pix_fmt_desc_get(avctx->pix_fmt)->flags & AV_PIX_FMT_FLAG_PLANAR) { if ((avctx->width & 1) != 0) diff --git a/libavcodec/pnm.h b/libavcodec/pnm.h index 5bc0aad29ff..89c3b5a2da9 100644 --- a/libavcodec/pnm.h +++ b/libavcodec/pnm.h @@ -30,6 +30,8 @@ typedef struct PNMContext { uint8_t *bytestream_end; int maxval; ///< maximum value of a pixel int type; + int endian; + float scale; } PNMContext; int ff_pnm_decode_header(AVCodecContext *avctx, PNMContext * const s); diff --git a/libavcodec/pnm_parser.c b/libavcodec/pnm_parser.c index 5339bebde97..d19dbfe98c0 100644 --- a/libavcodec/pnm_parser.c +++ b/libavcodec/pnm_parser.c @@ -41,8 +41,11 @@ static int pnm_parse(AVCodecParserContext *s, AVCodecContext *avctx, int next = END_NOT_FOUND; int skip = 0; - for (; pc->overread > 0; pc->overread--) { - pc->buffer[pc->index++]= pc->buffer[pc->overread_index++]; + if (pc->overread > 0) { + memmove(pc->buffer + pc->index, pc->buffer + pc->overread_index, pc->overread); + pc->index += pc->overread; + pc->overread_index += pc->overread; + pc->overread = 0; } if (pnmpc->remaining_bytes) { @@ -92,8 +95,11 @@ static int pnm_parse(AVCodecParserContext *s, AVCodecContext *avctx, sync = bs; c = *bs++; if (c == '#') { - while (c != '\n' && bs < end) - c = *bs++; + uint8_t *match = memchr(bs, '\n', end-bs); + if (match) + bs = match + 1; + else + break; } else if (c == 'P') { next = bs - pnmctx.bytestream_start + skip - 1; pnmpc->ascii_scan = 0; diff --git a/libavcodec/pnmdec.c b/libavcodec/pnmdec.c index 958c5e43b07..9add5cfc845 100644 --- a/libavcodec/pnmdec.c +++ b/libavcodec/pnmdec.c @@ -46,6 +46,7 @@ static int pnm_decode_frame(AVCodecContext *avctx, void *data, int i, j, k, n, linesize, h, upgrade = 0, is_mono = 0; unsigned char *ptr; int components, sample_len, ret; + float scale; s->bytestream_start = s->bytestream = (uint8_t *)buf; @@ -132,7 +133,7 @@ static int pnm_decode_frame(AVCodecContext *avctx, void *data, init_put_bits(&pb, ptr, linesize); for(j=0; jwidth * components; j++){ unsigned int c=0; - int v=0; + unsigned v=0; if(s->type < 4) while(s->bytestream < s->bytestream_end && (*s->bytestream < '0' || *s->bytestream > '9' )) s->bytestream++; @@ -143,7 +144,7 @@ static int pnm_decode_frame(AVCodecContext *avctx, void *data, v = (*s->bytestream++)&1; } else { /* read a sequence of digits */ - for (k = 0; k < 5 && c <= 9; k += 1) { + for (k = 0; k < 6 && c <= 9; k += 1) { v = 10*v + c; c = (*s->bytestream++) - '0'; } @@ -172,7 +173,7 @@ static int pnm_decode_frame(AVCodecContext *avctx, void *data, } else if (upgrade == 2) { unsigned int j, v, f = (65535 * 32768 + s->maxval / 2) / s->maxval; for (j = 0; j < n / 2; j++) { - v = av_be2ne16(((uint16_t *)s->bytestream)[j]); + v = AV_RB16(s->bytestream + 2*j); ((uint16_t *)ptr)[j] = (v * f + 16384) >> 15; } } @@ -226,7 +227,7 @@ static int pnm_decode_frame(AVCodecContext *avctx, void *data, return AVERROR_INVALIDDATA; for (i = 0; i < avctx->height; i++) { for (j = 0; j < n / 2; j++) { - v = av_be2ne16(((uint16_t *)s->bytestream)[j]); + v = AV_RB16(s->bytestream + 2*j); ((uint16_t *)ptr)[j] = (v * f + 16384) >> 15; } s->bytestream += n; @@ -238,13 +239,13 @@ static int pnm_decode_frame(AVCodecContext *avctx, void *data, h = avctx->height >> 1; for (i = 0; i < h; i++) { for (j = 0; j < n / 2; j++) { - v = av_be2ne16(((uint16_t *)s->bytestream)[j]); + v = AV_RB16(s->bytestream + 2*j); ptr1[j] = (v * f + 16384) >> 15; } s->bytestream += n; for (j = 0; j < n / 2; j++) { - v = av_be2ne16(((uint16_t *)s->bytestream)[j]); + v = AV_RB16(s->bytestream + 2*j); ptr2[j] = (v * f + 16384) >> 15; } s->bytestream += n; @@ -254,6 +255,48 @@ static int pnm_decode_frame(AVCodecContext *avctx, void *data, } } break; + case AV_PIX_FMT_GBRPF32: + if (avctx->width * avctx->height * 12 > s->bytestream_end - s->bytestream) + return AVERROR_INVALIDDATA; + scale = 1.f / s->scale; + if (s->endian) { + float *r, *g, *b; + + r = (float *)p->data[2]; + g = (float *)p->data[0]; + b = (float *)p->data[1]; + for (int i = 0; i < avctx->height; i++) { + for (int j = 0; j < avctx->width; j++) { + r[j] = av_int2float(AV_RL32(s->bytestream+0)) * scale; + g[j] = av_int2float(AV_RL32(s->bytestream+4)) * scale; + b[j] = av_int2float(AV_RL32(s->bytestream+8)) * scale; + s->bytestream += 12; + } + + r += p->linesize[2] / 4; + g += p->linesize[0] / 4; + b += p->linesize[1] / 4; + } + } else { + float *r, *g, *b; + + r = (float *)p->data[2]; + g = (float *)p->data[0]; + b = (float *)p->data[1]; + for (int i = 0; i < avctx->height; i++) { + for (int j = 0; j < avctx->width; j++) { + r[j] = av_int2float(AV_RB32(s->bytestream+0)) * scale; + g[j] = av_int2float(AV_RB32(s->bytestream+4)) * scale; + b[j] = av_int2float(AV_RB32(s->bytestream+8)) * scale; + s->bytestream += 12; + } + + r += p->linesize[2] / 4; + g += p->linesize[0] / 4; + b += p->linesize[1] / 4; + } + } + break; } *got_frame = 1; @@ -320,3 +363,15 @@ AVCodec ff_pam_decoder = { .capabilities = AV_CODEC_CAP_DR1, }; #endif + +#if CONFIG_PFM_DECODER +AVCodec ff_pfm_decoder = { + .name = "pfm", + .long_name = NULL_IF_CONFIG_SMALL("PFM (Portable FloatMap) image"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_PFM, + .priv_data_size = sizeof(PNMContext), + .decode = pnm_decode_frame, + .capabilities = AV_CODEC_CAP_DR1, +}; +#endif diff --git a/libavcodec/profiles.c b/libavcodec/profiles.c index eaf0d68d329..e59a3a5c12b 100644 --- a/libavcodec/profiles.c +++ b/libavcodec/profiles.c @@ -99,7 +99,6 @@ const AVProfile ff_mpeg2_video_profiles[] = { { FF_PROFILE_MPEG2_MAIN, "Main" }, { FF_PROFILE_MPEG2_SIMPLE, "Simple" }, { FF_PROFILE_RESERVED, "Reserved" }, - { FF_PROFILE_RESERVED, "Reserved" }, { FF_PROFILE_UNKNOWN }, }; diff --git a/libavcodec/profiles.h b/libavcodec/profiles.h index a53b67e7f2e..d2419257dc5 100644 --- a/libavcodec/profiles.h +++ b/libavcodec/profiles.h @@ -20,6 +20,36 @@ #define AVCODEC_PROFILES_H #include "avcodec.h" +#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"}, + +#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)\ + +#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)\ + +#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)\ extern const AVProfile ff_aac_profiles[]; extern const AVProfile ff_dca_profiles[]; diff --git a/libavcodec/prores_metadata_bsf.c b/libavcodec/prores_metadata_bsf.c index 0510d3520a5..d971243a8b3 100644 --- a/libavcodec/prores_metadata_bsf.c +++ b/libavcodec/prores_metadata_bsf.c @@ -28,7 +28,9 @@ #include "libavutil/common.h" #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" + #include "bsf.h" +#include "bsf_internal.h" typedef struct ProresMetadataContext { const AVClass *class; @@ -140,10 +142,12 @@ static const AVOption options[] = { {"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_BT709, FLAGS, "color_trc"}, + {"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"}, diff --git a/libavcodec/proresdec2.c b/libavcodec/proresdec2.c index 2652a31c81f..d5fbfc6711b 100644 --- a/libavcodec/proresdec2.c +++ b/libavcodec/proresdec2.c @@ -807,17 +807,6 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, return avpkt->size; } -#if HAVE_THREADS -static int decode_init_thread_copy(AVCodecContext *avctx) -{ - ProresContext *ctx = avctx->priv_data; - - ctx->slices = NULL; - - return 0; -} -#endif - static av_cold int decode_close(AVCodecContext *avctx) { ProresContext *ctx = avctx->priv_data; @@ -834,7 +823,6 @@ AVCodec ff_prores_decoder = { .id = AV_CODEC_ID_PRORES, .priv_data_size = sizeof(ProresContext), .init = decode_init, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy), .close = decode_close, .decode = decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS, diff --git a/libavcodec/proresdec_lgpl.c b/libavcodec/proresdec_lgpl.c deleted file mode 100644 index c86d433f507..00000000000 --- a/libavcodec/proresdec_lgpl.c +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Apple ProRes compatible decoder - * - * Copyright (c) 2010-2011 Maxim Poliakovski - * - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General 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 - * This is a decoder for Apple ProRes 422 SD/HQ/LT/Proxy and ProRes 4444. - * It is used for storing and editing high definition video data in Apple's Final Cut Pro. - * - * @see http://wiki.multimedia.cx/index.php?title=Apple_ProRes - */ - -#define LONG_BITSTREAM_READER // some ProRes vlc codes require up to 28 bits to be read at once - -#include - -#include "libavutil/intmath.h" -#include "avcodec.h" -#include "idctdsp.h" -#include "internal.h" -#include "proresdata.h" -#include "proresdsp.h" -#include "get_bits.h" - -typedef struct ProresThreadData { - const uint8_t *index; ///< pointers to the data of this slice - int slice_num; - int x_pos, y_pos; - int slice_width; - int prev_slice_sf; ///< scalefactor of the previous decoded slice - DECLARE_ALIGNED(16, int16_t, blocks)[8 * 4 * 64]; - DECLARE_ALIGNED(16, int16_t, qmat_luma_scaled)[64]; - DECLARE_ALIGNED(16, int16_t, qmat_chroma_scaled)[64]; -} ProresThreadData; - -typedef struct ProresContext { - ProresDSPContext dsp; - AVFrame *frame; - ScanTable scantable; - int scantable_type; ///< -1 = uninitialized, 0 = progressive, 1/2 = interlaced - - int frame_type; ///< 0 = progressive, 1 = top-field first, 2 = bottom-field first - int pic_format; ///< 2 = 422, 3 = 444 - uint8_t qmat_luma[64]; ///< dequantization matrix for luma - uint8_t qmat_chroma[64]; ///< dequantization matrix for chroma - int qmat_changed; ///< 1 - global quantization matrices changed - int total_slices; ///< total number of slices in a picture - ProresThreadData *slice_data; - int pic_num; - int chroma_factor; - int mb_chroma_factor; - int num_chroma_blocks; ///< number of chrominance blocks in a macroblock - int num_x_slices; - int num_y_slices; - int slice_width_factor; - int slice_height_factor; - int num_x_mbs; - int num_y_mbs; - int alpha_info; -} ProresContext; - - -static av_cold int decode_init(AVCodecContext *avctx) -{ - ProresContext *ctx = avctx->priv_data; - - ctx->total_slices = 0; - ctx->slice_data = NULL; - - avctx->bits_per_raw_sample = PRORES_BITS_PER_SAMPLE; - ff_proresdsp_init(&ctx->dsp, avctx); - - ctx->scantable_type = -1; // set scantable type to uninitialized - memset(ctx->qmat_luma, 4, 64); - memset(ctx->qmat_chroma, 4, 64); - - return 0; -} - - -static int decode_frame_header(ProresContext *ctx, const uint8_t *buf, - const int data_size, AVCodecContext *avctx) -{ - int hdr_size, version, width, height, flags; - const uint8_t *ptr; - - hdr_size = AV_RB16(buf); - if (hdr_size > data_size) { - av_log(avctx, AV_LOG_ERROR, "frame data too small\n"); - return AVERROR_INVALIDDATA; - } - - version = AV_RB16(buf + 2); - if (version >= 2) { - av_log(avctx, AV_LOG_ERROR, - "unsupported header version: %d\n", version); - return AVERROR_INVALIDDATA; - } - - width = AV_RB16(buf + 8); - height = AV_RB16(buf + 10); - if (width != avctx->width || height != avctx->height) { - av_log(avctx, AV_LOG_ERROR, - "picture dimension changed: old: %d x %d, new: %d x %d\n", - avctx->width, avctx->height, width, height); - return AVERROR_INVALIDDATA; - } - - ctx->frame_type = (buf[12] >> 2) & 3; - if (ctx->frame_type > 2) { - av_log(avctx, AV_LOG_ERROR, - "unsupported frame type: %d\n", ctx->frame_type); - return AVERROR_INVALIDDATA; - } - - ctx->chroma_factor = (buf[12] >> 6) & 3; - ctx->mb_chroma_factor = ctx->chroma_factor + 2; - ctx->num_chroma_blocks = (1 << ctx->chroma_factor) >> 1; - ctx->alpha_info = buf[17] & 0xf; - - if (ctx->alpha_info > 2) { - av_log(avctx, AV_LOG_ERROR, "Invalid alpha mode %d\n", ctx->alpha_info); - return AVERROR_INVALIDDATA; - } - if (avctx->skip_alpha) ctx->alpha_info = 0; - - switch (ctx->chroma_factor) { - case 2: - avctx->pix_fmt = ctx->alpha_info ? AV_PIX_FMT_YUVA422P10 - : AV_PIX_FMT_YUV422P10; - break; - case 3: - avctx->pix_fmt = ctx->alpha_info ? AV_PIX_FMT_YUVA444P10 - : AV_PIX_FMT_YUV444P10; - break; - default: - av_log(avctx, AV_LOG_ERROR, - "unsupported picture format: %d\n", ctx->pic_format); - return AVERROR_INVALIDDATA; - } - - if (ctx->scantable_type != ctx->frame_type) { - if (!ctx->frame_type) - ff_init_scantable(ctx->dsp.idct_permutation, &ctx->scantable, - ff_prores_progressive_scan); - else - ff_init_scantable(ctx->dsp.idct_permutation, &ctx->scantable, - ff_prores_interlaced_scan); - ctx->scantable_type = ctx->frame_type; - } - - if (ctx->frame_type) { /* if interlaced */ - ctx->frame->interlaced_frame = 1; - ctx->frame->top_field_first = ctx->frame_type & 1; - } else { - ctx->frame->interlaced_frame = 0; - } - - avctx->color_primaries = buf[14]; - avctx->color_trc = buf[15]; - avctx->colorspace = buf[16]; - avctx->color_range = AVCOL_RANGE_MPEG; - - ctx->qmat_changed = 0; - ptr = buf + 20; - flags = buf[19]; - if (flags & 2) { - if (ptr - buf > hdr_size - 64) { - av_log(avctx, AV_LOG_ERROR, "header data too small\n"); - return AVERROR_INVALIDDATA; - } - if (memcmp(ctx->qmat_luma, ptr, 64)) { - memcpy(ctx->qmat_luma, ptr, 64); - ctx->qmat_changed = 1; - } - ptr += 64; - } else { - memset(ctx->qmat_luma, 4, 64); - ctx->qmat_changed = 1; - } - - if (flags & 1) { - if (ptr - buf > hdr_size - 64) { - av_log(avctx, AV_LOG_ERROR, "header data too small\n"); - return -1; - } - if (memcmp(ctx->qmat_chroma, ptr, 64)) { - memcpy(ctx->qmat_chroma, ptr, 64); - ctx->qmat_changed = 1; - } - } else { - memset(ctx->qmat_chroma, 4, 64); - ctx->qmat_changed = 1; - } - - return hdr_size; -} - - -static int decode_picture_header(ProresContext *ctx, const uint8_t *buf, - const int data_size, AVCodecContext *avctx) -{ - int i, hdr_size, pic_data_size, num_slices; - int slice_width_factor, slice_height_factor; - int remainder, num_x_slices; - const uint8_t *data_ptr, *index_ptr; - - hdr_size = data_size > 0 ? buf[0] >> 3 : 0; - if (hdr_size < 8 || hdr_size > data_size) { - av_log(avctx, AV_LOG_ERROR, "picture header too small\n"); - return AVERROR_INVALIDDATA; - } - - pic_data_size = AV_RB32(buf + 1); - if (pic_data_size > data_size) { - av_log(avctx, AV_LOG_ERROR, "picture data too small\n"); - return AVERROR_INVALIDDATA; - } - - slice_width_factor = buf[7] >> 4; - slice_height_factor = buf[7] & 0xF; - if (slice_width_factor > 3 || slice_height_factor) { - av_log(avctx, AV_LOG_ERROR, - "unsupported slice dimension: %d x %d\n", - 1 << slice_width_factor, 1 << slice_height_factor); - return AVERROR_INVALIDDATA; - } - - ctx->slice_width_factor = slice_width_factor; - ctx->slice_height_factor = slice_height_factor; - - ctx->num_x_mbs = (avctx->width + 15) >> 4; - ctx->num_y_mbs = (avctx->height + - (1 << (4 + ctx->frame->interlaced_frame)) - 1) >> - (4 + ctx->frame->interlaced_frame); - - remainder = av_mod_uintp2(ctx->num_x_mbs, slice_width_factor); - num_x_slices = (ctx->num_x_mbs >> slice_width_factor) + (remainder & 1) + - ((remainder >> 1) & 1) + ((remainder >> 2) & 1); - - num_slices = num_x_slices * ctx->num_y_mbs; - if (num_slices != AV_RB16(buf + 5)) { - av_log(avctx, AV_LOG_ERROR, "invalid number of slices\n"); - return AVERROR_INVALIDDATA; - } - - if (ctx->total_slices != num_slices) { - av_freep(&ctx->slice_data); - ctx->slice_data = av_malloc_array(num_slices + 1, sizeof(ctx->slice_data[0])); - if (!ctx->slice_data) - return AVERROR(ENOMEM); - ctx->total_slices = num_slices; - } - - if (hdr_size + num_slices * 2 > data_size) { - av_log(avctx, AV_LOG_ERROR, "slice table too small\n"); - return AVERROR_INVALIDDATA; - } - - /* parse slice table allowing quick access to the slice data */ - index_ptr = buf + hdr_size; - data_ptr = index_ptr + num_slices * 2; - - for (i = 0; i < num_slices; i++) { - ctx->slice_data[i].index = data_ptr; - ctx->slice_data[i].prev_slice_sf = 0; - data_ptr += AV_RB16(index_ptr + i * 2); - } - ctx->slice_data[i].index = data_ptr; - ctx->slice_data[i].prev_slice_sf = 0; - - if (data_ptr > buf + data_size) { - av_log(avctx, AV_LOG_ERROR, "out of slice data\n"); - return -1; - } - - return pic_data_size; -} - - -/** - * Read an unsigned rice/exp golomb codeword. - */ -static inline int decode_vlc_codeword(GetBitContext *gb, unsigned codebook) -{ - unsigned int rice_order, exp_order, switch_bits; - unsigned int buf, code; - int log, prefix_len, len; - - OPEN_READER(re, gb); - UPDATE_CACHE(re, gb); - buf = GET_CACHE(re, gb); - - /* 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 */ - - log = 31 - av_log2(buf); /* count prefix bits (zeroes) */ - - if (log < switch_bits) { /* ok, we got a rice code */ - if (!rice_order) { - /* shortcut for faster decoding of rice codes without remainder */ - code = log; - LAST_SKIP_BITS(re, gb, log + 1); - } else { - prefix_len = log + 1; - code = (log << rice_order) + NEG_USR32(buf << prefix_len, rice_order); - LAST_SKIP_BITS(re, gb, prefix_len + rice_order); - } - } else { /* otherwise we got a exp golomb code */ - len = (log << 1) - switch_bits + exp_order + 1; - code = NEG_USR32(buf, len) - (1 << exp_order) + (switch_bits << rice_order); - LAST_SKIP_BITS(re, gb, len); - } - - CLOSE_READER(re, gb); - - return code; -} - -#define LSB2SIGN(x) (-((x) & 1)) -#define TOSIGNED(x) (((x) >> 1) ^ LSB2SIGN(x)) - -/** - * Decode DC coefficients for all blocks in a slice. - */ -static inline void decode_dc_coeffs(GetBitContext *gb, int16_t *out, - int nblocks) -{ - int16_t prev_dc; - int i, sign; - int16_t delta; - unsigned int code; - - code = decode_vlc_codeword(gb, FIRST_DC_CB); - out[0] = prev_dc = TOSIGNED(code); - - out += 64; /* move to the DC coeff of the next block */ - delta = 3; - - for (i = 1; i < nblocks; i++, out += 64) { - code = decode_vlc_codeword(gb, ff_prores_dc_codebook[FFMIN(FFABS(delta), 3)]); - - sign = -(((delta >> 15) & 1) ^ (code & 1)); - delta = (((code + 1) >> 1) ^ sign) - sign; - prev_dc += delta; - out[0] = prev_dc; - } -} - -#define MAX_PADDING 16 - -/** - * Decode AC coefficients for all blocks in a slice. - */ -static inline int decode_ac_coeffs(GetBitContext *gb, int16_t *out, - int blocks_per_slice, - int plane_size_factor, - const uint8_t *scan) -{ - int pos, block_mask, run, level, sign, run_cb_index, lev_cb_index; - int max_coeffs, bits_left; - - /* set initial prediction values */ - run = 4; - level = 2; - - max_coeffs = blocks_per_slice << 6; - block_mask = blocks_per_slice - 1; - - for (pos = blocks_per_slice - 1; pos < max_coeffs;) { - run_cb_index = ff_prores_run_to_cb_index[FFMIN(run, 15)]; - lev_cb_index = ff_prores_lev_to_cb_index[FFMIN(level, 9)]; - - bits_left = get_bits_left(gb); - if (bits_left <= 0 || (bits_left <= MAX_PADDING && !show_bits(gb, bits_left))) - return 0; - - run = decode_vlc_codeword(gb, ff_prores_ac_codebook[run_cb_index]); - if (run < 0) - return AVERROR_INVALIDDATA; - - bits_left = get_bits_left(gb); - if (bits_left <= 0 || (bits_left <= MAX_PADDING && !show_bits(gb, bits_left))) - return AVERROR_INVALIDDATA; - - level = decode_vlc_codeword(gb, ff_prores_ac_codebook[lev_cb_index]) + 1; - if (level < 0) - return AVERROR_INVALIDDATA; - - pos += run + 1; - if (pos >= max_coeffs) - break; - - sign = get_sbits(gb, 1); - out[((pos & block_mask) << 6) + scan[pos >> plane_size_factor]] = - (level ^ sign) - sign; - } - - return 0; -} - - -/** - * Decode a slice plane (luma or chroma). - */ -static int decode_slice_plane(ProresContext *ctx, ProresThreadData *td, - const uint8_t *buf, - int data_size, uint16_t *out_ptr, - int linesize, int mbs_per_slice, - int blocks_per_mb, int plane_size_factor, - const int16_t *qmat, int is_chroma) -{ - GetBitContext gb; - int16_t *block_ptr; - int mb_num, blocks_per_slice, ret; - - blocks_per_slice = mbs_per_slice * blocks_per_mb; - - memset(td->blocks, 0, 8 * 4 * 64 * sizeof(*td->blocks)); - - init_get_bits(&gb, buf, data_size << 3); - - decode_dc_coeffs(&gb, td->blocks, blocks_per_slice); - - ret = decode_ac_coeffs(&gb, td->blocks, blocks_per_slice, - plane_size_factor, ctx->scantable.permutated); - if (ret < 0) - return ret; - - /* inverse quantization, inverse transform and output */ - block_ptr = td->blocks; - - if (!is_chroma) { - for (mb_num = 0; mb_num < mbs_per_slice; mb_num++, out_ptr += blocks_per_mb * 4) { - ctx->dsp.idct_put(out_ptr, linesize, block_ptr, qmat); - block_ptr += 64; - if (blocks_per_mb > 2) { - ctx->dsp.idct_put(out_ptr + 8, linesize, block_ptr, qmat); - block_ptr += 64; - } - ctx->dsp.idct_put(out_ptr + linesize * 4, linesize, block_ptr, qmat); - block_ptr += 64; - if (blocks_per_mb > 2) { - ctx->dsp.idct_put(out_ptr + linesize * 4 + 8, linesize, block_ptr, qmat); - block_ptr += 64; - } - } - } else { - for (mb_num = 0; mb_num < mbs_per_slice; mb_num++, out_ptr += blocks_per_mb * 4) { - ctx->dsp.idct_put(out_ptr, linesize, block_ptr, qmat); - block_ptr += 64; - ctx->dsp.idct_put(out_ptr + linesize * 4, linesize, block_ptr, qmat); - block_ptr += 64; - if (blocks_per_mb > 2) { - ctx->dsp.idct_put(out_ptr + 8, linesize, block_ptr, qmat); - block_ptr += 64; - ctx->dsp.idct_put(out_ptr + linesize * 4 + 8, linesize, block_ptr, qmat); - block_ptr += 64; - } - } - } - return 0; -} - - -static void unpack_alpha(GetBitContext *gb, uint16_t *dst, int num_coeffs, - const int num_bits) -{ - const int mask = (1 << num_bits) - 1; - int i, idx, val, alpha_val; - - idx = 0; - alpha_val = mask; - do { - do { - if (get_bits1(gb)) - val = get_bits(gb, num_bits); - else { - int sign; - val = get_bits(gb, num_bits == 16 ? 7 : 4); - sign = val & 1; - val = (val + 2) >> 1; - if (sign) - val = -val; - } - alpha_val = (alpha_val + val) & mask; - if (num_bits == 16) - dst[idx++] = alpha_val >> 6; - else - dst[idx++] = (alpha_val << 2) | (alpha_val >> 6); - if (idx >= num_coeffs) { - break; - } - } while (get_bits1(gb)); - val = get_bits(gb, 4); - if (!val) - val = get_bits(gb, 11); - if (idx + val > num_coeffs) - val = num_coeffs - idx; - if (num_bits == 16) - for (i = 0; i < val; i++) - dst[idx++] = alpha_val >> 6; - else - for (i = 0; i < val; i++) - dst[idx++] = (alpha_val << 2) | (alpha_val >> 6); - } while (idx < num_coeffs); -} - -/** - * Decode alpha slice plane. - */ -static void decode_alpha_plane(ProresContext *ctx, ProresThreadData *td, - const uint8_t *buf, int data_size, - uint16_t *out_ptr, int linesize, - int mbs_per_slice) -{ - GetBitContext gb; - int i; - uint16_t *block_ptr; - - memset(td->blocks, 0, 8 * 4 * 64 * sizeof(*td->blocks)); - - init_get_bits(&gb, buf, data_size << 3); - - if (ctx->alpha_info == 2) - unpack_alpha(&gb, td->blocks, mbs_per_slice * 4 * 64, 16); - else - unpack_alpha(&gb, td->blocks, mbs_per_slice * 4 * 64, 8); - - block_ptr = td->blocks; - - for (i = 0; i < 16; i++) { - memcpy(out_ptr, block_ptr, 16 * mbs_per_slice * sizeof(*out_ptr)); - out_ptr += linesize >> 1; - block_ptr += 16 * mbs_per_slice; - } -} - -static int decode_slice(AVCodecContext *avctx, void *tdata) -{ - ProresThreadData *td = tdata; - ProresContext *ctx = avctx->priv_data; - int mb_x_pos = td->x_pos; - int mb_y_pos = td->y_pos; - int pic_num = ctx->pic_num; - int slice_num = td->slice_num; - int mbs_per_slice = td->slice_width; - const uint8_t *buf; - uint8_t *y_data, *u_data, *v_data, *a_data; - AVFrame *pic = ctx->frame; - int i, sf, slice_width_factor; - int slice_data_size, hdr_size; - int y_data_size, u_data_size, v_data_size, a_data_size; - int y_linesize, u_linesize, v_linesize, a_linesize; - int coff[4]; - int ret; - - buf = ctx->slice_data[slice_num].index; - slice_data_size = ctx->slice_data[slice_num + 1].index - buf; - - slice_width_factor = av_log2(mbs_per_slice); - - y_data = pic->data[0]; - u_data = pic->data[1]; - v_data = pic->data[2]; - a_data = pic->data[3]; - y_linesize = pic->linesize[0]; - u_linesize = pic->linesize[1]; - v_linesize = pic->linesize[2]; - a_linesize = pic->linesize[3]; - - if (pic->interlaced_frame) { - if (!(pic_num ^ pic->top_field_first)) { - y_data += y_linesize; - u_data += u_linesize; - v_data += v_linesize; - if (a_data) - a_data += a_linesize; - } - y_linesize <<= 1; - u_linesize <<= 1; - v_linesize <<= 1; - a_linesize <<= 1; - } - y_data += (mb_y_pos << 4) * y_linesize + (mb_x_pos << 5); - u_data += (mb_y_pos << 4) * u_linesize + (mb_x_pos << ctx->mb_chroma_factor); - v_data += (mb_y_pos << 4) * v_linesize + (mb_x_pos << ctx->mb_chroma_factor); - if (a_data) - a_data += (mb_y_pos << 4) * a_linesize + (mb_x_pos << 5); - - if (slice_data_size < 6) { - av_log(avctx, AV_LOG_ERROR, "slice data too small\n"); - return AVERROR_INVALIDDATA; - } - - /* parse slice header */ - hdr_size = buf[0] >> 3; - coff[0] = hdr_size; - y_data_size = AV_RB16(buf + 2); - coff[1] = coff[0] + y_data_size; - u_data_size = AV_RB16(buf + 4); - coff[2] = coff[1] + u_data_size; - v_data_size = hdr_size > 7 ? AV_RB16(buf + 6) : slice_data_size - coff[2]; - coff[3] = coff[2] + v_data_size; - a_data_size = ctx->alpha_info ? slice_data_size - coff[3] : 0; - - /* if V or alpha component size is negative that means that previous - component sizes are too large */ - if (v_data_size < 0 || a_data_size < 0 || hdr_size < 6 || coff[3] > slice_data_size) { - av_log(avctx, AV_LOG_ERROR, "invalid data size\n"); - return AVERROR_INVALIDDATA; - } - - sf = av_clip(buf[1], 1, 224); - sf = sf > 128 ? (sf - 96) << 2 : sf; - - /* scale quantization matrixes according with slice's scale factor */ - /* TODO: this can be SIMD-optimized a lot */ - if (ctx->qmat_changed || sf != td->prev_slice_sf) { - td->prev_slice_sf = sf; - for (i = 0; i < 64; i++) { - td->qmat_luma_scaled[ctx->dsp.idct_permutation[i]] = ctx->qmat_luma[i] * sf; - td->qmat_chroma_scaled[ctx->dsp.idct_permutation[i]] = ctx->qmat_chroma[i] * sf; - } - } - - /* decode luma plane */ - ret = decode_slice_plane(ctx, td, buf + coff[0], y_data_size, - (uint16_t*) y_data, y_linesize, - mbs_per_slice, 4, slice_width_factor + 2, - td->qmat_luma_scaled, 0); - - if (ret < 0) - return ret; - - /* decode U chroma plane */ - ret = decode_slice_plane(ctx, td, buf + coff[1], u_data_size, - (uint16_t*) u_data, u_linesize, - mbs_per_slice, ctx->num_chroma_blocks, - slice_width_factor + ctx->chroma_factor - 1, - td->qmat_chroma_scaled, 1); - if (ret < 0) - return ret; - - /* decode V chroma plane */ - ret = decode_slice_plane(ctx, td, buf + coff[2], v_data_size, - (uint16_t*) v_data, v_linesize, - mbs_per_slice, ctx->num_chroma_blocks, - slice_width_factor + ctx->chroma_factor - 1, - td->qmat_chroma_scaled, 1); - if (ret < 0) - return ret; - - /* decode alpha plane if available */ - if (a_data && a_data_size) - decode_alpha_plane(ctx, td, buf + coff[3], a_data_size, - (uint16_t*) a_data, a_linesize, - mbs_per_slice); - - return 0; -} - - -static int decode_picture(ProresContext *ctx, int pic_num, - AVCodecContext *avctx) -{ - int slice_num, slice_width, x_pos, y_pos; - - slice_num = 0; - - ctx->pic_num = pic_num; - for (y_pos = 0; y_pos < ctx->num_y_mbs; y_pos++) { - slice_width = 1 << ctx->slice_width_factor; - - for (x_pos = 0; x_pos < ctx->num_x_mbs && slice_width; - x_pos += slice_width) { - while (ctx->num_x_mbs - x_pos < slice_width) - slice_width >>= 1; - - ctx->slice_data[slice_num].slice_num = slice_num; - ctx->slice_data[slice_num].x_pos = x_pos; - ctx->slice_data[slice_num].y_pos = y_pos; - ctx->slice_data[slice_num].slice_width = slice_width; - - slice_num++; - } - } - - return avctx->execute(avctx, decode_slice, - ctx->slice_data, NULL, slice_num, - sizeof(ctx->slice_data[0])); -} - - -#define MOVE_DATA_PTR(nbytes) buf += (nbytes); buf_size -= (nbytes) - -static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, - AVPacket *avpkt) -{ - ProresContext *ctx = avctx->priv_data; - const uint8_t *buf = avpkt->data; - int buf_size = avpkt->size; - int frame_hdr_size, pic_num, pic_data_size; - - ctx->frame = data; - ctx->frame->pict_type = AV_PICTURE_TYPE_I; - ctx->frame->key_frame = 1; - - /* check frame atom container */ - if (buf_size < 28 || buf_size < AV_RB32(buf) || - AV_RB32(buf + 4) != FRAME_ID) { - av_log(avctx, AV_LOG_ERROR, "invalid frame\n"); - return AVERROR_INVALIDDATA; - } - - MOVE_DATA_PTR(8); - - frame_hdr_size = decode_frame_header(ctx, buf, buf_size, avctx); - if (frame_hdr_size < 0) - return AVERROR_INVALIDDATA; - - MOVE_DATA_PTR(frame_hdr_size); - - if (ff_get_buffer(avctx, ctx->frame, 0) < 0) - return -1; - - for (pic_num = 0; ctx->frame->interlaced_frame - pic_num + 1; pic_num++) { - pic_data_size = decode_picture_header(ctx, buf, buf_size, avctx); - if (pic_data_size < 0) - return AVERROR_INVALIDDATA; - - if (decode_picture(ctx, pic_num, avctx)) - return -1; - - MOVE_DATA_PTR(pic_data_size); - } - - ctx->frame = NULL; - *got_frame = 1; - - return avpkt->size; -} - - -static av_cold int decode_close(AVCodecContext *avctx) -{ - ProresContext *ctx = avctx->priv_data; - - av_freep(&ctx->slice_data); - - return 0; -} - - -AVCodec ff_prores_lgpl_decoder = { - .name = "prores_lgpl", - .long_name = NULL_IF_CONFIG_SMALL("Apple ProRes (iCodec Pro)"), - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_PRORES, - .priv_data_size = sizeof(ProresContext), - .init = decode_init, - .close = decode_close, - .decode = decode_frame, - .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS, -}; diff --git a/libavcodec/proresenc_anatoliy.c b/libavcodec/proresenc_anatoliy.c index 0fc79fc1dea..1128978330a 100644 --- a/libavcodec/proresenc_anatoliy.c +++ b/libavcodec/proresenc_anatoliy.c @@ -224,7 +224,7 @@ static void encode_codeword(PutBitContext *pb, int val, int codebook) } #define QSCALE(qmat,ind,val) ((val) / ((qmat)[ind])) -#define TO_GOLOMB(val) (((val) << 1) ^ ((val) >> 31)) +#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)) @@ -924,7 +924,7 @@ static av_cold int prores_encode_close(AVCodecContext *avctx) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "vendor", "vendor ID", OFFSET(vendor), AV_OPT_TYPE_STRING, { .str = "fmpg" }, CHAR_MIN, CHAR_MAX, VE }, + { "vendor", "vendor ID", OFFSET(vendor), AV_OPT_TYPE_STRING, { .str = "fmpg" }, 0, 0, VE }, { NULL } }; @@ -952,7 +952,7 @@ AVCodec ff_prores_aw_encoder = { .close = prores_encode_close, .encode2 = prores_encode_frame, .pix_fmts = (const enum AVPixelFormat[]){AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_NONE}, - .capabilities = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_FRAME_THREADS, .priv_class = &proresaw_enc_class, .profiles = NULL_IF_CONFIG_SMALL(ff_prores_profiles), }; @@ -967,7 +967,7 @@ AVCodec ff_prores_encoder = { .close = prores_encode_close, .encode2 = prores_encode_frame, .pix_fmts = (const enum AVPixelFormat[]){AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_NONE}, - .capabilities = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_FRAME_THREADS, .priv_class = &prores_enc_class, .profiles = NULL_IF_CONFIG_SMALL(ff_prores_profiles), }; diff --git a/libavcodec/proresenc_kostya.c b/libavcodec/proresenc_kostya.c index e045a972f1b..8e6f905a9fe 100644 --- a/libavcodec/proresenc_kostya.c +++ b/libavcodec/proresenc_kostya.c @@ -1390,7 +1390,7 @@ static const AVOption options[] = { { "4444xq", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_4444XQ }, 0, 0, VE, "profile" }, { "vendor", "vendor ID", OFFSET(vendor), - AV_OPT_TYPE_STRING, { .str = "Lavc" }, CHAR_MIN, CHAR_MAX, VE }, + 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, @@ -1428,7 +1428,7 @@ AVCodec ff_prores_ks_encoder = { .init = encode_init, .close = encode_close, .encode2 = encode_frame, - .capabilities = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_NONE diff --git a/libavcodec/psd.c b/libavcodec/psd.c index a31f73857a5..ae7ad4e559a 100644 --- a/libavcodec/psd.c +++ b/libavcodec/psd.c @@ -211,11 +211,9 @@ static int decode_header(PSDContext * s) case 2: avpriv_request_sample(s->avctx, "ZIP without predictor compression"); return AVERROR_PATCHWELCOME; - break; case 3: avpriv_request_sample(s->avctx, "ZIP with predictor compression"); return AVERROR_PATCHWELCOME; - break; default: av_log(s->avctx, AV_LOG_ERROR, "Unknown compression %d.\n", s->compression); return AVERROR_INVALIDDATA; diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c index 36ac0ac1e57..601f1704477 100644 --- a/libavcodec/pthread_frame.c +++ b/libavcodec/pthread_frame.c @@ -28,7 +28,7 @@ #include #include "avcodec.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "internal.h" #include "pthread_internal.h" #include "thread.h" @@ -93,9 +93,9 @@ typedef struct PerThreadContext { * Array of frames passed to ff_thread_release_buffer(). * Frames are released after all threads referencing them are finished. */ - AVFrame *released_buffers; - int num_released_buffers; - int released_buffers_allocated; + AVFrame **released_buffers; + int num_released_buffers; + int released_buffers_allocated; AVFrame *requested_frame; ///< AVFrame the codec passed to get_buffer() int requested_flags; ///< flags passed to get_buffer() for requested_frame @@ -201,7 +201,7 @@ static attribute_align_arg void *frame_worker_thread(void *arg) p->result = codec->decode(avctx, p->frame, &p->got_frame, &p->avpkt); if ((p->result < 0 || !p->got_frame) && p->frame->buf[0]) { - if (avctx->internal->allocate_progress) + if (avctx->codec->caps_internal & FF_CODEC_CAP_ALLOCATE_PROGRESS) av_log(avctx, AV_LOG_ERROR, "A frame threaded decoder did not " "free the frame on failure. This is a bug, please report it.\n"); av_frame_unref(p->frame); @@ -296,10 +296,20 @@ static int update_context_from_thread(AVCodecContext *dst, AVCodecContext *src, } dst->hwaccel_flags = src->hwaccel_flags; + + if (!!dst->internal->pool != !!src->internal->pool || + (dst->internal->pool && dst->internal->pool->data != src->internal->pool->data)) { + av_buffer_unref(&dst->internal->pool); + + if (src->internal->pool) { + dst->internal->pool = av_buffer_ref(src->internal->pool); + if (!dst->internal->pool) + return AVERROR(ENOMEM); + } + } } if (for_user) { - dst->delay = src->thread_count - 1; #if FF_API_CODED_FRAME FF_DISABLE_DEPRECATION_WARNINGS dst->coded_frame = src->coded_frame; @@ -322,7 +332,6 @@ FF_ENABLE_DEPRECATION_WARNINGS */ static int update_context_from_user(AVCodecContext *dst, AVCodecContext *src) { -#define copy_fields(s, e) memcpy(&dst->s, &src->s, (char*)&dst->e - (char*)&dst->s); dst->flags = src->flags; dst->draw_horiz_band= src->draw_horiz_band; @@ -334,8 +343,11 @@ static int update_context_from_user(AVCodecContext *dst, AVCodecContext *src) dst->slice_flags = src->slice_flags; dst->flags2 = src->flags2; + dst->export_side_data = src->export_side_data; - copy_fields(skip_loop_filter, subtitle_header); + dst->skip_loop_filter = src->skip_loop_filter; + dst->skip_idct = src->skip_idct; + dst->skip_frame = src->skip_frame; dst->frame_number = src->frame_number; dst->reordered_opaque = src->reordered_opaque; @@ -353,7 +365,6 @@ static int update_context_from_user(AVCodecContext *dst, AVCodecContext *src) } dst->slice_count = src->slice_count; return 0; -#undef copy_fields } /// Releases the buffers that this decoding thread was the last user of. @@ -369,7 +380,7 @@ static void release_delayed_buffers(PerThreadContext *p) // fix extended data in case the caller screwed it up av_assert0(p->avctx->codec_type == AVMEDIA_TYPE_VIDEO || p->avctx->codec_type == AVMEDIA_TYPE_AUDIO); - f = &p->released_buffers[--p->num_released_buffers]; + f = p->released_buffers[--p->num_released_buffers]; f->extended_data = f->data; av_frame_unref(f); @@ -653,10 +664,17 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) { FrameThreadContext *fctx = avctx->internal->thread_ctx; const AVCodec *codec = avctx->codec; - int i; + int i, j; park_frame_worker_threads(fctx, thread_count); + if (fctx->prev_thread && avctx->internal->hwaccel_priv_data != + fctx->prev_thread->avctx->internal->hwaccel_priv_data) { + if (update_context_from_thread(avctx, fctx->prev_thread->avctx, 1) < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to update user thread.\n"); + } + } + if (fctx->prev_thread && fctx->prev_thread != fctx->threads) if (update_context_from_thread(fctx->threads->avctx, fctx->prev_thread->avctx, 0) < 0) { av_log(avctx, AV_LOG_ERROR, "Final thread update failed\n"); @@ -692,14 +710,21 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) pthread_cond_destroy(&p->progress_cond); pthread_cond_destroy(&p->output_cond); av_packet_unref(&p->avpkt); + + for (j = 0; j < p->released_buffers_allocated; j++) + av_frame_free(&p->released_buffers[j]); av_freep(&p->released_buffers); - if (i && p->avctx) { + if (p->avctx) { + if (codec->priv_class) + av_opt_free(p->avctx->priv_data); av_freep(&p->avctx->priv_data); + av_freep(&p->avctx->slice_offset); } if (p->avctx) { + av_buffer_unref(&p->avctx->internal->pool); av_freep(&p->avctx->internal); av_buffer_unref(&p->avctx->hw_frames_ctx); } @@ -764,6 +789,9 @@ int ff_frame_thread_init(AVCodecContext *avctx) fctx->async_lock = 1; fctx->delaying = 1; + if (codec->type == AVMEDIA_TYPE_VIDEO) + avctx->delay = src->thread_count - 1; + for (i = 0; i < thread_count; i++) { AVCodecContext *copy = av_malloc(sizeof(AVCodecContext)); PerThreadContext *p = &fctx->threads[i]; @@ -801,28 +829,34 @@ int ff_frame_thread_init(AVCodecContext *avctx) copy->internal->thread_ctx = p; copy->internal->last_pkt_props = &p->avpkt; - if (!i) { - src = copy; - - if (codec->init) - err = codec->init(copy); + copy->delay = avctx->delay; - update_context_from_thread(avctx, copy, 1); - } else { - copy->priv_data = av_malloc(codec->priv_data_size); + if (codec->priv_data_size) { + copy->priv_data = av_mallocz(codec->priv_data_size); if (!copy->priv_data) { err = AVERROR(ENOMEM); goto error; } - memcpy(copy->priv_data, src->priv_data, codec->priv_data_size); - copy->internal->is_copy = 1; - if (codec->init_thread_copy) - err = codec->init_thread_copy(copy); + if (codec->priv_class) { + *(const AVClass **)copy->priv_data = codec->priv_class; + err = av_opt_copy(copy->priv_data, src->priv_data); + if (err < 0) + goto error; + } } + if (i) + copy->internal->is_copy = 1; + + if (codec->init) + err = codec->init(copy); + if (err) goto error; + if (!i) + update_context_from_thread(avctx, copy, 1); + atomic_init(&p->debug_threads, (copy->debug & FF_DEBUG_THREADS) != 0); err = AVERROR(pthread_create(&p->thread, NULL, frame_worker_thread, p)); @@ -895,7 +929,7 @@ static int thread_get_buffer_internal(AVCodecContext *avctx, ThreadFrame *f, int return -1; } - if (avctx->internal->allocate_progress) { + if (avctx->codec->caps_internal & FF_CODEC_CAP_ALLOCATE_PROGRESS) { atomic_int *progress; f->progress = av_buffer_alloc(2 * sizeof(*progress)); if (!f->progress) { @@ -973,11 +1007,12 @@ void ff_thread_release_buffer(AVCodecContext *avctx, ThreadFrame *f) { PerThreadContext *p = avctx->internal->thread_ctx; FrameThreadContext *fctx; - AVFrame *dst, *tmp; + AVFrame *dst; + int ret = 0; int can_direct_free = !(avctx->active_thread_type & FF_THREAD_FRAME) || THREAD_SAFE_CALLBACKS(avctx); - if (!f->f || !f->f->buf[0]) + if (!f->f) return; if (avctx->debug & FF_DEBUG_BUFFERS) @@ -986,7 +1021,8 @@ void ff_thread_release_buffer(AVCodecContext *avctx, ThreadFrame *f) av_buffer_unref(&f->progress); f->owner[0] = f->owner[1] = NULL; - if (can_direct_free) { + // when the frame buffers are not allocated, just reset it to clean state + if (can_direct_free || !f->f->buf[0]) { av_frame_unref(f->f); return; } @@ -994,20 +1030,36 @@ void ff_thread_release_buffer(AVCodecContext *avctx, ThreadFrame *f) fctx = p->parent; pthread_mutex_lock(&fctx->buffer_mutex); - if (p->num_released_buffers + 1 >= INT_MAX / sizeof(*p->released_buffers)) - goto fail; - tmp = av_fast_realloc(p->released_buffers, &p->released_buffers_allocated, - (p->num_released_buffers + 1) * - sizeof(*p->released_buffers)); - if (!tmp) - goto fail; - p->released_buffers = tmp; + if (p->num_released_buffers == p->released_buffers_allocated) { + AVFrame **tmp = av_realloc_array(p->released_buffers, p->released_buffers_allocated + 1, + sizeof(*p->released_buffers)); + if (tmp) { + tmp[p->released_buffers_allocated] = av_frame_alloc(); + p->released_buffers = tmp; + } - dst = &p->released_buffers[p->num_released_buffers]; + if (!tmp || !tmp[p->released_buffers_allocated]) { + ret = AVERROR(ENOMEM); + goto fail; + } + p->released_buffers_allocated++; + } + + dst = p->released_buffers[p->num_released_buffers]; av_frame_move_ref(dst, f->f); p->num_released_buffers++; fail: pthread_mutex_unlock(&fctx->buffer_mutex); + + // make sure the frame is clean even if we fail to free it + // this leaks, but it is better than crashing + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not queue a frame for freeing, this will leak\n"); + memset(f->f->buf, 0, sizeof(f->f->buf)); + if (f->f->extended_buf) + memset(f->f->extended_buf, 0, f->f->nb_extended_buf * sizeof(*f->f->extended_buf)); + av_frame_unref(f->f); + } } diff --git a/libavcodec/ptx.c b/libavcodec/ptx.c index 42147f4afce..19f9305cda0 100644 --- a/libavcodec/ptx.c +++ b/libavcodec/ptx.c @@ -55,6 +55,9 @@ static int ptx_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, buf += offset; + if (buf_end - buf < w * bytes_per_pixel) + return AVERROR_INVALIDDATA; + if ((ret = ff_set_dimensions(avctx, w, h)) < 0) return ret; diff --git a/libavcodec/put_bits.h b/libavcodec/put_bits.h index 1ceb1cc7662..7d11a3576a5 100644 --- a/libavcodec/put_bits.h +++ b/libavcodec/put_bits.h @@ -61,17 +61,25 @@ static inline void init_put_bits(PutBitContext *s, uint8_t *buffer, s->bit_buf = 0; } +/** + * @return the total number of bits written to the bitstream. + */ +static inline int put_bits_count(PutBitContext *s) +{ + return (s->buf_ptr - s->buf) * 8 + 32 - s->bit_left; +} + /** * Rebase the bit writer onto a reallocated buffer. * * @param buffer the buffer where to put bits * @param buffer_size the size in bytes of buffer, - * must be larger than the previous size + * must be large enough to hold everything written so far */ static inline void rebase_put_bits(PutBitContext *s, uint8_t *buffer, int buffer_size) { - av_assert0(8*buffer_size > s->size_in_bits); + av_assert0(8*buffer_size >= put_bits_count(s)); s->buf_end = buffer + buffer_size; s->buf_ptr = buffer + (s->buf_ptr - s->buf); @@ -79,14 +87,6 @@ static inline void rebase_put_bits(PutBitContext *s, uint8_t *buffer, s->size_in_bits = 8 * buffer_size; } -/** - * @return the total number of bits written to the bitstream. - */ -static inline int put_bits_count(PutBitContext *s) -{ - return (s->buf_ptr - s->buf) * 8 + 32 - s->bit_left; -} - /** * @return the number of bits available in the bitstream. */ diff --git a/libavcodec/qdm2.c b/libavcodec/qdm2.c index ac8ae8cbbb9..657b2da64dc 100644 --- a/libavcodec/qdm2.c +++ b/libavcodec/qdm2.c @@ -1334,6 +1334,9 @@ static void qdm2_fft_decode_tones(QDM2Context *q, int duration, if (q->frequency_range > (local_int_14 + 1)) { int sub_packet = (local_int_20 + local_int_28); + if (q->fft_coefs_index + stereo >= FF_ARRAY_ELEMS(q->fft_coefs)) + return; + qdm2_fft_init_coefficient(q, sub_packet, offset, duration, channel, exp, phase); if (stereo) @@ -1704,7 +1707,7 @@ static av_cold int qdm2_decode_init(AVCodecContext *avctx) s->group_size = bytestream2_get_be32(&gb); s->fft_size = bytestream2_get_be32(&gb); s->checksum_size = bytestream2_get_be32(&gb); - if (s->checksum_size >= 1U << 28 || !s->checksum_size) { + if (s->checksum_size >= 1U << 28 || s->checksum_size <= 1) { av_log(avctx, AV_LOG_ERROR, "data block size invalid (%u)\n", s->checksum_size); return AVERROR_INVALIDDATA; } diff --git a/libavcodec/qdmc.c b/libavcodec/qdmc.c index 8bea1552e12..10ceb7aa557 100644 --- a/libavcodec/qdmc.c +++ b/libavcodec/qdmc.c @@ -367,6 +367,8 @@ static int qdmc_get_vlc(GetBitContext *gb, VLC *table, int flag) { int v; + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; v = get_vlc2(gb, table->table, table->bits, 1); if (v < 0) return AVERROR_INVALIDDATA; diff --git a/libavcodec/qdrw.c b/libavcodec/qdrw.c index 32ba4109686..65279c98052 100644 --- a/libavcodec/qdrw.c +++ b/libavcodec/qdrw.c @@ -455,6 +455,8 @@ static int decode_frame(AVCodecContext *avctx, avpriv_request_sample(avctx, "Pack type %d", pack_type); return AVERROR_PATCHWELCOME; } + if (bytestream2_get_bytes_left(&gbc) < 30) + return AVERROR_INVALIDDATA; if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; diff --git a/libavcodec/qpeg.c b/libavcodec/qpeg.c index d4195c5f0b7..84304cae3b3 100644 --- a/libavcodec/qpeg.c +++ b/libavcodec/qpeg.c @@ -30,7 +30,7 @@ typedef struct QpegContext{ AVCodecContext *avctx; - AVFrame *pic, *ref; + AVFrame *ref; uint32_t pal[256]; GetByteContext buffer; } QpegContext; @@ -267,10 +267,10 @@ static int decode_frame(AVCodecContext *avctx, { uint8_t ctable[128]; QpegContext * const a = avctx->priv_data; - AVFrame * const p = a->pic; + AVFrame * const p = data; AVFrame * const ref = a->ref; uint8_t* outdata; - int delta, ret; + int delta, intra, ret; int pal_size; const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, &pal_size); @@ -281,9 +281,6 @@ static int decode_frame(AVCodecContext *avctx, bytestream2_init(&a->buffer, avpkt->data, avpkt->size); - av_frame_unref(ref); - av_frame_move_ref(ref, p); - if ((ret = ff_get_buffer(avctx, p, AV_GET_BUFFER_FLAG_REF)) < 0) return ret; outdata = p->data[0]; @@ -292,7 +289,8 @@ static int decode_frame(AVCodecContext *avctx, bytestream2_skip(&a->buffer, 1); delta = bytestream2_get_byte(&a->buffer); - if(delta == 0x10) { + intra = delta == 0x10; + if (intra) { qpeg_decode_intra(a, outdata, p->linesize[0], avctx->width, avctx->height); } else { qpeg_decode_inter(a, outdata, p->linesize[0], avctx->width, avctx->height, delta, ctable, ref->data[0]); @@ -307,9 +305,13 @@ static int decode_frame(AVCodecContext *avctx, } memcpy(p->data[1], a->pal, AVPALETTE_SIZE); - if ((ret = av_frame_ref(data, p)) < 0) + av_frame_unref(ref); + if ((ret = av_frame_ref(ref, p)) < 0) return ret; + p->key_frame = intra; + p->pict_type = intra ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + *got_frame = 1; return avpkt->size; @@ -320,6 +322,8 @@ static void decode_flush(AVCodecContext *avctx){ int i, pal_size; const uint8_t *pal_src; + av_frame_unref(a->ref); + pal_size = FFMIN(1024U, avctx->extradata_size); pal_src = avctx->extradata + avctx->extradata_size - pal_size; @@ -331,7 +335,6 @@ static av_cold int decode_end(AVCodecContext *avctx) { QpegContext * const a = avctx->priv_data; - av_frame_free(&a->pic); av_frame_free(&a->ref); return 0; @@ -343,14 +346,11 @@ static av_cold int decode_init(AVCodecContext *avctx){ a->avctx = avctx; avctx->pix_fmt= AV_PIX_FMT_PAL8; - decode_flush(avctx); - - a->pic = av_frame_alloc(); a->ref = av_frame_alloc(); - if (!a->pic || !a->ref) { - decode_end(avctx); + if (!a->ref) return AVERROR(ENOMEM); - } + + decode_flush(avctx); return 0; } @@ -366,4 +366,6 @@ AVCodec ff_qpeg_decoder = { .decode = decode_frame, .flush = decode_flush, .capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | + FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/qsv.c b/libavcodec/qsv.c index 986d4f6022c..17720070f1b 100644 --- a/libavcodec/qsv.c +++ b/libavcodec/qsv.c @@ -60,6 +60,11 @@ int ff_qsv_codec_id_to_mfx(enum AVCodecID codec_id) #endif case AV_CODEC_ID_MJPEG: return MFX_CODEC_JPEG; +#if QSV_VERSION_ATLEAST(1, 19) + case AV_CODEC_ID_VP9: + return MFX_CODEC_VP9; +#endif + default: break; } @@ -67,58 +72,6 @@ int ff_qsv_codec_id_to_mfx(enum AVCodecID codec_id) return AVERROR(ENOSYS); } - -static const struct { - enum AVCodecID codec_id; - int codec_profile; - int mfx_profile; -} qsv_profile_map[] = { -#define MAP(c, p, v) { AV_CODEC_ID_ ## c, FF_PROFILE_ ## p, MFX_PROFILE_ ## v } - MAP(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2_SIMPLE ), - MAP(MPEG2VIDEO, MPEG2_MAIN, MPEG2_MAIN ), - MAP(MPEG2VIDEO, MPEG2_HIGH, MPEG2_HIGH ), - - MAP(H264, H264_BASELINE, AVC_BASELINE ), - MAP(H264, H264_CONSTRAINED_BASELINE, AVC_BASELINE), -#if QSV_VERSION_ATLEAST(1, 3) - MAP(H264, H264_EXTENDED, AVC_EXTENDED ), -#endif - MAP(H264, H264_MAIN, AVC_MAIN ), - MAP(H264, H264_HIGH, AVC_HIGH ), - MAP(H264, H264_HIGH_422, AVC_HIGH_422 ), - -#if QSV_VERSION_ATLEAST(1, 8) - MAP(HEVC, HEVC_MAIN, HEVC_MAIN ), - MAP(HEVC, HEVC_MAIN_10, HEVC_MAIN10 ), - MAP(HEVC, HEVC_MAIN_STILL_PICTURE, HEVC_MAINSP ), -#endif -#if QSV_VERSION_ATLEAST(1, 16) - MAP(HEVC, HEVC_REXT, HEVC_REXT ), -#endif - - MAP(VC1, VC1_SIMPLE, VC1_SIMPLE ), - MAP(VC1, VC1_MAIN, VC1_MAIN ), - MAP(VC1, VC1_COMPLEX, VC1_ADVANCED ), - MAP(VC1, VC1_ADVANCED, VC1_ADVANCED ), -#undef MAP -}; - -int ff_qsv_profile_to_mfx(enum AVCodecID codec_id, int profile) -{ - int i; - if (profile == FF_PROFILE_UNKNOWN) - return MFX_PROFILE_UNKNOWN; - - for (i = 0; i < FF_ARRAY_ELEMS(qsv_profile_map); i++) { - if (qsv_profile_map[i].codec_id != codec_id) - continue; - if (qsv_profile_map[i].codec_profile == profile) - return qsv_profile_map[i].mfx_profile; - } - - return MFX_PROFILE_UNKNOWN; -} - int ff_qsv_level_to_mfx(enum AVCodecID codec_id, int level) { if (level == FF_LEVEL_UNKNOWN) @@ -132,6 +85,35 @@ int ff_qsv_level_to_mfx(enum AVCodecID codec_id, int level) } } +static const struct { + int mfx_iopattern; + const char *desc; +} qsv_iopatterns[] = { + {MFX_IOPATTERN_IN_VIDEO_MEMORY, "input is video memory surface" }, + {MFX_IOPATTERN_IN_SYSTEM_MEMORY, "input is system memory surface" }, + {MFX_IOPATTERN_IN_OPAQUE_MEMORY, "input is opaque memory surface" }, + {MFX_IOPATTERN_OUT_VIDEO_MEMORY, "output is video memory surface" }, + {MFX_IOPATTERN_OUT_SYSTEM_MEMORY, "output is system memory surface" }, + {MFX_IOPATTERN_OUT_OPAQUE_MEMORY, "output is opaque memory surface" }, +}; + +int ff_qsv_print_iopattern(void *log_ctx, int mfx_iopattern, + const char *extra_string) +{ + const char *desc = NULL; + + for (int i = 0; i < FF_ARRAY_ELEMS(qsv_iopatterns); i++) { + if (qsv_iopatterns[i].mfx_iopattern == mfx_iopattern) { + desc = qsv_iopatterns[i].desc; + } + } + if (!desc) + desc = "unknown iopattern"; + + av_log(log_ctx, AV_LOG_VERBOSE, "%s: %s\n", extra_string, desc); + return 0; +} + static const struct { mfxStatus mfxerr; int averr; @@ -207,12 +189,18 @@ int ff_qsv_print_warning(void *log_ctx, mfxStatus err, return ret; } -static enum AVPixelFormat qsv_map_fourcc(uint32_t fourcc) +enum AVPixelFormat ff_qsv_map_fourcc(uint32_t fourcc) { switch (fourcc) { case MFX_FOURCC_NV12: return AV_PIX_FMT_NV12; case MFX_FOURCC_P010: return AV_PIX_FMT_P010; case MFX_FOURCC_P8: return AV_PIX_FMT_PAL8; +#if CONFIG_VAAPI + case MFX_FOURCC_YUY2: return AV_PIX_FMT_YUYV422; +#if QSV_VERSION_ATLEAST(1, 27) + case MFX_FOURCC_Y210: return AV_PIX_FMT_Y210; +#endif +#endif } return AV_PIX_FMT_NONE; } @@ -229,6 +217,18 @@ int ff_qsv_map_pixfmt(enum AVPixelFormat format, uint32_t *fourcc) case AV_PIX_FMT_P010: *fourcc = MFX_FOURCC_P010; return AV_PIX_FMT_P010; +#if CONFIG_VAAPI + case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUYV422: + *fourcc = MFX_FOURCC_YUY2; + return AV_PIX_FMT_YUYV422; +#if QSV_VERSION_ATLEAST(1, 27) + case AV_PIX_FMT_YUV422P10: + case AV_PIX_FMT_Y210: + *fourcc = MFX_FOURCC_Y210; + return AV_PIX_FMT_Y210; +#endif +#endif default: return AVERROR(ENOSYS); } @@ -245,6 +245,24 @@ int ff_qsv_find_surface_idx(QSVFramesContext *ctx, QSVFrame *frame) return AVERROR_BUG; } +enum AVFieldOrder ff_qsv_map_picstruct(int mfx_pic_struct) +{ + enum AVFieldOrder field = AV_FIELD_UNKNOWN; + switch (mfx_pic_struct & 0xF) { + case MFX_PICSTRUCT_PROGRESSIVE: + field = AV_FIELD_PROGRESSIVE; + break; + case MFX_PICSTRUCT_FIELD_TFF: + field = AV_FIELD_TT; + break; + case MFX_PICSTRUCT_FIELD_BFF: + field = AV_FIELD_BB; + break; + } + + return field; +} + enum AVPictureType ff_qsv_map_pictype(int mfx_pic_type) { enum AVPictureType type; @@ -325,27 +343,73 @@ static int qsv_load_plugins(mfxSession session, const char *load_plugins, } -int ff_qsv_init_internal_session(AVCodecContext *avctx, mfxSession *session, - const char *load_plugins) +//This code is only required for Linux since a display handle is required. +//For Windows the session is complete and ready to use. + +#ifdef AVCODEC_QSV_LINUX_SESSION_HANDLE +static int ff_qsv_set_display_handle(AVCodecContext *avctx, QSVSession *qs) { - mfxIMPL impl = MFX_IMPL_AUTO_ANY; - mfxVersion ver = { { QSV_VERSION_MINOR, QSV_VERSION_MAJOR } }; + AVDictionary *child_device_opts = NULL; + AVVAAPIDeviceContext *hwctx; + int ret; + + av_dict_set(&child_device_opts, "kernel_driver", "i915", 0); + av_dict_set(&child_device_opts, "driver", "iHD", 0); + + ret = av_hwdevice_ctx_create(&qs->va_device_ref, AV_HWDEVICE_TYPE_VAAPI, NULL, child_device_opts, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to create a VAAPI device.\n"); + return ret; + } else { + qs->va_device_ctx = (AVHWDeviceContext*)qs->va_device_ref->data; + hwctx = qs->va_device_ctx->hwctx; + + ret = MFXVideoCORE_SetHandle(qs->session, + (mfxHandleType)MFX_HANDLE_VA_DISPLAY, (mfxHDL)hwctx->display); + if (ret < 0) { + return ff_qsv_print_error(avctx, ret, "Error during set display handle\n"); + } + } + + av_dict_free(&child_device_opts); + + return 0; +} +#endif //AVCODEC_QSV_LINUX_SESSION_HANDLE + +int ff_qsv_init_internal_session(AVCodecContext *avctx, QSVSession *qs, + const char *load_plugins, int gpu_copy) +{ + mfxIMPL impl = MFX_IMPL_AUTO_ANY; + mfxVersion ver = { { QSV_VERSION_MINOR, QSV_VERSION_MAJOR } }; + mfxInitParam init_par = { MFX_IMPL_AUTO_ANY }; const char *desc; int ret; - ret = MFXInit(impl, &ver, session); +#if QSV_VERSION_ATLEAST(1, 16) + init_par.GPUCopy = gpu_copy; +#endif + init_par.Implementation = impl; + init_par.Version = ver; + ret = MFXInitEx(init_par, &qs->session); if (ret < 0) return ff_qsv_print_error(avctx, ret, "Error initializing an internal MFX session"); - ret = qsv_load_plugins(*session, load_plugins, avctx); +#ifdef AVCODEC_QSV_LINUX_SESSION_HANDLE + ret = ff_qsv_set_display_handle(avctx, qs); + if (ret < 0) + return ret; +#endif + + ret = qsv_load_plugins(qs->session, load_plugins, avctx); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "Error loading plugins\n"); return ret; } - MFXQueryIMPL(*session, &impl); + MFXQueryIMPL(qs->session, &impl); switch (MFX_IMPL_BASETYPE(impl)) { case MFX_IMPL_SOFTWARE: @@ -500,7 +564,7 @@ static mfxStatus qsv_frame_alloc(mfxHDL pthis, mfxFrameAllocRequest *req, frames_hwctx = frames_ctx->hwctx; frames_ctx->format = AV_PIX_FMT_QSV; - frames_ctx->sw_format = qsv_map_fourcc(i->FourCC); + frames_ctx->sw_format = ff_qsv_map_fourcc(i->FourCC); frames_ctx->width = i->Width; frames_ctx->height = i->Height; frames_ctx->initial_pool_size = req->NumFrameSuggested; @@ -620,7 +684,8 @@ static mfxStatus qsv_frame_get_hdl(mfxHDL pthis, mfxMemId mid, mfxHDL *hdl) } int ff_qsv_init_session_device(AVCodecContext *avctx, mfxSession *psession, - AVBufferRef *device_ref, const char *load_plugins) + AVBufferRef *device_ref, const char *load_plugins, + int gpu_copy) { static const mfxHandleType handle_types[] = { MFX_HANDLE_VA_DISPLAY, @@ -630,11 +695,12 @@ int ff_qsv_init_session_device(AVCodecContext *avctx, mfxSession *psession, AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)device_ref->data; AVQSVDeviceContext *device_hwctx = device_ctx->hwctx; mfxSession parent_session = device_hwctx->session; + mfxInitParam init_par = { MFX_IMPL_AUTO_ANY }; + mfxHDL handle = NULL; mfxSession session; mfxVersion ver; mfxIMPL impl; - mfxHDL handle = NULL; mfxHandleType handle_type; mfxStatus err; @@ -660,7 +726,12 @@ int ff_qsv_init_session_device(AVCodecContext *avctx, mfxSession *psession, "from the session\n"); } - err = MFXInit(impl, &ver, &session); +#if QSV_VERSION_ATLEAST(1, 16) + init_par.GPUCopy = gpu_copy; +#endif + init_par.Implementation = impl; + init_par.Version = ver; + err = MFXInitEx(init_par, &session); if (err != MFX_ERR_NONE) return ff_qsv_print_error(avctx, err, "Error initializing a child MFX session"); @@ -691,7 +762,7 @@ int ff_qsv_init_session_device(AVCodecContext *avctx, mfxSession *psession, int ff_qsv_init_session_frames(AVCodecContext *avctx, mfxSession *psession, QSVFramesContext *qsv_frames_ctx, - const char *load_plugins, int opaque) + const char *load_plugins, int opaque, int gpu_copy) { mfxFrameAllocator frame_allocator = { .pthis = qsv_frames_ctx, @@ -711,7 +782,7 @@ int ff_qsv_init_session_frames(AVCodecContext *avctx, mfxSession *psession, int ret; ret = ff_qsv_init_session_device(avctx, &session, - frames_ctx->device_ref, load_plugins); + frames_ctx->device_ref, load_plugins, gpu_copy); if (ret < 0) return ret; @@ -735,3 +806,15 @@ int ff_qsv_init_session_frames(AVCodecContext *avctx, mfxSession *psession, *psession = session; return 0; } + +int ff_qsv_close_internal_session(QSVSession *qs) +{ + if (qs->session) { + MFXClose(qs->session); + qs->session = NULL; + } +#ifdef AVCODEC_QSV_LINUX_SESSION_HANDLE + av_buffer_unref(&qs->va_device_ref); +#endif + return 0; +} diff --git a/libavcodec/qsv_internal.h b/libavcodec/qsv_internal.h index b63a7d6a31e..6489836a677 100644 --- a/libavcodec/qsv_internal.h +++ b/libavcodec/qsv_internal.h @@ -21,6 +21,22 @@ #ifndef AVCODEC_QSV_INTERNAL_H #define AVCODEC_QSV_INTERNAL_H +#if CONFIG_VAAPI +#define AVCODEC_QSV_LINUX_SESSION_HANDLE +#endif //CONFIG_VAAPI + +#ifdef AVCODEC_QSV_LINUX_SESSION_HANDLE +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include "libavutil/hwcontext_vaapi.h" +#endif + #include #include "libavutil/frame.h" @@ -64,6 +80,14 @@ typedef struct QSVFrame { struct QSVFrame *next; } QSVFrame; +typedef struct QSVSession { + mfxSession session; +#ifdef AVCODEC_QSV_LINUX_SESSION_HANDLE + AVBufferRef *va_device_ref; + AVHWDeviceContext *va_device_ctx; +#endif +} QSVSession; + typedef struct QSVFramesContext { AVBufferRef *hw_frames_ctx; void *logctx; @@ -77,6 +101,9 @@ typedef struct QSVFramesContext { int nb_mids; } QSVFramesContext; +int ff_qsv_print_iopattern(void *log_ctx, int mfx_iopattern, + const char *extra_string); + /** * Convert a libmfx error code into an ffmpeg error code. */ @@ -89,21 +116,27 @@ int ff_qsv_print_warning(void *log_ctx, mfxStatus err, const char *warning_string); int ff_qsv_codec_id_to_mfx(enum AVCodecID codec_id); -int ff_qsv_profile_to_mfx(enum AVCodecID codec_id, int profile); int ff_qsv_level_to_mfx(enum AVCodecID codec_id, int level); +enum AVPixelFormat ff_qsv_map_fourcc(uint32_t fourcc); + int ff_qsv_map_pixfmt(enum AVPixelFormat format, uint32_t *fourcc); enum AVPictureType ff_qsv_map_pictype(int mfx_pic_type); -int ff_qsv_init_internal_session(AVCodecContext *avctx, mfxSession *session, - const char *load_plugins); +enum AVFieldOrder ff_qsv_map_picstruct(int mfx_pic_struct); + +int ff_qsv_init_internal_session(AVCodecContext *avctx, QSVSession *qs, + const char *load_plugins, int gpu_copy); + +int ff_qsv_close_internal_session(QSVSession *qs); int ff_qsv_init_session_device(AVCodecContext *avctx, mfxSession *psession, - AVBufferRef *device_ref, const char *load_plugins); + AVBufferRef *device_ref, const char *load_plugins, + int gpu_copy); int ff_qsv_init_session_frames(AVCodecContext *avctx, mfxSession *session, QSVFramesContext *qsv_frames_ctx, - const char *load_plugins, int opaque); + const char *load_plugins, int opaque, int gpu_copy); int ff_qsv_find_surface_idx(QSVFramesContext *ctx, QSVFrame *frame); diff --git a/libavcodec/qsvdec.c b/libavcodec/qsvdec.c index 46aa2d68147..fc25dc73e57 100644 --- a/libavcodec/qsvdec.c +++ b/libavcodec/qsvdec.c @@ -34,9 +34,11 @@ #include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" #include "libavutil/time.h" +#include "libavutil/imgutils.h" #include "avcodec.h" #include "internal.h" +#include "decode.h" #include "qsv.h" #include "qsv_internal.h" #include "qsvdec.h" @@ -54,17 +56,60 @@ const AVCodecHWConfigInternal *ff_qsv_hw_configs[] = { NULL }; +static int ff_qsv_get_continuous_buffer(AVCodecContext *avctx, AVFrame *frame, AVBufferPool *pool) +{ + int ret = 0; + + ff_decode_frame_props(avctx, frame); + + frame->width = avctx->width; + frame->height = avctx->height; + + switch (avctx->pix_fmt) { + case AV_PIX_FMT_NV12: + frame->linesize[0] = FFALIGN(avctx->width, 128); + break; + case AV_PIX_FMT_P010: + frame->linesize[0] = 2 * FFALIGN(avctx->width, 128); + break; + default: + av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format.\n"); + return AVERROR(EINVAL); + } + + frame->linesize[1] = frame->linesize[0]; + frame->buf[0] = av_buffer_pool_get(pool); + if (!frame->buf[0]) + return AVERROR(ENOMEM); + + frame->data[0] = frame->buf[0]->data; + frame->data[1] = frame->data[0] + + frame->linesize[0] * FFALIGN(avctx->height, 64); + + ret = ff_attach_decode_data(frame); + if (ret < 0) + return ret; + + return 0; +} + static int qsv_init_session(AVCodecContext *avctx, QSVContext *q, mfxSession session, AVBufferRef *hw_frames_ref, AVBufferRef *hw_device_ref) { int ret; + if (q->gpu_copy == MFX_GPUCOPY_ON && + !(q->iopattern & MFX_IOPATTERN_OUT_SYSTEM_MEMORY)) { + av_log(avctx, AV_LOG_WARNING, "GPU-accelerated memory copy " + "only works in system memory mode.\n"); + q->gpu_copy = MFX_GPUCOPY_OFF; + } if (session) { q->session = session; } else if (hw_frames_ref) { - if (q->internal_session) { - MFXClose(q->internal_session); - q->internal_session = NULL; + if (q->internal_qs.session) { + MFXClose(q->internal_qs.session); + q->internal_qs.session = NULL; } av_buffer_unref(&q->frames_ctx.hw_frames_ctx); @@ -72,36 +117,37 @@ static int qsv_init_session(AVCodecContext *avctx, QSVContext *q, mfxSession ses if (!q->frames_ctx.hw_frames_ctx) return AVERROR(ENOMEM); - ret = ff_qsv_init_session_frames(avctx, &q->internal_session, + ret = ff_qsv_init_session_frames(avctx, &q->internal_qs.session, &q->frames_ctx, q->load_plugins, - q->iopattern == MFX_IOPATTERN_OUT_OPAQUE_MEMORY); + q->iopattern == MFX_IOPATTERN_OUT_OPAQUE_MEMORY, + q->gpu_copy); if (ret < 0) { av_buffer_unref(&q->frames_ctx.hw_frames_ctx); return ret; } - q->session = q->internal_session; + q->session = q->internal_qs.session; } else if (hw_device_ref) { - if (q->internal_session) { - MFXClose(q->internal_session); - q->internal_session = NULL; + if (q->internal_qs.session) { + MFXClose(q->internal_qs.session); + q->internal_qs.session = NULL; } - ret = ff_qsv_init_session_device(avctx, &q->internal_session, - hw_device_ref, q->load_plugins); + ret = ff_qsv_init_session_device(avctx, &q->internal_qs.session, + hw_device_ref, q->load_plugins, q->gpu_copy); if (ret < 0) return ret; - q->session = q->internal_session; + q->session = q->internal_qs.session; } else { - if (!q->internal_session) { - ret = ff_qsv_init_internal_session(avctx, &q->internal_session, - q->load_plugins); + if (!q->internal_qs.session) { + ret = ff_qsv_init_internal_session(avctx, &q->internal_qs, + q->load_plugins, q->gpu_copy); if (ret < 0) return ret; } - q->session = q->internal_session; + q->session = q->internal_qs.session; } /* make sure the decoder is uninitialized */ @@ -120,46 +166,21 @@ static inline unsigned int qsv_fifo_size(const AVFifoBuffer* fifo) return av_fifo_size(fifo) / qsv_fifo_item_size(); } -static int check_dec_param(AVCodecContext *avctx, QSVContext *q, mfxVideoParam *param_in) -{ - mfxVideoParam param_out = { .mfx.CodecId = param_in->mfx.CodecId }; - mfxStatus ret; - -#define CHECK_MATCH(x) \ - do { \ - if (param_out.mfx.x != param_in->mfx.x) { \ - av_log(avctx, AV_LOG_WARNING, "Required "#x" %d is unsupported\n", \ - param_in->mfx.x); \ - } \ - } while (0) - - ret = MFXVideoDECODE_Query(q->session, param_in, ¶m_out); - - if (ret < 0) { - CHECK_MATCH(CodecId); - CHECK_MATCH(CodecProfile); - CHECK_MATCH(CodecLevel); - CHECK_MATCH(FrameInfo.Width); - CHECK_MATCH(FrameInfo.Height); -#undef CHECK_MATCH - return 0; - } - return 1; -} - -static int qsv_decode_init(AVCodecContext *avctx, QSVContext *q) +static int qsv_decode_preinit(AVCodecContext *avctx, QSVContext *q, enum AVPixelFormat pix_fmt, mfxVideoParam *param) { - const AVPixFmtDescriptor *desc; mfxSession session = NULL; int iopattern = 0; - mfxVideoParam param = { 0 }; - int frame_width = avctx->coded_width; - int frame_height = avctx->coded_height; int ret; + enum AVPixelFormat pix_fmts[3] = { + AV_PIX_FMT_QSV, /* opaque format in case of video memory output */ + pix_fmt, /* system memory format obtained from bitstream parser */ + AV_PIX_FMT_NONE }; - desc = av_pix_fmt_desc_get(avctx->sw_pix_fmt); - if (!desc) - return AVERROR_BUG; + ret = ff_get_format(avctx, pix_fmts); + if (ret < 0) { + q->orig_pix_fmt = avctx->pix_fmt = AV_PIX_FMT_NONE; + return ret; + } if (!q->async_fifo) { q->async_fifo = av_fifo_alloc(q->async_depth * qsv_fifo_item_size()); @@ -191,60 +212,83 @@ static int qsv_decode_init(AVCodecContext *avctx, QSVContext *q) iopattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY; q->iopattern = iopattern; + ff_qsv_print_iopattern(avctx, q->iopattern, "Decoder"); + ret = qsv_init_session(avctx, q, session, avctx->hw_frames_ctx, avctx->hw_device_ctx); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "Error initializing an MFX session\n"); return ret; } - ret = ff_qsv_codec_id_to_mfx(avctx->codec_id); - if (ret < 0) - return ret; + param->IOPattern = q->iopattern; + param->AsyncDepth = q->async_depth; + param->ExtParam = q->ext_buffers; + param->NumExtParam = q->nb_ext_buffers; - param.mfx.CodecId = ret; - param.mfx.CodecProfile = ff_qsv_profile_to_mfx(avctx->codec_id, avctx->profile); - param.mfx.CodecLevel = ff_qsv_level_to_mfx(avctx->codec_id, avctx->level); - - param.mfx.FrameInfo.BitDepthLuma = desc->comp[0].depth; - param.mfx.FrameInfo.BitDepthChroma = desc->comp[0].depth; - param.mfx.FrameInfo.Shift = desc->comp[0].depth > 8; - param.mfx.FrameInfo.FourCC = q->fourcc; - param.mfx.FrameInfo.Width = frame_width; - param.mfx.FrameInfo.Height = frame_height; - param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420; - - switch (avctx->field_order) { - case AV_FIELD_PROGRESSIVE: - param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; - break; - case AV_FIELD_TT: - param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_FIELD_TFF; - break; - case AV_FIELD_BB: - param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_FIELD_BFF; - break; - default: - param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_UNKNOWN; - break; - } + return 0; + } - param.IOPattern = q->iopattern; - param.AsyncDepth = q->async_depth; - param.ExtParam = q->ext_buffers; - param.NumExtParam = q->nb_ext_buffers; +static int qsv_decode_init(AVCodecContext *avctx, QSVContext *q, mfxVideoParam *param) +{ + int ret; - if (!check_dec_param(avctx, q, ¶m)) { - //Just give a warning instead of an error since it is still decodable possibly. - av_log(avctx, AV_LOG_WARNING, - "Current input bitstream is not supported by QSV decoder.\n"); - } + avctx->width = param->mfx.FrameInfo.CropW; + avctx->height = param->mfx.FrameInfo.CropH; + avctx->coded_width = param->mfx.FrameInfo.Width; + avctx->coded_height = param->mfx.FrameInfo.Height; + avctx->level = param->mfx.CodecLevel; + avctx->profile = param->mfx.CodecProfile; + avctx->field_order = ff_qsv_map_picstruct(param->mfx.FrameInfo.PicStruct); + avctx->pix_fmt = ff_qsv_map_fourcc(param->mfx.FrameInfo.FourCC); - ret = MFXVideoDECODE_Init(q->session, ¶m); + ret = MFXVideoDECODE_Init(q->session, param); if (ret < 0) return ff_qsv_print_error(avctx, ret, "Error initializing the MFX video decoder"); - q->frame_info = param.mfx.FrameInfo; + 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); + return 0; +} + +static int qsv_decode_header(AVCodecContext *avctx, QSVContext *q, AVPacket *avpkt, enum AVPixelFormat pix_fmt, mfxVideoParam *param) +{ + int ret; + + mfxBitstream bs = { 0 }; + + if (avpkt->size) { + bs.Data = avpkt->data; + bs.DataLength = avpkt->size; + bs.MaxLength = bs.DataLength; + bs.TimeStamp = avpkt->pts; + if (avctx->field_order == AV_FIELD_PROGRESSIVE) + bs.DataFlag |= MFX_BITSTREAM_COMPLETE_FRAME; + } else + return AVERROR_INVALIDDATA; + + + if(!q->session) { + ret = qsv_decode_preinit(avctx, q, pix_fmt, param); + if (ret < 0) + return ret; + } + + ret = ff_qsv_codec_id_to_mfx(avctx->codec_id); + if (ret < 0) + return ret; + + param->mfx.CodecId = ret; + ret = MFXVideoDECODE_DecodeHeader(q->session, &bs, param); + if (MFX_ERR_MORE_DATA == ret) { + return AVERROR(EAGAIN); + } + if (ret < 0) + return ff_qsv_print_error(avctx, ret, + "Error decoding stream header"); return 0; } @@ -253,7 +297,11 @@ static int alloc_frame(AVCodecContext *avctx, QSVContext *q, QSVFrame *frame) { int ret; - ret = ff_get_buffer(avctx, frame->frame, AV_GET_BUFFER_FLAG_REF); + if (q->pool) + ret = ff_qsv_get_continuous_buffer(avctx, frame->frame, q->pool); + else + ret = ff_get_buffer(avctx, frame->frame, AV_GET_BUFFER_FLAG_REF); + if (ret < 0) return ret; @@ -509,14 +557,11 @@ int ff_qsv_decode_close(QSVContext *q) av_fifo_free(q->async_fifo); q->async_fifo = NULL; - av_parser_close(q->parser); - avcodec_free_context(&q->avctx_internal); - - if (q->internal_session) - MFXClose(q->internal_session); + ff_qsv_close_internal_session(&q->internal_qs); av_buffer_unref(&q->frames_ctx.hw_frames_ctx); av_buffer_unref(&q->frames_ctx.mids_buf); + av_buffer_pool_uninit(&q->pool); return 0; } @@ -524,45 +569,30 @@ int ff_qsv_decode_close(QSVContext *q) int ff_qsv_process_data(AVCodecContext *avctx, QSVContext *q, AVFrame *frame, int *got_frame, AVPacket *pkt) { - uint8_t *dummy_data; - int dummy_size; int ret; - const AVPixFmtDescriptor *desc; - - if (!q->avctx_internal) { - q->avctx_internal = avcodec_alloc_context3(NULL); - if (!q->avctx_internal) - return AVERROR(ENOMEM); - - q->avctx_internal->codec_id = avctx->codec_id; - - q->parser = av_parser_init(avctx->codec_id); - if (!q->parser) - return AVERROR(ENOMEM); - - q->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; - q->orig_pix_fmt = AV_PIX_FMT_NONE; - } + mfxVideoParam param = { 0 }; + enum AVPixelFormat pix_fmt = AV_PIX_FMT_NV12; if (!pkt->size) return qsv_decode(avctx, q, frame, got_frame, pkt); - /* we assume the packets are already split properly and want - * just the codec parameters here */ - av_parser_parse2(q->parser, q->avctx_internal, - &dummy_data, &dummy_size, - pkt->data, pkt->size, pkt->pts, pkt->dts, - pkt->pos); - - avctx->field_order = q->parser->field_order; /* TODO: flush delayed frames on reinit */ - if (q->parser->format != q->orig_pix_fmt || - FFALIGN(q->parser->coded_width, 16) != FFALIGN(avctx->coded_width, 16) || - FFALIGN(q->parser->coded_height, 16) != FFALIGN(avctx->coded_height, 16)) { - enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_QSV, - AV_PIX_FMT_NONE, - AV_PIX_FMT_NONE }; - enum AVPixelFormat qsv_format; + + // sw_pix_fmt, coded_width/height should be set for ff_get_format(), + // assume sw_pix_fmt is NV12 and coded_width/height to be 1280x720, + // the assumption may be not corret but will be updated after header decoded if not true. + if (q->orig_pix_fmt != AV_PIX_FMT_NONE) + pix_fmt = q->orig_pix_fmt; + if (!avctx->coded_width) + avctx->coded_width = 1280; + if (!avctx->coded_height) + avctx->coded_height = 720; + + ret = qsv_decode_header(avctx, q, pkt, pix_fmt, ¶m); + + if (ret >= 0 && (q->orig_pix_fmt != ff_qsv_map_fourcc(param.mfx.FrameInfo.FourCC) || + avctx->coded_width != param.mfx.FrameInfo.Width || + avctx->coded_height != param.mfx.FrameInfo.Height)) { AVPacket zero_pkt = {0}; if (q->buffered_count) { @@ -571,55 +601,35 @@ int ff_qsv_process_data(AVCodecContext *avctx, QSVContext *q, q->buffered_count--; return qsv_decode(avctx, q, frame, got_frame, &zero_pkt); } - q->reinit_flag = 0; - qsv_format = ff_qsv_map_pixfmt(q->parser->format, &q->fourcc); - if (qsv_format < 0) { - av_log(avctx, AV_LOG_ERROR, - "Decoding pixel format '%s' is not supported\n", - av_get_pix_fmt_name(q->parser->format)); - ret = AVERROR(ENOSYS); - goto reinit_fail; - } + q->orig_pix_fmt = avctx->pix_fmt = pix_fmt = ff_qsv_map_fourcc(param.mfx.FrameInfo.FourCC); - q->orig_pix_fmt = q->parser->format; - avctx->pix_fmt = pix_fmts[1] = qsv_format; - avctx->width = q->parser->width; - avctx->height = q->parser->height; - avctx->coded_width = FFALIGN(q->parser->coded_width, 16); - avctx->coded_height = FFALIGN(q->parser->coded_height, 16); - avctx->level = q->avctx_internal->level; - avctx->profile = q->avctx_internal->profile; + avctx->coded_width = param.mfx.FrameInfo.Width; + avctx->coded_height = param.mfx.FrameInfo.Height; - ret = ff_get_format(avctx, pix_fmts); + ret = qsv_decode_preinit(avctx, q, pix_fmt, ¶m); if (ret < 0) goto reinit_fail; + q->initialized = 0; + } - avctx->pix_fmt = ret; - - desc = av_pix_fmt_desc_get(avctx->pix_fmt); - if (!desc) - goto reinit_fail; - - if (desc->comp[0].depth > 8) { - avctx->coded_width = FFALIGN(q->parser->coded_width, 32); - avctx->coded_height = FFALIGN(q->parser->coded_height, 32); - } - - ret = qsv_decode_init(avctx, q); + if (!q->initialized) { + ret = qsv_decode_init(avctx, q, ¶m); if (ret < 0) goto reinit_fail; + q->initialized = 1; } return qsv_decode(avctx, q, frame, got_frame, pkt); reinit_fail: - q->orig_pix_fmt = q->parser->format = avctx->pix_fmt = AV_PIX_FMT_NONE; + q->orig_pix_fmt = avctx->pix_fmt = AV_PIX_FMT_NONE; return ret; } void ff_qsv_decode_flush(AVCodecContext *avctx, QSVContext *q) { q->orig_pix_fmt = AV_PIX_FMT_NONE; + q->initialized = 0; } diff --git a/libavcodec/qsvdec.h b/libavcodec/qsvdec.h index 111536caba3..cb948f516d1 100644 --- a/libavcodec/qsvdec.h +++ b/libavcodec/qsvdec.h @@ -33,7 +33,7 @@ #include "libavutil/pixfmt.h" #include "avcodec.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "qsv_internal.h" typedef struct QSVContext { @@ -42,7 +42,7 @@ typedef struct QSVContext { // the session we allocated internally, in case the caller did not provide // one - mfxSession internal_session; + QSVSession internal_qs; QSVFramesContext frames_ctx; @@ -56,16 +56,17 @@ typedef struct QSVContext { int buffered_count; int reinit_flag; - // the internal parser and codec context for parsing the data - AVCodecParserContext *parser; - AVCodecContext *avctx_internal; enum AVPixelFormat orig_pix_fmt; uint32_t fourcc; mfxFrameInfo frame_info; + AVBufferPool *pool; + + int initialized; // options set by the caller int async_depth; int iopattern; + int gpu_copy; char *load_plugins; diff --git a/libavcodec/qsvdec_h2645.c b/libavcodec/qsvdec_h2645.c index 9b49f5506e5..02c41883b6a 100644 --- a/libavcodec/qsvdec_h2645.c +++ b/libavcodec/qsvdec_h2645.c @@ -103,6 +103,7 @@ static av_cold int qsv_decode_init(AVCodecContext *avctx) } } + s->qsv.orig_pix_fmt = AV_PIX_FMT_NV12; s->packet_fifo = av_fifo_alloc(sizeof(AVPacket)); if (!s->packet_fifo) { ret = AVERROR(ENOMEM); @@ -124,7 +125,7 @@ static int qsv_decode_frame(AVCodecContext *avctx, void *data, /* buffer the input packet */ if (avpkt->size) { - AVPacket input_ref = { 0 }; + AVPacket input_ref; if (av_fifo_space(s->packet_fifo) < sizeof(input_ref)) { ret = av_fifo_realloc2(s->packet_fifo, @@ -192,6 +193,11 @@ static const AVOption hevc_options[] = { { "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"}, { NULL }, }; @@ -227,6 +233,11 @@ AVCodec ff_hevc_qsv_decoder = { #if CONFIG_H264_QSV_DECODER 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"}, { NULL }, }; diff --git a/libavcodec/qsvdec_other.c b/libavcodec/qsvdec_other.c index 03251d2c853..b4df76739cd 100644 --- a/libavcodec/qsvdec_other.c +++ b/libavcodec/qsvdec_other.c @@ -1,5 +1,5 @@ /* - * Intel MediaSDK QSV based MPEG-2, VC-1 and VP8 decoders + * Intel MediaSDK QSV based MPEG-2, VC-1, VP8, MJPEG and VP9 decoders * * copyright (c) 2015 Anton Khirnov * @@ -60,8 +60,8 @@ static av_cold int qsv_decode_close(AVCodecContext *avctx) { QSVOtherContext *s = avctx->priv_data; -#if CONFIG_VP8_QSV_DECODER - if (avctx->codec_id == AV_CODEC_ID_VP8) +#if CONFIG_VP8_QSV_DECODER || CONFIG_VP9_QSV_DECODER + if (avctx->codec_id == AV_CODEC_ID_VP8 || avctx->codec_id == AV_CODEC_ID_VP9) av_freep(&s->qsv.load_plugins); #endif @@ -90,6 +90,18 @@ static av_cold int qsv_decode_init(AVCodecContext *avctx) } #endif +#if CONFIG_VP9_QSV_DECODER + if (avctx->codec_id == AV_CODEC_ID_VP9) { + static const char *uid_vp9dec_hw = "a922394d8d87452f878c51f2fc9b4131"; + + av_freep(&s->qsv.load_plugins); + s->qsv.load_plugins = av_strdup(uid_vp9dec_hw); + if (!s->qsv.load_plugins) + return AVERROR(ENOMEM); + } +#endif + + s->qsv.orig_pix_fmt = AV_PIX_FMT_NV12; s->packet_fifo = av_fifo_alloc(sizeof(AVPacket)); if (!s->packet_fifo) { ret = AVERROR(ENOMEM); @@ -111,7 +123,7 @@ static int qsv_decode_frame(AVCodecContext *avctx, void *data, /* buffer the input packet */ if (avpkt->size) { - AVPacket input_ref = { 0 }; + AVPacket input_ref; if (av_fifo_space(s->packet_fifo) < sizeof(input_ref)) { ret = av_fifo_realloc2(s->packet_fifo, @@ -169,6 +181,11 @@ static void qsv_decode_flush(AVCodecContext *avctx) #define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM 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"}, { NULL }, }; @@ -255,3 +272,58 @@ AVCodec ff_vp8_qsv_decoder = { .wrapper_name = "qsv", }; #endif + +#if CONFIG_MJPEG_QSV_DECODER +static const AVClass mjpeg_qsv_class = { + .class_name = "mjpeg_qsv", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVCodec ff_mjpeg_qsv_decoder = { + .name = "mjpeg_qsv", + .long_name = NULL_IF_CONFIG_SMALL("MJPEG video (Intel Quick Sync Video acceleration)"), + .priv_data_size = sizeof(QSVOtherContext), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_MJPEG, + .init = qsv_decode_init, + .decode = qsv_decode_frame, + .flush = qsv_decode_flush, + .close = qsv_decode_close, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HYBRID, + .priv_class = &mjpeg_qsv_class, + .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_NV12, + AV_PIX_FMT_QSV, + AV_PIX_FMT_NONE }, +}; +#endif + +#if CONFIG_VP9_QSV_DECODER +static const AVClass vp9_qsv_class = { + .class_name = "vp9_qsv", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVCodec ff_vp9_qsv_decoder = { + .name = "vp9_qsv", + .long_name = NULL_IF_CONFIG_SMALL("VP9 video (Intel Quick Sync Video acceleration)"), + .priv_data_size = sizeof(QSVOtherContext), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_VP9, + .init = qsv_decode_init, + .decode = qsv_decode_frame, + .flush = qsv_decode_flush, + .close = qsv_decode_close, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HYBRID, + .priv_class = &vp9_qsv_class, + .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_NV12, + AV_PIX_FMT_P010, + AV_PIX_FMT_QSV, + AV_PIX_FMT_NONE }, + .hw_configs = ff_qsv_hw_configs, + .wrapper_name = "qsv", +}; +#endif diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c index 9bf8574e303..ed4539f697a 100644 --- a/libavcodec/qsvenc.c +++ b/libavcodec/qsvenc.c @@ -36,6 +36,7 @@ #include "avcodec.h" #include "internal.h" +#include "packet_internal.h" #include "qsv.h" #include "qsv_internal.h" #include "qsvenc.h" @@ -66,6 +67,7 @@ static const struct { { MFX_PROFILE_HEVC_MAIN, "main" }, { MFX_PROFILE_HEVC_MAIN10, "main10" }, { MFX_PROFILE_HEVC_MAINSP, "mainsp" }, + { MFX_PROFILE_HEVC_REXT, "rext" }, #endif }; @@ -139,6 +141,9 @@ static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q, #if QSV_HAVE_CO3 mfxExtCodingOption3 *co3 = (mfxExtCodingOption3*)coding_opts[2]; #endif +#if QSV_HAVE_EXT_HEVC_TILES + mfxExtHEVCTiles *exthevctiles = (mfxExtHEVCTiles *)coding_opts[3 + QSV_HAVE_CO_VPS]; +#endif av_log(avctx, AV_LOG_VERBOSE, "profile: %s; level: %"PRIu16"\n", print_profile(info->CodecProfile), info->CodecLevel); @@ -204,14 +209,20 @@ static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q, av_log(avctx, AV_LOG_VERBOSE, "RateDistortionOpt: %s\n", print_threestate(co->RateDistortionOpt)); +#if QSV_HAVE_EXT_HEVC_TILES + if (avctx->codec_id == AV_CODEC_ID_HEVC) + av_log(avctx, AV_LOG_VERBOSE, "NumTileColumns: %"PRIu16"; NumTileRows: %"PRIu16"\n", + exthevctiles->NumTileColumns, exthevctiles->NumTileRows); +#endif + #if QSV_HAVE_CO2 av_log(avctx, AV_LOG_VERBOSE, "RecoveryPointSEI: %s IntRefType: %"PRIu16"; IntRefCycleSize: %"PRIu16"; IntRefQPDelta: %"PRId16"\n", print_threestate(co->RecoveryPointSEI), co2->IntRefType, co2->IntRefCycleSize, co2->IntRefQPDelta); - av_log(avctx, AV_LOG_VERBOSE, "MaxFrameSize: %"PRIu16"; ", co2->MaxFrameSize); + av_log(avctx, AV_LOG_VERBOSE, "MaxFrameSize: %d; ", co2->MaxFrameSize); #if QSV_HAVE_MAX_SLICE_SIZE - av_log(avctx, AV_LOG_VERBOSE, "MaxSliceSize: %"PRIu16"; ", co2->MaxSliceSize); + av_log(avctx, AV_LOG_VERBOSE, "MaxSliceSize: %d; ", co2->MaxSliceSize); #endif av_log(avctx, AV_LOG_VERBOSE, "\n"); @@ -461,6 +472,12 @@ static int init_video_param_jpeg(AVCodecContext *avctx, QSVEncContext *q) q->param.mfx.Quality = av_clip(avctx->global_quality, 1, 100); q->param.mfx.RestartInterval = 0; + q->width_align = 16; + q->height_align = 16; + + q->param.mfx.FrameInfo.Width = FFALIGN(avctx->width, q->width_align); + q->param.mfx.FrameInfo.Height = FFALIGN(avctx->height, q->height_align); + return 0; } @@ -529,7 +546,8 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) q->param.mfx.FrameInfo.CropH = avctx->height; q->param.mfx.FrameInfo.AspectRatioW = avctx->sample_aspect_ratio.num; q->param.mfx.FrameInfo.AspectRatioH = avctx->sample_aspect_ratio.den; - q->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420; + q->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420 + + !desc->log2_chroma_w + !desc->log2_chroma_h; q->param.mfx.FrameInfo.BitDepthLuma = desc->comp[0].depth; q->param.mfx.FrameInfo.BitDepthChroma = desc->comp[0].depth; q->param.mfx.FrameInfo.Shift = desc->comp[0].depth > 8; @@ -637,7 +655,8 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) // The HEVC encoder plugin currently fails with some old libmfx version if coding options // are provided. Can't find the extract libmfx version which fixed it, just enable it from // V1.28 in order to keep compatibility security. - if ((avctx->codec_id != AV_CODEC_ID_HEVC) || QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 28)) { + if (((avctx->codec_id != AV_CODEC_ID_HEVC) || QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 28)) + && (avctx->codec_id != AV_CODEC_ID_VP9)) { q->extco.Header.BufferId = MFX_EXTBUFF_CODING_OPTION; q->extco.Header.BufferSz = sizeof(q->extco); @@ -671,11 +690,8 @@ FF_ENABLE_DEPRECATION_WARNINGS q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extco; - if (avctx->codec_id == AV_CODEC_ID_H264) { #if QSV_HAVE_CO2 - q->extco2.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2; - q->extco2.Header.BufferSz = sizeof(q->extco2); - + if (avctx->codec_id == AV_CODEC_ID_H264) { if (q->int_ref_type >= 0) q->extco2.IntRefType = q->int_ref_type; if (q->int_ref_cycle_size >= 0) @@ -687,8 +703,6 @@ FF_ENABLE_DEPRECATION_WARNINGS q->extco2.BitrateLimit = q->bitrate_limit ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF; if (q->mbbrc >= 0) q->extco2.MBBRC = q->mbbrc ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF; - if (q->extbrc >= 0) - q->extco2.ExtBRC = q->extbrc ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF; if (q->max_frame_size >= 0) q->extco2.MaxFrameSize = q->max_frame_size; @@ -736,9 +750,20 @@ FF_ENABLE_DEPRECATION_WARNINGS q->extco2.MaxQPP = q->extco2.MaxQPB = q->extco2.MaxQPI; } #endif + } + + if (avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_HEVC) { + if (q->extbrc >= 0) + q->extco2.ExtBRC = q->extbrc ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF; + + q->extco2.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2; + q->extco2.Header.BufferSz = sizeof(q->extco2); + q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extco2; + } #endif + if (avctx->codec_id == AV_CODEC_ID_H264) { #if QSV_HAVE_MF if (QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 25)) { q->extmfp.Header.BufferId = MFX_EXTBUFF_MULTI_FRAME_PARAM; @@ -761,6 +786,25 @@ FF_ENABLE_DEPRECATION_WARNINGS #endif } +#if QSV_HAVE_EXT_VP9_PARAM + if (avctx->codec_id == AV_CODEC_ID_VP9) { + q->extvp9param.Header.BufferId = MFX_EXTBUFF_VP9_PARAM; + q->extvp9param.Header.BufferSz = sizeof(q->extvp9param); + q->extvp9param.WriteIVFHeaders = MFX_CODINGOPTION_OFF; + q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extvp9param; + } +#endif + +#if QSV_HAVE_EXT_HEVC_TILES + if (avctx->codec_id == AV_CODEC_ID_HEVC) { + q->exthevctiles.Header.BufferId = MFX_EXTBUFF_HEVC_TILES; + q->exthevctiles.Header.BufferSz = sizeof(q->exthevctiles); + q->exthevctiles.NumTileColumns = q->tile_cols; + q->exthevctiles.NumTileRows = q->tile_rows; + q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->exthevctiles; + } +#endif + if (!check_enc_param(avctx,q)) { av_log(avctx, AV_LOG_ERROR, "some encoding parameters are not supported by the QSV " @@ -789,6 +833,55 @@ static int qsv_retrieve_enc_jpeg_params(AVCodecContext *avctx, QSVEncContext *q) return 0; } +static int qsv_retrieve_enc_vp9_params(AVCodecContext *avctx, QSVEncContext *q) +{ + int ret = 0; +#if QSV_HAVE_EXT_VP9_PARAM + mfxExtVP9Param vp9_extend_buf = { + .Header.BufferId = MFX_EXTBUFF_VP9_PARAM, + .Header.BufferSz = sizeof(vp9_extend_buf), + }; +#endif + +#if QSV_HAVE_CO2 + mfxExtCodingOption2 co2 = { + .Header.BufferId = MFX_EXTBUFF_CODING_OPTION2, + .Header.BufferSz = sizeof(co2), + }; +#endif + +#if QSV_HAVE_CO3 + mfxExtCodingOption3 co3 = { + .Header.BufferId = MFX_EXTBUFF_CODING_OPTION3, + .Header.BufferSz = sizeof(co3), + }; +#endif + + mfxExtBuffer *ext_buffers[] = { +#if QSV_HAVE_EXT_VP9_PARAM + (mfxExtBuffer*)&vp9_extend_buf, +#endif +#if QSV_HAVE_CO2 + (mfxExtBuffer*)&co2, +#endif +#if QSV_HAVE_CO3 + (mfxExtBuffer*)&co3, +#endif + }; + + q->param.ExtParam = ext_buffers; + q->param.NumExtParam = FF_ARRAY_ELEMS(ext_buffers); + + ret = MFXVideoENCODE_GetVideoParam(q->session, &q->param); + if (ret < 0) + return ff_qsv_print_error(avctx, ret, + "Error calling GetVideoParam"); + + q->packet_size = q->param.mfx.BufferSizeInKB * q->param.mfx.BRCParamMultiplier * 1000; + + return 0; +} + static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q) { AVCPBProperties *cpb_props; @@ -830,7 +923,14 @@ static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q) }; #endif - mfxExtBuffer *ext_buffers[2 + QSV_HAVE_CO2 + QSV_HAVE_CO3 + QSV_HAVE_CO_VPS]; +#if QSV_HAVE_EXT_HEVC_TILES + mfxExtHEVCTiles hevc_tile_buf = { + .Header.BufferId = MFX_EXTBUFF_HEVC_TILES, + .Header.BufferSz = sizeof(hevc_tile_buf), + }; +#endif + + mfxExtBuffer *ext_buffers[2 + QSV_HAVE_CO2 + QSV_HAVE_CO3 + QSV_HAVE_CO_VPS + QSV_HAVE_EXT_HEVC_TILES]; int need_pps = avctx->codec_id != AV_CODEC_ID_MPEG2VIDEO; int ret, ext_buf_num = 0, extradata_offset = 0; @@ -848,6 +948,10 @@ static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q) if (q->hevc_vps) ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&extradata_vps; #endif +#if QSV_HAVE_EXT_HEVC_TILES + if (avctx->codec_id == AV_CODEC_ID_HEVC) + ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&hevc_tile_buf; +#endif q->param.ExtParam = ext_buffers; q->param.NumExtParam = ext_buf_num; @@ -954,29 +1058,31 @@ static int qsvenc_init_session(AVCodecContext *avctx, QSVEncContext *q) if (!q->frames_ctx.hw_frames_ctx) return AVERROR(ENOMEM); - ret = ff_qsv_init_session_frames(avctx, &q->internal_session, + ret = ff_qsv_init_session_frames(avctx, &q->internal_qs.session, &q->frames_ctx, q->load_plugins, - q->param.IOPattern == MFX_IOPATTERN_IN_OPAQUE_MEMORY); + q->param.IOPattern == MFX_IOPATTERN_IN_OPAQUE_MEMORY, + MFX_GPUCOPY_OFF); if (ret < 0) { av_buffer_unref(&q->frames_ctx.hw_frames_ctx); return ret; } - q->session = q->internal_session; + q->session = q->internal_qs.session; } else if (avctx->hw_device_ctx) { - ret = ff_qsv_init_session_device(avctx, &q->internal_session, - avctx->hw_device_ctx, q->load_plugins); + ret = ff_qsv_init_session_device(avctx, &q->internal_qs.session, + avctx->hw_device_ctx, q->load_plugins, + MFX_GPUCOPY_OFF); if (ret < 0) return ret; - q->session = q->internal_session; + q->session = q->internal_qs.session; } else { - ret = ff_qsv_init_internal_session(avctx, &q->internal_session, - q->load_plugins); + ret = ff_qsv_init_internal_session(avctx, &q->internal_qs, + q->load_plugins, MFX_GPUCOPY_OFF); if (ret < 0) return ret; - q->session = q->internal_session; + q->session = q->internal_qs.session; } return 0; @@ -1050,25 +1156,6 @@ int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q) if (ret < 0) return ret; - ret = MFXVideoENCODE_Query(q->session, &q->param, &q->param); - if (ret == MFX_WRN_PARTIAL_ACCELERATION) { - av_log(avctx, AV_LOG_WARNING, "Encoder will work with partial HW acceleration\n"); - } else if (ret < 0) { - return ff_qsv_print_error(avctx, ret, - "Error querying encoder params"); - } - - ret = MFXVideoENCODE_QueryIOSurf(q->session, &q->param, &q->req); - if (ret < 0) - return ff_qsv_print_error(avctx, ret, - "Error querying (IOSurf) the encoding parameters"); - - if (opaque_alloc) { - ret = qsv_init_opaque_alloc(avctx, q); - if (ret < 0) - return ret; - } - if (avctx->hwaccel_context) { AVQSVContext *qsv = avctx->hwaccel_context; int i, j; @@ -1098,6 +1185,25 @@ int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q) q->param.NumExtParam = q->nb_extparam_internal; } + ret = MFXVideoENCODE_Query(q->session, &q->param, &q->param); + if (ret == MFX_WRN_PARTIAL_ACCELERATION) { + av_log(avctx, AV_LOG_WARNING, "Encoder will work with partial HW acceleration\n"); + } else if (ret < 0) { + return ff_qsv_print_error(avctx, ret, + "Error querying encoder params"); + } + + ret = MFXVideoENCODE_QueryIOSurf(q->session, &q->param, &q->req); + if (ret < 0) + return ff_qsv_print_error(avctx, ret, + "Error querying (IOSurf) the encoding parameters"); + + if (opaque_alloc) { + ret = qsv_init_opaque_alloc(avctx, q); + if (ret < 0) + return ret; + } + ret = MFXVideoENCODE_Init(q->session, &q->param); if (ret < 0) return ff_qsv_print_error(avctx, ret, @@ -1110,6 +1216,9 @@ int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q) case AV_CODEC_ID_MJPEG: ret = qsv_retrieve_enc_jpeg_params(avctx, q); break; + case AV_CODEC_ID_VP9: + ret = qsv_retrieve_enc_vp9_params(avctx, q); + break; default: ret = qsv_retrieve_enc_params(avctx, q); break; @@ -1507,10 +1616,9 @@ int ff_qsv_enc_close(AVCodecContext *avctx, QSVEncContext *q) if (q->session) MFXVideoENCODE_Close(q->session); - if (q->internal_session) - MFXClose(q->internal_session); + q->session = NULL; - q->internal_session = NULL; + ff_qsv_close_internal_session(&q->internal_qs); av_buffer_unref(&q->frames_ctx.hw_frames_ctx); av_buffer_unref(&q->frames_ctx.mids_buf); @@ -1547,3 +1655,10 @@ int ff_qsv_enc_close(AVCodecContext *avctx, QSVEncContext *q) return 0; } + +const AVCodecHWConfigInternal *ff_qsv_enc_hw_configs[] = { + HW_CONFIG_ENCODER_FRAMES(QSV, QSV), + HW_CONFIG_ENCODER_DEVICE(NV12, QSV), + HW_CONFIG_ENCODER_DEVICE(P010, QSV), + NULL, +}; diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h index f2f4d385035..4f579d1db14 100644 --- a/libavcodec/qsvenc.h +++ b/libavcodec/qsvenc.h @@ -32,12 +32,16 @@ #include "libavutil/fifo.h" #include "avcodec.h" +#include "hwconfig.h" #include "qsv_internal.h" #define QSV_HAVE_CO2 QSV_VERSION_ATLEAST(1, 6) #define QSV_HAVE_CO3 QSV_VERSION_ATLEAST(1, 11) #define QSV_HAVE_CO_VPS QSV_VERSION_ATLEAST(1, 17) +#define QSV_HAVE_EXT_HEVC_TILES QSV_VERSION_ATLEAST(1, 13) +#define QSV_HAVE_EXT_VP9_PARAM QSV_VERSION_ATLEAST(1, 26) + #define QSV_HAVE_TRELLIS QSV_VERSION_ATLEAST(1, 8) #define QSV_HAVE_MAX_SLICE_SIZE QSV_VERSION_ATLEAST(1, 9) #define QSV_HAVE_BREF_TYPE QSV_VERSION_ATLEAST(1, 8) @@ -94,6 +98,8 @@ { "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 = 0}, 0, 1, VE},\ +extern const AVCodecHWConfigInternal *ff_qsv_enc_hw_configs[]; + typedef int SetEncodeCtrlCB (AVCodecContext *avctx, const AVFrame *frame, mfxEncodeCtrl* enc_ctrl); typedef struct QSVEncContext { @@ -102,7 +108,7 @@ typedef struct QSVEncContext { QSVFrame *work_frames; mfxSession session; - mfxSession internal_session; + QSVSession internal_qs; int packet_size; int width_align; @@ -122,6 +128,13 @@ typedef struct QSVEncContext { mfxExtMultiFrameParam extmfp; mfxExtMultiFrameControl extmfc; #endif +#if QSV_HAVE_EXT_HEVC_TILES + mfxExtHEVCTiles exthevctiles; +#endif +#if QSV_HAVE_EXT_VP9_PARAM + mfxExtVP9Param extvp9param; +#endif + mfxExtOpaqueSurfaceAlloc opaque_alloc; mfxFrameSurface1 **opaque_surfaces; AVBufferRef *opaque_alloc_buf; @@ -155,6 +168,9 @@ typedef struct QSVEncContext { int max_frame_size; int max_slice_size; + int tile_cols; + int tile_rows; + int aud; int single_sei_nal_unit; diff --git a/libavcodec/qsvenc_h264.c b/libavcodec/qsvenc_h264.c index 27f36b9f7b3..364975bea5b 100644 --- a/libavcodec/qsvenc_h264.c +++ b/libavcodec/qsvenc_h264.c @@ -197,4 +197,5 @@ AVCodec ff_h264_qsv_encoder = { .defaults = qsv_enc_defaults, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .wrapper_name = "qsv", + .hw_configs = ff_qsv_enc_hw_configs, }; diff --git a/libavcodec/qsvenc_hevc.c b/libavcodec/qsvenc_hevc.c index da64b4c21be..88c78a81356 100644 --- a/libavcodec/qsvenc_hevc.c +++ b/libavcodec/qsvenc_hevc.c @@ -240,9 +240,13 @@ static const AVOption options[] = { { "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" }, { "gpb", "1: GPB (generalized P/B frame); 0: regular P frame", OFFSET(qsv.gpb), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE}, + { "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 }, + { NULL }, }; @@ -285,4 +289,5 @@ AVCodec ff_hevc_qsv_encoder = { .defaults = qsv_enc_defaults, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .wrapper_name = "qsv", + .hw_configs = ff_qsv_enc_hw_configs, }; diff --git a/libavcodec/qsvenc_jpeg.c b/libavcodec/qsvenc_jpeg.c index 1619a335c79..f76af9486ba 100644 --- a/libavcodec/qsvenc_jpeg.c +++ b/libavcodec/qsvenc_jpeg.c @@ -95,4 +95,5 @@ AVCodec ff_mjpeg_qsv_encoder = { .priv_class = &class, .defaults = qsv_enc_defaults, .wrapper_name = "qsv", + .hw_configs = ff_qsv_enc_hw_configs, }; diff --git a/libavcodec/qsvenc_mpeg2.c b/libavcodec/qsvenc_mpeg2.c index e4ade56d624..0e34bb75dc0 100644 --- a/libavcodec/qsvenc_mpeg2.c +++ b/libavcodec/qsvenc_mpeg2.c @@ -112,4 +112,5 @@ AVCodec ff_mpeg2_qsv_encoder = { .defaults = qsv_enc_defaults, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .wrapper_name = "qsv", + .hw_configs = ff_qsv_enc_hw_configs, }; diff --git a/libavcodec/qsvenc_vp9.c b/libavcodec/qsvenc_vp9.c new file mode 100644 index 00000000000..ce44c09397e --- /dev/null +++ b/libavcodec/qsvenc_vp9.c @@ -0,0 +1,114 @@ +/* + * Intel MediaSDK QSV based VP9 encoder + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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/common.h" +#include "libavutil/opt.h" + +#include "avcodec.h" +#include "internal.h" +#include "qsv.h" +#include "qsv_internal.h" +#include "qsvenc.h" + +typedef struct QSVVP9EncContext { + AVClass *class; + QSVEncContext qsv; +} QSVVP9EncContext; + +static av_cold int qsv_enc_init(AVCodecContext *avctx) +{ + QSVVP9EncContext *q = avctx->priv_data; + q->qsv.low_power = 1; + + return ff_qsv_enc_init(avctx, &q->qsv); +} + +static int qsv_enc_frame(AVCodecContext *avctx, AVPacket *pkt, + const AVFrame *frame, int *got_packet) +{ + QSVVP9EncContext *q = avctx->priv_data; + + return ff_qsv_encode(avctx, &q->qsv, pkt, frame, got_packet); +} + +static av_cold int qsv_enc_close(AVCodecContext *avctx) +{ + QSVVP9EncContext *q = avctx->priv_data; + + return ff_qsv_enc_close(avctx, &q->qsv); +} + +#define OFFSET(x) offsetof(QSVVP9EncContext, x) +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM +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" }, + + { NULL }, +}; + +static const AVClass class = { + .class_name = "vp9_qsv encoder", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static const AVCodecDefault qsv_enc_defaults[] = { + { "b", "1M" }, + { "refs", "0" }, + { "g", "250" }, + { "trellis", "-1" }, + { "flags", "+cgop" }, + { NULL }, +}; + +AVCodec ff_vp9_qsv_encoder = { + .name = "vp9_qsv", + .long_name = NULL_IF_CONFIG_SMALL("VP9 video (Intel Quick Sync Video acceleration)"), + .priv_data_size = sizeof(QSVVP9EncContext), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_VP9, + .init = qsv_enc_init, + .encode2 = qsv_enc_frame, + .close = qsv_enc_close, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HYBRID, + .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_NV12, + AV_PIX_FMT_P010, + AV_PIX_FMT_QSV, + AV_PIX_FMT_NONE }, + .priv_class = &class, + .defaults = qsv_enc_defaults, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, + .wrapper_name = "qsv", + .hw_configs = ff_qsv_enc_hw_configs, +}; diff --git a/libavcodec/qtrle.c b/libavcodec/qtrle.c index 2c29547e5ae..52394f52640 100644 --- a/libavcodec/qtrle.c +++ b/libavcodec/qtrle.c @@ -452,13 +452,16 @@ static int qtrle_decode_frame(AVCodecContext *avctx, int header, start_line; int height, row_ptr; int has_palette = 0; + int duplicate = 0; int ret, size; bytestream2_init(&s->g, avpkt->data, avpkt->size); /* check if this frame is even supposed to change */ - if (avpkt->size < 8) - return avpkt->size; + if (avpkt->size < 8) { + duplicate = 1; + goto done; + } /* start after the chunk size */ size = bytestream2_get_be32(&s->g) & 0x3FFFFFFF; @@ -471,19 +474,23 @@ static int qtrle_decode_frame(AVCodecContext *avctx, /* if a header is present, fetch additional decoding parameters */ if (header & 0x0008) { - if (avpkt->size < 14) - return avpkt->size; + if (avpkt->size < 14) { + duplicate = 1; + goto done; + } start_line = bytestream2_get_be16(&s->g); bytestream2_skip(&s->g, 2); height = bytestream2_get_be16(&s->g); bytestream2_skip(&s->g, 2); - if (height > s->avctx->height - start_line) - return avpkt->size; + if (height > s->avctx->height - start_line) { + duplicate = 1; + goto done; + } } else { start_line = 0; height = s->avctx->height; } - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; row_ptr = s->frame->linesize[0] * start_line; @@ -546,6 +553,17 @@ static int qtrle_decode_frame(AVCodecContext *avctx, memcpy(s->frame->data[1], s->pal, AVPALETTE_SIZE); } +done: + if (!s->frame->data[0]) + return AVERROR_INVALIDDATA; + if (duplicate) { + // ff_reget_buffer() isn't needed when frames don't change, so just update + // frame props. + ret = ff_decode_frame_props(avctx, s->frame); + if (ret < 0) + return ret; + } + if ((ret = av_frame_ref(data, s->frame)) < 0) return ret; *got_frame = 1; @@ -554,6 +572,13 @@ static int qtrle_decode_frame(AVCodecContext *avctx, return avpkt->size; } +static void qtrle_decode_flush(AVCodecContext *avctx) +{ + QtrleContext *s = avctx->priv_data; + + av_frame_unref(s->frame); +} + static av_cold int qtrle_decode_end(AVCodecContext *avctx) { QtrleContext *s = avctx->priv_data; @@ -572,5 +597,6 @@ AVCodec ff_qtrle_decoder = { .init = qtrle_decode_init, .close = qtrle_decode_end, .decode = qtrle_decode_frame, + .flush = qtrle_decode_flush, .capabilities = AV_CODEC_CAP_DR1, }; diff --git a/libavcodec/qtrleenc.c b/libavcodec/qtrleenc.c index cdd864bf820..6669c1302f9 100644 --- a/libavcodec/qtrleenc.c +++ b/libavcodec/qtrleenc.c @@ -259,9 +259,10 @@ static void qtrle_encode_line(QtrleEncContext *s, const AVFrame *p, int line, ui /* These bulk costs increase every iteration */ lowest_bulk_cost += s->pixel_size; sec_lowest_bulk_cost += s->pixel_size; - - this_line -= s->pixel_size; - prev_line -= s->pixel_size; + if (this_line >= p->data[0] + s->pixel_size) + this_line -= s->pixel_size; + if (prev_line >= s->previous_frame->data[0] + s->pixel_size) + prev_line -= s->pixel_size; } /* Good! Now we have the best sequence for this line, let's output it. */ diff --git a/libavcodec/r210enc.c b/libavcodec/r210enc.c index 02412f3684a..be1943f5f92 100644 --- a/libavcodec/r210enc.c +++ b/libavcodec/r210enc.c @@ -60,9 +60,9 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, uint16_t *srcb = (uint16_t *)srcb_line; for (j = 0; j < avctx->width; j++) { uint32_t pixel; - uint16_t r = *srcr++; - uint16_t g = *srcg++; - uint16_t b = *srcb++; + unsigned r = *srcr++; + unsigned g = *srcg++; + unsigned b = *srcb++; if (avctx->codec_id == AV_CODEC_ID_R210) pixel = (r << 20) | (g << 10) | b; else @@ -94,7 +94,6 @@ AVCodec ff_r210_encoder = { .init = encode_init, .encode2 = encode_frame, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_GBRP10, AV_PIX_FMT_NONE }, - .capabilities = AV_CODEC_CAP_INTRA_ONLY, }; #endif #if CONFIG_R10K_ENCODER @@ -106,7 +105,6 @@ AVCodec ff_r10k_encoder = { .init = encode_init, .encode2 = encode_frame, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_GBRP10, AV_PIX_FMT_NONE }, - .capabilities = AV_CODEC_CAP_INTRA_ONLY, }; #endif #if CONFIG_AVRP_ENCODER @@ -118,6 +116,5 @@ AVCodec ff_avrp_encoder = { .init = encode_init, .encode2 = encode_frame, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_GBRP10, AV_PIX_FMT_NONE }, - .capabilities = AV_CODEC_CAP_INTRA_ONLY, }; #endif diff --git a/libavcodec/ra144enc.c b/libavcodec/ra144enc.c index cc4f381606a..059f582334d 100644 --- a/libavcodec/ra144enc.c +++ b/libavcodec/ra144enc.c @@ -477,8 +477,8 @@ static int ra144_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, LPC_ORDER, 16, lpc_coefs, shift, FF_LPC_TYPE_LEVINSON, 0, ORDER_METHOD_EST, 0, 12, 0); for (i = 0; i < LPC_ORDER; i++) - block_coefs[NBLOCKS - 1][i] = -(lpc_coefs[LPC_ORDER - 1][i] << - (12 - shift[LPC_ORDER - 1])); + block_coefs[NBLOCKS - 1][i] = -lpc_coefs[LPC_ORDER - 1][i] + * (1 << (12 - shift[LPC_ORDER - 1])); /** * TODO: apply perceptual weighting of the input speech through bandwidth diff --git a/libavcodec/ra288.c b/libavcodec/ra288.c index f1b3c8eab55..aa4bd5d90fa 100644 --- a/libavcodec/ra288.c +++ b/libavcodec/ra288.c @@ -77,7 +77,7 @@ static av_cold int ra288_decode_init(AVCodecContext *avctx) avctx->channel_layout = AV_CH_LAYOUT_MONO; avctx->sample_fmt = AV_SAMPLE_FMT_FLT; - if (avctx->block_align <= 0) { + if (avctx->block_align != 38) { av_log(avctx, AV_LOG_ERROR, "unsupported block align\n"); return AVERROR_PATCHWELCOME; } diff --git a/libavcodec/ralf.c b/libavcodec/ralf.c index 75c9371b953..0080b239419 100644 --- a/libavcodec/ralf.c +++ b/libavcodec/ralf.c @@ -60,7 +60,7 @@ typedef struct RALFContext { int filter_bits; ///< filter precision for the current channel data int32_t filter[64]; - int bias[2]; ///< a constant value added to channel data after filtering + unsigned bias[2]; ///< a constant value added to channel data after filtering int num_blocks; ///< number of blocks inside the frame int sample_offset; @@ -234,8 +234,10 @@ static int decode_channel(RALFContext *ctx, GetBitContext *gb, int ch, int *dst = ctx->channel_data[ch]; ctx->filter_params = get_vlc2(gb, set->filter_params.table, 9, 2); - ctx->filter_bits = (ctx->filter_params - 2) >> 6; - ctx->filter_length = ctx->filter_params - (ctx->filter_bits << 6) - 1; + if (ctx->filter_params > 1) { + ctx->filter_bits = (ctx->filter_params - 2) >> 6; + ctx->filter_length = ctx->filter_params - (ctx->filter_bits << 6) - 1; + } if (ctx->filter_params == FILTER_RAW) { for (i = 0; i < length; i++) @@ -262,8 +264,8 @@ static int decode_channel(RALFContext *ctx, GetBitContext *gb, int ch, t = get_vlc2(gb, vlc[cmode].table, vlc[cmode].bits, 2); t = extend_code(gb, t, 21, add_bits); if (!cmode) - coeff -= 12 << add_bits; - coeff = t - coeff; + coeff -= 12U << add_bits; + coeff = (unsigned)t - coeff; ctx->filter[i] = coeff; cmode = coeff >> add_bits; @@ -300,8 +302,8 @@ static int decode_channel(RALFContext *ctx, GetBitContext *gb, int ch, t = get_vlc2(gb, code_vlc->table, code_vlc->bits, 2); code1 = t / range2; code2 = t % range2; - dst[i] = extend_code(gb, code1, range, 0) * (1 << add_bits); - dst[i + 1] = extend_code(gb, code2, range, 0) * (1 << add_bits); + dst[i] = extend_code(gb, code1, range, 0) * (1U << add_bits); + dst[i + 1] = extend_code(gb, code2, range, 0) * (1U << add_bits); if (add_bits) { dst[i] |= get_bits(gb, add_bits); dst[i + 1] |= get_bits(gb, add_bits); @@ -328,7 +330,7 @@ static void apply_lpc(RALFContext *ctx, int ch, int length, int bits) acc = (acc + bias - 1) >> ctx->filter_bits; acc = FFMAX(acc, min_clip); } else { - acc = (acc + bias) >> ctx->filter_bits; + acc = ((unsigned)acc + bias) >> ctx->filter_bits; acc = FFMIN(acc, max_clip); } audio[i] += acc; @@ -342,7 +344,8 @@ static int decode_block(AVCodecContext *avctx, GetBitContext *gb, int len, ch, ret; int dmode, mode[2], bits[2]; int *ch0, *ch1; - int i, t, t2; + int i; + unsigned int t, t2; len = 12 - get_unary(gb, 0, 6); @@ -406,9 +409,9 @@ static int decode_block(AVCodecContext *avctx, GetBitContext *gb, case 4: for (i = 0; i < len; i++) { t = ch1[i] + ctx->bias[1]; - t2 = ((ch0[i] + ctx->bias[0]) << 1) | (t & 1); - dst0[i] = (t2 + t) / 2; - dst1[i] = (t2 - t) / 2; + t2 = ((ch0[i] + ctx->bias[0]) * 2) | (t & 1); + dst0[i] = (int)(t2 + t) / 2; + dst1[i] = (int)(t2 - t) / 2; } break; } @@ -479,6 +482,8 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame_ptr, init_get_bits(&gb, src + 2, table_size); ctx->num_blocks = 0; while (get_bits_left(&gb) > 0) { + if (ctx->num_blocks >= FF_ARRAY_ELEMS(ctx->block_size)) + return AVERROR_INVALIDDATA; ctx->block_size[ctx->num_blocks] = get_bits(&gb, 13 + avctx->channels); if (get_bits1(&gb)) { ctx->block_pts[ctx->num_blocks] = get_bits(&gb, 9); diff --git a/libavcodec/rasc.c b/libavcodec/rasc.c index 21fc43f325c..cdf20a6db9a 100644 --- a/libavcodec/rasc.c +++ b/libavcodec/rasc.c @@ -124,6 +124,8 @@ static int decode_fint(AVCodecContext *avctx, clear_plane(avctx, s->frame1); return 0; } + if (bytestream2_get_bytes_left(gb) < 72) + return AVERROR_INVALIDDATA; bytestream2_skip(gb, 8); w = bytestream2_get_le32(gb); diff --git a/libavcodec/ratecontrol.c b/libavcodec/ratecontrol.c index 49d169ba25c..6b77ccd006b 100644 --- a/libavcodec/ratecontrol.c +++ b/libavcodec/ratecontrol.c @@ -999,11 +999,11 @@ float ff_rate_estimate_qscale(MpegEncContext *s, int dry_run) if (s->avctx->debug & FF_DEBUG_RC) { av_log(s->avctx, AV_LOG_DEBUG, - "%c qp:%d<%2.1f<%d %d want:%d total:%d comp:%f st_q:%2.2f " + "%c qp:%d<%2.1f<%d %d want:%"PRId64" total:%"PRId64" comp:%f st_q:%2.2f " "size:%d var:%"PRId64"/%"PRId64" br:%"PRId64" fps:%d\n", av_get_picture_type_char(pict_type), qmin, q, qmax, picture_number, - (int)wanted_bits / 1000, (int)s->total_bits / 1000, + wanted_bits / 1000, s->total_bits / 1000, br_compensation, short_term_q, s->frame_bits, pic->mb_var_sum, pic->mc_mb_var_sum, s->bit_rate / 1000, (int)fps); diff --git a/libavcodec/rawdec.c b/libavcodec/rawdec.c index 53f5b76e93e..a110a690f57 100644 --- a/libavcodec/rawdec.c +++ b/libavcodec/rawdec.c @@ -223,7 +223,7 @@ static int raw_decode(AVCodecContext *avctx, void *data, int *got_frame, FFALIGN(avctx->width, 16), avctx->height, 1); } else { - context->is_lt_16bpp = av_get_bits_per_pixel(desc) == 16 && avctx->bits_per_coded_sample && avctx->bits_per_coded_sample < 16; + context->is_lt_16bpp = av_get_bits_per_pixel(desc) == 16 && avctx->bits_per_coded_sample > 8 && avctx->bits_per_coded_sample < 16; context->frame_size = av_image_get_buffer_size(avctx->pix_fmt, avctx->width, avctx->height, 1); } @@ -467,10 +467,13 @@ static int raw_decode(AVCodecContext *avctx, void *data, int *got_frame, avctx->pix_fmt == AV_PIX_FMT_RGBA64BE) { uint8_t *dst = frame->data[0]; uint64_t v; - int x; - for (x = 0; x >> 3 < avctx->width * avctx->height; x += 8) { - v = AV_RB64(&dst[x]); - AV_WB64(&dst[x], v << 16 | v >> 48); + int x, y; + for (y = 0; y < avctx->height; y++) { + for (x = 0; x >> 3 < avctx->width; x += 8) { + v = AV_RB64(&dst[x]); + AV_WB64(&dst[x], v << 16 | v >> 48); + } + dst += frame->linesize[0]; } } diff --git a/libavcodec/remove_extradata_bsf.c b/libavcodec/remove_extradata_bsf.c index b762079e05b..5783b075f01 100644 --- a/libavcodec/remove_extradata_bsf.c +++ b/libavcodec/remove_extradata_bsf.c @@ -23,6 +23,7 @@ #include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" enum RemoveFreq { REMOVE_FREQ_KEYFRAME, diff --git a/libavcodec/rkmppdec.c b/libavcodec/rkmppdec.c index 143d05bd517..248020d5d61 100644 --- a/libavcodec/rkmppdec.c +++ b/libavcodec/rkmppdec.c @@ -28,7 +28,7 @@ #include "avcodec.h" #include "decode.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "internal.h" #include "libavutil/buffer.h" #include "libavutil/common.h" diff --git a/libavcodec/roqvideodec.c b/libavcodec/roqvideodec.c index 0ab7d399d67..a0c293f2f04 100644 --- a/libavcodec/roqvideodec.c +++ b/libavcodec/roqvideodec.c @@ -206,7 +206,7 @@ static int roq_decode_frame(AVCodecContext *avctx, int copy = !s->current_frame->data[0] && s->last_frame->data[0]; int ret; - if ((ret = ff_reget_buffer(avctx, s->current_frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->current_frame, 0)) < 0) return ret; if (copy) { diff --git a/libavcodec/rpza.c b/libavcodec/rpza.c index 8e1efa24450..02bbfe753f8 100644 --- a/libavcodec/rpza.c +++ b/libavcodec/rpza.c @@ -108,7 +108,7 @@ static int rpza_decode_stream(RpzaContext *s) if (total_blocks / 32 > bytestream2_get_bytes_left(&s->gb)) return AVERROR_INVALIDDATA; - if ((ret = ff_reget_buffer(s->avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(s->avctx, s->frame, 0)) < 0) return ret; pixels = (uint16_t *)s->frame->data[0]; stride = s->frame->linesize[0] / 2; diff --git a/libavcodec/rscc.c b/libavcodec/rscc.c index f494c30ed86..bd0520950ff 100644 --- a/libavcodec/rscc.c +++ b/libavcodec/rscc.c @@ -310,7 +310,7 @@ static int rscc_decode_frame(AVCodecContext *avctx, void *data, } /* Allocate when needed */ - ret = ff_reget_buffer(avctx, ctx->reference); + ret = ff_reget_buffer(avctx, ctx->reference, 0); if (ret < 0) goto end; diff --git a/libavcodec/rv10.c b/libavcodec/rv10.c index 729e4a8d2c7..3b41d30b92b 100644 --- a/libavcodec/rv10.c +++ b/libavcodec/rv10.c @@ -550,7 +550,7 @@ static av_cold int rv10_decode_end(AVCodecContext *avctx) } static int rv10_decode_packet(AVCodecContext *avctx, const uint8_t *buf, - int buf_size, int buf_size2) + int buf_size, int buf_size2, int whole_size) { RVDecContext *rv = avctx->priv_data; MpegEncContext *s = &rv->m; @@ -580,6 +580,9 @@ static int rv10_decode_packet(AVCodecContext *avctx, const uint8_t *buf, return AVERROR_INVALIDDATA; } + if (whole_size < s->mb_width * s->mb_height / 8) + return AVERROR_INVALIDDATA; + 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) { @@ -754,7 +757,7 @@ static int rv10_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, offset + FFMAX(size, size2) > buf_size) return AVERROR_INVALIDDATA; - if ((ret = rv10_decode_packet(avctx, buf + offset, size, size2)) < 0) + if ((ret = rv10_decode_packet(avctx, buf + offset, size, size2, buf_size)) < 0) return ret; if (ret > 8 * size) diff --git a/libavcodec/rv10enc.c b/libavcodec/rv10enc.c index 8691d1880ec..55538148f28 100644 --- a/libavcodec/rv10enc.c +++ b/libavcodec/rv10enc.c @@ -79,6 +79,7 @@ AVCodec ff_rv10_encoder = { .init = ff_mpv_encode_init, .encode2 = ff_mpv_encode_picture, .close = ff_mpv_encode_end, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, .priv_class = &rv10_class, }; diff --git a/libavcodec/rv20enc.c b/libavcodec/rv20enc.c index 81fb4fc1bad..d9d63d4d9cf 100644 --- a/libavcodec/rv20enc.c +++ b/libavcodec/rv20enc.c @@ -76,6 +76,7 @@ AVCodec ff_rv20_encoder = { .init = ff_mpv_encode_init, .encode2 = ff_mpv_encode_picture, .close = ff_mpv_encode_end, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, .priv_class = &rv20_class, }; diff --git a/libavcodec/rv30.c b/libavcodec/rv30.c index ddaaac651c7..36cd5345fdc 100644 --- a/libavcodec/rv30.c +++ b/libavcodec/rv30.c @@ -304,6 +304,6 @@ AVCodec ff_rv30_decoder = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(ff_rv34_decode_init_thread_copy), .update_thread_context = ONLY_IF_THREADS_ENABLED(ff_rv34_decode_update_thread_context), + .caps_internal = FF_CODEC_CAP_ALLOCATE_PROGRESS, }; diff --git a/libavcodec/rv34.c b/libavcodec/rv34.c index d171e6e1bde..ec0cd279162 100644 --- a/libavcodec/rv34.c +++ b/libavcodec/rv34.c @@ -1526,36 +1526,6 @@ av_cold int ff_rv34_decode_init(AVCodecContext *avctx) if(!intra_vlcs[0].cbppattern[0].bits) rv34_init_tables(); - avctx->internal->allocate_progress = 1; - - return 0; -} - -int ff_rv34_decode_init_thread_copy(AVCodecContext *avctx) -{ - int err; - RV34DecContext *r = avctx->priv_data; - - r->s.avctx = avctx; - - if (avctx->internal->is_copy) { - r->tmp_b_block_base = NULL; - r->cbp_chroma = NULL; - r->cbp_luma = NULL; - r->deblock_coefs = NULL; - r->intra_types_hist = NULL; - r->mb_type = NULL; - - ff_mpv_idct_init(&r->s); - - if ((err = ff_mpv_common_init(&r->s)) < 0) - return err; - if ((err = rv34_decoder_alloc(r)) < 0) { - ff_mpv_common_end(&r->s); - return err; - } - } - return 0; } diff --git a/libavcodec/rv34.h b/libavcodec/rv34.h index efff94a1d90..1d5522538b8 100644 --- a/libavcodec/rv34.h +++ b/libavcodec/rv34.h @@ -136,7 +136,6 @@ int ff_rv34_get_start_offset(GetBitContext *gb, int blocks); int ff_rv34_decode_init(AVCodecContext *avctx); int ff_rv34_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *avpkt); int ff_rv34_decode_end(AVCodecContext *avctx); -int ff_rv34_decode_init_thread_copy(AVCodecContext *avctx); int ff_rv34_decode_update_thread_context(AVCodecContext *dst, const AVCodecContext *src); #endif /* AVCODEC_RV34_H */ diff --git a/libavcodec/rv40.c b/libavcodec/rv40.c index dfeebda8381..462024c81e2 100644 --- a/libavcodec/rv40.c +++ b/libavcodec/rv40.c @@ -583,6 +583,6 @@ AVCodec ff_rv40_decoder = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(ff_rv34_decode_init_thread_copy), .update_thread_context = ONLY_IF_THREADS_ENABLED(ff_rv34_decode_update_thread_context), + .caps_internal = FF_CODEC_CAP_ALLOCATE_PROGRESS, }; diff --git a/libavcodec/rv40dsp.c b/libavcodec/rv40dsp.c index 5579bd9bedc..2ac791d674c 100644 --- a/libavcodec/rv40dsp.c +++ b/libavcodec/rv40dsp.c @@ -385,7 +385,7 @@ static void rv40_weight_func_rnd_ ## size (uint8_t *dst, uint8_t *src1, uint8_t \ for (j = 0; j < size; j++) {\ for (i = 0; i < size; i++)\ - dst[i] = (((w2 * src1[i]) >> 9) + ((w1 * src2[i]) >> 9) + 0x10) >> 5;\ + dst[i] = ((((unsigned)w2 * src1[i]) >> 9) + (((unsigned)w1 * src2[i]) >> 9) + 0x10) >> 5;\ src1 += stride;\ src2 += stride;\ dst += stride;\ @@ -397,7 +397,7 @@ static void rv40_weight_func_nornd_ ## size (uint8_t *dst, uint8_t *src1, uint8_ \ for (j = 0; j < size; j++) {\ for (i = 0; i < size; i++)\ - dst[i] = (w2 * src1[i] + w1 * src2[i] + 0x10) >> 5;\ + dst[i] = ((unsigned)w2 * src1[i] + (unsigned)w1 * src2[i] + 0x10) >> 5;\ src1 += stride;\ src2 += stride;\ dst += stride;\ diff --git a/libavcodec/sbcdec.c b/libavcodec/sbcdec.c index 546b38c1068..5361ee2c89a 100644 --- a/libavcodec/sbcdec.c +++ b/libavcodec/sbcdec.c @@ -30,7 +30,6 @@ * SBC decoder implementation */ -#include #include "avcodec.h" #include "internal.h" #include "libavutil/intreadwrite.h" @@ -227,10 +226,10 @@ static inline void sbc_synthesize_four(struct sbc_decoder_state *state, /* Distribute the new matrix value to the shifted position */ v[offset[i]] = - ( ff_synmatrix4[i][0] * frame->sb_sample[blk][ch][0] + - ff_synmatrix4[i][1] * frame->sb_sample[blk][ch][1] + - ff_synmatrix4[i][2] * frame->sb_sample[blk][ch][2] + - ff_synmatrix4[i][3] * frame->sb_sample[blk][ch][3] ) >> 15; + (int)( (unsigned)ff_synmatrix4[i][0] * frame->sb_sample[blk][ch][0] + + (unsigned)ff_synmatrix4[i][1] * frame->sb_sample[blk][ch][1] + + (unsigned)ff_synmatrix4[i][2] * frame->sb_sample[blk][ch][2] + + (unsigned)ff_synmatrix4[i][3] * frame->sb_sample[blk][ch][3] ) >> 15; } /* Compute the samples */ @@ -239,16 +238,16 @@ static inline void sbc_synthesize_four(struct sbc_decoder_state *state, /* Store in output, Q0 */ AV_WN16A(&output_frame->data[ch][blk * 8 + i * 2], av_clip_int16( - ( v[offset[i] + 0] * ff_sbc_proto_4_40m0[idx + 0] + - v[offset[k] + 1] * ff_sbc_proto_4_40m1[idx + 0] + - v[offset[i] + 2] * ff_sbc_proto_4_40m0[idx + 1] + - v[offset[k] + 3] * ff_sbc_proto_4_40m1[idx + 1] + - v[offset[i] + 4] * ff_sbc_proto_4_40m0[idx + 2] + - v[offset[k] + 5] * ff_sbc_proto_4_40m1[idx + 2] + - v[offset[i] + 6] * ff_sbc_proto_4_40m0[idx + 3] + - v[offset[k] + 7] * ff_sbc_proto_4_40m1[idx + 3] + - v[offset[i] + 8] * ff_sbc_proto_4_40m0[idx + 4] + - v[offset[k] + 9] * ff_sbc_proto_4_40m1[idx + 4] ) >> 15)); + (int)( (unsigned)v[offset[i] + 0] * ff_sbc_proto_4_40m0[idx + 0] + + (unsigned)v[offset[k] + 1] * ff_sbc_proto_4_40m1[idx + 0] + + (unsigned)v[offset[i] + 2] * ff_sbc_proto_4_40m0[idx + 1] + + (unsigned)v[offset[k] + 3] * ff_sbc_proto_4_40m1[idx + 1] + + (unsigned)v[offset[i] + 4] * ff_sbc_proto_4_40m0[idx + 2] + + (unsigned)v[offset[k] + 5] * ff_sbc_proto_4_40m1[idx + 2] + + (unsigned)v[offset[i] + 6] * ff_sbc_proto_4_40m0[idx + 3] + + (unsigned)v[offset[k] + 7] * ff_sbc_proto_4_40m1[idx + 3] + + (unsigned)v[offset[i] + 8] * ff_sbc_proto_4_40m0[idx + 4] + + (unsigned)v[offset[k] + 9] * ff_sbc_proto_4_40m1[idx + 4] ) >> 15)); } } @@ -270,14 +269,14 @@ static inline void sbc_synthesize_eight(struct sbc_decoder_state *state, /* Distribute the new matrix value to the shifted position */ v[offset[i]] = - ( ff_synmatrix8[i][0] * frame->sb_sample[blk][ch][0] + - ff_synmatrix8[i][1] * frame->sb_sample[blk][ch][1] + - ff_synmatrix8[i][2] * frame->sb_sample[blk][ch][2] + - ff_synmatrix8[i][3] * frame->sb_sample[blk][ch][3] + - ff_synmatrix8[i][4] * frame->sb_sample[blk][ch][4] + - ff_synmatrix8[i][5] * frame->sb_sample[blk][ch][5] + - ff_synmatrix8[i][6] * frame->sb_sample[blk][ch][6] + - ff_synmatrix8[i][7] * frame->sb_sample[blk][ch][7] ) >> 15; + (int)( (unsigned)ff_synmatrix8[i][0] * frame->sb_sample[blk][ch][0] + + (unsigned)ff_synmatrix8[i][1] * frame->sb_sample[blk][ch][1] + + (unsigned)ff_synmatrix8[i][2] * frame->sb_sample[blk][ch][2] + + (unsigned)ff_synmatrix8[i][3] * frame->sb_sample[blk][ch][3] + + (unsigned)ff_synmatrix8[i][4] * frame->sb_sample[blk][ch][4] + + (unsigned)ff_synmatrix8[i][5] * frame->sb_sample[blk][ch][5] + + (unsigned)ff_synmatrix8[i][6] * frame->sb_sample[blk][ch][6] + + (unsigned)ff_synmatrix8[i][7] * frame->sb_sample[blk][ch][7] ) >> 15; } /* Compute the samples */ @@ -286,16 +285,16 @@ static inline void sbc_synthesize_eight(struct sbc_decoder_state *state, /* Store in output, Q0 */ AV_WN16A(&output_frame->data[ch][blk * 16 + i * 2], av_clip_int16( - ( v[offset[i] + 0] * ff_sbc_proto_8_80m0[idx + 0] + - v[offset[k] + 1] * ff_sbc_proto_8_80m1[idx + 0] + - v[offset[i] + 2] * ff_sbc_proto_8_80m0[idx + 1] + - v[offset[k] + 3] * ff_sbc_proto_8_80m1[idx + 1] + - v[offset[i] + 4] * ff_sbc_proto_8_80m0[idx + 2] + - v[offset[k] + 5] * ff_sbc_proto_8_80m1[idx + 2] + - v[offset[i] + 6] * ff_sbc_proto_8_80m0[idx + 3] + - v[offset[k] + 7] * ff_sbc_proto_8_80m1[idx + 3] + - v[offset[i] + 8] * ff_sbc_proto_8_80m0[idx + 4] + - v[offset[k] + 9] * ff_sbc_proto_8_80m1[idx + 4] ) >> 15)); + (int)( (unsigned)v[offset[i] + 0] * ff_sbc_proto_8_80m0[idx + 0] + + (unsigned)v[offset[k] + 1] * ff_sbc_proto_8_80m1[idx + 0] + + (unsigned)v[offset[i] + 2] * ff_sbc_proto_8_80m0[idx + 1] + + (unsigned)v[offset[k] + 3] * ff_sbc_proto_8_80m1[idx + 1] + + (unsigned)v[offset[i] + 4] * ff_sbc_proto_8_80m0[idx + 2] + + (unsigned)v[offset[k] + 5] * ff_sbc_proto_8_80m1[idx + 2] + + (unsigned)v[offset[i] + 6] * ff_sbc_proto_8_80m0[idx + 3] + + (unsigned)v[offset[k] + 7] * ff_sbc_proto_8_80m1[idx + 3] + + (unsigned)v[offset[i] + 8] * ff_sbc_proto_8_80m0[idx + 4] + + (unsigned)v[offset[k] + 9] * ff_sbc_proto_8_80m1[idx + 4] ) >> 15)); } } @@ -324,6 +323,8 @@ static int sbc_decode_init(AVCodecContext *avctx) SBCDecContext *sbc = avctx->priv_data; int i, ch; + avctx->sample_fmt = AV_SAMPLE_FMT_S16P; + sbc->frame.crc_ctx = av_crc_get_table(AV_CRC_8_EBU); memset(sbc->dsp.V, 0, sizeof(sbc->dsp.V)); @@ -348,8 +349,8 @@ static int sbc_decode_frame(AVCodecContext *avctx, if (frame_length <= 0) return frame_length; - frame->channels = sbc->frame.channels; - frame->format = AV_SAMPLE_FMT_S16P; + avctx->channels = sbc->frame.channels; + frame->nb_samples = sbc->frame.blocks * sbc->frame.subbands; if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) return ret; diff --git a/libavcodec/sbcenc.c b/libavcodec/sbcenc.c index e2929e22ac5..47a136e4e5f 100644 --- a/libavcodec/sbcenc.c +++ b/libavcodec/sbcenc.c @@ -30,7 +30,6 @@ * SBC encoder implementation */ -#include #include "libavutil/opt.h" #include "avcodec.h" #include "internal.h" @@ -95,7 +94,7 @@ static int sbc_analyze_audio(SBCDSPContext *s, struct sbc_frame *frame) * Returns the length of the packed frame. */ static size_t sbc_pack_frame(AVPacket *avpkt, struct sbc_frame *frame, - int joint, bool msbc) + int joint, int msbc) { PutBitContext pb; @@ -331,6 +330,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) { NULL }, }; diff --git a/libavcodec/scpr.c b/libavcodec/scpr.c index dc890a87e51..2a0ebcecfcd 100644 --- a/libavcodec/scpr.c +++ b/libavcodec/scpr.c @@ -504,7 +504,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, return ret; } - if ((ret = ff_reget_buffer(avctx, s->current_frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->current_frame, 0)) < 0) return ret; bytestream2_init(gb, avpkt->data, avpkt->size); @@ -534,6 +534,9 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, uint32_t clr, *dst = (uint32_t *)s->current_frame->data[0]; int y; + if (bytestream2_get_bytes_left(gb) < 3) + return AVERROR_INVALIDDATA; + frame->key_frame = 1; bytestream2_skip(gb, 1); if (avctx->bits_per_coded_sample == 16) { diff --git a/libavcodec/scpr3.c b/libavcodec/scpr3.c index b4d2e21a179..1ed764baa12 100644 --- a/libavcodec/scpr3.c +++ b/libavcodec/scpr3.c @@ -234,6 +234,8 @@ static int update_model6_to_7(PixelModel3 *m) } p = (e + 127) >> 7; k = ((f + e - 1) >> 7) + 1; + if (k > FF_ARRAY_ELEMS(n.dectab)) + return AVERROR_INVALIDDATA; for (i = 0; i < k - p; i++) n.dectab[p + i] = j; e += f; @@ -702,7 +704,11 @@ static int update_model3_to_7(PixelModel3 *m, uint8_t value) e = d; n.cntsum += n.cnts[e]; n.freqs1[e] = c; - for (g = n.freqs[e], q = c + 128 - 1 >> 7, f = (c + g - 1 >> 7) + 1; q < f; q++) { + g = n.freqs[e]; + f = (c + g - 1 >> 7) + 1; + if (f > FF_ARRAY_ELEMS(n.dectab)) + return AVERROR_INVALIDDATA; + for (q = c + 128 - 1 >> 7; q < f; q++) { n.dectab[q] = e; } c += g; @@ -837,6 +843,7 @@ static int decode_unit3(SCPRContext *s, PixelModel3 *m, uint32_t code, uint32_t uint16_t a = 0, b = 0; uint32_t param; int type; + int ret; type = m->type; switch (type) { @@ -859,7 +866,9 @@ static int decode_unit3(SCPRContext *s, PixelModel3 *m, uint32_t code, uint32_t break; case 3: *value = bytestream2_get_byte(&s->gb); - decode_static3(m, *value); + ret = decode_static3(m, *value); + if (ret < 0) + return AVERROR_INVALIDDATA; sync_code3(gb, rc); break; case 4: @@ -877,7 +886,9 @@ static int decode_unit3(SCPRContext *s, PixelModel3 *m, uint32_t code, uint32_t break; case 6: if (!decode_adaptive6(m, code, value, &a, &b)) { - update_model6_to_7(m); + ret = update_model6_to_7(m); + if (ret < 0) + return AVERROR_INVALIDDATA; } decode3(gb, rc, a, b); sync_code3(gb, rc); diff --git a/libavcodec/screenpresso.c b/libavcodec/screenpresso.c index fb8bfd4701b..d73c24df834 100644 --- a/libavcodec/screenpresso.c +++ b/libavcodec/screenpresso.c @@ -94,8 +94,9 @@ static void sum_delta_flipped(uint8_t *dst, int dst_linesize, { int i; for (; height > 0; height--) { + const uint8_t *src1 = &src[(height - 1) * src_linesize]; for (i = 0; i < bytewidth; i++) - dst[i] += src[(height - 1) * src_linesize + i]; + dst[i] += src1[i]; dst += dst_linesize; } } @@ -145,7 +146,7 @@ static int screenpresso_decode_frame(AVCodecContext *avctx, void *data, return AVERROR_UNKNOWN; } - ret = ff_reget_buffer(avctx, ctx->current); + ret = ff_reget_buffer(avctx, ctx->current, 0); if (ret < 0) return ret; diff --git a/libavcodec/sheervideo.c b/libavcodec/sheervideo.c index 50c3ebcee72..1a43727a305 100644 --- a/libavcodec/sheervideo.c +++ b/libavcodec/sheervideo.c @@ -2063,19 +2063,6 @@ static int decode_frame(AVCodecContext *avctx, return avpkt->size; } -#if HAVE_THREADS -static int decode_init_thread_copy(AVCodecContext *avctx) -{ - SheerVideoContext *s = avctx->priv_data; - - s->format = 0; - memset(&s->vlc[0], 0, sizeof(s->vlc[0])); - memset(&s->vlc[1], 0, sizeof(s->vlc[1])); - - return 0; -} -#endif - static av_cold int decode_end(AVCodecContext *avctx) { SheerVideoContext *s = avctx->priv_data; @@ -2092,7 +2079,6 @@ AVCodec ff_sheervideo_decoder = { .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_SHEERVIDEO, .priv_data_size = sizeof(SheerVideoContext), - .init_thread_copy = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy), .close = decode_end, .decode = decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, diff --git a/libavcodec/simple_idct_template.c b/libavcodec/simple_idct_template.c index d8fcfd7c53f..5ddd0b45a2b 100644 --- a/libavcodec/simple_idct_template.c +++ b/libavcodec/simple_idct_template.c @@ -121,7 +121,7 @@ static inline void FUNC6(idctRowCondDC)(idctin *row, int extra_shift) // TODO: Add DC-only support for int32_t input #if IN_IDCT_DEPTH == 16 #if HAVE_FAST_64BIT -#define ROW0_MASK (0xffffLL << 48 * HAVE_BIGENDIAN) +#define ROW0_MASK (0xffffULL << 48 * HAVE_BIGENDIAN) if (((AV_RN64A(row) & ~ROW0_MASK) | AV_RN64A(row+4)) == 0) { uint64_t temp; if (DC_SHIFT - extra_shift >= 0) { diff --git a/libavcodec/siren.c b/libavcodec/siren.c new file mode 100644 index 00000000000..1c17d4505db --- /dev/null +++ b/libavcodec/siren.c @@ -0,0 +1,780 @@ +/* + * Siren audio decoder + * Copyright (c) 2012 Youness Alaoui + * Copyright (c) 2018 Paul B Mahol + * Copyright (c) 2019 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/tx.h" +#include "libavutil/float_dsp.h" + +#include "avcodec.h" +#include "get_bits.h" +#include "internal.h" +#include "mathops.h" + +static const uint8_t index_table[8] = {4, 4, 3, 3, 2, 2, 1, 0}; +static const uint8_t vector_dimension[8] = { 2, 2, 2, 4, 4, 5, 5, 1 }; +static const uint8_t number_of_vectors[8] = { 10, 10, 10, 5, 5, 4, 4, 20 }; +static const uint8_t expected_bits_table[8] = { 52, 47, 43, 37, 29, 22, 16, 0 }; +static const int8_t differential_decoder_tree[27][24][2] = { + { + {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, -12}, {-11, -10}, {-8, -9}, {-7, -6}, {-13, 12}, + {-5, -4}, {0, 13}, {-3, -14}, {-2, 14}, {-1, 15}, {-15, 16}, {-16, 17}, {-17, 18}, {19, 20}, + {21, 22}, {-18, -19}, {-20, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {5, 6}, {7, 8}, {-10, -9}, {-8, -11}, {-7, -6}, {9, -5}, {10, -12}, {-4, 11}, + {-13, -3}, {12, -2}, {13, -14}, {-1, 14}, {15, -15}, {0, 16}, {-16, 17}, {-17, 18}, {-18, 19}, + {20, 21},{22, -19}, {-20, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {-12, 11}, {-11, -13}, {-10, -9}, {12, -14}, {-8, -7}, + {-15, -6}, {13, -5}, {-16, -4}, {14, -17}, {15, -3}, {16, -18}, {-2, 17}, {18, -19}, {-1, 19}, + {-20, 20}, {0, 21}, {22, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {5, 6}, {-11, -10}, {7, -12}, {8, -9}, {9, -13}, {-14, 10}, {-8, -15}, {-16, 11}, + {-7, 12}, {-17, -6}, {13, 14}, {-18, 15}, {-5, -4}, {16, 17}, {-3, -2}, {-19, 18}, {-1, 19}, + {-20, 20}, {21, 22}, {0, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {5, 6}, {-12, -11}, {-13, 7}, {8, -14}, {-10, 9}, {10, -15}, {-9, 11}, {-8, 12}, + {-16, 13}, {-7, -6}, {-17, 14}, {-5, -18}, {15, -4}, {16, -19}, {17, -3}, {-20, 18}, {-2, 19}, + {-21, 20}, {0, 21}, {22, -1}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {5, 6}, {-11, 7}, {-12, -10}, {-13, -9}, {8, 9}, {-14, -8}, {10, -15}, {-7, 11}, + {-16, 12}, {-6, -17}, {13, 14}, {-5, 15}, {-18, 16}, {-4, 17}, {-3, -19}, {18, -2}, {-20, 19}, + {-1, 20}, {0, 21}, {22, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {5, -12}, {6, -11}, {-10, -13}, {-9, 7}, {8, -14}, {9, -8}, {-15, 10}, {-7, -16}, + {11, -6}, {12, -17}, {13, -5}, {-18, 14}, {15, -4}, {-19, 16}, {17, -3}, {-20, 18}, {19, 20}, + {21, 22}, {0, -2}, {-1, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {5, -12}, {6, -13}, {-11, -10}, {7, -14}, {8, -9}, {9, -15}, {-8, 10}, {-7, -16}, + {11, 12}, {-6, -17}, {-5, 13}, {14, 15}, {-18, -4}, {-19, 16}, {-3, 17}, {18, -2}, {-20, 19}, + {20, 21}, {22, 0}, {-1, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {5, 6}, {-11, -10}, {-12, -9}, {7, 8}, {-13, -8}, {9, -14}, {-7, 10}, {-6, -15}, + {11, 12}, {-5, -16}, {13, 14}, {-17, 15}, {-4, 16}, {17, -18}, {18, -3}, {-2, 19}, {-1, 0}, + {-19, 20}, {-20, 21}, {22, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {5, 6}, {-11, 7}, {-10, -12}, {-9, 8}, {-8, -13}, {9, -7}, {10, -14}, {-6, 11}, + {-15, 12}, {-5, 13}, {-16, -4}, {14, 15}, {-17, -3}, {-18, 16}, {17, -19}, {-2, 18}, {-20, 19}, + {-1, 20}, {21, 22}, {0, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {5, -12}, {6, -11}, {7, 8}, {-10, -13}, {-9, 9}, {-8, -14}, {10, -7}, {11, -15}, + {-6, 12}, {-5, 13}, {-4, -16}, {14, 15}, {-3, -17}, {16, 17}, {-18, -2}, {18, -19}, {-1, 19}, + {-20, 20}, {-21, 21}, {22, 0}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {5, -12}, {-13, 6}, {-11, 7}, {-14, 8}, {-10, 9}, {-15, -9}, {-8, 10}, {-7, -16}, + {11, -6}, {12, -5}, {-17, 13}, {14, -18}, {15, -4}, {16, -19}, {17, -3}, {18, -2}, {19, -1}, + {-20, 20}, {21, 22}, {0, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, + { + {1, 2}, {3, 4}, {-12, 5}, {-11, -13}, {6, -14}, {-10, 7}, {8, -15}, {-9, 9}, {-16, 10}, {-8, -17}, + {11, 12}, {-7, -18}, {-6, 13}, {14, -5}, {15, -19}, {-4, 16}, {-20, 17}, {18, 19}, {20, 21}, + {22, 0}, {-1, -3}, {-2, -21}, {-22, -23}, {-32, -32} + }, +}; + +static const uint16_t decoder_tree0[360] = { + 2, 1, 4, 6, 8, 10, 12, 14, 16, 18, 33, 3, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 35, 40, + 42, 44, 46, 5, 48, 65, 50, 52, 54, 56, 58, 60, 62, 64, 37, 66, 67, 68, 97, 70, 72, 74, 7, + 76, 78, 80, 82, 84, 86, 88, 99, 90, 39, 92, 94, 96, 129, 98, 9, 100, 102, 104, 106, 108, + 110, 112, 41, 161, 69, 114, 116, 118, 131, 120, 122, 11, 124, 126, 128, 193, 130, 132, 71, + 134, 43, 136, 138, 140, 163, 101, 13, 142, 144, 146, 148, 150, 152, 154, 225, 156, 158, 195, + 160, 162, 45, 164, 15, 166, 73, 168, 170, 133, 47, 172, 257, 174, 176, 178, 75, 103, 180, 165, + 182, 17, 227, 184, 105, 49, 135, 186, 289, 188, 259, 190, 192, 194, 196, 198, 291, 77, 200, + 202, 197, 107, 204, 19, 51, 229, 206, 167, 208, 210, 212, 214, 21, 79, 81, 109, 216, 218, 220, + 222, 53, 137, 224, 199, 226, 323, 321, 169, 228, 111, 230, 232, 139, 261, 234, 83, 236, 201, + 238, 240, 293, 242, 353, 231, 141, 244, 246, 113, 23, 355, 85, 248, 55, 115, 250, 263, 252, + 254, 203, 171, 256, 258, 233, 235, 143, 357, 325, 260, 295, 262, 173, 145, 177, 87, 264, 327, + 267, 266, 268, 175, 270, 272, 117, 297, 274, 265, 147, 179, 205, 276, 207, 237, 269, 278, 57, + 59, 387, 209, 280, 282, 149, 329, 385, 284, 25, 286, 239, 119, 288, 27, 290, 292, 299, 294, 359, + 89, 296, 298, 419, 181, 300, 331, 271, 417, 211, 361, 151, 389, 241, 302, 304, 303, 306, 308, + 421, 91, 310, 312, 391, 314, 121, 316, 333, 318, 275, 213, 301, 243, 183, 335, 320, 363, 322, + 215, 324, 393, 273, 337, 153, 326, 423, 365, 328, 367, 247, 395, 185, 123, 330, 425, 245, 155, + 332, 334, 305, 397, 336, 277, 217, 338, 340, 339, 427, 342, 344, 346, 307, 399, 187, 348, 309, + 341, 350, 369, 279, 311, 429, 249, 219, 352, 354, 356, 358, 431, 373, 401, 371, 313, 281, 433, + 343, 403, 251, 283 +}; + +static const uint16_t decoder_tree1[188] = { + 2, 1, 4, 6, 8, 10, 12, 14, 16, 3, 33, 18, 20, 22, 24, 26, 35, 28, 30, 32, 34, 36, 5, 65, 38, 40, + 37, 42, 44, 46, 67, 48, 50, 52, 54, 56, 58, 60, 7, 62, 39, 97, 64, 69, 66, 99, 68, 70, 72, 74, 76, + 78, 80, 129, 41, 131, 82, 9, 71, 84, 86, 101, 88, 90, 92, 94, 96, 161, 43, 11, 73, 98, 103, 100, + 163, 102, 104, 106, 108, 133, 110, 105, 112, 75, 114, 45, 13, 116, 165, 118, 195, 135, 193, 120, 77, + 122, 47, 124, 167, 225, 126, 79, 107, 227, 128, 137, 197, 15, 130, 169, 199, 132, 109, 134, 17, 139, + 49, 136, 229, 138, 140, 81, 259, 142, 144, 171, 146, 141, 148, 111, 150, 201, 231, 152, 51, 257, 289, + 154, 19, 113, 156, 261, 158, 203, 173, 263, 143, 160, 291, 235, 83, 162, 233, 265, 164, 205, 166, 293, + 145, 168, 175, 177, 237, 115, 295, 170, 207, 172, 267, 174, 176, 297, 147, 178, 180, 269, 182, 271, + 209, 299, 239, 179, 184, 301, 241, 211, 0, 0 +}; + +static const uint16_t decoder_tree2[96] = { + 2, 1, 4, 6, 8, 10, 12, 3, 17, 14, 19, 16, 18, 20, 22, 24, 26, 5, 21, 35, 33, 28, 30, 32, 34, 36, 38, 37, + 40, 23, 51, 42, 7, 49, 44, 46, 48, 50, 39, 53, 52, 54, 56, 25, 67, 9, 58, 60, 65, 55, 41, 62, 64, 69, 66, + 11, 27, 68, 57, 83, 70, 71, 81, 43, 72, 74, 13, 76, 85, 29, 73, 78, 99, 59, 87, 101, 80, 97, 45, 82, 84, + 75, 89, 61, 86, 103, 88, 77, 90, 105, 91, 92, 107, 93, 0, 0 +}; + +static const uint16_t decoder_tree3[1040] = { + 2, 4, 6, 8, 10, 1, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 3, 36, 1025, 38, 40, 42, 44, 46, 48, 50, + 129, 17, 52, 54, 1153, 19, 56, 58, 60, 62, 64, 66, 68, 145, 70, 72, 74, 76, 78, 1169, 1027, 147, 80, 82, 1171, + 84, 86, 131, 88, 1155, 1043, 1041, 90, 92, 5, 94, 96, 98, 100, 102, 104, 21, 106, 108, 2049, 2177, 110, 112, 114, + 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 33, 144, 163, 146, 148, 150, 152, 154, 161, + 156, 35, 158, 1297, 160, 162, 273, 257, 164, 166, 149, 168, 1281, 170, 172, 2193, 174, 176, 178, 1299, 180, 1045, + 182, 184, 1173, 186, 3201, 188, 190, 192, 194, 2195, 1187, 23, 2179, 196, 7, 198, 275, 200, 2051, 202, 2065, 204, + 206, 1029, 1185, 208, 210, 1157, 37, 3073, 2067, 133, 212, 214, 2321, 216, 165, 218, 1059, 220, 1283, 222, 2305, + 224, 226, 228, 230, 259, 232, 234, 2323, 236, 1409, 1057, 1315, 238, 240, 242, 244, 246, 1425, 248, 1313, 250, 252, + 254, 256, 258, 260, 289, 262, 264, 1189, 266, 268, 179, 151, 270, 272, 274, 276, 278, 291, 280, 282, 9, 385, 284, + 286, 177, 49, 401, 1061, 288, 290, 292, 51, 294, 296, 298, 300, 302, 304, 25, 306, 2083, 39, 308, 310, 3329, 167, + 312, 314, 1175, 316, 318, 1203, 135, 320, 322, 324, 326, 328, 2211, 2307, 330, 1301, 332, 334, 1047, 336, 338, 2449, + 3217, 340, 1427, 2209, 53, 342, 2339, 3345, 344, 346, 348, 403, 181, 4097, 2197, 350, 2181, 1285, 1317, 1031, 352, + 354, 356, 3089, 358, 360, 4225, 277, 362, 364, 366, 368, 2069, 370, 3203, 293, 1201, 305, 372, 3219, 307, 2433, 374, + 376, 378, 380, 2081, 1411, 382, 384, 3075, 1443, 513, 386, 387, 388, 390, 1331, 261, 392, 394, 396, 398, 400, 1441, + 1075, 67, 1159, 402, 404, 406, 408, 410, 412, 414, 3347, 2325, 416, 65, 418, 420, 422, 424, 426, 2053, 193, 1073, 428, + 430, 432, 1537, 1329, 2337, 2213, 434, 417, 183, 41, 436, 438, 440, 442, 444, 446, 448, 450, 195, 2435, 452, 2085, 1063, + 1191, 454, 456, 458, 460, 419, 2071, 1553, 3091, 55, 137, 462, 464, 466, 468, 470, 472, 474, 476, 478, 2309, 4113, 480, + 482, 484, 486, 2451, 2465, 1205, 153, 488, 490, 492, 494, 496, 498, 500, 502, 504, 506, 508, 510, 512, 514, 516, 518, + 520, 522, 524, 1333, 526, 1555, 2467, 2227, 3205, 3331, 528, 530, 532, 534, 536, 538, 540, 542, 544, 546, 548, 529, 309, + 1303, 3473, 3457, 389, 1569, 1445, 1077, 69, 2199, 1539, 4353, 550, 552, 554, 556, 558, 560, 562, 1459, 4241, 3221, 1429, + 2341, 279, 3475, 169, 564, 545, 3105, 323, 2353, 2097, 3235, 421, 2229, 3107, 3233, 566, 568, 570, 572, 574, 576, 578, + 580, 582, 584, 586, 588, 590, 592, 594, 596, 2099, 1091, 531, 2437, 4227, 405, 197, 263, 1287, 2577, 1049, 1571, 598, 600, + 602, 604, 606, 608, 610, 612, 614, 616, 618, 620, 622, 624, 626, 628, 630, 632, 634, 636, 638, 640, 642, 644, 646, 648, 650, + 1345, 1219, 3077, 1457, 2225, 2579, 515, 2561, 2469, 433, 1221, 2183, 4243, 652, 654, 656, 658, 660, 662, 664, 666, 668, + 670, 1217, 3333, 3093, 435, 321, 4369, 1089, 2055, 4099, 3361, 1319, 547, 1161, 1177, 672, 2355, 4115, 1413, 4257, 3349, + 2453, 3109, 2357, 2215, 3363, 1079, 1207, 311, 1033, 1347, 1065, 674, 676, 678, 680, 682, 684, 686, 688, 690, 692, 694, 696, + 698, 700, 702, 704, 706, 708, 710, 712, 714, 716, 718, 720, 722, 724, 726, 728, 730, 732, 734, 736, 738, 740, 742, 744, 746, + 748, 750, 752, 754, 756, 758, 760, 762, 764, 766, 768, 770, 772, 774, 776, 778, 780, 782, 784, 786, 788, 790, 792, 794, 796, + 798, 800, 802, 804, 806, 808, 810, 812, 814, 2593, 2565, 4261, 3253, 437, 325, 3489, 2311, 4259, 1431, 2087, 2563, 295, 2343, + 449, 199, 265, 2201, 4371, 1193, 816, 533, 1557, 2581, 2241, 3365, 3491, 3603, 549, 2101, 1461, 1093, 2117, 3459, 3079, 4481, + 3095, 2327, 3461, 4129, 3249, 1447, 2471, 2231, 71, 4497, 2609, 1289, 393, 3251, 2073, 3097, 2371, 1305, 2089, 818, 820, 822, + 824, 826, 828, 830, 832, 834, 836, 838, 840, 842, 844, 846, 848, 850, 852, 854, 856, 858, 860, 862, 864, 866, 868, 870, 872, + 874, 876, 878, 880, 882, 884, 886, 888, 890, 892, 894, 896, 898, 900, 902, 904, 906, 908, 910, 912, 914, 916, 918, 920, 922, + 924, 926, 928, 930, 932, 934, 936, 938, 940, 942, 944, 946, 948, 950, 952, 954, 956, 958, 960, 962, 964, 966, 968, 970, 972, + 974, 976, 978, 980, 982, 984, 986, 988, 990, 992, 994, 996, 998, 1000, 1002, 1004, 1006, 1008, 1010, 1012, 1014, 1016, 1018, + 1020, 1022, 1024, 1026, 1028, 1030, 1032, 1034, 1036, 4161, 4273, 3507, 3493, 4517, 2497, 1573, 2597, 3621, 4531, 4627, 3523, + 3125, 4149, 4529, 3139, 4515, 451, 4277, 2113, 4163, 4499, 3381, 4405, 1473, 4373, 2485, 3509, 565, 1589, 2613, 3585, 3123, + 4403, 3141, 4147, 563, 2245, 3269, 4357, 1349, 2373, 3397, 453, 1477, 2501, 2481, 579, 1601, 3477, 4103, 3265, 2243, 1587, + 3207, 4231, 3267, 4501, 1475, 3335, 4359, 391, 1415, 2439, 3463, 4487, 519, 1543, 2567, 3591, 4609, 4289, 4611, 2499, 4119, + 4385, 4145, 4401, 3223, 4247, 3379, 577, 3393, 3351, 4375, 407, 1585, 2455, 3479, 4503, 535, 1559, 2583, 3607, 3605, 4513, + 4485, 3111, 4135, 3121, 517, 3377, 3239, 4263, 1541, 4291, 4229, 3367, 4391, 423, 2115, 4131, 3495, 551, 1575, 2599, 3635, 3395, + 2103, 3127, 4151, 3589, 4101, 1603, 3255, 4279, 3601, 1335, 2359, 3383, 439, 1463, 2487, 3511, 567, 1591, 4133, 1095, 2119, 3143, + 2369, 1223, 2247, 3271, 327, 1351, 2375, 455, 1479, 3137, 3521, 2057, 3081, 4105, 4387, 3505, 2185, 3209, 4233, 3587, 4355, 2313, + 3337, 3237, 1417, 2441, 3465, 521, 1545, 3617, 3633, 561, 4625, 4121, 2611, 2483, 2595, 3225, 4249, 281, 4245, 2329, 3353, 409, + 1433, 2457, 3481, 537, 1561, 4483, 3619, 4389, 3113, 4275, 4117, 2217, 3241, 297, 1321, 2345, 3369, 425, 1449, 2473, 57, 1081, + 2105, 3129, 185, 1209, 2233, 3257, 313, 1337, 2361, 441, 1465, 73, 1097, 201, 1225, 0, 0 +}; + +static const uint16_t decoder_tree4[416] = { + 2, 4, 6, 1, 8, 10, 12, 14, 16, 18, 20, 22, 24, 3, 129, 26, 28, 9, 33, 30, 32, + 34, 36, 11, 161, 38, 40, 42, 41, 44, 46, 131, 43, 169, 35, 48, 137, 50, 52, 54, 56, 139, + 163, 171, 58, 60, 62, 64, 5, 66, 68, 70, 257, 72, 74, 76, 13, 78, 80, 289, 82, 84, 17, + 86, 88, 65, 90, 201, 19, 92, 94, 51, 193, 96, 98, 49, 100, 73, 102, 104, 106, 45, 108, 110, + 297, 112, 114, 116, 37, 203, 118, 120, 179, 122, 177, 124, 265, 126, 75, 133, 259, 291, 147, 128, 67, + 195, 130, 141, 173, 299, 132, 145, 134, 165, 136, 138, 140, 142, 7, 144, 146, 21, 267, 148, 53, 150, + 321, 152, 154, 15, 156, 81, 158, 160, 385, 162, 417, 164, 166, 168, 83, 170, 172, 329, 174, 211, 176, + 27, 178, 180, 182, 209, 184, 186, 188, 190, 25, 192, 331, 194, 196, 105, 57, 198, 97, 200, 202, 323, + 225, 59, 149, 204, 206, 233, 307, 208, 77, 181, 210, 212, 214, 216, 218, 220, 222, 47, 224, 226, 69, + 228, 230, 197, 232, 425, 393, 205, 275, 293, 39, 234, 236, 238, 305, 135, 155, 301, 143, 240, 242, 235, + 395, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 273, 269, 185, 264, 266, 268, 270, 272, 274, 276, + 261, 153, 278, 280, 282, 187, 337, 387, 107, 284, 427, 227, 167, 419, 286, 288, 290, 292, 294, 296, 298, + 300, 302, 304, 306, 308, 310, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 332, 334, 336, 338, 115, + 99, 85, 213, 29, 113, 23, 89, 241, 61, 449, 339, 175, 340, 342, 344, 346, 348, 350, 352, 354, 356, + 358, 360, 362, 364, 366, 368, 370, 372, 374, 376, 378, 380, 382, 384, 386, 388, 390, 392, 394, 396, 398, + 400, 402, 404, 406, 408, 410, 412, 414, 389, 361, 457, 465, 429, 451, 333, 109, 277, 243, 263, 295, 199, + 283, 151, 55, 183, 229, 357, 363, 123, 491, 397, 411, 251, 313, 441, 467, 345, 433, 461, 219, 237, 365, + 435, 353, 347, 405, 409, 217, 309, 437, 369, 371, 341, 117, 245, 249, 157, 285, 403, 189, 317, 93, 221, + 315, 401, 481, 391, 489, 121, 421, 423, 71, 483, 327, 103, 231, 443, 459, 271, 399, 355, 91, 303, 431, + 79, 207, 335, 111, 239, 281, 325, 279, 453, 101, 311, 87, 215, 31, 159, 63, 191 +}; + +static const uint16_t decoder_tree5[384] = { + 2, 4, 1, 6, 8, 10, 12, 14, 16, 18, 20, 22, 3, 513, 24, 26, 28, 9, 129, 33, 30, 32, 34, 36, 38, 40, 11, 42, 641, 44, 46, 41, + 161, 48, 515, 50, 52, 131, 54, 35, 545, 137, 56, 58, 60, 521, 62, 43, 673, 64, 169, 66, 68, 523, 70, 163, 643, 139, 553, 72, 649, 74, 547, + 76, 78, 80, 681, 171, 82, 84, 555, 86, 675, 88, 651, 5, 90, 92, 1025, 94, 96, 98, 683, 13, + 100, 17, 102, 104, 106, 65, 108, 110, 257, 112, 114, 1153, 19, 116, 118, 120, 122, 124, 49, 126, 128, + 769, 289, 130, 132, 134, 73, 136, 138, 140, 142, 193, 144, 146, 148, 150, 152, 154, 517, 156, 158, 37, + 51, 160, 201, 162, 145, 164, 166, 168, 133, 170, 801, 45, 172, 174, 1057, 176, 178, 67, 180, 1027, 577, + 182, 184, 186, 188, 190, 192, 194, 196, 198, 259, 200, 202, 204, 525, 177, 265, 141, 206, 208, 210, 212, + 195, 297, 214, 75, 216, 1033, 203, 585, 1155, 1185, 267, 1161, 549, 218, 220, 657, 777, 147, 222, 224, 226, + 228, 230, 232, 234, 236, 238, 240, 587, 645, 165, 242, 244, 246, 248, 250, 771, 291, 252, 579, 1065, 1035, + 705, 531, 529, 659, 173, 254, 561, 653, 256, 713, 677, 557, 258, 260, 262, 264, 266, 268, 270, 272, 274, + 276, 278, 280, 282, 284, 286, 288, 290, 292, 294, 296, 298, 300, 707, 1059, 809, 715, 563, 179, 691, 1193, + 21, 779, 1067, 299, 1187, 302, 304, 306, 308, 310, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 332, + 334, 336, 338, 340, 342, 344, 346, 348, 350, 352, 354, 356, 358, 360, 362, 364, 366, 368, 370, 372, 374, + 376, 378, 380, 83, 69, 1281, 803, 321, 1195, 1163, 811, 1323, 689, 1321, 1099, 305, 835, 1227, 331, 843, 785, + 593, 1043, 1291, 1283, 1171, 275, 787, 1217, 833, 1075, 1313, 1219, 1203, 307, 819, 841, 595, 211, 723, 721, 817, + 1029, 329, 81, 1157, 261, 773, 1097, 1089, 1061, 1169, 1091, 1189, 293, 805, 1201, 581, 197, 709, 1289, 273, 1037, + 1315, 1041, 1165, 269, 781, 209, 1073, 1069, 323, 685, 1197, 301, 813, 77, 589, 205, 717, 1225, 533, 149, 661, + 53, 565, 181, 693, 0, 0 +}; + +static const uint16_t decoder_tree6[62] = { + 2, 1, 4, 6, 8, 10, 12, 14, 16, 3, 33, 5, 17, 9, 18, 20, 22, 24, 26, 28, 30, 32, 34, 7, 49, 13, 25, 36, 38, 11, + 21, 41, 35, 37, 19, 40, 42, 44, 46, 48, 50, 15, 52, 57, 29, 27, 23, 53, 54, 51, 39, 45, 43, 56, 58, 31, 55, 60, + 61, 47, 59, 63 +}; + +static const uint16_t *const decoder_tables[7] = { + decoder_tree0, + decoder_tree1, + decoder_tree2, + decoder_tree3, + decoder_tree4, + decoder_tree5, + decoder_tree6, +}; + +static const int decoder_tables_elements[7] = { + FF_ARRAY_ELEMS(decoder_tree0), + FF_ARRAY_ELEMS(decoder_tree1), + FF_ARRAY_ELEMS(decoder_tree2), + FF_ARRAY_ELEMS(decoder_tree3), + FF_ARRAY_ELEMS(decoder_tree4), + FF_ARRAY_ELEMS(decoder_tree5), + FF_ARRAY_ELEMS(decoder_tree6), +}; + +static const float mlt_quant[7][14] = { + { 0.0f, 0.392f, 0.761f, 1.120f, 1.477f, 1.832f, 2.183f, 2.541f, 2.893f, 3.245f, 3.598f, 3.942f, 4.288f, 4.724f }, + { 0.0f, 0.544f, 1.060f, 1.563f, 2.068f, 2.571f, 3.072f, 3.562f, 4.070f, 4.620f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.746f, 1.464f, 2.180f, 2.882f, 3.584f, 4.316f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.006f, 2.000f, 2.993f, 3.985f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.321f, 2.703f, 3.983f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.657f, 3.491f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.964f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } +}; + +static const float noise_category5[20] = { + 0.70711f, 0.6179f, 0.5005f, 0.3220f, 0.17678f, 0.17678f, 0.17678f, 0.17678f, 0.17678f, 0.17678f, 0.17678f, + 0.17678f, 0.17678f, 0.17678f, 0.17678f, 0.17678f, 0.17678f, 0.17678f, 0.17678f, 0.17678f +}; + +static const float noise_category6[20] = { + 0.70711f, 0.5686f, 0.3563f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, + 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f +}; + +#define FRAME_SIZE 320 + +typedef struct SirenContext { + GetBitContext gb; + + int rate_control_possibilities; + int esf_adjustment; + int number_of_regions; + int scale_factor; + int sample_rate_bits; + int region_size; + + unsigned dw1, dw2, dw3, dw4; + + int absolute_region_power_index[32]; + float decoder_standard_deviation[32]; + int power_categories[32]; + int category_balance[32]; + float standard_deviation[64]; + float backup_frame[FRAME_SIZE]; + + AVFloatDSPContext *fdsp; + av_tx_fn tx_fn; + AVTXContext *tx_ctx; + + DECLARE_ALIGNED(32, float, imdct_buf)[4][FRAME_SIZE]; + float *window; + float *imdct_in; + float *imdct_out; + float *imdct_prev; +} SirenContext; + +static av_cold int siren_init(AVCodecContext *avctx) +{ + const float scale = 1.0f / (22.f * 32768.f); + SirenContext *s = avctx->priv_data; + int i; + + s->imdct_in = s->imdct_buf[0]; + s->imdct_out = s->imdct_buf[1]; + s->imdct_prev = s->imdct_buf[2]; + s->window = s->imdct_buf[3]; + + avctx->channels = 1; + avctx->channel_layout = AV_CH_LAYOUT_MONO; + avctx->sample_fmt = AV_SAMPLE_FMT_FLT; + + s->rate_control_possibilities = 16; + s->esf_adjustment = 7; + s->number_of_regions = 14; + s->scale_factor = 22; + s->region_size = 20; + s->dw1 = s->dw2 = s->dw3 = s->dw4 = 1; + + for (i = 0; i < 64; i++) { + float region_power = powf(10, (i - 24) * 0.3010299957); + + s->standard_deviation[i] = sqrtf(region_power); + } + + for (i = 0; i < FRAME_SIZE; i++) { + float angle = ((i + 0.5f) * M_PI_2) / 320.f; + s->window[i] = sinf(angle); + } + + s->fdsp = avpriv_float_dsp_alloc(avctx->flags & AV_CODEC_FLAG_BITEXACT); + if (!s->fdsp) + return AVERROR(ENOMEM); + + return av_tx_init(&s->tx_ctx, &s->tx_fn, AV_TX_FLOAT_MDCT, 1, FRAME_SIZE, &scale, 0); +} + +static int decode_envelope(SirenContext *s, GetBitContext *gb, + int number_of_regions, float *decoder_standard_deviation, + int *absolute_region_power_index, int esf_adjustment) +{ + absolute_region_power_index[0] = (int)get_bits(gb, 5) - esf_adjustment; + absolute_region_power_index[0] = av_clip(absolute_region_power_index[0], -24, 39); + decoder_standard_deviation[0] = s->standard_deviation[absolute_region_power_index[0] + 24]; + + for (int i = 1; i < number_of_regions; i++) { + int index = 0; + + do { + index = differential_decoder_tree[i - 1][index][get_bits1(gb)]; + } while (index > 0); + + absolute_region_power_index[i] = av_clip(absolute_region_power_index[i - 1] - index - 12, -24, 39); + decoder_standard_deviation[i] = s->standard_deviation[absolute_region_power_index[i] + 24]; + } + + return get_bits_count(gb); +} + +static int categorize_regions(int number_of_regions, int number_of_available_bits, + int *absolute_region_power_index, int *power_categories, + int *category_balance) +{ + int region, delta, i, temp; + int expected_number_of_code_bits; + int min, max; + int offset, num_rate_control_possibilities = 16, + raw_value, raw_max_idx = 0, raw_min_idx = 0; + int max_rate_categories[28]; + int min_rate_categories[28]; + int temp_category_balances[64]; + int *min_rate_ptr = NULL; + int *max_rate_ptr = NULL; + + offset = -32; + for (delta = 32; number_of_regions > 0 && delta > 0; delta /= 2) { + expected_number_of_code_bits = 0; + for (region = 0; region < number_of_regions; region++) { + i = (delta + offset - + absolute_region_power_index[region]) >> 1; + i = av_clip_uintp2(i, 3); + power_categories[region] = i; + expected_number_of_code_bits += expected_bits_table[i]; + + } + if (expected_number_of_code_bits >= number_of_available_bits - 32) + offset += delta; + } + + expected_number_of_code_bits = 0; + for (region = 0; region < number_of_regions; region++) { + i = (offset - absolute_region_power_index[region]) >> 1; + i = av_clip_uintp2(i, 3); + max_rate_categories[region] = min_rate_categories[region] = + power_categories[region] = i; + expected_number_of_code_bits += expected_bits_table[i]; + } + + min = max = expected_number_of_code_bits; + min_rate_ptr = max_rate_ptr = + temp_category_balances + num_rate_control_possibilities; + for (i = 0; i < num_rate_control_possibilities - 1; i++) { + if (min + max > number_of_available_bits * 2) { + raw_value = -99; + for (region = number_of_regions - 1; region >= 0; region--) { + if (min_rate_categories[region] < 7) { + temp = + offset - absolute_region_power_index[region] - + 2 * min_rate_categories[region]; + if (temp > raw_value) { + raw_value = temp; + raw_min_idx = region; + } + } + } + if (raw_value == -99) + return AVERROR_INVALIDDATA; + *min_rate_ptr++ = raw_min_idx; + min += + expected_bits_table[min_rate_categories[raw_min_idx] + 1] - + expected_bits_table[min_rate_categories[raw_min_idx]]; + min_rate_categories[raw_min_idx]++; + } else { + raw_value = 99; + for (region = 0; region < number_of_regions; region++) { + if (max_rate_categories[region] > 0) { + temp = + offset - absolute_region_power_index[region] - + 2 * max_rate_categories[region]; + if (temp < raw_value) { + raw_value = temp; + raw_max_idx = region; + } + } + } + if (raw_value == 99) + return AVERROR_INVALIDDATA; + + *--max_rate_ptr = raw_max_idx; + max += expected_bits_table[max_rate_categories[raw_max_idx] - 1] - + expected_bits_table[max_rate_categories[raw_max_idx]]; + max_rate_categories[raw_max_idx]--; + } + } + + for (region = 0; region < number_of_regions; region++) + power_categories[region] = max_rate_categories[region]; + + for (i = 0; i < num_rate_control_possibilities - 1; i++) + category_balance[i] = *max_rate_ptr++; + + return 0; +} + +static int get_dw(SirenContext *s) +{ + int ret = s->dw1 + s->dw4; + + if ((ret & 0x8000) != 0) + ret++; + + s->dw1 = s->dw2; + s->dw2 = s->dw3; + s->dw3 = s->dw4; + s->dw4 = ret; + + return ret; +} + +static int decode_vector(SirenContext *s, int number_of_regions, + int number_of_available_bits, float *decoder_standard_deviation, + int *power_categories, float *coefs, int scale_factor) +{ + GetBitContext *gb = &s->gb; + float *coefs_ptr; + float decoded_value; + float noise; + const uint16_t *decoder_tree; + int region; + int category; + int i, j; + int index; + int error = 0; + int dw1; + int dw2; + + for (region = 0; region < number_of_regions; region++) { + category = power_categories[region]; + coefs_ptr = coefs + (region * s->region_size); + + if (category >= 0 && category < 7) { + decoder_tree = decoder_tables[category]; + + for (i = 0; i < number_of_vectors[category]; i++) { + index = 0; + do { + if (get_bits_left(gb) <= 0) { + error = 1; + break; + } + + if (index + show_bits1(gb) >= decoder_tables_elements[category]) { + error = 1; + break; + } + index = decoder_tree[index + get_bits1(gb)]; + } while ((index & 1) == 0); + + index >>= 1; + + if (error == 0 && get_bits_left(gb) >= 0) { + for (j = 0; j < vector_dimension[category]; j++) { + decoded_value = mlt_quant[category][index & ((1 << index_table[category]) - 1)]; + index >>= index_table[category]; + + if (decoded_value) { + if (!get_bits1(gb)) + decoded_value *= -decoder_standard_deviation[region]; + else + decoded_value *= decoder_standard_deviation[region]; + } + + *coefs_ptr++ = decoded_value * scale_factor; + } + } else { + error = 1; + break; + } + } + + if (error == 1) { + for (j = region + 1; j < number_of_regions; j++) + power_categories[j] = 7; + category = 7; + } + } + + coefs_ptr = coefs + (region * s->region_size); + + if (category == 5) { + i = 0; + for (j = 0; j < s->region_size; j++) { + if (*coefs_ptr != 0) + i++; + coefs_ptr++; + } + + noise = decoder_standard_deviation[region] * noise_category5[i]; + } else if (category == 6) { + i = 0; + for (j = 0; j < s->region_size; j++) { + if (*coefs_ptr++ != 0) + i++; + } + + noise = decoder_standard_deviation[region] * noise_category6[i]; + } else if (category == 7) { + noise = decoder_standard_deviation[region] * 0.70711f; + } else { + noise = 0; + } + + coefs_ptr = coefs + (region * s->region_size); + + if (category == 5 || category == 6 || category == 7) { + dw1 = get_dw(s); + dw2 = get_dw(s); + + for (j = 0; j < 10; j++) { + if (category == 7 || *coefs_ptr == 0) + *coefs_ptr = dw1 & 1 ? noise : -noise; + coefs_ptr++; + dw1 >>= 1; + + if (category == 7 || *coefs_ptr == 0) + *coefs_ptr = dw2 & 1 ? noise : -noise; + coefs_ptr++; + dw2 >>= 1; + } + } + } + + return error == 1 ? AVERROR_INVALIDDATA : get_bits_left(gb); +} + +static int siren_decode(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *avpkt) +{ + SirenContext *s = avctx->priv_data; + GetBitContext *gb = &s->gb; + AVFrame *frame = data; + int ret, number_of_valid_coefs = 20 * s->number_of_regions; + int frame_error = 0, rate_control = 0; + + if ((ret = init_get_bits8(gb, avpkt->data, avpkt->size)) < 0) + return ret; + + decode_envelope(s, gb, s->number_of_regions, + s->decoder_standard_deviation, + s->absolute_region_power_index, s->esf_adjustment); + + rate_control = get_bits(gb, 4); + + ret = categorize_regions(s->number_of_regions, get_bits_left(gb), + s->absolute_region_power_index, s->power_categories, + s->category_balance); + if (ret < 0) + return ret; + + for (int i = 0; i < rate_control; i++) + s->power_categories[s->category_balance[i]]++; + + ret = decode_vector(s, s->number_of_regions, get_bits_left(gb), + s->decoder_standard_deviation, s->power_categories, + s->imdct_in, s->scale_factor); + if (ret < 0) + return ret; + + if (get_bits_left(gb) > 0) { + do { + frame_error |= !get_bits1(gb); + } while (get_bits_left(gb) > 0); + } else if (get_bits_left(gb) < 0 && + rate_control + 1 < s->rate_control_possibilities) { + frame_error = 1; + } + + for (int i = 0; i < s->number_of_regions; i++) { + if (s->absolute_region_power_index[i] > 33 || + s->absolute_region_power_index[i] < -31) + frame_error = 1; + } + + if (frame_error) { + memcpy(s->imdct_in, s->backup_frame, number_of_valid_coefs * sizeof(float)); + memset(s->backup_frame, 0, number_of_valid_coefs * sizeof(float)); + } else { + memcpy(s->backup_frame, s->imdct_in, number_of_valid_coefs * sizeof(float)); + } + + frame->nb_samples = FRAME_SIZE; + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + return ret; + + for (int i = 0; i < 320; i += 2) + s->imdct_in[i] *= -1; + + s->tx_fn(s->tx_ctx, s->imdct_out, s->imdct_in, sizeof(float)); + s->fdsp->vector_fmul_window((float *)frame->data[0], + s->imdct_prev + (FRAME_SIZE >> 1), + s->imdct_out, s->window, + FRAME_SIZE >> 1); + FFSWAP(float *, s->imdct_out, s->imdct_prev); + + *got_frame = 1; + + return avpkt->size; +} + +static av_cold void siren_flush(AVCodecContext *avctx) +{ + SirenContext *s = avctx->priv_data; + + memset(s->backup_frame, 0, sizeof(s->backup_frame)); + memset(s->imdct_prev, 0, FRAME_SIZE * sizeof(*s->imdct_prev)); + memset(s->imdct_out, 0, FRAME_SIZE * sizeof(*s->imdct_out)); +} + +static av_cold int siren_close(AVCodecContext *avctx) +{ + SirenContext *s = avctx->priv_data; + + av_freep(&s->fdsp); + av_tx_uninit(&s->tx_ctx); + + return 0; +} + +AVCodec ff_siren_decoder = { + .name = "siren", + .long_name = NULL_IF_CONFIG_SMALL("Siren"), + .priv_data_size = sizeof(SirenContext), + .type = AVMEDIA_TYPE_AUDIO, + .id = AV_CODEC_ID_SIREN, + .init = siren_init, + .close = siren_close, + .decode = siren_decode, + .flush = siren_flush, + .capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | + FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/libavcodec/smacker.c b/libavcodec/smacker.c index 61e316916bf..b4c463b4b92 100644 --- a/libavcodec/smacker.c +++ b/libavcodec/smacker.c @@ -319,6 +319,7 @@ static int smacker_decode_header_tree(SmackVContext *smk, GetBitContext *gb, int static int decode_header_trees(SmackVContext *smk) { GetBitContext gb; int mmap_size, mclr_size, full_size, type_size, ret; + int skip = 0; mmap_size = AV_RL32(smk->avctx->extradata); mclr_size = AV_RL32(smk->avctx->extradata + 4); @@ -330,6 +331,7 @@ static int decode_header_trees(SmackVContext *smk) { return ret; if(!get_bits1(&gb)) { + skip ++; av_log(smk->avctx, AV_LOG_INFO, "Skipping MMAP tree\n"); smk->mmap_tbl = av_malloc(sizeof(int) * 2); if (!smk->mmap_tbl) @@ -342,6 +344,7 @@ static int decode_header_trees(SmackVContext *smk) { return ret; } if(!get_bits1(&gb)) { + skip ++; av_log(smk->avctx, AV_LOG_INFO, "Skipping MCLR tree\n"); smk->mclr_tbl = av_malloc(sizeof(int) * 2); if (!smk->mclr_tbl) @@ -354,6 +357,7 @@ static int decode_header_trees(SmackVContext *smk) { return ret; } if(!get_bits1(&gb)) { + skip ++; av_log(smk->avctx, AV_LOG_INFO, "Skipping FULL tree\n"); smk->full_tbl = av_malloc(sizeof(int) * 2); if (!smk->full_tbl) @@ -366,6 +370,7 @@ static int decode_header_trees(SmackVContext *smk) { return ret; } if(!get_bits1(&gb)) { + skip ++; av_log(smk->avctx, AV_LOG_INFO, "Skipping TYPE tree\n"); smk->type_tbl = av_malloc(sizeof(int) * 2); if (!smk->type_tbl) @@ -377,6 +382,8 @@ static int decode_header_trees(SmackVContext *smk) { if (ret < 0) return ret; } + if (skip == 4) + return AVERROR_INVALIDDATA; return 0; } @@ -391,6 +398,8 @@ static av_always_inline int smk_get_code(GetBitContext *gb, int *recode, int *la int v; while(*table & SMK_NODE) { + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; if(get_bits1(gb)) table += (*table) & (~SMK_NODE); table++; @@ -421,7 +430,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, if (avpkt->size <= 769) return AVERROR_INVALIDDATA; - if ((ret = ff_reget_buffer(avctx, smk->pic)) < 0) + if ((ret = ff_reget_buffer(avctx, smk->pic, 0)) < 0) return ret; /* make the palette available on the way out */ @@ -455,6 +464,8 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, uint16_t pix; type = smk_get_code(&gb, smk->type_tbl, smk->type_last); + if (type < 0) + return type; run = block_runs[(type >> 2) & 0x3F]; switch(type & 3){ case SMK_BLK_MONO: @@ -536,7 +547,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, while(run-- && blk < blocks){ uint32_t col; out = smk->pic->data[0] + (blk / bw) * (stride * 4) + (blk % bw) * 4; - col = mode * 0x01010101; + col = mode * 0x01010101U; for(i = 0; i < 4; i++) { *((uint32_t*)out) = col; out += stride; @@ -714,8 +725,10 @@ static int smka_decode_frame(AVCodecContext *avctx, void *data, for(i = 0; i <= stereo; i++) *samples++ = pred[i]; for(; i < unp_size / 2; i++) { - if(get_bits_left(&gb)<0) - return AVERROR_INVALIDDATA; + if (get_bits_left(&gb) < 0) { + ret = AVERROR_INVALIDDATA; + goto error; + } if(i & stereo) { if(vlc[2].table) res = get_vlc2(&gb, vlc[2].table, SMKTREE_BITS, 3); @@ -723,7 +736,8 @@ static int smka_decode_frame(AVCodecContext *avctx, void *data, res = 0; if (res < 0) { av_log(avctx, AV_LOG_ERROR, "invalid vlc\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto error; } val = h[2].values[res]; if(vlc[3].table) @@ -732,10 +746,11 @@ static int smka_decode_frame(AVCodecContext *avctx, void *data, res = 0; if (res < 0) { av_log(avctx, AV_LOG_ERROR, "invalid vlc\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto error; } val |= h[3].values[res] << 8; - pred[1] += sign_extend(val, 16); + pred[1] += (unsigned)sign_extend(val, 16); *samples++ = pred[1]; } else { if(vlc[0].table) @@ -744,7 +759,8 @@ static int smka_decode_frame(AVCodecContext *avctx, void *data, res = 0; if (res < 0) { av_log(avctx, AV_LOG_ERROR, "invalid vlc\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto error; } val = h[0].values[res]; if(vlc[1].table) @@ -753,10 +769,11 @@ static int smka_decode_frame(AVCodecContext *avctx, void *data, res = 0; if (res < 0) { av_log(avctx, AV_LOG_ERROR, "invalid vlc\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto error; } val |= h[1].values[res] << 8; - pred[0] += sign_extend(val, 16); + pred[0] += (unsigned)sign_extend(val, 16); *samples++ = pred[0]; } } @@ -766,8 +783,10 @@ static int smka_decode_frame(AVCodecContext *avctx, void *data, for(i = 0; i <= stereo; i++) *samples8++ = pred[i]; for(; i < unp_size; i++) { - if(get_bits_left(&gb)<0) - return AVERROR_INVALIDDATA; + if (get_bits_left(&gb) < 0) { + ret = AVERROR_INVALIDDATA; + goto error; + } if(i & stereo){ if(vlc[1].table) res = get_vlc2(&gb, vlc[1].table, SMKTREE_BITS, 3); @@ -775,7 +794,8 @@ static int smka_decode_frame(AVCodecContext *avctx, void *data, res = 0; if (res < 0) { av_log(avctx, AV_LOG_ERROR, "invalid vlc\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto error; } pred[1] += sign_extend(h[1].values[res], 8); *samples8++ = pred[1]; @@ -786,7 +806,8 @@ static int smka_decode_frame(AVCodecContext *avctx, void *data, res = 0; if (res < 0) { av_log(avctx, AV_LOG_ERROR, "invalid vlc\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto error; } pred[0] += sign_extend(h[0].values[res], 8); *samples8++ = pred[0]; diff --git a/libavcodec/smc.c b/libavcodec/smc.c index 3cb48347378..2beb1ce2915 100644 --- a/libavcodec/smc.c +++ b/libavcodec/smc.c @@ -445,7 +445,7 @@ static int smc_decode_frame(AVCodecContext *avctx, bytestream2_init(&s->gb, buf, buf_size); - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; if (pal && pal_size == AVPALETTE_SIZE) { diff --git a/libavcodec/snappy.c b/libavcodec/snappy.c index 7900b0f9785..f5c4c6578b0 100644 --- a/libavcodec/snappy.c +++ b/libavcodec/snappy.c @@ -39,6 +39,8 @@ static int64_t bytestream2_get_levarint(GetByteContext *gb) do { tmp = bytestream2_get_byte(gb); + if (shift > 31 || ((tmp & 127LL) << shift) > INT_MAX) + return AVERROR_INVALIDDATA; val |= (tmp & 127) << shift; shift += 7; } while (tmp & 128); diff --git a/libavcodec/snowdec.c b/libavcodec/snowdec.c index 59bd24e8811..88664dc472b 100644 --- a/libavcodec/snowdec.c +++ b/libavcodec/snowdec.c @@ -117,7 +117,7 @@ static av_always_inline void predict_slice_buffered(SnowContext *s, slice_buffer static inline void decode_subband_slice_buffered(SnowContext *s, SubBand *b, slice_buffer * sb, int start_y, int h, int save_state[1]){ const int w= b->width; int y; - const int qlog= av_clip(s->qlog + b->qlog, 0, QROOT*16); + const int qlog= av_clip(s->qlog + (int64_t)b->qlog, 0, QROOT*16); int qmul= ff_qexp[qlog&(QROOT-1)]<<(qlog>>QSHIFT); int qadd= (s->qbias*qmul)>>QBIAS_SHIFT; int new_index = 0; @@ -224,7 +224,7 @@ static int decode_q_branch(SnowContext *s, int level, int x, int y){ static void dequantize_slice_buffered(SnowContext *s, slice_buffer * sb, SubBand *b, IDWTELEM *src, int stride, int start_y, int end_y){ const int w= b->width; - const int qlog= av_clip(s->qlog + b->qlog, 0, QROOT*16); + const int qlog= av_clip(s->qlog + (int64_t)b->qlog, 0, QROOT*16); const int qmul= ff_qexp[qlog&(QROOT-1)]<<(qlog>>QSHIFT); const int qadd= (s->qbias*qmul)>>QBIAS_SHIFT; int x,y; @@ -502,7 +502,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, ); av_assert0(!s->avmv); - if (s->avctx->flags2 & AV_CODEC_FLAG2_EXPORT_MVS) { + if (s->avctx->export_side_data & AV_CODEC_EXPORT_DATA_MVS) { s->avmv = av_malloc_array(s->b_width * s->b_height, sizeof(AVMotionVector) << (s->block_max_depth*2)); } s->avmv_index = 0; diff --git a/libavcodec/snowenc.c b/libavcodec/snowenc.c index df1729a0833..b1cf1426eeb 100644 --- a/libavcodec/snowenc.c +++ b/libavcodec/snowenc.c @@ -25,6 +25,7 @@ #include "libavutil/pixdesc.h" #include "avcodec.h" #include "internal.h" +#include "packet_internal.h" #include "snow_dwt.h" #include "snow.h" @@ -81,6 +82,7 @@ FF_ENABLE_DEPRECATION_WARNINGS 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_mallocz_array((avctx->width+64), 2*16*2*sizeof(uint8_t)); @@ -312,7 +314,7 @@ static int encode_q_branch(SnowContext *s, int level, int x, int y){ if(P_LEFT[1] > (c->ymax<ymax< (c->xmax<xmax< (c->ymax<ymax<xmin<xmin<xmin * (1<xmin * (1< (c->xmax<xmax< (c->ymax<ymax<lambda = 0; }//else keep previous frame's qlog until after motion estimation +#if FF_API_CODED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + av_frame_unref(avctx->coded_frame); +FF_ENABLE_DEPRECATION_WARNINGS +#endif + if (s->current_picture->data[0]) { int w = s->avctx->width; int h = s->avctx->height; +#if FF_API_CODED_FRAME + ret = av_frame_make_writable(s->current_picture); + if (ret < 0) + return ret; +#endif + s->mpvencdsp.draw_edges(s->current_picture->data[0], s->current_picture->linesize[0], w , h , EDGE_WIDTH , EDGE_WIDTH , EDGE_TOP | EDGE_BOTTOM); @@ -1644,7 +1658,6 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, ff_snow_frame_start(s); #if FF_API_CODED_FRAME FF_DISABLE_DEPRECATION_WARNINGS - av_frame_unref(avctx->coded_frame); ret = av_frame_ref(avctx->coded_frame, s->current_picture); FF_ENABLE_DEPRECATION_WARNINGS #endif @@ -1774,7 +1787,7 @@ FF_ENABLE_DEPRECATION_WARNINGS }else{ for(y=0; yspatial_dwt_buffer[y*w + x]=s->spatial_idwt_buffer[y*w + x]<spatial_dwt_buffer[y*w + x]= s->spatial_idwt_buffer[y*w + x] * (1 << ENCODER_EXTRA_BITS); } } } diff --git a/libavcodec/sonic.c b/libavcodec/sonic.c index 34d2952e69c..b82c44344c3 100644 --- a/libavcodec/sonic.c +++ b/libavcodec/sonic.c @@ -140,10 +140,13 @@ static inline av_flatten int get_symbol(RangeCoder *c, uint8_t *state, int is_si if(get_rac(c, state+0)) return 0; else{ - int i, e, a; + 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; @@ -472,8 +475,8 @@ static int predictor_calc_error(int *k, int *state, int order, int error) for (i = order-2; i >= 0; i--, k_ptr--, state_ptr--) { int k_value = *k_ptr, state_value = *state_ptr; - x -= shift_down(k_value * state_value, LATTICE_SHIFT); - state_ptr[1] = state_value + shift_down(k_value * x, LATTICE_SHIFT); + x -= shift_down(k_value * (unsigned)state_value, LATTICE_SHIFT); + state_ptr[1] = state_value + shift_down(k_value * (unsigned)x, LATTICE_SHIFT); } #else for (i = order-2; i >= 0; i--) @@ -1042,7 +1045,7 @@ static int sonic_decode_frame(AVCodecContext *avctx, x += s->channels; } - s->int_samples[x] = predictor_calc_error(s->predictor_k, s->predictor_state[ch], s->num_taps, s->coded_samples[ch][i] * quant); + s->int_samples[x] = predictor_calc_error(s->predictor_k, s->predictor_state[ch], s->num_taps, s->coded_samples[ch][i] * (unsigned)quant); x += s->channels; } diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c index 34f0f0d5e66..655b65679ee 100644 --- a/libavcodec/srtenc.c +++ b/libavcodec/srtenc.c @@ -241,7 +241,7 @@ static int encode_frame(AVCodecContext *avctx, if (sub->rects[i]->type != SUBTITLE_ASS) { av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); - return AVERROR(ENOSYS); + return AVERROR(EINVAL); } #if FF_API_ASS_TIMING @@ -276,7 +276,7 @@ static int encode_frame(AVCodecContext *avctx, if (s->buffer.len > bufsize) { av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); - return -1; + return AVERROR_BUFFER_TOO_SMALL; } memcpy(buf, s->buffer.str, s->buffer.len); diff --git a/libavcodec/sunrast.c b/libavcodec/sunrast.c index 0af5626e351..e1ec8a08327 100644 --- a/libavcodec/sunrast.c +++ b/libavcodec/sunrast.c @@ -72,7 +72,7 @@ static int sunrast_decode_frame(AVCodecContext *avctx, void *data, if (type == RT_FORMAT_TIFF || type == RT_FORMAT_IFF) { av_log(avctx, AV_LOG_ERROR, "unsupported (compression) type\n"); - return -1; + return AVERROR_PATCHWELCOME; } switch (depth) { @@ -100,14 +100,18 @@ static int sunrast_decode_frame(AVCodecContext *avctx, void *data, if (ret < 0) return ret; + /* scanlines are aligned on 16 bit boundaries */ + len = (depth * w + 7) >> 3; + alen = len + (len & 1); + + if (buf_end - buf < maplength + (len * h) * 3 / 256) + return AVERROR_INVALIDDATA; + if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - if (buf_end - buf < maplength) - return AVERROR_INVALIDDATA; - if (depth > 8 && maplength) { av_log(avctx, AV_LOG_WARNING, "useless colormap found or file is corrupted, trying to recover\n"); @@ -132,14 +136,10 @@ static int sunrast_decode_frame(AVCodecContext *avctx, void *data, return AVERROR(ENOMEM); stride = (w + 15 >> 3) * depth; } else { - ptr = p->data[0]; - stride = p->linesize[0]; + ptr = p->data[0]; + stride = p->linesize[0]; } - /* scanlines are aligned on 16 bit boundaries */ - len = (depth * w + 7) >> 3; - alen = len + (len & 1); - if (type == RT_BYTE_ENCODED) { int value, run; uint8_t *end = ptr + h * stride; diff --git a/libavcodec/svq1.h b/libavcodec/svq1.h index 63c0479329c..0ebc73a9336 100644 --- a/libavcodec/svq1.h +++ b/libavcodec/svq1.h @@ -42,9 +42,6 @@ #define SVQ1_BLOCK_INTER_4V 2 #define SVQ1_BLOCK_INTRA 3 -uint16_t ff_svq1_packet_checksum(const uint8_t *data, - const int length, int value); - extern const int8_t *const ff_svq1_inter_codebooks[6]; extern const int8_t *const ff_svq1_intra_codebooks[6]; diff --git a/libavcodec/svq13.c b/libavcodec/svq13.c deleted file mode 100644 index b821a446d68..00000000000 --- a/libavcodec/svq13.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SVQ1/SVQ3 decoder common 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 - */ - -#include - -#include "svq1.h" - -static const uint16_t checksum_table[256] = { - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, - 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, - 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, - 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, - 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, - 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, - 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, - 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, - 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, - 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, - 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, - 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, - 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, - 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, - 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, - 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, - 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, - 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, - 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, - 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, - 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, - 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, - 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 -}; - -uint16_t ff_svq1_packet_checksum (const uint8_t *data, - const int length, int value) -{ - int i; - - for (i = 0; i < length; i++) - value = checksum_table[data[i] ^ (value >> 8)] ^ ((value & 0xFF) << 8); - - return value; -} diff --git a/libavcodec/svq1dec.c b/libavcodec/svq1dec.c index d3e60c3a4ab..25bd3d0253d 100644 --- a/libavcodec/svq1dec.c +++ b/libavcodec/svq1dec.c @@ -32,6 +32,8 @@ * http://www.pcisys.net/~melanson/codecs/ */ +#include "libavutil/crc.h" + #include "avcodec.h" #include "get_bits.h" #include "h263.h" @@ -546,9 +548,7 @@ static int svq1_decode_frame_header(AVCodecContext *avctx, AVFrame *frame) if (s->frame_code == 0x50 || s->frame_code == 0x60) { int csum = get_bits(bitbuf, 16); - csum = ff_svq1_packet_checksum(bitbuf->buffer, - bitbuf->size_in_bits >> 3, - csum); + csum = av_bswap16(av_crc(av_crc_get_table(AV_CRC_16_CCITT), av_bswap16(csum), bitbuf->buffer, bitbuf->size_in_bits >> 3)); ff_dlog(avctx, "%s checksum (%02x) for packet data\n", (csum == 0) ? "correct" : "incorrect", csum); @@ -602,6 +602,8 @@ static int svq1_decode_frame_header(AVCodecContext *avctx, AVFrame *frame) if (skip_1stop_8data_bits(bitbuf) < 0) return AVERROR_INVALIDDATA; } + if (get_bits_left(bitbuf) <= 0) + return AVERROR_INVALIDDATA; s->width = width; s->height = height; diff --git a/libavcodec/svq1enc.c b/libavcodec/svq1enc.c index 80a8af1ef79..cb215c250c4 100644 --- a/libavcodec/svq1enc.c +++ b/libavcodec/svq1enc.c @@ -33,6 +33,7 @@ #include "h263.h" #include "internal.h" #include "mpegutils.h" +#include "packet_internal.h" #include "svq1.h" #include "svq1enc.h" #include "svq1enc_cb.h" @@ -345,7 +346,7 @@ static int svq1_encode_plane(SVQ1EncContext *s, int plane, s->m.first_slice_line = 0; } - ff_fix_long_p_mvs(&s->m); + ff_fix_long_p_mvs(&s->m, CANDIDATE_MB_TYPE_INTRA); ff_fix_long_mvs(&s->m, NULL, 0, s->m.p_mv_table, s->m.f_code, CANDIDATE_MB_TYPE_INTER, 0); } diff --git a/libavcodec/svq3.c b/libavcodec/svq3.c index 9cea9ac840f..c8db08a32f0 100644 --- a/libavcodec/svq3.c +++ b/libavcodec/svq3.c @@ -43,6 +43,8 @@ #include #include "libavutil/attributes.h" +#include "libavutil/crc.h" + #include "internal.h" #include "avcodec.h" #include "mpegutils.h" @@ -1293,7 +1295,8 @@ static av_cold int svq3_decode_init(AVCodecContext *avctx) ret = -1; goto fail; } - s->watermark_key = ff_svq1_packet_checksum(buf, buf_len, 0); + s->watermark_key = av_bswap16(av_crc(av_crc_get_table(AV_CRC_16_CCITT), 0, buf, buf_len)); + s->watermark_key = s->watermark_key << 16 | s->watermark_key; av_log(avctx, AV_LOG_DEBUG, "watermark key %#"PRIx32"\n", s->watermark_key); diff --git a/libavcodec/takdec.c b/libavcodec/takdec.c index 0439a3ac9b9..9fa1cb1f7fc 100644 --- a/libavcodec/takdec.c +++ b/libavcodec/takdec.c @@ -176,8 +176,8 @@ static void set_sample_rate_params(AVCodecContext *avctx) } else { shift = 0; } - s->uval = FFALIGN(avctx->sample_rate + 511 >> 9, 4) << shift; - s->subframe_scale = FFALIGN(avctx->sample_rate + 511 >> 9, 4) << 1; + s->uval = FFALIGN(avctx->sample_rate + 511LL >> 9, 4) << shift; + s->subframe_scale = FFALIGN(avctx->sample_rate + 511LL >> 9, 4) << 1; } static av_cold int tak_decode_init(AVCodecContext *avctx) @@ -653,7 +653,7 @@ static int decorrelate(TAKDecContext *s, int c1, int c2, int length) s->residues[i ] * s->filter[0]; } - v = av_clip_intp2(v >> 10, 13) * (1 << dshift) - *p1; + v = av_clip_intp2(v >> 10, 13) * (1U << dshift) - *p1; *p1++ = v; } @@ -915,13 +915,6 @@ static int tak_decode_frame(AVCodecContext *avctx, void *data, } #if HAVE_THREADS -static int init_thread_copy(AVCodecContext *avctx) -{ - TAKDecContext *s = avctx->priv_data; - s->avctx = avctx; - return 0; -} - static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src) { @@ -953,7 +946,6 @@ AVCodec ff_tak_decoder = { .init = tak_decode_init, .close = tak_decode_close, .decode = tak_decode_frame, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(init_thread_copy), .update_thread_context = ONLY_IF_THREADS_ENABLED(update_thread_context), .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_U8P, diff --git a/libavcodec/targa.c b/libavcodec/targa.c index 93e0ef7905d..a61fef1d7ba 100644 --- a/libavcodec/targa.c +++ b/libavcodec/targa.c @@ -132,12 +132,6 @@ static int decode_frame(AVCodecContext *avctx, h = bytestream2_get_le16(&s->gb); bpp = bytestream2_get_byte(&s->gb); - if (bytestream2_get_bytes_left(&s->gb) <= idlen) { - av_log(avctx, AV_LOG_ERROR, - "Not enough data to read header\n"); - return AVERROR_INVALIDDATA; - } - flags = bytestream2_get_byte(&s->gb); if (!pal && (first_clr || colors || csize)) { @@ -146,6 +140,12 @@ static int decode_frame(AVCodecContext *avctx, first_clr = colors = csize = 0; } + if (bytestream2_get_bytes_left(&s->gb) < idlen + 2*colors) { + av_log(avctx, AV_LOG_ERROR, + "Not enough data to read header\n"); + return AVERROR_INVALIDDATA; + } + // skip identifier if any bytestream2_skip(&s->gb, idlen); diff --git a/libavcodec/tdsc.c b/libavcodec/tdsc.c index 4182404cf01..eaea41c1f5c 100644 --- a/libavcodec/tdsc.c +++ b/libavcodec/tdsc.c @@ -187,7 +187,7 @@ static void tdsc_paint_cursor(AVCodecContext *avctx, uint8_t *dst, int stride) static int tdsc_load_cursor(AVCodecContext *avctx) { TDSCContext *ctx = avctx->priv_data; - int i, j, k, ret, bits, cursor_fmt; + int i, j, k, ret, cursor_fmt; uint8_t *dst; ctx->cursor_hot_x = bytestream2_get_le16(&ctx->gbc); @@ -231,7 +231,7 @@ static int tdsc_load_cursor(AVCodecContext *avctx) case CUR_FMT_MONO: for (j = 0; j < ctx->cursor_h; j++) { for (i = 0; i < ctx->cursor_w; i += 32) { - bits = bytestream2_get_be32(&ctx->gbc); + uint32_t bits = bytestream2_get_be32(&ctx->gbc); for (k = 0; k < 32; k++) { dst[0] = !!(bits & 0x80000000); dst += 4; @@ -244,7 +244,7 @@ static int tdsc_load_cursor(AVCodecContext *avctx) dst = ctx->cursor; for (j = 0; j < ctx->cursor_h; j++) { for (i = 0; i < ctx->cursor_w; i += 32) { - bits = bytestream2_get_be32(&ctx->gbc); + uint32_t bits = bytestream2_get_be32(&ctx->gbc); for (k = 0; k < 32; k++) { int mask_bit = !!(bits & 0x80000000); switch (dst[0] * 2 + mask_bit) { @@ -484,7 +484,7 @@ static int tdsc_parse_tdsf(AVCodecContext *avctx, int number_tiles) /* Allocate the reference frame if not already done or on size change */ if (init_refframe) { - ret = av_frame_get_buffer(ctx->refframe, 32); + ret = av_frame_get_buffer(ctx->refframe, 0); if (ret < 0) return ret; } diff --git a/libavcodec/tests/.gitignore b/libavcodec/tests/.gitignore new file mode 100644 index 00000000000..a01a700e2da --- /dev/null +++ b/libavcodec/tests/.gitignore @@ -0,0 +1,24 @@ +/avfft +/avpacket +/cabac +/celp_math +/codec_desc +/dct +/fft +/fft-fixed +/fft-fixed32 +/golomb +/h264_levels +/h265_levels +/htmlsubtitles +/iirfilter +/imgconvert +/jpeg2000dwt +/mathops +/mjpegenc_huffman +/motion +/mpeg12framerate +/options +/rangecoder +/snowenc +/utils diff --git a/libavcodec/tests/utils.c b/libavcodec/tests/utils.c index f6ba7fe66e3..9232647ff0d 100644 --- a/libavcodec/tests/utils.c +++ b/libavcodec/tests/utils.c @@ -19,10 +19,11 @@ #include "libavcodec/avcodec.h" int main(void){ - AVCodec *codec = NULL; + void *iter = NULL; + const AVCodec *codec = NULL; int ret = 0; - while (codec = av_codec_next(codec)) { + while (codec = av_codec_iterate(&iter)) { if (av_codec_is_encoder(codec)) { if (codec->type == AVMEDIA_TYPE_AUDIO) { if (!codec->sample_fmts) { diff --git a/libavcodec/tiertexseqv.c b/libavcodec/tiertexseqv.c index af39f74d7dd..91c8314379c 100644 --- a/libavcodec/tiertexseqv.c +++ b/libavcodec/tiertexseqv.c @@ -239,7 +239,7 @@ static int seqvideo_decode_frame(AVCodecContext *avctx, SeqVideoContext *seq = avctx->priv_data; - if ((ret = ff_reget_buffer(avctx, seq->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, seq->frame, 0)) < 0) return ret; if (seqvideo_decode(seq, buf, buf_size)) diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c index 1f1a1a36985..18b327e800f 100644 --- a/libavcodec/tiff.c +++ b/libavcodec/tiff.c @@ -35,6 +35,7 @@ #include "libavutil/attributes.h" #include "libavutil/avstring.h" +#include "libavutil/error.h" #include "libavutil/intreadwrite.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" @@ -46,6 +47,7 @@ #include "mathops.h" #include "tiff.h" #include "tiff_data.h" +#include "mjpegdec.h" #include "thread.h" #include "get_bits.h" @@ -54,6 +56,10 @@ typedef struct TiffContext { AVCodecContext *avctx; GetByteContext gb; + /* JPEG decoding for DNG */ + AVCodecContext *avctx_mjpeg; // wrapper context for MJPEG + AVFrame *jpgframe; // decoded JPEG tile + int get_subimage; uint16_t get_page; int get_thumbnail; @@ -76,7 +82,9 @@ typedef struct TiffContext { int is_bayer; uint8_t pattern[4]; + unsigned black_level; unsigned white_level; + uint16_t dng_lut[65536]; uint32_t sub_ifd; uint16_t cur_page; @@ -86,6 +94,14 @@ typedef struct TiffContext { int stripsizesoff, stripsize, stripoff, strippos; LZWState *lzw; + /* Tile support */ + int is_tiled; + int tile_byte_counts_offset, tile_offsets_offset; + int tile_width, tile_length; + int tile_count; + + int is_jpeg; + uint8_t *deinvert_buf; int deinvert_buf_size; uint8_t *yuv_line; @@ -257,6 +273,10 @@ static int add_metadata(int count, int type, }; } +static void av_always_inline dng_blit(TiffContext *s, uint8_t *dst, int dst_stride, + const uint8_t *src, int src_stride, int width, int height, + int is_single_comp, int is_u16); + static void av_always_inline horizontal_fill(TiffContext *s, unsigned int bpp, uint8_t* dst, int usePtr, const uint8_t *src, @@ -289,14 +309,19 @@ static void av_always_inline horizontal_fill(TiffContext *s, dst[(width+offset)*2+0] = (usePtr ? src[width] : c) >> 4; } break; - case 12: { - uint16_t *dst16 = (uint16_t *)dst; - GetBitContext gb; - init_get_bits8(&gb, src, width); - for (int i = 0; i < s->width; i++) { - dst16[i] = get_bits(&gb, 12) << 4; - } - } + case 10: + case 12: + case 14: { + uint16_t *dst16 = (uint16_t *)dst; + int is_dng = (s->tiff_type == TIFF_TYPE_DNG || s->tiff_type == TIFF_TYPE_CINEMADNG); + uint8_t shift = is_dng ? 0 : 16 - bpp; + GetBitContext gb; + + init_get_bits8(&gb, src, width); + for (int i = 0; i < s->width; i++) { + dst16[i] = get_bits(&gb, bpp) << shift; + } + } break; default: if (usePtr) { @@ -368,7 +393,7 @@ static int tiff_uncompress(uint8_t *dst, unsigned long *len, const uint8_t *src, z_stream zstream = { 0 }; int zret; - zstream.next_in = (uint8_t *)src; + zstream.next_in = src; zstream.avail_in = size; zstream.next_out = dst; zstream.avail_out = *len; @@ -526,6 +551,8 @@ static int tiff_unpack_fax(TiffContext *s, uint8_t *dst, int stride, return ret; } +static int dng_decode_strip(AVCodecContext *avctx, AVFrame *frame); + static int tiff_unpack_strip(TiffContext *s, AVFrame *p, uint8_t *dst, int stride, const uint8_t *src, int size, int strip_start, int lines) { @@ -537,6 +564,7 @@ static int tiff_unpack_strip(TiffContext *s, AVFrame *p, uint8_t *dst, int strid int is_yuv = !(desc->flags & AV_PIX_FMT_FLAG_RGB) && (desc->flags & AV_PIX_FMT_FLAG_PLANAR) && desc->nb_components >= 3; + int is_dng; if (s->planar) width /= s->bppcount; @@ -561,7 +589,7 @@ static int tiff_unpack_strip(TiffContext *s, AVFrame *p, uint8_t *dst, int strid av_assert0(s->bpp == 24); } if (s->is_bayer) { - width = (s->bpp * s->width + 7) >> 3; + av_assert0(width == (s->bpp * s->width + 7) >> 3); } if (p->format == AV_PIX_FMT_GRAY12) { av_fast_padded_malloc(&s->yuv_line, &s->yuv_line_size, width); @@ -638,6 +666,22 @@ static int tiff_unpack_strip(TiffContext *s, AVFrame *p, uint8_t *dst, int strid bytestream2_init(&s->gb, src, size); bytestream2_init_writer(&pb, dst, is_yuv ? s->yuv_line_size : (stride * lines)); + is_dng = (s->tiff_type == TIFF_TYPE_DNG || s->tiff_type == TIFF_TYPE_CINEMADNG); + + /* Decode JPEG-encoded DNGs with strips */ + if (s->compr == TIFF_NEWJPEG && is_dng) { + if (s->strips > 1) { + av_log(s->avctx, AV_LOG_ERROR, "More than one DNG JPEG strips unsupported\n"); + return AVERROR_PATCHWELCOME; + } + if ((ret = dng_decode_strip(s->avctx, p)) < 0) + return ret; + return 0; + } + + if (is_dng && stride == 0) + return AVERROR_INVALIDDATA; + for (line = 0; line < lines; line++) { if (src - ssrc > size) { av_log(s->avctx, AV_LOG_ERROR, "Source data overread\n"); @@ -660,6 +704,28 @@ static int tiff_unpack_strip(TiffContext *s, AVFrame *p, uint8_t *dst, int strid for (i = 0; i < width; i++) dst[i] = ff_reverse[src[i]]; } + + /* Color processing for DNG images with uncompressed strips (non-tiled) */ + if (is_dng) { + int is_u16, pixel_size_bytes, pixel_size_bits, elements; + + is_u16 = (s->bpp > 8); + pixel_size_bits = (is_u16 ? 16 : 8); + pixel_size_bytes = (is_u16 ? sizeof(uint16_t) : sizeof(uint8_t)); + + elements = width / pixel_size_bytes * pixel_size_bits / s->bpp * s->bppcount; // need to account for [1, 16] bpp + av_assert0 (elements * pixel_size_bytes <= FFABS(stride)); + dng_blit(s, + dst, + 0, // no stride, only 1 line + dst, + 0, // no stride, only 1 line + elements, + 1, + 0, // single-component variation is only preset in JPEG-encoded DNGs + is_u16); + } + src += width; break; case TIFF_PACKBITS: @@ -712,6 +778,274 @@ static int tiff_unpack_strip(TiffContext *s, AVFrame *p, uint8_t *dst, int strid return 0; } +/** + * Map stored raw sensor values into linear reference values (see: DNG Specification - Chapter 5) + */ +static uint16_t av_always_inline dng_process_color16(uint16_t value, + const uint16_t *lut, + uint16_t black_level, + float scale_factor) { + float value_norm; + + // Lookup table lookup + if (lut) + value = lut[value]; + + // Black level subtraction + value = av_clip_uint16_c((unsigned)value - black_level); + + // Color scaling + value_norm = (float)value * scale_factor; + + value = av_clip_uint16_c(value_norm * 65535); + + return value; +} + +static uint16_t av_always_inline dng_process_color8(uint16_t value, + const uint16_t *lut, + uint16_t black_level, + float scale_factor) { + return dng_process_color16(value, lut, black_level, scale_factor) >> 8; +} + +static void dng_blit(TiffContext *s, uint8_t *dst, int dst_stride, + const uint8_t *src, int src_stride, + int width, int height, int is_single_comp, int is_u16) +{ + int line, col; + float scale_factor; + + scale_factor = 1.0f / (s->white_level - s->black_level); + + if (is_single_comp) { + if (!is_u16) + return; /* <= 8bpp unsupported */ + + /* Image is double the width and half the height we need, each row comprises 2 rows of the output + (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; + + /* Blit first half of input row row to initial row of output */ + for (col = 0; col < width; col++) + *dst_u16++ = dng_process_color16(*src_u16++, s->dng_lut, s->black_level, scale_factor); + + /* Advance the destination pointer by a row (source pointer remains in the same place) */ + dst += dst_stride * sizeof(uint16_t); + dst_u16 = (uint16_t *)dst; + + /* Blit second half of input row row to next row of output */ + for (col = 0; col < width; col++) + *dst_u16++ = dng_process_color16(*src_u16++, s->dng_lut, s->black_level, scale_factor); + + dst += dst_stride * sizeof(uint16_t); + src += src_stride * sizeof(uint16_t); + } + } else { + /* Input and output image are the same size and the MJpeg decoder has done per-component + deinterleaving, so blitting here is straightforward. */ + if (is_u16) { + for (line = 0; line < height; line++) { + uint16_t *dst_u16 = (uint16_t *)dst; + uint16_t *src_u16 = (uint16_t *)src; + + for (col = 0; col < width; col++) + *dst_u16++ = dng_process_color16(*src_u16++, s->dng_lut, s->black_level, scale_factor); + + dst += dst_stride * sizeof(uint16_t); + src += src_stride * sizeof(uint16_t); + } + } else { + for (line = 0; line < height; line++) { + uint8_t *dst_u8 = dst; + const uint8_t *src_u8 = src; + + for (col = 0; col < width; col++) + *dst_u8++ = dng_process_color8(*src_u8++, s->dng_lut, s->black_level, scale_factor); + + dst += dst_stride; + src += src_stride; + } + } + } +} + +static int dng_decode_jpeg(AVCodecContext *avctx, AVFrame *frame, + int tile_byte_count, int dst_x, int dst_y, int w, int h) +{ + TiffContext *s = avctx->priv_data; + AVPacket jpkt; + uint8_t *dst_data, *src_data; + uint32_t dst_offset; /* offset from dst buffer in pixels */ + int is_single_comp, is_u16, pixel_size; + int ret; + + if (tile_byte_count < 0 || tile_byte_count > bytestream2_get_bytes_left(&s->gb)) + return AVERROR_INVALIDDATA; + + /* Prepare a packet and send to the MJPEG decoder */ + av_init_packet(&jpkt); + jpkt.data = (uint8_t*)s->gb.buffer; + jpkt.size = tile_byte_count; + + if (s->is_bayer) { + MJpegDecodeContext *mjpegdecctx = s->avctx_mjpeg->priv_data; + /* We have to set this information here, there is no way to know if a given JPEG is a DNG-embedded + image or not from its own data (and we need that information when decoding it). */ + mjpegdecctx->bayer = 1; + } + + ret = avcodec_send_packet(s->avctx_mjpeg, &jpkt); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error submitting a packet for decoding\n"); + return ret; + } + + ret = avcodec_receive_frame(s->avctx_mjpeg, s->jpgframe); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "JPEG decoding error: %s.\n", av_err2str(ret)); + + /* Normally skip, error if explode */ + if (avctx->err_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + else + return 0; + } + + is_u16 = (s->bpp > 8); + + /* Copy the outputted tile's pixels from 'jpgframe' to 'frame' (final buffer) */ + + /* See dng_blit for explanation */ + if (s->avctx_mjpeg->width == w * 2 && + s->avctx_mjpeg->height == h / 2 && + s->avctx_mjpeg->pix_fmt == AV_PIX_FMT_GRAY16LE) { + is_single_comp = 1; + } else if (s->avctx_mjpeg->width == w && + s->avctx_mjpeg->height == h && + s->avctx_mjpeg->pix_fmt == (is_u16 ? AV_PIX_FMT_GRAY16 : AV_PIX_FMT_GRAY8) + ) { + is_single_comp = 0; + } else + return AVERROR_INVALIDDATA; + + pixel_size = (is_u16 ? sizeof(uint16_t) : sizeof(uint8_t)); + + if (is_single_comp && !is_u16) { + av_log(s->avctx, AV_LOG_ERROR, "DNGs with bpp <= 8 and 1 component are unsupported\n"); + av_frame_unref(s->jpgframe); + return AVERROR_PATCHWELCOME; + } + + dst_offset = dst_x + frame->linesize[0] * dst_y / pixel_size; + dst_data = frame->data[0] + dst_offset * pixel_size; + src_data = s->jpgframe->data[0]; + + dng_blit(s, + dst_data, + frame->linesize[0] / pixel_size, + src_data, + s->jpgframe->linesize[0] / pixel_size, + w, + h, + is_single_comp, + is_u16); + + av_frame_unref(s->jpgframe); + + return 0; +} + +static int dng_decode_tiles(AVCodecContext *avctx, AVFrame *frame, AVPacket *avpkt) +{ + TiffContext *s = avctx->priv_data; + int tile_idx; + int tile_offset_offset, tile_offset; + int tile_byte_count_offset, tile_byte_count; + int tile_count_x, tile_count_y; + int tile_width, tile_length; + int has_width_leftover, has_height_leftover; + int tile_x = 0, tile_y = 0; + int pos_x = 0, pos_y = 0; + int ret; + + s->jpgframe->width = s->tile_width; + s->jpgframe->height = s->tile_length; + + s->avctx_mjpeg->width = s->tile_width; + s->avctx_mjpeg->height = s->tile_length; + + has_width_leftover = (s->width % s->tile_width != 0); + has_height_leftover = (s->height % s->tile_length != 0); + + /* Calculate tile counts (round up) */ + tile_count_x = (s->width + s->tile_width - 1) / s->tile_width; + tile_count_y = (s->height + s->tile_length - 1) / s->tile_length; + + /* Iterate over the number of tiles */ + for (tile_idx = 0; tile_idx < s->tile_count; tile_idx++) { + tile_x = tile_idx % tile_count_x; + tile_y = tile_idx / tile_count_x; + + if (has_width_leftover && tile_x == tile_count_x - 1) // If on the right-most tile + tile_width = s->width % s->tile_width; + else + tile_width = s->tile_width; + + if (has_height_leftover && tile_y == tile_count_y - 1) // If on the bottom-most tile + tile_length = s->height % s->tile_length; + else + tile_length = s->tile_length; + + /* Read tile offset */ + tile_offset_offset = s->tile_offsets_offset + tile_idx * sizeof(int); + bytestream2_seek(&s->gb, tile_offset_offset, SEEK_SET); + tile_offset = ff_tget_long(&s->gb, s->le); + + /* Read tile byte size */ + tile_byte_count_offset = s->tile_byte_counts_offset + tile_idx * sizeof(int); + bytestream2_seek(&s->gb, tile_byte_count_offset, SEEK_SET); + tile_byte_count = ff_tget_long(&s->gb, s->le); + + /* Seek to tile data */ + bytestream2_seek(&s->gb, tile_offset, SEEK_SET); + + /* Decode JPEG tile and copy it in the reference frame */ + ret = dng_decode_jpeg(avctx, frame, tile_byte_count, pos_x, pos_y, tile_width, tile_length); + + if (ret < 0) + return ret; + + /* Advance current positions */ + pos_x += tile_width; + if (tile_x == tile_count_x - 1) { // If on the right edge + pos_x = 0; + pos_y += tile_length; + } + } + + /* Frame is ready to be output */ + frame->pict_type = AV_PICTURE_TYPE_I; + frame->key_frame = 1; + + return avpkt->size; +} + +static int dng_decode_strip(AVCodecContext *avctx, AVFrame *frame) +{ + TiffContext *s = avctx->priv_data; + + s->jpgframe->width = s->width; + s->jpgframe->height = s->height; + + s->avctx_mjpeg->width = s->width; + s->avctx_mjpeg->height = s->height; + + return dng_decode_jpeg(avctx, frame, s->stripsize, 0, 0, s->width, s->height); +} + static int init_image(TiffContext *s, ThreadFrame *frame) { int ret; @@ -764,39 +1098,22 @@ static int init_image(TiffContext *s, ThreadFrame *frame) return AVERROR_PATCHWELCOME; } break; + case 10101: case 10121: - switch (AV_RL32(s->pattern)) { - case 0x02010100: - s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_RGGB16LE : AV_PIX_FMT_BAYER_RGGB16BE; - break; - case 0x00010102: - s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_BGGR16LE : AV_PIX_FMT_BAYER_BGGR16BE; - break; - case 0x01000201: - s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_GBRG16LE : AV_PIX_FMT_BAYER_GBRG16BE; - break; - case 0x01020001: - s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_GRBG16LE : AV_PIX_FMT_BAYER_GRBG16BE; - break; - default: - av_log(s->avctx, AV_LOG_ERROR, "Unsupported Bayer pattern: 0x%X\n", - AV_RL32(s->pattern)); - return AVERROR_PATCHWELCOME; - } - break; + case 10141: case 10161: switch (AV_RL32(s->pattern)) { case 0x02010100: - s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_RGGB16LE : AV_PIX_FMT_BAYER_RGGB16BE; + s->avctx->pix_fmt = AV_PIX_FMT_BAYER_RGGB16; break; case 0x00010102: - s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_BGGR16LE : AV_PIX_FMT_BAYER_BGGR16BE; + s->avctx->pix_fmt = AV_PIX_FMT_BAYER_BGGR16; break; case 0x01000201: - s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_GBRG16LE : AV_PIX_FMT_BAYER_GBRG16BE; + s->avctx->pix_fmt = AV_PIX_FMT_BAYER_GBRG16; break; case 0x01020001: - s->avctx->pix_fmt = s->le ? AV_PIX_FMT_BAYER_GRBG16LE : AV_PIX_FMT_BAYER_GRBG16BE; + s->avctx->pix_fmt = AV_PIX_FMT_BAYER_GRBG16; break; default: av_log(s->avctx, AV_LOG_ERROR, "Unsupported Bayer pattern: 0x%X\n", @@ -923,7 +1240,9 @@ static void set_sar(TiffContext *s, unsigned tag, unsigned num, unsigned den) static int tiff_decode_tag(TiffContext *s, AVFrame *frame) { - unsigned tag, type, count, off, value = 0, value2 = 0; + AVFrameSideData *sd; + GetByteContext gb_temp; + unsigned tag, type, count, off, value = 0, value2 = 1; // value2 is a denominator so init. to 1 int i, start; int pos; int ret; @@ -945,6 +1264,11 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) case TIFF_RATIONAL: value = ff_tget(&s->gb, TIFF_LONG, s->le); value2 = ff_tget(&s->gb, TIFF_LONG, s->le); + if (!value2) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid denominator in rational\n"); + return AVERROR_INVALIDDATA; + } + break; case TIFF_STRING: if (count <= 4) { @@ -958,6 +1282,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) switch (tag) { case TIFF_SUBFILE: s->is_thumbnail = (value != 0); + break; case TIFF_WIDTH: s->width = value; break; @@ -1029,8 +1354,8 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) #endif case TIFF_JPEG: case TIFF_NEWJPEG: - avpriv_report_missing_feature(s->avctx, "JPEG compression"); - return AVERROR_PATCHWELCOME; + s->is_jpeg = 1; + break; case TIFF_LZMA: #if CONFIG_LZMA break; @@ -1085,12 +1410,19 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) case TIFF_YRES: set_sar(s, tag, value, value2); break; + case TIFF_TILE_OFFSETS: + s->tile_offsets_offset = off; + s->tile_count = count; + s->is_tiled = 1; + break; case TIFF_TILE_BYTE_COUNTS: + s->tile_byte_counts_offset = off; + break; case TIFF_TILE_LENGTH: - case TIFF_TILE_OFFSETS: + s->tile_length = value; + break; case TIFF_TILE_WIDTH: - av_log(s->avctx, AV_LOG_ERROR, "Tiled images are not supported\n"); - return AVERROR_PATCHWELCOME; + s->tile_width = value; break; case TIFF_PREDICTOR: s->predictor = value; @@ -1101,6 +1433,28 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) else if (count > 1) s->sub_ifd = ff_tget(&s->gb, TIFF_LONG, s->le); /** Only get the first SubIFD */ break; + case DNG_LINEARIZATION_TABLE: + for (int i = 0; i < FFMIN(count, 1 << s->bpp); i++) + s->dng_lut[i] = ff_tget(&s->gb, type, s->le); + break; + case DNG_BLACK_LEVEL: + if (count > 1) { /* Use the first value in the pattern (assume they're all the same) */ + if (type == TIFF_RATIONAL) { + value = ff_tget(&s->gb, TIFF_LONG, s->le); + value2 = ff_tget(&s->gb, TIFF_LONG, s->le); + if (!value2) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid black level denominator\n"); + return AVERROR_INVALIDDATA; + } + + s->black_level = value / value2; + } else + s->black_level = ff_tget(&s->gb, type, s->le); + av_log(s->avctx, AV_LOG_WARNING, "Assuming black level pattern values are identical\n"); + } else { + s->black_level = value / value2; + } + break; case DNG_WHITE_LEVEL: s->white_level = value; break; @@ -1127,6 +1481,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) case TIFF_PHOTOMETRIC_SEPARATED: case TIFF_PHOTOMETRIC_YCBCR: case TIFF_PHOTOMETRIC_CFA: + case TIFF_PHOTOMETRIC_LINEAR_RAW: // Used by DNG images s->photometric = value; break; case TIFF_PHOTOMETRIC_ALPHA_MASK: @@ -1135,7 +1490,6 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) case TIFF_PHOTOMETRIC_ITU_LAB: case TIFF_PHOTOMETRIC_LOG_L: case TIFF_PHOTOMETRIC_LOG_LUV: - case TIFF_PHOTOMETRIC_LINEAR_RAW: avpriv_report_missing_feature(s->avctx, "PhotometricInterpretation 0x%04X", value); @@ -1313,6 +1667,22 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) } } break; + case TIFF_ICC_PROFILE: + if (type != TIFF_UNDEFINED) + return AVERROR_INVALIDDATA; + + gb_temp = s->gb; + bytestream2_seek(&gb_temp, SEEK_SET, off); + + 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); + break; case TIFF_ARTIST: ADD_METADATA(count, "artist", NULL); break; @@ -1407,6 +1777,8 @@ static int decode_frame(AVCodecContext *avctx, GetByteContext stripsizes; GetByteContext stripdata; int retry_for_subifd, retry_for_page; + int is_dng; + int has_tile_bits, has_strip_bits; bytestream2_init(&s->gb, avpkt->data, avpkt->size); @@ -1420,6 +1792,8 @@ static int decode_frame(AVCodecContext *avctx, } s->le = le; // TIFF_BPP is not a required tag and defaults to 1 + + s->tiff_type = TIFF_TYPE_TIFF; again: s->is_thumbnail = 0; s->bppcount = s->bpp = 1; @@ -1428,8 +1802,13 @@ static int decode_frame(AVCodecContext *avctx, s->fill_order = 0; s->white_level = 0; s->is_bayer = 0; + s->is_tiled = 0; + s->is_jpeg = 0; s->cur_page = 0; - s->tiff_type = TIFF_TYPE_TIFF; + + for (i = 0; i < 65536; i++) + s->dng_lut[i] = i; + free_geotags(s); // Reset these offsets so we can tell if they were set this frame @@ -1480,6 +1859,10 @@ static int decode_frame(AVCodecContext *avctx, goto again; } + /* At this point we've decided on which (Sub)IFD to process */ + + is_dng = (s->tiff_type == TIFF_TYPE_DNG || s->tiff_type == TIFF_TYPE_CINEMADNG); + for (i = 0; igeotag_count; i++) { const char *keyname = get_geokey_name(s->geotags[i].key); if (!keyname) { @@ -1497,37 +1880,94 @@ static int decode_frame(AVCodecContext *avctx, } } - if (!s->strippos && !s->stripoff) { + if (is_dng) { + int bps; + + if (s->white_level == 0) + s->white_level = (1 << s->bpp) - 1; /* Default value as per the spec */ + + if (s->white_level <= s->black_level) { + av_log(avctx, AV_LOG_ERROR, "BlackLevel (%"PRId32") must be less than WhiteLevel (%"PRId32")\n", + s->black_level, s->white_level); + return AVERROR_INVALIDDATA; + } + + if (s->bpp % s->bppcount) + return AVERROR_INVALIDDATA; + bps = s->bpp / s->bppcount; + if (bps < 8 || bps > 32) + return AVERROR_INVALIDDATA; + if (s->planar) + return AVERROR_PATCHWELCOME; + } + + if (!s->is_tiled && !s->strippos && !s->stripoff) { av_log(avctx, AV_LOG_ERROR, "Image data is missing\n"); return AVERROR_INVALIDDATA; } + + has_tile_bits = s->is_tiled || s->tile_byte_counts_offset || s->tile_offsets_offset || s->tile_width || s->tile_length || s->tile_count; + has_strip_bits = s->strippos || s->strips || s->stripoff || s->rps || s->sot || s->sstype || s->stripsize || s->stripsizesoff; + + if (has_tile_bits && has_strip_bits) { + av_log(avctx, AV_LOG_ERROR, "Tiled TIFF is not allowed to strip\n"); + return AVERROR_INVALIDDATA; + } + /* now we have the data and may start decoding */ if ((ret = init_image(s, &frame)) < 0) return ret; - if (s->strips == 1 && !s->stripsize) { - av_log(avctx, AV_LOG_WARNING, "Image data size missing\n"); - s->stripsize = avpkt->size - s->stripoff; - } + if (!s->is_tiled) { + if (s->strips == 1 && !s->stripsize) { + av_log(avctx, AV_LOG_WARNING, "Image data size missing\n"); + s->stripsize = avpkt->size - s->stripoff; + } - if (s->stripsizesoff) { - if (s->stripsizesoff >= (unsigned)avpkt->size) + if (s->stripsizesoff) { + if (s->stripsizesoff >= (unsigned)avpkt->size) + return AVERROR_INVALIDDATA; + bytestream2_init(&stripsizes, avpkt->data + s->stripsizesoff, + avpkt->size - s->stripsizesoff); + } + if (s->strippos) { + if (s->strippos >= (unsigned)avpkt->size) + return AVERROR_INVALIDDATA; + bytestream2_init(&stripdata, avpkt->data + s->strippos, + avpkt->size - s->strippos); + } + + if (s->rps <= 0 || s->rps % s->subsampling[1]) { + av_log(avctx, AV_LOG_ERROR, "rps %d invalid\n", s->rps); return AVERROR_INVALIDDATA; - bytestream2_init(&stripsizes, avpkt->data + s->stripsizesoff, - avpkt->size - s->stripsizesoff); + } } - if (s->strippos) { - if (s->strippos >= (unsigned)avpkt->size) - return AVERROR_INVALIDDATA; - bytestream2_init(&stripdata, avpkt->data + s->strippos, - avpkt->size - s->strippos); + + if (s->photometric == TIFF_PHOTOMETRIC_LINEAR_RAW || + s->photometric == TIFF_PHOTOMETRIC_CFA) { + p->color_trc = AVCOL_TRC_LINEAR; + } else if (s->photometric == TIFF_PHOTOMETRIC_BLACK_IS_ZERO) { + p->color_trc = AVCOL_TRC_GAMMA22; } - if (s->rps <= 0 || s->rps % s->subsampling[1]) { - av_log(avctx, AV_LOG_ERROR, "rps %d invalid\n", s->rps); - return AVERROR_INVALIDDATA; + /* Handle DNG images with JPEG-compressed tiles */ + + if (is_dng && s->is_tiled) { + if (!s->is_jpeg) { + avpriv_report_missing_feature(avctx, "DNG uncompressed tiled images"); + return AVERROR_PATCHWELCOME; + } else if (!s->is_bayer) { + avpriv_report_missing_feature(avctx, "DNG JPG-compressed tiled non-bayer-encoded images"); + return AVERROR_PATCHWELCOME; + } else { + if ((ret = dng_decode_tiles(avctx, (AVFrame*)data, avpkt)) > 0) + *got_frame = 1; + return ret; + } } + /* Handle TIFF images and DNG images with uncompressed strips (non-tiled) */ + planes = s->planar ? s->bppcount : 1; for (plane = 0; plane < planes; plane++) { uint8_t *five_planes = NULL; @@ -1670,7 +2110,7 @@ static int decode_frame(AVCodecContext *avctx, FFSWAP(int, p->linesize[0], p->linesize[1]); } - if (s->is_bayer && s->white_level && s->bpp == 16) { + if (s->is_bayer && s->white_level && s->bpp == 16 && !is_dng) { uint16_t *dst = (uint16_t *)p->data[0]; for (i = 0; i < s->height; i++) { for (j = 0; j < s->width; j++) @@ -1687,6 +2127,8 @@ static int decode_frame(AVCodecContext *avctx, static av_cold int tiff_init(AVCodecContext *avctx) { TiffContext *s = avctx->priv_data; + const AVCodec *codec; + int ret; s->width = 0; s->height = 0; @@ -1698,6 +2140,27 @@ static av_cold int tiff_init(AVCodecContext *avctx) return AVERROR(ENOMEM); ff_ccitt_unpack_init(); + /* Allocate JPEG frame */ + s->jpgframe = av_frame_alloc(); + if (!s->jpgframe) + return AVERROR(ENOMEM); + + /* Prepare everything needed for JPEG decoding */ + codec = avcodec_find_decoder(AV_CODEC_ID_MJPEG); + if (!codec) + return AVERROR_BUG; + s->avctx_mjpeg = avcodec_alloc_context3(codec); + if (!s->avctx_mjpeg) + 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; + ret = ff_codec_open2_recursive(s->avctx_mjpeg, codec, NULL); + if (ret < 0) { + return ret; + } + return 0; } @@ -1714,6 +2177,8 @@ static av_cold int tiff_end(AVCodecContext *avctx) s->yuv_line_size = 0; av_freep(&s->fax_buffer); s->fax_buffer_size = 0; + av_frame_free(&s->jpgframe); + avcodec_free_context(&s->avctx_mjpeg); return 0; } @@ -1741,7 +2206,7 @@ AVCodec ff_tiff_decoder = { .init = tiff_init, .close = tiff_end, .decode = decode_frame, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(tiff_init), .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .priv_class = &tiff_decoder_class, }; diff --git a/libavcodec/tiff.h b/libavcodec/tiff.h index 81913c6b1ae..c07a5d4fa93 100644 --- a/libavcodec/tiff.h +++ b/libavcodec/tiff.h @@ -92,6 +92,7 @@ enum TiffTags { TIFF_MODEL_TIEPOINT = 0x8482, TIFF_MODEL_PIXEL_SCALE = 0x830E, TIFF_MODEL_TRANSFORMATION= 0x8480, + TIFF_ICC_PROFILE = 0x8773, TIFF_GEO_KEY_DIRECTORY = 0x87AF, TIFF_GEO_DOUBLE_PARAMS = 0x87B0, TIFF_GEO_ASCII_PARAMS = 0x87B1, @@ -101,6 +102,8 @@ enum TiffTags { enum DngTags { DNG_VERSION = 0xC612, DNG_BACKWARD_VERSION = 0xC613, + DNG_LINEARIZATION_TABLE = 0xC618, + DNG_BLACK_LEVEL = 0xC61A, DNG_WHITE_LEVEL = 0xC61D, }; diff --git a/libavcodec/tiffenc.c b/libavcodec/tiffenc.c index f59816ec825..a122f51de46 100644 --- a/libavcodec/tiffenc.c +++ b/libavcodec/tiffenc.c @@ -582,7 +582,7 @@ AVCodec ff_tiff_encoder = { .priv_data_size = sizeof(TiffEncoderContext), .init = encode_init, .close = encode_close, - .capabilities = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_FRAME_THREADS, .encode2 = encode_frame, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_RGB24, AV_PIX_FMT_RGB48LE, AV_PIX_FMT_PAL8, diff --git a/libavcodec/trace_headers_bsf.c b/libavcodec/trace_headers_bsf.c index 3ec78fe822d..8ee4dbd6686 100644 --- a/libavcodec/trace_headers_bsf.c +++ b/libavcodec/trace_headers_bsf.c @@ -23,6 +23,7 @@ #include "libavutil/log.h" #include "bsf.h" +#include "bsf_internal.h" #include "cbs.h" diff --git a/libavcodec/truehd_core_bsf.c b/libavcodec/truehd_core_bsf.c index dbd05b34cae..cc2779cc2e3 100644 --- a/libavcodec/truehd_core_bsf.c +++ b/libavcodec/truehd_core_bsf.c @@ -18,8 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "get_bits.h" #include "mlp_parse.h" #include "mlp.h" @@ -31,8 +31,6 @@ typedef struct AccessUnit { } AccessUnit; typedef struct TrueHDCoreContext { - const AVClass *class; - MLPHeaderInfo hdr; } TrueHDCoreContext; diff --git a/libavcodec/truemotion1.c b/libavcodec/truemotion1.c index e1824384c5e..b6481cbd7c7 100644 --- a/libavcodec/truemotion1.c +++ b/libavcodec/truemotion1.c @@ -444,6 +444,8 @@ static int truemotion1_decode_header(TrueMotion1Context *s) if (s->flags & FLAG_KEYFRAME) { /* no change bits specified for a keyframe; only index bytes */ s->index_stream = s->mb_change_bits; + if (s->avctx->width * s->avctx->height / 2048 + header.header_size > s->size) + return AVERROR_INVALIDDATA; } else { /* one change bit per 4x4 block */ s->index_stream = s->mb_change_bits + @@ -882,7 +884,7 @@ static int truemotion1_decode_frame(AVCodecContext *avctx, if ((ret = truemotion1_decode_header(s)) < 0) return ret; - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; if (compression_types[s->compression].algorithm == ALGO_RGB24H) { diff --git a/libavcodec/truemotion2.c b/libavcodec/truemotion2.c index 27c876fd7de..d90a8baff37 100644 --- a/libavcodec/truemotion2.c +++ b/libavcodec/truemotion2.c @@ -155,7 +155,7 @@ static int tm2_build_huff_table(TM2Context *ctx, TM2Codes *code) huff.val_bits = get_bits(&ctx->gb, 5); huff.max_bits = get_bits(&ctx->gb, 5); huff.min_bits = get_bits(&ctx->gb, 5); - huff.nodes = get_bits_long(&ctx->gb, 17); + huff.nodes = get_bits(&ctx->gb, 17); huff.num = 0; /* check for correct codes parameters */ @@ -443,7 +443,7 @@ static inline int GET_TOK(TM2Context *ctx,int type) clast = ctx->clast + bx * 4; #define TM2_INIT_POINTERS_2() \ - int *Yo, *Uo, *Vo;\ + unsigned *Yo, *Uo, *Vo;\ int oYstride, oUstride, oVstride;\ \ TM2_INIT_POINTERS();\ @@ -581,15 +581,15 @@ static inline void tm2_low_res_block(TM2Context *ctx, AVFrame *pic, int bx, int deltas[10] = GET_TOK(ctx, TM2_L_LO); if (bx > 0) - last[0] = (last[-1] - ctx->D[0] - ctx->D[1] - ctx->D[2] - ctx->D[3] + last[1]) >> 1; + last[0] = (int)((unsigned)last[-1] - ctx->D[0] - ctx->D[1] - ctx->D[2] - ctx->D[3] + last[1]) >> 1; else - last[0] = (last[1] - ctx->D[0] - ctx->D[1] - ctx->D[2] - ctx->D[3])>> 1; - last[2] = (last[1] + last[3]) >> 1; + last[0] = (int)((unsigned)last[1] - ctx->D[0] - ctx->D[1] - ctx->D[2] - ctx->D[3])>> 1; + last[2] = (int)((unsigned)last[1] + last[3]) >> 1; - t1 = ctx->D[0] + ctx->D[1]; + t1 = ctx->D[0] + (unsigned)ctx->D[1]; ctx->D[0] = t1 >> 1; ctx->D[1] = t1 - (t1 >> 1); - t2 = ctx->D[2] + ctx->D[3]; + t2 = ctx->D[2] + (unsigned)ctx->D[3]; ctx->D[2] = t2 >> 1; ctx->D[3] = t2 - (t2 >> 1); @@ -616,7 +616,7 @@ static inline void tm2_null_res_block(TM2Context *ctx, AVFrame *pic, int bx, int for (i = 0; i < 16; i++) deltas[i] = 0; - ct = ctx->D[0] + ctx->D[1] + ctx->D[2] + ctx->D[3]; + ct = (unsigned)ctx->D[0] + ctx->D[1] + ctx->D[2] + ctx->D[3]; if (bx > 0) left = last[-1] - (unsigned)ct; @@ -687,8 +687,8 @@ static inline void tm2_update_block(TM2Context *ctx, AVFrame *pic, int bx, int b /* update chroma */ for (j = 0; j < 2; j++) { for (i = 0; i < 2; i++) { - U[i] = Uo[i] + (unsigned)GET_TOK(ctx, TM2_UPD); - V[i] = Vo[i] + (unsigned)GET_TOK(ctx, TM2_UPD); + U[i] = Uo[i] + GET_TOK(ctx, TM2_UPD); + V[i] = Vo[i] + GET_TOK(ctx, TM2_UPD); } U += Ustride; V += Vstride; @@ -701,10 +701,10 @@ static inline void tm2_update_block(TM2Context *ctx, AVFrame *pic, int bx, int b TM2_RECALC_BLOCK(V, Vstride, (clast + 2), (ctx->CD + 2)); /* update deltas */ - ctx->D[0] = (unsigned)Yo[3] - last[3]; - ctx->D[1] = (unsigned)Yo[3 + oYstride] - Yo[3]; - ctx->D[2] = (unsigned)Yo[3 + oYstride * 2] - Yo[3 + oYstride]; - ctx->D[3] = (unsigned)Yo[3 + oYstride * 3] - Yo[3 + oYstride * 2]; + ctx->D[0] = Yo[3] - last[3]; + ctx->D[1] = Yo[3 + oYstride] - Yo[3]; + ctx->D[2] = Yo[3 + oYstride * 2] - Yo[3 + oYstride]; + ctx->D[3] = Yo[3 + oYstride * 3] - Yo[3 + oYstride * 2]; for (j = 0; j < 4; j++) { d = last[3]; @@ -915,7 +915,7 @@ static int decode_frame(AVCodecContext *avctx, return AVERROR(ENOMEM); } - if ((ret = ff_reget_buffer(avctx, p)) < 0) + if ((ret = ff_reget_buffer(avctx, p, 0)) < 0) return ret; l->bdsp.bswap_buf((uint32_t *) l->buffer, (const uint32_t *) buf, diff --git a/libavcodec/truespeech.c b/libavcodec/truespeech.c index d4ddfcbf9c1..3cdae8c556f 100644 --- a/libavcodec/truespeech.c +++ b/libavcodec/truespeech.c @@ -132,8 +132,7 @@ static void truespeech_correlate_filter(TSContext *dec) if(i > 0){ memcpy(tmp, dec->cvector, i * sizeof(*tmp)); for(j = 0; j < i; j++) - dec->cvector[j] = ((tmp[i - j - 1] * dec->vector[i]) + - (dec->cvector[j] << 15) + 0x4000) >> 15; + dec->cvector[j] += (tmp[i - j - 1] * dec->vector[i] + 0x4000) >> 15; } dec->cvector[i] = (8 - dec->vector[i]) >> 3; } @@ -255,8 +254,8 @@ static void truespeech_synth(TSContext *dec, int16_t *out, int quart) for(i = 0; i < 60; i++){ int sum = 0; for(k = 0; k < 8; k++) - sum += ptr0[k] * ptr1[k]; - sum = (sum + (out[i] << 12) + 0x800) >> 12; + sum += ptr0[k] * (unsigned)ptr1[k]; + sum = out[i] + ((int)(sum + 0x800U) >> 12); out[i] = av_clip(sum, -0x7FFE, 0x7FFE); for(k = 7; k > 0; k--) ptr0[k] = ptr0[k - 1]; @@ -274,7 +273,7 @@ static void truespeech_synth(TSContext *dec, int16_t *out, int quart) for(k = 7; k > 0; k--) ptr0[k] = ptr0[k - 1]; ptr0[0] = out[i]; - out[i] = ((out[i] << 12) - sum) >> 12; + out[i] += (- sum) >> 12; } for(i = 0; i < 8; i++) @@ -282,7 +281,7 @@ static void truespeech_synth(TSContext *dec, int16_t *out, int quart) ptr0 = dec->tmp3; for(i = 0; i < 60; i++){ - int sum = out[i] << 12; + int sum = out[i] * (1 << 12); for(k = 0; k < 8; k++) sum += ptr0[k] * t[k]; for(k = 7; k > 0; k--) diff --git a/libavcodec/tscc.c b/libavcodec/tscc.c index fc1ec4de0d4..6d03081bb0a 100644 --- a/libavcodec/tscc.c +++ b/libavcodec/tscc.c @@ -103,7 +103,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, return AVERROR_UNKNOWN; } - if ((ret = ff_reget_buffer(avctx, frame)) < 0) + if ((ret = ff_reget_buffer(avctx, frame, 0)) < 0) return ret; if (ret != Z_DATA_ERROR) { diff --git a/libavcodec/tscc2.c b/libavcodec/tscc2.c index a8c7ee79961..65fbb010f60 100644 --- a/libavcodec/tscc2.c +++ b/libavcodec/tscc2.c @@ -240,7 +240,7 @@ static int tscc2_decode_frame(AVCodecContext *avctx, void *data, return buf_size; } - if ((ret = ff_reget_buffer(avctx, c->pic)) < 0) { + if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) { return ret; } diff --git a/libavcodec/tta.c b/libavcodec/tta.c index 304f3a81df2..e68e4fbb363 100644 --- a/libavcodec/tta.c +++ b/libavcodec/tta.c @@ -129,7 +129,7 @@ static av_cold int tta_decode_init(AVCodecContext * avctx) s->avctx = avctx; - // 30bytes includes TTA1 header + // 22 bytes for a TTA1 header if (avctx->extradata_size < 22) return AVERROR_INVALIDDATA; @@ -389,13 +389,6 @@ static int tta_decode_frame(AVCodecContext *avctx, void *data, return ret; } -static int init_thread_copy(AVCodecContext *avctx) -{ - TTAContext *s = avctx->priv_data; - s->avctx = avctx; - return allocate_buffers(avctx); -} - static av_cold int tta_decode_close(AVCodecContext *avctx) { TTAContext *s = avctx->priv_data; @@ -430,7 +423,6 @@ AVCodec ff_tta_decoder = { .init = tta_decode_init, .close = tta_decode_close, .decode = tta_decode_frame, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(init_thread_copy), .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, .priv_class = &tta_decoder_class, }; diff --git a/libavcodec/ttadsp.c b/libavcodec/ttadsp.c index 056a2c7ef17..1d1443aee05 100644 --- a/libavcodec/ttadsp.c +++ b/libavcodec/ttadsp.c @@ -20,9 +20,11 @@ #include "ttadsp.h" #include "config.h" -static void tta_filter_process_c(int32_t *qm, int32_t *dx, int32_t *dl, +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) { + uint32_t *qm = qmi; + if (*error < 0) { qm[0] -= dx[0]; qm[1] -= dx[1]; qm[2] -= dx[2]; qm[3] -= dx[3]; qm[4] -= dx[4]; qm[5] -= dx[5]; qm[6] -= dx[6]; qm[7] -= dx[7]; diff --git a/libavcodec/ttaenc.c b/libavcodec/ttaenc.c index 3cc54d78c5a..ac8a4328737 100644 --- a/libavcodec/ttaenc.c +++ b/libavcodec/ttaenc.c @@ -164,7 +164,7 @@ static int tta_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, put_bits(&pb, 31, 0x7FFFFFFF); unary -= 31; } else { - put_bits(&pb, unary, (1 << unary) - 1); + put_bits(&pb, unary, (1U << unary) - 1); unary = 0; } } while (unary); @@ -209,7 +209,7 @@ AVCodec ff_tta_encoder = { .init = tta_encode_init, .close = tta_encode_close, .encode2 = tta_encode_frame, - .capabilities = AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_LOSSLESS, + .capabilities = AV_CODEC_CAP_SMALL_LAST_FRAME, .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32, diff --git a/libavcodec/twinvq.c b/libavcodec/twinvq.c index 7b2e19e536b..34ca1846b9f 100644 --- a/libavcodec/twinvq.c +++ b/libavcodec/twinvq.c @@ -771,23 +771,26 @@ av_cold int ff_twinvq_decode_init(AVCodecContext *avctx) { int ret; TwinVQContext *tctx = avctx->priv_data; + int64_t frames_per_packet; tctx->avctx = avctx; avctx->sample_fmt = AV_SAMPLE_FMT_FLTP; if (!avctx->block_align) { avctx->block_align = tctx->frame_size + 7 >> 3; - } else if (avctx->block_align * 8 < tctx->frame_size) { - av_log(avctx, AV_LOG_ERROR, "Block align is %d bits, expected %d\n", - avctx->block_align * 8, tctx->frame_size); + } + frames_per_packet = avctx->block_align * 8LL / tctx->frame_size; + if (frames_per_packet <= 0) { + av_log(avctx, AV_LOG_ERROR, "Block align is %"PRId64" bits, expected %d\n", + avctx->block_align * (int64_t)8, tctx->frame_size); return AVERROR_INVALIDDATA; } - tctx->frames_per_packet = avctx->block_align * 8 / tctx->frame_size; - if (tctx->frames_per_packet > TWINVQ_MAX_FRAMES_PER_PACKET) { - av_log(avctx, AV_LOG_ERROR, "Too many frames per packet (%d)\n", - tctx->frames_per_packet); + if (frames_per_packet > TWINVQ_MAX_FRAMES_PER_PACKET) { + av_log(avctx, AV_LOG_ERROR, "Too many frames per packet (%"PRId64")\n", + frames_per_packet); return AVERROR_INVALIDDATA; } + tctx->frames_per_packet = frames_per_packet; tctx->fdsp = avpriv_float_dsp_alloc(avctx->flags & AV_CODEC_FLAG_BITEXACT); if (!tctx->fdsp) { diff --git a/libavcodec/twinvqdec.c b/libavcodec/twinvqdec.c index c2353f51b56..c00ebb2ad5a 100644 --- a/libavcodec/twinvqdec.c +++ b/libavcodec/twinvqdec.c @@ -404,7 +404,7 @@ static av_cold int twinvq_decode_init(AVCodecContext *avctx) tctx->frame_size = avctx->bit_rate * tctx->mtab->size / avctx->sample_rate + 8; tctx->is_6kbps = 0; - if (avctx->block_align && avctx->block_align * 8 / tctx->frame_size > 1) { + if (avctx->block_align && avctx->block_align * 8LL / tctx->frame_size > 1) { av_log(avctx, AV_LOG_ERROR, "VQF TwinVQ should have only one frame per packet\n"); return AVERROR_INVALIDDATA; diff --git a/libavcodec/txd.c b/libavcodec/txd.c index 8b20475d392..f00ba89e82f 100644 --- a/libavcodec/txd.c +++ b/libavcodec/txd.c @@ -43,6 +43,9 @@ static int txd_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, int i, j; int ret; + if (avpkt->size < 88) + return AVERROR_INVALIDDATA; + ff_texturedsp_init(&dxtc); bytestream2_init(&gb, avpkt->data, avpkt->size); diff --git a/libavcodec/ulti.c b/libavcodec/ulti.c index 9318af064bd..e6fb661f6e3 100644 --- a/libavcodec/ulti.c +++ b/libavcodec/ulti.c @@ -230,7 +230,7 @@ static int ulti_decode_frame(AVCodecContext *avctx, int skip; int tmp; - if ((ret = ff_reget_buffer(avctx, s->frame)) < 0) + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; bytestream2_init(&s->gb, buf, buf_size); diff --git a/libavcodec/utils.c b/libavcodec/utils.c index a6a646636d6..a9c69e30dd8 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -44,7 +44,7 @@ #include "libavutil/thread.h" #include "avcodec.h" #include "decode.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "libavutil/opt.h" #include "mpegvideo.h" #include "thread.h" @@ -412,7 +412,7 @@ int avcodec_fill_audio_frame(AVFrame *frame, int nb_channels, void ff_color_frame(AVFrame *frame, const int c[4]) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); - int p, y, x; + int p, y; av_assert0(desc->flags & AV_PIX_FMT_FLAG_PLANAR); @@ -421,13 +421,19 @@ void ff_color_frame(AVFrame *frame, const int c[4]) 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; - for (y = 0; y < height; y++) { - if (desc->comp[0].depth >= 9) { - for (x = 0; xcomp[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]; + } } } } @@ -545,15 +551,16 @@ int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *code int codec_init_ok = 0; AVDictionary *tmp = NULL; const AVPixFmtDescriptor *pixdesc; + AVCodecInternal *avci; if (avcodec_is_open(avctx)) return 0; - if ((!codec && !avctx->codec)) { + if (!codec && !avctx->codec) { av_log(avctx, AV_LOG_ERROR, "No codec provided to avcodec_open2()\n"); return AVERROR(EINVAL); } - if ((codec && avctx->codec && codec != avctx->codec)) { + if (codec && avctx->codec && codec != avctx->codec) { av_log(avctx, AV_LOG_ERROR, "This AVCodecContext was allocated for %s, " "but %s passed to avcodec_open2()\n", avctx->codec->name, codec->name); return AVERROR(EINVAL); @@ -569,55 +576,27 @@ int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *code ff_lock_avcodec(avctx, codec); - avctx->internal = av_mallocz(sizeof(*avctx->internal)); - if (!avctx->internal) { + avci = av_mallocz(sizeof(*avci)); + if (!avci) { ret = AVERROR(ENOMEM); goto end; } - - avctx->internal->pool = av_mallocz(sizeof(*avctx->internal->pool)); - if (!avctx->internal->pool) { - ret = AVERROR(ENOMEM); - goto free_and_end; - } - - avctx->internal->to_free = av_frame_alloc(); - if (!avctx->internal->to_free) { - ret = AVERROR(ENOMEM); - goto free_and_end; - } - - avctx->internal->compat_decode_frame = av_frame_alloc(); - if (!avctx->internal->compat_decode_frame) { - ret = AVERROR(ENOMEM); - goto free_and_end; - } - - avctx->internal->buffer_frame = av_frame_alloc(); - if (!avctx->internal->buffer_frame) { + avctx->internal = avci; + + avci->to_free = av_frame_alloc(); + avci->compat_decode_frame = av_frame_alloc(); + avci->buffer_frame = av_frame_alloc(); + avci->buffer_pkt = av_packet_alloc(); + avci->ds.in_pkt = av_packet_alloc(); + avci->last_pkt_props = av_packet_alloc(); + if (!avci->to_free || !avci->compat_decode_frame || + !avci->buffer_frame || !avci->buffer_pkt || + !avci->ds.in_pkt || !avci->last_pkt_props) { ret = AVERROR(ENOMEM); goto free_and_end; } - avctx->internal->buffer_pkt = av_packet_alloc(); - if (!avctx->internal->buffer_pkt) { - ret = AVERROR(ENOMEM); - goto free_and_end; - } - - avctx->internal->ds.in_pkt = av_packet_alloc(); - if (!avctx->internal->ds.in_pkt) { - ret = AVERROR(ENOMEM); - goto free_and_end; - } - - avctx->internal->last_pkt_props = av_packet_alloc(); - if (!avctx->internal->last_pkt_props) { - ret = AVERROR(ENOMEM); - goto free_and_end; - } - - avctx->internal->skip_samples_multiplier = 1; + avci->skip_samples_multiplier = 1; if (codec->priv_data_size > 0) { if (!avctx->priv_data) { @@ -648,12 +627,12 @@ int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *code // only call ff_set_dimensions() for non H.264/VP6F/DXV codecs so as not to overwrite previously setup dimensions if (!(avctx->coded_width && avctx->coded_height && avctx->width && avctx->height && (avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_VP6F || avctx->codec_id == AV_CODEC_ID_DXV))) { - if (avctx->coded_width && avctx->coded_height) - ret = ff_set_dimensions(avctx, avctx->coded_width, avctx->coded_height); - else if (avctx->width && avctx->height) - ret = ff_set_dimensions(avctx, avctx->width, avctx->height); - if (ret < 0) - goto free_and_end; + if (avctx->coded_width && avctx->coded_height) + ret = ff_set_dimensions(avctx, avctx->coded_width, avctx->coded_height); + else if (avctx->width && avctx->height) + ret = ff_set_dimensions(avctx, avctx->width, avctx->height); + if (ret < 0) + goto free_and_end; } if ((avctx->coded_width || avctx->coded_height || avctx->width || avctx->height) @@ -678,8 +657,18 @@ int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *code if (av_codec_is_decoder(codec)) av_freep(&avctx->subtitle_header); - if (avctx->channels > FF_SANE_NB_CHANNELS) { - av_log(avctx, AV_LOG_ERROR, "Too many channels: %d\n", avctx->channels); + if (avctx->channels > FF_SANE_NB_CHANNELS || avctx->channels < 0) { + av_log(avctx, AV_LOG_ERROR, "Too many or invalid channels: %d\n", avctx->channels); + ret = AVERROR(EINVAL); + goto free_and_end; + } + if (avctx->sample_rate < 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid sample rate: %d\n", avctx->sample_rate); + ret = AVERROR(EINVAL); + goto free_and_end; + } + if (avctx->block_align < 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid block align: %d\n", avctx->block_align); ret = AVERROR(EINVAL); goto free_and_end; } @@ -739,7 +728,7 @@ int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *code } if (HAVE_THREADS - && !(avctx->internal->frame_thread_encoder && (avctx->active_thread_type&FF_THREAD_FRAME))) { + && !(avci->frame_thread_encoder && (avctx->active_thread_type&FF_THREAD_FRAME))) { ret = ff_thread_init(avctx); if (ret < 0) { goto free_and_end; @@ -929,9 +918,12 @@ FF_ENABLE_DEPRECATION_WARNINGS && avctx->codec_descriptor->type == AVMEDIA_TYPE_VIDEO) av_log(avctx, AV_LOG_WARNING, "gray decoding requested but not enabled at configuration time\n"); + if (avctx->flags2 & AV_CODEC_FLAG2_EXPORT_MVS) { + avctx->export_side_data |= AV_CODEC_EXPORT_DATA_MVS; + } if ( avctx->codec->init && (!(avctx->active_thread_type&FF_THREAD_FRAME) - || avctx->internal->frame_thread_encoder)) { + || avci->frame_thread_encoder)) { ret = avctx->codec->init(avctx); if (ret < 0) { goto free_and_end; @@ -1029,6 +1021,9 @@ FF_ENABLE_DEPRECATION_WARNINGS (avctx->codec->caps_internal & FF_CODEC_CAP_INIT_CLEANUP))) avctx->codec->close(avctx); + if (HAVE_THREADS && avci->thread_ctx) + ff_thread_free(avctx); + if (codec->priv_class && codec->priv_data_size) av_opt_free(avctx->priv_data); av_opt_free(avctx); @@ -1042,23 +1037,68 @@ FF_ENABLE_DEPRECATION_WARNINGS av_dict_free(&tmp); av_freep(&avctx->priv_data); av_freep(&avctx->subtitle_header); - if (avctx->internal) { - av_frame_free(&avctx->internal->to_free); - av_frame_free(&avctx->internal->compat_decode_frame); - av_frame_free(&avctx->internal->buffer_frame); - av_packet_free(&avctx->internal->buffer_pkt); - av_packet_free(&avctx->internal->last_pkt_props); + if (avci) { + av_frame_free(&avci->to_free); + av_frame_free(&avci->compat_decode_frame); + av_frame_free(&avci->buffer_frame); + av_packet_free(&avci->buffer_pkt); + av_packet_free(&avci->last_pkt_props); - av_packet_free(&avctx->internal->ds.in_pkt); - ff_decode_bsfs_uninit(avctx); + av_packet_free(&avci->ds.in_pkt); + av_bsf_free(&avci->bsf); - av_freep(&avctx->internal->pool); + av_buffer_unref(&avci->pool); } - av_freep(&avctx->internal); + av_freep(&avci); + avctx->internal = NULL; avctx->codec = NULL; goto end; } +void avcodec_flush_buffers(AVCodecContext *avctx) +{ + AVCodecInternal *avci = avctx->internal; + + if (av_codec_is_encoder(avctx->codec)) { + int caps = avctx->codec->capabilities; + + if (!(caps & AV_CODEC_CAP_ENCODER_FLUSH)) { + // Only encoders that explicitly declare support for it can be + // flushed. Otherwise, this is a no-op. + av_log(avctx, AV_LOG_WARNING, "Ignoring attempt to flush encoder " + "that doesn't support it\n"); + return; + } + + // We haven't implemented flushing for frame-threaded encoders. + av_assert0(!(caps & AV_CODEC_CAP_FRAME_THREADS)); + } + + avci->draining = 0; + avci->draining_done = 0; + avci->nb_draining_errors = 0; + av_frame_unref(avci->buffer_frame); + av_frame_unref(avci->compat_decode_frame); + av_packet_unref(avci->buffer_pkt); + avci->buffer_pkt_valid = 0; + + av_packet_unref(avci->ds.in_pkt); + + if (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME) + ff_thread_flush(avctx); + else if (avctx->codec->flush) + avctx->codec->flush(avctx); + + avctx->pts_correction_last_pts = + avctx->pts_correction_last_dts = INT64_MIN; + + if (av_codec_is_decoder(avctx->codec)) + av_bsf_flush(avci->bsf); + + if (!avctx->refcounted_frames) + av_frame_unref(avci->to_free); +} + void avsubtitle_free(AVSubtitle *sub) { int i; @@ -1086,7 +1126,6 @@ av_cold int avcodec_close(AVCodecContext *avctx) return 0; if (avcodec_is_open(avctx)) { - FramePool *pool = avctx->internal->pool; if (CONFIG_FRAME_THREAD_ENCODER && avctx->internal->frame_thread_encoder && avctx->thread_count > 1) { ff_frame_thread_encoder_free(avctx); @@ -1105,15 +1144,13 @@ av_cold int avcodec_close(AVCodecContext *avctx) av_packet_free(&avctx->internal->ds.in_pkt); - for (i = 0; i < FF_ARRAY_ELEMS(pool->pools); i++) - av_buffer_pool_uninit(&pool->pools[i]); - av_freep(&avctx->internal->pool); + av_buffer_unref(&avctx->internal->pool); if (avctx->hwaccel && avctx->hwaccel->uninit) avctx->hwaccel->uninit(avctx); av_freep(&avctx->internal->hwaccel_priv_data); - ff_decode_bsfs_uninit(avctx); + av_bsf_free(&avctx->internal->bsf); av_freep(&avctx->internal); } @@ -1425,7 +1462,7 @@ const char *avcodec_configuration(void) const char *avcodec_license(void) { #define LICENSE_PREFIX "libavcodec license: " - return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1; + return &LICENSE_PREFIX FFMPEG_LICENSE[sizeof(LICENSE_PREFIX) - 1]; } int av_get_exact_bits_per_sample(enum AVCodecID codec_id) @@ -1438,6 +1475,7 @@ int av_get_exact_bits_per_sample(enum AVCodecID codec_id) case AV_CODEC_ID_ADPCM_IMA_EA_SEAD: case AV_CODEC_ID_ADPCM_IMA_OKI: case AV_CODEC_ID_ADPCM_IMA_WS: + case AV_CODEC_ID_ADPCM_IMA_SSI: case AV_CODEC_ID_ADPCM_G722: case AV_CODEC_ID_ADPCM_YAMAHA: case AV_CODEC_ID_ADPCM_AICA: @@ -1452,8 +1490,8 @@ int av_get_exact_bits_per_sample(enum AVCodecID codec_id) case AV_CODEC_ID_PCM_S8: case AV_CODEC_ID_PCM_S8_PLANAR: case AV_CODEC_ID_PCM_U8: - case AV_CODEC_ID_PCM_ZORK: case AV_CODEC_ID_SDX2_DPCM: + case AV_CODEC_ID_DERF_DPCM: return 8; case AV_CODEC_ID_PCM_S16BE: case AV_CODEC_ID_PCM_S16BE_PLANAR: @@ -1491,7 +1529,7 @@ int av_get_exact_bits_per_sample(enum AVCodecID codec_id) enum AVCodecID av_get_pcm_codec(enum AVSampleFormat fmt, int be) { - static const enum AVCodecID map[AV_SAMPLE_FMT_NB][2] = { + static const enum AVCodecID map[][2] = { [AV_SAMPLE_FMT_U8 ] = { AV_CODEC_ID_PCM_U8, AV_CODEC_ID_PCM_U8 }, [AV_SAMPLE_FMT_S16 ] = { AV_CODEC_ID_PCM_S16LE, AV_CODEC_ID_PCM_S16BE }, [AV_SAMPLE_FMT_S32 ] = { AV_CODEC_ID_PCM_S32LE, AV_CODEC_ID_PCM_S32BE }, @@ -1504,7 +1542,7 @@ enum AVCodecID av_get_pcm_codec(enum AVSampleFormat fmt, int be) [AV_SAMPLE_FMT_FLTP] = { AV_CODEC_ID_PCM_F32LE, AV_CODEC_ID_PCM_F32BE }, [AV_SAMPLE_FMT_DBLP] = { AV_CODEC_ID_PCM_F64LE, AV_CODEC_ID_PCM_F64BE }, }; - if (fmt < 0 || fmt >= AV_SAMPLE_FMT_NB) + if (fmt < 0 || fmt >= FF_ARRAY_ELEMS(map)) return AV_CODEC_ID_NONE; if (be < 0 || be > 1) be = AV_NE(1, 0); @@ -1956,6 +1994,11 @@ 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) diff --git a/libavcodec/utvideodec.c b/libavcodec/utvideodec.c index d5af9d53a8c..c07636d4351 100644 --- a/libavcodec/utvideodec.c +++ b/libavcodec/utvideodec.c @@ -317,7 +317,7 @@ static int decode_plane(UtvideoContext *c, int plane_no, for (i = 0; i < width; i++) { pix = fsym; if (use_pred) { - prev += pix; + prev += (unsigned)pix; pix = prev; } dest[i] = pix; @@ -890,6 +890,15 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, } } break; + case AV_PIX_FMT_YUV420P10: + for (i = 0; i < 3; i++) { + ret = decode_plane10(c, i, (uint16_t *)frame.f->data[i], frame.f->linesize[i] / 2, + avctx->width >> !!i, avctx->height >> !!i, + plane_start[i], plane_start[i + 1] - 1024, c->frame_pred == PRED_LEFT); + if (ret) + return ret; + } + break; case AV_PIX_FMT_YUV422P10: for (i = 0; i < 3; i++) { ret = decode_plane10(c, i, (uint16_t *)frame.f->data[i], frame.f->linesize[i] / 2, @@ -948,6 +957,11 @@ static av_cold int decode_init(AVCodecContext *avctx) avctx->pix_fmt = AV_PIX_FMT_YUV444P; avctx->colorspace = AVCOL_SPC_BT470BG; break; + case MKTAG('U', 'Q', 'Y', '0'): + c->planes = 3; + c->pro = 1; + avctx->pix_fmt = AV_PIX_FMT_YUV420P10; + break; case MKTAG('U', 'Q', 'Y', '2'): c->planes = 3; c->pro = 1; diff --git a/libavcodec/utvideoenc.c b/libavcodec/utvideoenc.c index db00e1eff5f..f1b9d11c960 100644 --- a/libavcodec/utvideoenc.c +++ b/libavcodec/utvideoenc.c @@ -677,7 +677,7 @@ AVCodec ff_utvideo_encoder = { .init = utvideo_encode_init, .encode2 = utvideo_encode_frame, .close = utvideo_encode_close, - .capabilities = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_FRAME_THREADS, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_NONE diff --git a/libavcodec/v210_template.c b/libavcodec/v210_template.c new file mode 100644 index 00000000000..9e1d9f9cd3e --- /dev/null +++ b/libavcodec/v210_template.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2009 Michael Niedermayer + * Copyright (c) 2009 Baptiste Coudurier + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 "bytestream.h" +#include "internal.h" + +#define CLIP(v, depth) av_clip(v, 1<<(depth-8), ((1<priv_data; + int aligned_width = ((avctx->width + 47) / 48) * 48; + int stride = aligned_width * 8 / 3; + int line_padding = stride - ((avctx->width * 8 + 11) / 12) * 4; + int h, w; + const TYPE *y = (const TYPE *)pic->data[0]; + const TYPE *u = (const TYPE *)pic->data[1]; + const TYPE *v = (const TYPE *)pic->data[2]; + const int sample_size = 6 * s->RENAME(sample_factor); + const int sample_w = avctx->width / sample_size; + + for (h = 0; h < avctx->height; h++) { + uint32_t val; + w = sample_w * sample_size; + s->RENAME(pack_line)(y, u, v, dst, w); + + y += w; + u += w >> 1; + v += w >> 1; + dst += sample_w * 16 * s->RENAME(sample_factor); + + for (; w < avctx->width - 5; w += 6) { + WRITE_PIXELS(u, y, v, DEPTH); + WRITE_PIXELS(y, u, y, DEPTH); + WRITE_PIXELS(v, y, u, DEPTH); + WRITE_PIXELS(y, v, y, DEPTH); + } + if (w < avctx->width - 1) { + WRITE_PIXELS(u, y, v, DEPTH); + + val = CLIP(*y++, DEPTH) << (10-DEPTH); + if (w == avctx->width - 2) { + AV_WL32(dst, val); + dst += 4; + } + } + if (w < avctx->width - 3) { + val |= (CLIP(*u++, DEPTH) << (20-DEPTH)) | (CLIP(*y++, DEPTH) << (30-DEPTH)); + AV_WL32(dst, val); + dst += 4; + + val = CLIP(*v++, DEPTH) << (10-DEPTH) | (CLIP(*y++, DEPTH) << (20-DEPTH)); + AV_WL32(dst, val); + dst += 4; + } + + memset(dst, 0, line_padding); + dst += line_padding; + y += pic->linesize[0] / BYTES_PER_PIXEL - avctx->width; + u += pic->linesize[1] / BYTES_PER_PIXEL - avctx->width / 2; + v += pic->linesize[2] / BYTES_PER_PIXEL - avctx->width / 2; + } +} diff --git a/libavcodec/v210dec.c b/libavcodec/v210dec.c index 5a33d8c0899..044d35338bd 100644 --- a/libavcodec/v210dec.c +++ b/libavcodec/v210dec.c @@ -28,6 +28,7 @@ #include "libavutil/internal.h" #include "libavutil/mem.h" #include "libavutil/intreadwrite.h" +#include "thread.h" #define READ_PIXELS(a, b, c) \ do { \ @@ -37,6 +38,12 @@ *c++ = (val >> 20) & 0x3FF; \ } while (0) +typedef struct ThreadData { + AVFrame *frame; + uint8_t *buf; + int stride; +} ThreadData; + static void v210_planar_unpack_c(const uint32_t *src, uint16_t *y, uint16_t *u, uint16_t *v, int width) { uint32_t val; @@ -64,21 +71,81 @@ static av_cold int decode_init(AVCodecContext *avctx) avctx->pix_fmt = AV_PIX_FMT_YUV422P10; avctx->bits_per_raw_sample = 10; + s->thread_count = av_clip(avctx->thread_count, 1, avctx->height/4); s->aligned_input = 0; ff_v210dec_init(s); return 0; } +static int v210_decode_slice(AVCodecContext *avctx, void *arg, int jobnr, int threadnr) +{ + V210DecContext *s = avctx->priv_data; + int h, w; + ThreadData *td = arg; + AVFrame *frame = td->frame; + 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; + uint16_t *y, *u, *v; + + y = (uint16_t*)frame->data[0] + slice_start * frame->linesize[0] / 2; + u = (uint16_t*)frame->data[1] + slice_start * frame->linesize[1] / 2; + v = (uint16_t*)frame->data[2] + slice_start * frame->linesize[2] / 2; + for (h = slice_start; h < slice_end; h++) { + const uint32_t *src = (const uint32_t*)psrc; + uint32_t val; + + w = (avctx->width / 12) * 12; + s->unpack_frame(src, y, u, v, w); + + y += w; + u += w >> 1; + v += w >> 1; + src += (w << 1) / 3; + + if (w < avctx->width - 5) { + READ_PIXELS(u, y, v); + READ_PIXELS(y, u, y); + READ_PIXELS(v, y, u); + READ_PIXELS(y, v, y); + w += 6; + } + + if (w < avctx->width - 1) { + READ_PIXELS(u, y, v); + + val = av_le2ne32(*src++); + *y++ = val & 0x3FF; + if (w < avctx->width - 3) { + *u++ = (val >> 10) & 0x3FF; + *y++ = (val >> 20) & 0x3FF; + + val = av_le2ne32(*src++); + *v++ = val & 0x3FF; + *y++ = (val >> 10) & 0x3FF; + } + } + + psrc += stride; + y += frame->linesize[0] / 2 - avctx->width + (avctx->width & 1); + u += frame->linesize[1] / 2 - avctx->width / 2; + v += frame->linesize[2] / 2 - avctx->width / 2; + } + + return 0; +} + static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *avpkt) { V210DecContext *s = avctx->priv_data; - - int h, w, ret, stride, aligned_input; + ThreadData td; + int ret, stride, aligned_input; + ThreadFrame frame = { .f = data }; AVFrame *pic = data; const uint8_t *psrc = avpkt->data; - uint16_t *y, *u, *v; if (s->custom_stride ) stride = s->custom_stride; @@ -98,6 +165,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, return AVERROR_INVALIDDATA; } } + td.stride = stride; if ( avctx->codec_tag == MKTAG('C', '2', '1', '0') && avpkt->size > 64 && AV_RN32(psrc) == AV_RN32("INFO") @@ -110,55 +178,15 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, ff_v210dec_init(s); } - if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) + if ((ret = ff_thread_get_buffer(avctx, &frame, 0)) < 0) return ret; - y = (uint16_t*)pic->data[0]; - u = (uint16_t*)pic->data[1]; - v = (uint16_t*)pic->data[2]; pic->pict_type = AV_PICTURE_TYPE_I; pic->key_frame = 1; - for (h = 0; h < avctx->height; h++) { - const uint32_t *src = (const uint32_t*)psrc; - uint32_t val; - - w = (avctx->width / 12) * 12; - s->unpack_frame(src, y, u, v, w); - - y += w; - u += w >> 1; - v += w >> 1; - src += (w << 1) / 3; - - if (w < avctx->width - 5) { - READ_PIXELS(u, y, v); - READ_PIXELS(y, u, y); - READ_PIXELS(v, y, u); - READ_PIXELS(y, v, y); - w += 6; - } - - if (w < avctx->width - 1) { - READ_PIXELS(u, y, v); - - val = av_le2ne32(*src++); - *y++ = val & 0x3FF; - if (w < avctx->width - 3) { - *u++ = (val >> 10) & 0x3FF; - *y++ = (val >> 20) & 0x3FF; - - val = av_le2ne32(*src++); - *v++ = val & 0x3FF; - *y++ = (val >> 10) & 0x3FF; - } - } - - psrc += stride; - y += pic->linesize[0] / 2 - avctx->width + (avctx->width & 1); - u += pic->linesize[1] / 2 - avctx->width / 2; - v += pic->linesize[2] / 2 - avctx->width / 2; - } + td.buf = (uint8_t*)psrc; + td.frame = pic; + avctx->execute2(avctx, v210_decode_slice, &td, NULL, s->thread_count); if (avctx->field_order > AV_FIELD_PROGRESSIVE) { /* we have interlaced material flagged in container */ @@ -194,6 +222,8 @@ AVCodec ff_v210_decoder = { .priv_data_size = sizeof(V210DecContext), .init = decode_init, .decode = decode_frame, - .capabilities = AV_CODEC_CAP_DR1, + .capabilities = AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_FRAME_THREADS, .priv_class = &v210dec_class, }; diff --git a/libavcodec/v210dec.h b/libavcodec/v210dec.h index cfdb29da097..662e266315b 100644 --- a/libavcodec/v210dec.h +++ b/libavcodec/v210dec.h @@ -27,6 +27,7 @@ typedef struct { AVClass *av_class; int custom_stride; int aligned_input; + int thread_count; int stride_warning_shown; void (*unpack_frame)(const uint32_t *src, uint16_t *y, uint16_t *u, uint16_t *v, int width); } V210DecContext; diff --git a/libavcodec/v210enc.c b/libavcodec/v210enc.c index b024806d0b4..16e88102712 100644 --- a/libavcodec/v210enc.c +++ b/libavcodec/v210enc.c @@ -26,26 +26,25 @@ #include "internal.h" #include "v210enc.h" -#define CLIP(v) av_clip(v, 4, 1019) -#define CLIP8(v) av_clip(v, 1, 254) - -#define WRITE_PIXELS(a, b, c) \ - do { \ - val = CLIP(*a++); \ - val |= (CLIP(*b++) << 10) | \ - (CLIP(*c++) << 20); \ - AV_WL32(dst, val); \ - dst += 4; \ - } while (0) - -#define WRITE_PIXELS8(a, b, c) \ - do { \ - val = (CLIP8(*a++) << 2); \ - val |= (CLIP8(*b++) << 12) | \ - (CLIP8(*c++) << 22); \ - AV_WL32(dst, val); \ - dst += 4; \ - } while (0) +#define TYPE uint8_t +#define DEPTH 8 +#define BYTES_PER_PIXEL 1 +#define RENAME(a) a ## _ ## 8 +#include "v210_template.c" +#undef RENAME +#undef DEPTH +#undef BYTES_PER_PIXEL +#undef TYPE + +#define TYPE uint16_t +#define DEPTH 10 +#define BYTES_PER_PIXEL 2 +#define RENAME(a) a ## _ ## 10 +#include "v210_template.c" +#undef RENAME +#undef DEPTH +#undef BYTES_PER_PIXEL +#undef TYPE static void v210_planar_pack_8_c(const uint8_t *y, const uint8_t *u, const uint8_t *v, uint8_t *dst, @@ -56,14 +55,14 @@ static void v210_planar_pack_8_c(const uint8_t *y, const uint8_t *u, /* unroll this to match the assembly */ for (i = 0; i < width - 11; i += 12) { - WRITE_PIXELS8(u, y, v); - WRITE_PIXELS8(y, u, y); - WRITE_PIXELS8(v, y, u); - WRITE_PIXELS8(y, v, y); - WRITE_PIXELS8(u, y, v); - WRITE_PIXELS8(y, u, y); - WRITE_PIXELS8(v, y, u); - WRITE_PIXELS8(y, v, y); + WRITE_PIXELS(u, y, v, 8); + WRITE_PIXELS(y, u, y, 8); + WRITE_PIXELS(v, y, u, 8); + WRITE_PIXELS(y, v, y, 8); + WRITE_PIXELS(u, y, v, 8); + WRITE_PIXELS(y, u, y, 8); + WRITE_PIXELS(v, y, u, 8); + WRITE_PIXELS(y, v, y, 8); } } @@ -75,10 +74,10 @@ static void v210_planar_pack_10_c(const uint16_t *y, const uint16_t *u, int i; for (i = 0; i < width - 5; i += 6) { - WRITE_PIXELS(u, y, v); - WRITE_PIXELS(y, u, y); - WRITE_PIXELS(v, y, u); - WRITE_PIXELS(y, v, y); + WRITE_PIXELS(u, y, v, 10); + WRITE_PIXELS(y, u, y, 10); + WRITE_PIXELS(v, y, u, 10); + WRITE_PIXELS(y, v, y, 10); } } @@ -86,7 +85,7 @@ av_cold void ff_v210enc_init(V210EncContext *s) { s->pack_line_8 = v210_planar_pack_8_c; s->pack_line_10 = v210_planar_pack_10_c; - s->sample_factor_8 = 1; + s->sample_factor_8 = 2; s->sample_factor_10 = 1; if (ARCH_X86) @@ -119,12 +118,10 @@ FF_ENABLE_DEPRECATION_WARNINGS static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *pic, int *got_packet) { - V210EncContext *s = avctx->priv_data; int aligned_width = ((avctx->width + 47) / 48) * 48; int stride = aligned_width * 8 / 3; - int line_padding = stride - ((avctx->width * 8 + 11) / 12) * 4; AVFrameSideData *side_data; - int h, w, ret; + int ret; uint8_t *dst; ret = ff_alloc_packet2(avctx, pkt, avctx->height * stride, avctx->height * stride); @@ -134,105 +131,10 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, } dst = pkt->data; - if (pic->format == AV_PIX_FMT_YUV422P10) { - const uint16_t *y = (const uint16_t *)pic->data[0]; - const uint16_t *u = (const uint16_t *)pic->data[1]; - const uint16_t *v = (const uint16_t *)pic->data[2]; - - const int sample_size = 6 * s->sample_factor_10; - const int sample_w = avctx->width / sample_size; - - for (h = 0; h < avctx->height; h++) { - uint32_t val; - w = sample_w * sample_size; - s->pack_line_10(y, u, v, dst, w); - - y += w; - u += w >> 1; - v += w >> 1; - dst += sample_w * 16 * s->sample_factor_10; - - for (; w < avctx->width - 5; w += 6) { - WRITE_PIXELS(u, y, v); - WRITE_PIXELS(y, u, y); - WRITE_PIXELS(v, y, u); - WRITE_PIXELS(y, v, y); - } - if (w < avctx->width - 1) { - WRITE_PIXELS(u, y, v); - - val = CLIP(*y++); - if (w == avctx->width - 2) { - AV_WL32(dst, val); - dst += 4; - } - } - if (w < avctx->width - 3) { - val |= (CLIP(*u++) << 10) | (CLIP(*y++) << 20); - AV_WL32(dst, val); - dst += 4; - - val = CLIP(*v++) | (CLIP(*y++) << 10); - AV_WL32(dst, val); - dst += 4; - } - - memset(dst, 0, line_padding); - dst += line_padding; - y += pic->linesize[0] / 2 - avctx->width; - u += pic->linesize[1] / 2 - avctx->width / 2; - v += pic->linesize[2] / 2 - avctx->width / 2; - } - } else if(pic->format == AV_PIX_FMT_YUV422P) { - const uint8_t *y = pic->data[0]; - const uint8_t *u = pic->data[1]; - const uint8_t *v = pic->data[2]; - - const int sample_size = 12 * s->sample_factor_8; - const int sample_w = avctx->width / sample_size; - - for (h = 0; h < avctx->height; h++) { - uint32_t val; - w = sample_w * sample_size; - s->pack_line_8(y, u, v, dst, w); - - y += w; - u += w >> 1; - v += w >> 1; - dst += sample_w * 32 * s->sample_factor_8; - - for (; w < avctx->width - 5; w += 6) { - WRITE_PIXELS8(u, y, v); - WRITE_PIXELS8(y, u, y); - WRITE_PIXELS8(v, y, u); - WRITE_PIXELS8(y, v, y); - } - if (w < avctx->width - 1) { - WRITE_PIXELS8(u, y, v); - - val = CLIP8(*y++) << 2; - if (w == avctx->width - 2) { - AV_WL32(dst, val); - dst += 4; - } - } - if (w < avctx->width - 3) { - val |= (CLIP8(*u++) << 12) | (CLIP8(*y++) << 22); - AV_WL32(dst, val); - dst += 4; - - val = (CLIP8(*v++) << 2) | (CLIP8(*y++) << 12); - AV_WL32(dst, val); - dst += 4; - } - memset(dst, 0, line_padding); - dst += line_padding; - - y += pic->linesize[0] - avctx->width; - u += pic->linesize[1] - avctx->width / 2; - v += pic->linesize[2] - avctx->width / 2; - } - } + if (pic->format == AV_PIX_FMT_YUV422P10) + v210_enc_10(avctx, dst, pic); + else if(pic->format == AV_PIX_FMT_YUV422P) + v210_enc_8(avctx, dst, pic); side_data = av_frame_get_side_data(pic, AV_FRAME_DATA_A53_CC); if (side_data && side_data->size) { diff --git a/libavcodec/v308enc.c b/libavcodec/v308enc.c index e88f1f46481..29b7f8bedd3 100644 --- a/libavcodec/v308enc.c +++ b/libavcodec/v308enc.c @@ -82,5 +82,4 @@ AVCodec ff_v308_encoder = { .encode2 = v308_encode_frame, .close = v308_encode_close, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV444P, AV_PIX_FMT_NONE }, - .capabilities = AV_CODEC_CAP_INTRA_ONLY, }; diff --git a/libavcodec/v408enc.c b/libavcodec/v408enc.c index e12965b7ad6..a2ab66d9117 100644 --- a/libavcodec/v408enc.c +++ b/libavcodec/v408enc.c @@ -88,7 +88,6 @@ AVCodec ff_ayuv_encoder = { .encode2 = v408_encode_frame, .close = v408_encode_close, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUVA444P, AV_PIX_FMT_NONE }, - .capabilities = AV_CODEC_CAP_INTRA_ONLY, }; #endif #if CONFIG_V408_ENCODER @@ -101,6 +100,5 @@ AVCodec ff_v408_encoder = { .encode2 = v408_encode_frame, .close = v408_encode_close, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUVA444P, AV_PIX_FMT_NONE }, - .capabilities = AV_CODEC_CAP_INTRA_ONLY, }; #endif diff --git a/libavcodec/v410dec.c b/libavcodec/v410dec.c index 48fab682735..7ad5eb8fb53 100644 --- a/libavcodec/v410dec.c +++ b/libavcodec/v410dec.c @@ -24,6 +24,13 @@ #include "libavutil/intreadwrite.h" #include "avcodec.h" #include "internal.h" +#include "thread.h" + +typedef struct ThreadData { + AVFrame *frame; + uint8_t *buf; + int stride; +} ThreadData; static av_cold int v410_decode_init(AVCodecContext *avctx) { @@ -42,31 +49,24 @@ static av_cold int v410_decode_init(AVCodecContext *avctx) return 0; } -static int v410_decode_frame(AVCodecContext *avctx, void *data, - int *got_frame, AVPacket *avpkt) +static int v410_decode_slice(AVCodecContext *avctx, void *arg, int jobnr, int threadnr) { - AVFrame *pic = data; - uint8_t *src = avpkt->data; + ThreadData *td = arg; + AVFrame *pic = td->frame; + int stride = td->stride; + int thread_count = av_clip(avctx->thread_count, 1, avctx->height/4); + int slice_start = (avctx->height * jobnr) / thread_count; + int slice_end = (avctx->height * (jobnr+1)) / thread_count; + const uint8_t *src = td->buf + stride * slice_start; uint16_t *y, *u, *v; uint32_t val; - int i, j, ret; - - if (avpkt->size < 4 * avctx->height * avctx->width) { - av_log(avctx, AV_LOG_ERROR, "Insufficient input data.\n"); - return AVERROR(EINVAL); - } - - if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) - return ret; + int i, j; - pic->key_frame = 1; - pic->pict_type = AV_PICTURE_TYPE_I; + y = (uint16_t*)pic->data[0] + slice_start * (pic->linesize[0] >> 1); + u = (uint16_t*)pic->data[1] + slice_start * (pic->linesize[1] >> 1); + v = (uint16_t*)pic->data[2] + slice_start * (pic->linesize[2] >> 1); - y = (uint16_t *)pic->data[0]; - u = (uint16_t *)pic->data[1]; - v = (uint16_t *)pic->data[2]; - - for (i = 0; i < avctx->height; i++) { + for (i = slice_start; i < slice_end; i++) { for (j = 0; j < avctx->width; j++) { val = AV_RL32(src); @@ -82,6 +82,35 @@ static int v410_decode_frame(AVCodecContext *avctx, void *data, v += pic->linesize[2] >> 1; } + return 0; +} + +static int v410_decode_frame(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *avpkt) +{ + ThreadData td; + ThreadFrame frame = { .f = data }; + AVFrame *pic = data; + uint8_t *src = avpkt->data; + int ret; + int thread_count = av_clip(avctx->thread_count, 1, avctx->height/4); + + td.stride = avctx->width * 4; + if (avpkt->size < 4 * avctx->height * avctx->width) { + av_log(avctx, AV_LOG_ERROR, "Insufficient input data.\n"); + return AVERROR(EINVAL); + } + + if ((ret = ff_thread_get_buffer(avctx, &frame, 0)) < 0) + return ret; + + pic->key_frame = 1; + pic->pict_type = AV_PICTURE_TYPE_I; + + td.buf = src; + td.frame = pic; + avctx->execute2(avctx, v410_decode_slice, &td, NULL, thread_count); + *got_frame = 1; return avpkt->size; @@ -94,5 +123,6 @@ AVCodec ff_v410_decoder = { .id = AV_CODEC_ID_V410, .init = v410_decode_init, .decode = v410_decode_frame, - .capabilities = AV_CODEC_CAP_DR1, + .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_FRAME_THREADS }; diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c index aef911f3bbc..02f23d954b2 100644 --- a/libavcodec/v4l2_buffers.c +++ b/libavcodec/v4l2_buffers.c @@ -29,11 +29,13 @@ #include #include "libavcodec/avcodec.h" #include "libavcodec/internal.h" +#include "libavutil/pixdesc.h" #include "v4l2_context.h" #include "v4l2_buffers.h" #include "v4l2_m2m.h" #define USEC_PER_SEC 1000000 +static AVRational v4l2_timebase = { 1, USEC_PER_SEC }; static inline V4L2m2mContext *buf_to_m2mctx(V4L2Buffer *buf) { @@ -47,32 +49,37 @@ static inline AVCodecContext *logger(V4L2Buffer *buf) return buf_to_m2mctx(buf)->avctx; } +static inline AVRational v4l2_get_timebase(V4L2Buffer *avbuf) +{ + V4L2m2mContext *s = buf_to_m2mctx(avbuf); + + if (s->avctx->pkt_timebase.num) + return s->avctx->pkt_timebase; + return s->avctx->time_base; +} + static inline void v4l2_set_pts(V4L2Buffer *out, int64_t pts) { - V4L2m2mContext *s = buf_to_m2mctx(out); - AVRational v4l2_timebase = { 1, USEC_PER_SEC }; int64_t v4l2_pts; if (pts == AV_NOPTS_VALUE) pts = 0; /* convert pts to v4l2 timebase */ - v4l2_pts = av_rescale_q(pts, s->avctx->time_base, v4l2_timebase); + v4l2_pts = av_rescale_q(pts, v4l2_get_timebase(out), v4l2_timebase); out->buf.timestamp.tv_usec = v4l2_pts % USEC_PER_SEC; out->buf.timestamp.tv_sec = v4l2_pts / USEC_PER_SEC; } -static inline uint64_t v4l2_get_pts(V4L2Buffer *avbuf) +static inline int64_t v4l2_get_pts(V4L2Buffer *avbuf) { - V4L2m2mContext *s = buf_to_m2mctx(avbuf); - AVRational v4l2_timebase = { 1, USEC_PER_SEC }; int64_t v4l2_pts; /* convert pts back to encoder timebase */ v4l2_pts = (int64_t)avbuf->buf.timestamp.tv_sec * USEC_PER_SEC + avbuf->buf.timestamp.tv_usec; - return av_rescale_q(v4l2_pts, v4l2_timebase, s->avctx->time_base); + return av_rescale_q(v4l2_pts, v4l2_timebase, v4l2_get_timebase(avbuf)); } static enum AVColorPrimaries v4l2_get_color_primaries(V4L2Buffer *buf) @@ -215,7 +222,7 @@ static void v4l2_free_buffer(void *opaque, uint8_t *unused) if (!atomic_load(&s->refcount)) sem_post(&s->refsync); } else { - if (s->draining) { + if (s->draining && V4L2_TYPE_IS_OUTPUT(avbuf->context->type)) { /* no need to queue more buffers to the driver */ avbuf->status = V4L2BUF_AVAILABLE; } @@ -227,27 +234,17 @@ static void v4l2_free_buffer(void *opaque, uint8_t *unused) } } -static int v4l2_buf_to_bufref(V4L2Buffer *in, int plane, AVBufferRef **buf) +static int v4l2_buf_increase_ref(V4L2Buffer *in) { V4L2m2mContext *s = buf_to_m2mctx(in); - if (plane >= in->num_planes) - return AVERROR(EINVAL); - - /* even though most encoders return 0 in data_offset encoding vp8 does require this value */ - *buf = av_buffer_create((char *)in->plane_info[plane].mm_addr + in->planes[plane].data_offset, - in->plane_info[plane].length, v4l2_free_buffer, in, 0); - if (!*buf) - return AVERROR(ENOMEM); - if (in->context_ref) atomic_fetch_add(&in->context_refcount, 1); else { in->context_ref = av_buffer_ref(s->self_ref); - if (!in->context_ref) { - av_buffer_unref(buf); + if (!in->context_ref) return AVERROR(ENOMEM); - } + in->context_refcount = 1; } @@ -257,17 +254,37 @@ static int v4l2_buf_to_bufref(V4L2Buffer *in, int plane, AVBufferRef **buf) return 0; } -static int v4l2_bufref_to_buf(V4L2Buffer *out, int plane, const uint8_t* data, int size, AVBufferRef* bref) +static int v4l2_buf_to_bufref(V4L2Buffer *in, int plane, AVBufferRef **buf) +{ + int ret; + + if (plane >= in->num_planes) + return AVERROR(EINVAL); + + /* even though most encoders return 0 in data_offset encoding vp8 does require this value */ + *buf = av_buffer_create((char *)in->plane_info[plane].mm_addr + in->planes[plane].data_offset, + in->plane_info[plane].length, v4l2_free_buffer, in, 0); + if (!*buf) + return AVERROR(ENOMEM); + + ret = v4l2_buf_increase_ref(in); + if (ret) + av_buffer_unref(buf); + + return ret; +} + +static int v4l2_bufref_to_buf(V4L2Buffer *out, int plane, const uint8_t* data, int size, int offset, AVBufferRef* bref) { unsigned int bytesused, length; if (plane >= out->num_planes) return AVERROR(EINVAL); - bytesused = FFMIN(size, out->plane_info[plane].length); length = out->plane_info[plane].length; + bytesused = FFMIN(size+offset, length); - memcpy(out->plane_info[plane].mm_addr, data, FFMIN(size, out->plane_info[plane].length)); + memcpy((uint8_t*)out->plane_info[plane].mm_addr+offset, data, FFMIN(size, length-offset)); if (V4L2_TYPE_IS_MULTIPLANAR(out->buf.type)) { out->planes[plane].bytesused = bytesused; @@ -280,35 +297,12 @@ static int v4l2_bufref_to_buf(V4L2Buffer *out, int plane, const uint8_t* data, i return 0; } -/****************************************************************************** - * - * V4L2uffer interface - * - ******************************************************************************/ - -int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer* out) +static int v4l2_buffer_buf_to_swframe(AVFrame *frame, V4L2Buffer *avbuf) { int i, ret; - for(i = 0; i < out->num_planes; i++) { - ret = v4l2_bufref_to_buf(out, i, frame->buf[i]->data, frame->buf[i]->size, frame->buf[i]); - if (ret) - return ret; - } - - v4l2_set_pts(out, frame->pts); - - return 0; -} - -int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf) -{ - V4L2m2mContext *s = buf_to_m2mctx(avbuf); - int i, ret; - - av_frame_unref(frame); + frame->format = avbuf->context->av_pix_fmt; - /* 1. get references to the actual data */ for (i = 0; i < avbuf->num_planes; i++) { ret = v4l2_buf_to_bufref(avbuf, i, &frame->buf[i]); if (ret) @@ -318,30 +312,134 @@ int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf) frame->data[i] = frame->buf[i]->data; } - /* 1.1 fixup special cases */ + /* fixup special cases */ switch (avbuf->context->av_pix_fmt) { case AV_PIX_FMT_NV12: + case AV_PIX_FMT_NV21: if (avbuf->num_planes > 1) break; frame->linesize[1] = avbuf->plane_info[0].bytesperline; frame->data[1] = frame->buf[0]->data + avbuf->plane_info[0].bytesperline * avbuf->context->format.fmt.pix_mp.height; break; + + case AV_PIX_FMT_YUV420P: + if (avbuf->num_planes > 1) + break; + frame->linesize[1] = avbuf->plane_info[0].bytesperline >> 1; + frame->linesize[2] = avbuf->plane_info[0].bytesperline >> 1; + frame->data[1] = frame->buf[0]->data + avbuf->plane_info[0].bytesperline * avbuf->context->format.fmt.pix_mp.height; + frame->data[2] = frame->data[1] + ((avbuf->plane_info[0].bytesperline * avbuf->context->format.fmt.pix_mp.height) >> 2); + break; + default: break; } + return 0; +} + +static int v4l2_buffer_swframe_to_buf(const AVFrame *frame, V4L2Buffer *out) +{ + int i, ret; + struct v4l2_format fmt = out->context->format; + int pixel_format = V4L2_TYPE_IS_MULTIPLANAR(fmt.type) ? + fmt.fmt.pix_mp.pixelformat : fmt.fmt.pix.pixelformat; + int height = V4L2_TYPE_IS_MULTIPLANAR(fmt.type) ? + fmt.fmt.pix_mp.height : fmt.fmt.pix.height; + int is_planar_format = 0; + + switch (pixel_format) { + case V4L2_PIX_FMT_YUV420M: + case V4L2_PIX_FMT_YVU420M: +#ifdef V4L2_PIX_FMT_YUV422M + case V4L2_PIX_FMT_YUV422M: +#endif +#ifdef V4L2_PIX_FMT_YVU422M + case V4L2_PIX_FMT_YVU422M: +#endif +#ifdef V4L2_PIX_FMT_YUV444M + case V4L2_PIX_FMT_YUV444M: +#endif +#ifdef V4L2_PIX_FMT_YVU444M + case V4L2_PIX_FMT_YVU444M: +#endif + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_NV21M: + case V4L2_PIX_FMT_NV12MT_16X16: + case V4L2_PIX_FMT_NV12MT: + case V4L2_PIX_FMT_NV16M: + case V4L2_PIX_FMT_NV61M: + is_planar_format = 1; + } + + if (!is_planar_format) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); + int planes_nb = 0; + int offset = 0; + + for (i = 0; i < desc->nb_components; i++) + planes_nb = FFMAX(planes_nb, desc->comp[i].plane + 1); + + for (i = 0; i < planes_nb; i++) { + int size, h = height; + if (i == 1 || i == 2) { + h = AV_CEIL_RSHIFT(h, desc->log2_chroma_h); + } + size = frame->linesize[i] * h; + ret = v4l2_bufref_to_buf(out, 0, frame->data[i], size, offset, frame->buf[i]); + if (ret) + return ret; + offset += size; + } + return 0; + } + + for (i = 0; i < out->num_planes; i++) { + ret = v4l2_bufref_to_buf(out, i, frame->buf[i]->data, frame->buf[i]->size, 0, frame->buf[i]); + if (ret) + return ret; + } + + return 0; +} + +/****************************************************************************** + * + * V4L2Buffer interface + * + ******************************************************************************/ + +int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer *out) +{ + v4l2_set_pts(out, frame->pts); + + return v4l2_buffer_swframe_to_buf(frame, out); +} + +int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf) +{ + int ret; + + av_frame_unref(frame); + + /* 1. get references to the actual data */ + ret = v4l2_buffer_buf_to_swframe(frame, avbuf); + if (ret) + return ret; + /* 2. get frame information */ frame->key_frame = !!(avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME); - frame->format = avbuf->context->av_pix_fmt; frame->color_primaries = v4l2_get_color_primaries(avbuf); frame->colorspace = v4l2_get_color_space(avbuf); frame->color_range = v4l2_get_color_range(avbuf); frame->color_trc = v4l2_get_color_trc(avbuf); frame->pts = v4l2_get_pts(avbuf); + frame->pkt_dts = AV_NOPTS_VALUE; - /* these two values are updated also during re-init in v4l2_process_driver_event */ - frame->height = s->output.height; - frame->width = s->output.width; + /* these values are updated also during re-init in v4l2_process_driver_event */ + frame->height = avbuf->context->height; + frame->width = avbuf->context->width; + frame->sample_aspect_ratio = avbuf->context->sample_aspect_ratio; /* 3. report errors upstream */ if (avbuf->buf.flags & V4L2_BUF_FLAG_ERROR) { @@ -381,7 +479,7 @@ int ff_v4l2_buffer_avpkt_to_buf(const AVPacket *pkt, V4L2Buffer *out) { int ret; - ret = v4l2_bufref_to_buf(out, 0, pkt->data, pkt->size, pkt->buf); + ret = v4l2_bufref_to_buf(out, 0, pkt->data, pkt->size, 0, pkt->buf); if (ret) return ret; @@ -413,11 +511,9 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { avbuf->num_planes = 0; - for (;;) { - /* in MP, the V4L2 API states that buf.length means num_planes */ - if (avbuf->num_planes >= avbuf->buf.length) - break; - if (avbuf->buf.m.planes[avbuf->num_planes].length) + /* in MP, the V4L2 API states that buf.length means num_planes */ + for (i = 0; i < avbuf->buf.length; i++) { + if (avbuf->buf.m.planes[i].length) avbuf->num_planes++; } } else diff --git a/libavcodec/v4l2_buffers.h b/libavcodec/v4l2_buffers.h index 7a57caf9495..8dbc7fc104d 100644 --- a/libavcodec/v4l2_buffers.h +++ b/libavcodec/v4l2_buffers.h @@ -106,7 +106,7 @@ int ff_v4l2_buffer_avpkt_to_buf(const AVPacket *pkt, V4L2Buffer *out); * * @returns 0 in case of success, a negative AVERROR code otherwise */ -int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer* out); +int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer *out); /** * Initializes a V4L2Buffer diff --git a/libavcodec/v4l2_context.c b/libavcodec/v4l2_context.c index efcb0426e49..29b144ed73e 100644 --- a/libavcodec/v4l2_context.c +++ b/libavcodec/v4l2_context.c @@ -63,6 +63,24 @@ static inline unsigned int v4l2_get_height(struct v4l2_format *fmt) return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.height : fmt->fmt.pix.height; } +static AVRational v4l2_get_sar(V4L2Context *ctx) +{ + struct AVRational sar = { 0, 1 }; + struct v4l2_cropcap cropcap; + int ret; + + memset(&cropcap, 0, sizeof(cropcap)); + cropcap.type = ctx->type; + + ret = ioctl(ctx_to_m2mctx(ctx)->fd, VIDIOC_CROPCAP, &cropcap); + if (ret) + return sar; + + sar.num = cropcap.pixelaspect.numerator; + sar.den = cropcap.pixelaspect.denominator; + return sar; +} + static inline unsigned int v4l2_resolution_changed(V4L2Context *ctx, struct v4l2_format *fmt2) { struct v4l2_format *fmt1 = &ctx->format; @@ -96,7 +114,7 @@ static inline int v4l2_get_framesize_compressed(V4L2Context* ctx, int width, int const int SZ_4K = 0x1000; int size; - if (av_codec_is_decoder(s->avctx->codec)) + if (s->avctx && av_codec_is_decoder(s->avctx->codec)) return ((width * height * 3 / 2) / 2) + 128; /* encoder */ @@ -136,6 +154,7 @@ static inline void v4l2_save_to_context(V4L2Context* ctx, struct v4l2_format_upd } /** + * handle resolution change event and end of stream event * returns 1 if reinit was successful, negative if it failed * returns 0 if reinit was not executed */ @@ -153,6 +172,11 @@ static int v4l2_handle_event(V4L2Context *ctx) return 0; } + if (evt.type == V4L2_EVENT_EOS) { + ctx->done = 1; + return 0; + } + if (evt.type != V4L2_EVENT_SOURCE_CHANGE) return 0; @@ -172,12 +196,14 @@ static int v4l2_handle_event(V4L2Context *ctx) if (full_reinit) { s->output.height = v4l2_get_height(&out_fmt); s->output.width = v4l2_get_width(&out_fmt); + s->output.sample_aspect_ratio = v4l2_get_sar(&s->output); } reinit = v4l2_resolution_changed(&s->capture, &cap_fmt); if (reinit) { s->capture.height = v4l2_get_height(&cap_fmt); s->capture.width = v4l2_get_width(&cap_fmt); + s->capture.sample_aspect_ratio = v4l2_get_sar(&s->capture); } if (full_reinit || reinit) @@ -187,20 +213,21 @@ static int v4l2_handle_event(V4L2Context *ctx) ret = ff_v4l2_m2m_codec_full_reinit(s); if (ret) { av_log(logger(ctx), AV_LOG_ERROR, "v4l2_m2m_codec_full_reinit\n"); - return -EINVAL; + return AVERROR(EINVAL); } goto reinit_run; } if (reinit) { - ret = ff_set_dimensions(s->avctx, s->capture.width, s->capture.height); + if (s->avctx) + ret = ff_set_dimensions(s->avctx, s->capture.width, s->capture.height); if (ret < 0) av_log(logger(ctx), AV_LOG_WARNING, "update avcodec height and width\n"); ret = ff_v4l2_m2m_codec_reinit(s); if (ret) { av_log(logger(ctx), AV_LOG_ERROR, "v4l2_m2m_codec_reinit\n"); - return -EINVAL; + return AVERROR(EINVAL); } goto reinit_run; } @@ -226,6 +253,8 @@ static int v4l2_stop_decode(V4L2Context *ctx) /* DECODER_CMD is optional */ if (errno == ENOTTY) return ff_v4l2_context_set_status(ctx, VIDIOC_STREAMOFF); + else + return AVERROR(errno); } return 0; @@ -244,6 +273,8 @@ static int v4l2_stop_encode(V4L2Context *ctx) /* ENCODER_CMD is optional */ if (errno == ENOTTY) return ff_v4l2_context_set_status(ctx, VIDIOC_STREAMOFF); + else + return AVERROR(errno); } return 0; @@ -253,16 +284,34 @@ static V4L2Buffer* v4l2_dequeue_v4l2buf(V4L2Context *ctx, int timeout) { struct v4l2_plane planes[VIDEO_MAX_PLANES]; struct v4l2_buffer buf = { 0 }; - V4L2Buffer* avbuf = NULL; + V4L2Buffer *avbuf; struct pollfd pfd = { .events = POLLIN | POLLRDNORM | POLLPRI | POLLOUT | POLLWRNORM, /* default blocking capture */ .fd = ctx_to_m2mctx(ctx)->fd, }; int i, ret; + if (!V4L2_TYPE_IS_OUTPUT(ctx->type) && ctx->buffers) { + for (i = 0; i < ctx->num_buffers; i++) { + if (ctx->buffers[i].status == V4L2BUF_IN_DRIVER) + break; + } + if (i == ctx->num_buffers) + av_log(logger(ctx), AV_LOG_WARNING, "All capture buffers returned to " + "userspace. Increase num_capture_buffers " + "to prevent device deadlock or dropped " + "packets/frames.\n"); + } + /* if we are draining and there are no more capture buffers queued in the driver we are done */ if (!V4L2_TYPE_IS_OUTPUT(ctx->type) && ctx_to_m2mctx(ctx)->draining) { for (i = 0; i < ctx->num_buffers; i++) { + /* capture buffer initialization happens during decode hence + * detection happens at runtime + */ + if (!ctx->buffers) + break; + if (ctx->buffers[i].status == V4L2BUF_IN_DRIVER) goto start; } @@ -356,6 +405,19 @@ static V4L2Buffer* v4l2_dequeue_v4l2buf(V4L2Context *ctx, int timeout) return NULL; } + if (ctx_to_m2mctx(ctx)->draining && !V4L2_TYPE_IS_OUTPUT(ctx->type)) { + int bytesused = V4L2_TYPE_IS_MULTIPLANAR(buf.type) ? + buf.m.planes[0].bytesused : buf.bytesused; + if (bytesused == 0) { + ctx->done = 1; + return NULL; + } +#ifdef V4L2_BUF_FLAG_LAST + if (buf.flags & V4L2_BUF_FLAG_LAST) + ctx->done = 1; +#endif + } + avbuf = &ctx->buffers[buf.index]; avbuf->status = V4L2BUF_AVAILABLE; avbuf->buf = buf; @@ -562,7 +624,7 @@ int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt) avbuf = v4l2_getfree_v4l2buf(ctx); if (!avbuf) - return AVERROR(ENOMEM); + return AVERROR(EAGAIN); ret = ff_v4l2_buffer_avpkt_to_buf(pkt, avbuf); if (ret) @@ -571,16 +633,16 @@ int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt) return ff_v4l2_buffer_enqueue(avbuf); } -int ff_v4l2_context_dequeue_frame(V4L2Context* ctx, AVFrame* frame) +int ff_v4l2_context_dequeue_frame(V4L2Context* ctx, AVFrame* frame, int timeout) { - V4L2Buffer* avbuf = NULL; + V4L2Buffer *avbuf; /* - * blocks until: + * timeout=-1 blocks until: * 1. decoded frame available * 2. an input buffer is ready to be dequeued */ - avbuf = v4l2_dequeue_v4l2buf(ctx, -1); + avbuf = v4l2_dequeue_v4l2buf(ctx, timeout); if (!avbuf) { if (ctx->done) return AVERROR_EOF; @@ -593,7 +655,7 @@ int ff_v4l2_context_dequeue_frame(V4L2Context* ctx, AVFrame* frame) int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt) { - V4L2Buffer* avbuf = NULL; + V4L2Buffer *avbuf; /* * blocks until: @@ -611,7 +673,7 @@ int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt) return ff_v4l2_buffer_buf_to_avpkt(pkt, avbuf); } -int ff_v4l2_context_get_format(V4L2Context* ctx) +int ff_v4l2_context_get_format(V4L2Context* ctx, int probe) { struct v4l2_format_update fmt = { 0 }; int ret; @@ -621,7 +683,7 @@ int ff_v4l2_context_get_format(V4L2Context* ctx) if (ret) return ret; - fmt.update_avfmt = 1; + fmt.update_avfmt = !probe; v4l2_save_to_context(ctx, &fmt); /* format has been tried already */ @@ -654,8 +716,7 @@ void ff_v4l2_context_release(V4L2Context* ctx) if (ret) av_log(logger(ctx), AV_LOG_WARNING, "V4L2 failed to unmap the %s buffers\n", ctx->name); - av_free(ctx->buffers); - ctx->buffers = NULL; + av_freep(&ctx->buffers); } int ff_v4l2_context_init(V4L2Context* ctx) @@ -678,23 +739,24 @@ int ff_v4l2_context_init(V4L2Context* ctx) req.memory = V4L2_MEMORY_MMAP; req.type = ctx->type; ret = ioctl(s->fd, VIDIOC_REQBUFS, &req); - if (ret < 0) + if (ret < 0) { + av_log(logger(ctx), AV_LOG_ERROR, "%s VIDIOC_REQBUFS failed: %s\n", ctx->name, strerror(errno)); return AVERROR(errno); + } ctx->num_buffers = req.count; ctx->buffers = av_mallocz(ctx->num_buffers * sizeof(V4L2Buffer)); if (!ctx->buffers) { - av_log(logger(ctx), AV_LOG_ERROR, "%s malloc enomem\n", ctx->name); - return AVERROR(ENOMEM); + av_log(logger(ctx), AV_LOG_ERROR, "%s malloc enomem\n", ctx->name); + return AVERROR(ENOMEM); } for (i = 0; i < req.count; i++) { ctx->buffers[i].context = ctx; ret = ff_v4l2_buffer_initialize(&ctx->buffers[i], i); if (ret < 0) { - av_log(logger(ctx), AV_LOG_ERROR, "%s buffer initialization (%s)\n", ctx->name, av_err2str(ret)); - av_free(ctx->buffers); - return ret; + av_log(logger(ctx), AV_LOG_ERROR, "%s buffer[%d] initialization (%s)\n", ctx->name, i, av_err2str(ret)); + goto error; } } @@ -707,4 +769,11 @@ int ff_v4l2_context_init(V4L2Context* ctx) V4L2_TYPE_IS_MULTIPLANAR(ctx->type) ? ctx->format.fmt.pix_mp.plane_fmt[0].bytesperline : ctx->format.fmt.pix.bytesperline); return 0; + +error: + v4l2_release_buffers(ctx); + + av_freep(&ctx->buffers); + + return ret; } diff --git a/libavcodec/v4l2_context.h b/libavcodec/v4l2_context.h index 632f1d0aac0..22a9532444b 100644 --- a/libavcodec/v4l2_context.h +++ b/libavcodec/v4l2_context.h @@ -69,6 +69,7 @@ typedef struct V4L2Context { * or accepts (in case of an output context, e.g. when encoding). */ int width, height; + AVRational sample_aspect_ratio; /** * Indexed array of V4L2Buffers @@ -113,9 +114,10 @@ int ff_v4l2_context_set_format(V4L2Context* ctx); * Queries the driver for a valid v4l2 format and copies it to the context. * * @param[in] ctx A pointer to a V4L2Context. See V4L2Context description for required variables. + * @param[in] probe Probe only and ignore changes to the format. * @return 0 in case of success, a negative value representing the error otherwise. */ -int ff_v4l2_context_get_format(V4L2Context* ctx); +int ff_v4l2_context_get_format(V4L2Context* ctx, int probe); /** * Releases a V4L2Context. @@ -153,9 +155,10 @@ int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt); * The frame must be non NULL. * @param[in] ctx The V4L2Context to dequeue from. * @param[inout] f The AVFrame to dequeue to. + * @param[in] timeout The timeout for dequeue (-1 to block, 0 to return immediately, or milliseconds) * @return 0 in case of success, AVERROR(EAGAIN) if no buffer was ready, another negative error in case of error. */ -int ff_v4l2_context_dequeue_frame(V4L2Context* ctx, AVFrame* f); +int ff_v4l2_context_dequeue_frame(V4L2Context* ctx, AVFrame* f, int timeout); /** * Enqueues a buffer to a V4L2Context from an AVPacket diff --git a/libavcodec/v4l2_m2m.c b/libavcodec/v4l2_m2m.c index 427e165f586..e48b3a8ccf7 100644 --- a/libavcodec/v4l2_m2m.c +++ b/libavcodec/v4l2_m2m.c @@ -60,14 +60,15 @@ static inline int v4l2_mplane_video(struct v4l2_capability *cap) return 0; } -static int v4l2_prepare_contexts(V4L2m2mContext* s) +static int v4l2_prepare_contexts(V4L2m2mContext *s, int probe) { struct v4l2_capability cap; + void *log_ctx = s->avctx; int ret; s->capture.done = s->output.done = 0; s->capture.name = "capture"; - s->output.name = "output "; + s->output.name = "output"; atomic_init(&s->refcount, 0); sem_init(&s->refsync, 0, 0); @@ -76,7 +77,10 @@ static int v4l2_prepare_contexts(V4L2m2mContext* s) if (ret < 0) return ret; - av_log(s->avctx, AV_LOG_INFO, "driver '%s' on card '%s'\n", cap.driver, cap.card); + av_log(log_ctx, probe ? AV_LOG_DEBUG : AV_LOG_INFO, + "driver '%s' on card '%s' in %s mode\n", cap.driver, cap.card, + v4l2_mplane_video(&cap) ? "mplane" : + v4l2_splane_video(&cap) ? "splane" : "unknown"); if (v4l2_mplane_video(&cap)) { s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; @@ -93,34 +97,35 @@ static int v4l2_prepare_contexts(V4L2m2mContext* s) return AVERROR(EINVAL); } -static int v4l2_probe_driver(V4L2m2mContext* s) +static int v4l2_probe_driver(V4L2m2mContext *s) { + void *log_ctx = s->avctx; int ret; s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0); if (s->fd < 0) return AVERROR(errno); - ret = v4l2_prepare_contexts(s); + ret = v4l2_prepare_contexts(s, 1); if (ret < 0) goto done; - ret = ff_v4l2_context_get_format(&s->output); + ret = ff_v4l2_context_get_format(&s->output, 1); if (ret) { - av_log(s->avctx, AV_LOG_DEBUG, "v4l2 output format not supported\n"); + av_log(log_ctx, AV_LOG_DEBUG, "v4l2 output format not supported\n"); goto done; } - ret = ff_v4l2_context_get_format(&s->capture); + ret = ff_v4l2_context_get_format(&s->capture, 1); if (ret) { - av_log(s->avctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n"); + av_log(log_ctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n"); goto done; } done: if (close(s->fd) < 0) { ret = AVERROR(errno); - av_log(s->avctx, AV_LOG_ERROR, "failure closing %s (%s)\n", s->devname, av_err2str(AVERROR(errno))); + av_log(log_ctx, AV_LOG_ERROR, "failure closing %s (%s)\n", s->devname, av_err2str(AVERROR(errno))); } s->fd = -1; @@ -128,19 +133,30 @@ static int v4l2_probe_driver(V4L2m2mContext* s) return ret; } -static int v4l2_configure_contexts(V4L2m2mContext* s) +static int v4l2_configure_contexts(V4L2m2mContext *s) { void *log_ctx = s->avctx; int ret; + struct v4l2_format ofmt, cfmt; s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0); if (s->fd < 0) return AVERROR(errno); - ret = v4l2_prepare_contexts(s); + ret = v4l2_prepare_contexts(s, 0); if (ret < 0) goto error; + ofmt = s->output.format; + cfmt = s->capture.format; + av_log(log_ctx, AV_LOG_INFO, "requesting formats: output=%s capture=%s\n", + av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(ofmt.type) ? + ofmt.fmt.pix_mp.pixelformat : + ofmt.fmt.pix.pixelformat), + av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(cfmt.type) ? + cfmt.fmt.pix_mp.pixelformat : + cfmt.fmt.pix.pixelformat)); + ret = ff_v4l2_context_set_format(&s->output); if (ret) { av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n"); @@ -160,7 +176,7 @@ static int v4l2_configure_contexts(V4L2m2mContext* s) } /* decoder's buffers need to be updated at a later stage */ - if (!av_codec_is_decoder(s->avctx->codec)) { + if (s->avctx && !av_codec_is_decoder(s->avctx->codec)) { ret = ff_v4l2_context_init(&s->capture); if (ret) { av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n"); @@ -186,38 +202,39 @@ static int v4l2_configure_contexts(V4L2m2mContext* s) * V4L2 M2M Interface * ******************************************************************************/ -int ff_v4l2_m2m_codec_reinit(V4L2m2mContext* s) +int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s) { + void *log_ctx = s->avctx; int ret; - av_log(s->avctx, AV_LOG_DEBUG, "reinit context\n"); + av_log(log_ctx, AV_LOG_DEBUG, "reinit context\n"); /* 1. streamoff */ ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF); if (ret) - av_log(s->avctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n"); + av_log(log_ctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n"); /* 2. unmap the capture buffers (v4l2 and ffmpeg): * we must wait for all references to be released before being allowed * to queue new buffers. */ - av_log(s->avctx, AV_LOG_DEBUG, "waiting for user to release AVBufferRefs\n"); + av_log(log_ctx, AV_LOG_DEBUG, "waiting for user to release AVBufferRefs\n"); if (atomic_load(&s->refcount)) while(sem_wait(&s->refsync) == -1 && errno == EINTR); ff_v4l2_context_release(&s->capture); /* 3. get the new capture format */ - ret = ff_v4l2_context_get_format(&s->capture); + ret = ff_v4l2_context_get_format(&s->capture, 0); if (ret) { - av_log(s->avctx, AV_LOG_ERROR, "query the new capture format\n"); + av_log(log_ctx, AV_LOG_ERROR, "query the new capture format\n"); return ret; } /* 4. set the capture format */ ret = ff_v4l2_context_set_format(&s->capture); if (ret) { - av_log(s->avctx, AV_LOG_ERROR, "setting capture format\n"); + av_log(log_ctx, AV_LOG_ERROR, "setting capture format\n"); return ret; } @@ -241,14 +258,14 @@ int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *s) ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF); if (ret) { - av_log(s->avctx, AV_LOG_ERROR, "output VIDIOC_STREAMOFF\n"); + av_log(log_ctx, AV_LOG_ERROR, "output VIDIOC_STREAMOFF\n"); goto error; } ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF); if (ret) { - av_log(s->avctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n"); - goto error; + av_log(log_ctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n"); + goto error; } /* release and unmmap the buffers */ @@ -259,13 +276,13 @@ int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *s) s->draining = 0; s->reinit = 0; - ret = ff_v4l2_context_get_format(&s->output); + ret = ff_v4l2_context_get_format(&s->output, 0); if (ret) { av_log(log_ctx, AV_LOG_DEBUG, "v4l2 output format not supported\n"); goto error; } - ret = ff_v4l2_context_get_format(&s->capture); + ret = ff_v4l2_context_get_format(&s->capture, 0); if (ret) { av_log(log_ctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n"); goto error; @@ -290,7 +307,7 @@ int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *s) } /* decoder's buffers need to be updated at a later stage */ - if (!av_codec_is_decoder(s->avctx->codec)) { + if (s->avctx && !av_codec_is_decoder(s->avctx->codec)) { ret = ff_v4l2_context_init(&s->capture); if (ret) { av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n"); @@ -316,19 +333,18 @@ static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context) av_free(s); } -int ff_v4l2_m2m_codec_end(AVCodecContext *avctx) +int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv) { - V4L2m2mPriv *priv = avctx->priv_data; - V4L2m2mContext* s = priv->context; + V4L2m2mContext *s = priv->context; int ret; ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF); if (ret) - av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name); + av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name); ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF); if (ret) - av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name); + av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name); ff_v4l2_context_release(&s->output); @@ -338,15 +354,13 @@ int ff_v4l2_m2m_codec_end(AVCodecContext *avctx) return 0; } -int ff_v4l2_m2m_codec_init(AVCodecContext *avctx) +int ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv) { int ret = AVERROR(EINVAL); struct dirent *entry; - char node[PATH_MAX]; DIR *dirp; - V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context; - s->avctx = avctx; + V4L2m2mContext *s = priv->context; dirp = opendir("/dev"); if (!dirp) @@ -357,12 +371,11 @@ int ff_v4l2_m2m_codec_init(AVCodecContext *avctx) if (strncmp(entry->d_name, "video", 5)) continue; - snprintf(node, sizeof(node), "/dev/%s", entry->d_name); - av_log(s->avctx, AV_LOG_DEBUG, "probing device %s\n", node); - strncpy(s->devname, node, strlen(node) + 1); + snprintf(s->devname, sizeof(s->devname), "/dev/%s", entry->d_name); + av_log(s->avctx, AV_LOG_DEBUG, "probing device %s\n", s->devname); ret = v4l2_probe_driver(s); if (!ret) - break; + break; } closedir(dirp); @@ -374,15 +387,13 @@ int ff_v4l2_m2m_codec_init(AVCodecContext *avctx) return ret; } - av_log(s->avctx, AV_LOG_INFO, "Using device %s\n", node); + av_log(s->avctx, AV_LOG_INFO, "Using device %s\n", s->devname); return v4l2_configure_contexts(s); } -int ff_v4l2_m2m_create_context(AVCodecContext *avctx, V4L2m2mContext **s) +int ff_v4l2_m2m_create_context(V4L2m2mPriv *priv, V4L2m2mContext **s) { - V4L2m2mPriv *priv = avctx->priv_data; - *s = av_mallocz(sizeof(V4L2m2mContext)); if (!*s) return AVERROR(ENOMEM); @@ -396,11 +407,13 @@ int ff_v4l2_m2m_create_context(AVCodecContext *avctx, V4L2m2mContext **s) /* assign the context */ priv->context = *s; + (*s)->priv = priv; /* populate it */ priv->context->capture.num_buffers = priv->num_capture_buffers; priv->context->output.num_buffers = priv->num_output_buffers; priv->context->self_ref = priv->context_ref; + priv->context->fd = -1; return 0; } diff --git a/libavcodec/v4l2_m2m.h b/libavcodec/v4l2_m2m.h index 0d4671beb1a..456281f48c5 100644 --- a/libavcodec/v4l2_m2m.h +++ b/libavcodec/v4l2_m2m.h @@ -56,13 +56,16 @@ typedef struct V4L2m2mContext { /* null frame/packet received */ int draining; + AVPacket buf_pkt; /* Reference to self; only valid while codec is active. */ AVBufferRef *self_ref; + + /* reference back to V4L2m2mPriv */ + void *priv; } V4L2m2mContext; -typedef struct V4L2m2mPriv -{ +typedef struct V4L2m2mPriv { AVClass *class; V4L2m2mContext *context; @@ -75,33 +78,33 @@ typedef struct V4L2m2mPriv /** * Allocate a new context and references for a V4L2 M2M instance. * - * @param[in] ctx The AVCodecContext instantiated by the encoder/decoder. + * @param[in] ctx The V4L2m2mPriv instantiated by the encoder/decoder. * @param[out] ctx The V4L2m2mContext. * * @returns 0 in success, a negative error code otherwise. */ -int ff_v4l2_m2m_create_context(AVCodecContext *avctx, V4L2m2mContext **s); +int ff_v4l2_m2m_create_context(V4L2m2mPriv *priv, V4L2m2mContext **s); /** * Probes the video nodes looking for the required codec capabilities. * - * @param[in] ctx The AVCodecContext instantiated by the encoder/decoder. + * @param[in] ctx The V4L2m2mPriv instantiated by the encoder/decoder. * * @returns 0 if a driver is found, a negative number otherwise. */ -int ff_v4l2_m2m_codec_init(AVCodecContext *avctx); +int ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv); /** * Releases all the codec resources if all AVBufferRefs have been returned to the * ctx. Otherwise keep the driver open. * - * @param[in] The AVCodecContext instantiated by the encoder/decoder. + * @param[in] The V4L2m2mPriv instantiated by the encoder/decoder. * * @returns 0 * */ -int ff_v4l2_m2m_codec_end(AVCodecContext *avctx); +int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv); /** * Reinitializes the V4L2m2mContext when the driver cannot continue processing diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c index d0601f0e2fe..3e17e0fcaca 100644 --- a/libavcodec/v4l2_m2m_dec.c +++ b/libavcodec/v4l2_m2m_dec.c @@ -28,6 +28,7 @@ #include "libavutil/opt.h" #include "libavcodec/avcodec.h" #include "libavcodec/decode.h" +#include "libavcodec/internal.h" #include "v4l2_context.h" #include "v4l2_m2m.h" @@ -38,7 +39,7 @@ static int v4l2_try_start(AVCodecContext *avctx) V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context; V4L2Context *const capture = &s->capture; V4L2Context *const output = &s->output; - struct v4l2_selection selection; + struct v4l2_selection selection = { 0 }; int ret; /* 1. start the output process */ @@ -86,8 +87,8 @@ static int v4l2_try_start(AVCodecContext *avctx) if (!capture->buffers) { ret = ff_v4l2_context_init(capture); if (ret) { - av_log(avctx, AV_LOG_DEBUG, "can't request output buffers\n"); - return ret; + av_log(avctx, AV_LOG_ERROR, "can't request capture buffers\n"); + return AVERROR(ENOMEM); } } @@ -122,6 +123,13 @@ static int v4l2_prepare_decoder(V4L2m2mContext *s) } } + memset(&sub, 0, sizeof(sub)); + sub.type = V4L2_EVENT_EOS; + ret = ioctl(s->fd, VIDIOC_SUBSCRIBE_EVENT, &sub); + if (ret < 0) + av_log(s->avctx, AV_LOG_WARNING, + "the v4l2 driver does not support end of stream VIDIOC_SUBSCRIBE_EVENT\n"); + return 0; } @@ -133,17 +141,24 @@ static int v4l2_receive_frame(AVCodecContext *avctx, AVFrame *frame) AVPacket avpkt = {0}; int ret; - ret = ff_decode_get_packet(avctx, &avpkt); - if (ret < 0 && ret != AVERROR_EOF) - return ret; + if (s->buf_pkt.size) { + avpkt = s->buf_pkt; + memset(&s->buf_pkt, 0, sizeof(AVPacket)); + } else { + ret = ff_decode_get_packet(avctx, &avpkt); + if (ret < 0 && ret != AVERROR_EOF) + return ret; + } if (s->draining) goto dequeue; ret = ff_v4l2_context_enqueue_packet(output, &avpkt); if (ret < 0) { - if (ret != AVERROR(ENOMEM)) + if (ret != AVERROR(EAGAIN)) return ret; + + s->buf_pkt = avpkt; /* no input buffers available, continue dequeing */ } @@ -151,22 +166,29 @@ static int v4l2_receive_frame(AVCodecContext *avctx, AVFrame *frame) ret = v4l2_try_start(avctx); if (ret) { av_packet_unref(&avpkt); + + /* cant recover */ + if (ret == AVERROR(ENOMEM)) + return ret; + return 0; } } dequeue: - av_packet_unref(&avpkt); - return ff_v4l2_context_dequeue_frame(capture, frame); + if (!s->buf_pkt.size) + av_packet_unref(&avpkt); + return ff_v4l2_context_dequeue_frame(capture, frame, -1); } static av_cold int v4l2_decode_init(AVCodecContext *avctx) { V4L2Context *capture, *output; V4L2m2mContext *s; + V4L2m2mPriv *priv = avctx->priv_data; int ret; - ret = ff_v4l2_m2m_create_context(avctx, &s); + ret = ff_v4l2_m2m_create_context(priv, &s); if (ret < 0) return ret; @@ -186,9 +208,9 @@ static av_cold int v4l2_decode_init(AVCodecContext *avctx) capture->av_codec_id = AV_CODEC_ID_RAWVIDEO; capture->av_pix_fmt = avctx->pix_fmt; - ret = ff_v4l2_m2m_codec_init(avctx); + s->avctx = avctx; + ret = ff_v4l2_m2m_codec_init(priv); if (ret) { - V4L2m2mPriv *priv = avctx->priv_data; av_log(avctx, AV_LOG_ERROR, "can't configure decoder\n"); s->self_ref = NULL; av_buffer_unref(&priv->context_ref); @@ -199,6 +221,14 @@ static av_cold int v4l2_decode_init(AVCodecContext *avctx) return v4l2_prepare_decoder(s); } +static av_cold int v4l2_decode_close(AVCodecContext *avctx) +{ + V4L2m2mPriv *priv = avctx->priv_data; + V4L2m2mContext *s = priv->context; + av_packet_unref(&s->buf_pkt); + return ff_v4l2_m2m_codec_end(priv); +} + #define OFFSET(x) offsetof(V4L2m2mPriv, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM @@ -209,29 +239,31 @@ static const AVOption options[] = { { NULL}, }; +#define M2MDEC_CLASS(NAME) \ + static const AVClass v4l2_m2m_ ## NAME ## _dec_class = { \ + .class_name = #NAME "_v4l2m2m_decoder", \ + .item_name = av_default_item_name, \ + .option = options, \ + .version = LIBAVUTIL_VERSION_INT, \ + }; + #define M2MDEC(NAME, LONGNAME, CODEC, bsf_name) \ -static const AVClass v4l2_m2m_ ## NAME ## _dec_class = {\ - .class_name = #NAME "_v4l2_m2m_decoder",\ - .item_name = av_default_item_name,\ - .option = options,\ - .version = LIBAVUTIL_VERSION_INT,\ -};\ -\ -AVCodec ff_ ## NAME ## _v4l2m2m_decoder = { \ - .name = #NAME "_v4l2m2m" ,\ - .long_name = NULL_IF_CONFIG_SMALL("V4L2 mem2mem " LONGNAME " decoder wrapper"),\ - .type = AVMEDIA_TYPE_VIDEO,\ - .id = CODEC ,\ - .priv_data_size = sizeof(V4L2m2mPriv),\ - .priv_class = &v4l2_m2m_ ## NAME ## _dec_class,\ - .init = v4l2_decode_init,\ - .receive_frame = v4l2_receive_frame,\ - .close = ff_v4l2_m2m_codec_end,\ - .bsfs = bsf_name, \ - .capabilities = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY | \ - AV_CODEC_CAP_AVOID_PROBING, \ - .wrapper_name = "v4l2m2m", \ -}; + M2MDEC_CLASS(NAME) \ + AVCodec ff_ ## NAME ## _v4l2m2m_decoder = { \ + .name = #NAME "_v4l2m2m" , \ + .long_name = NULL_IF_CONFIG_SMALL("V4L2 mem2mem " LONGNAME " decoder wrapper"), \ + .type = AVMEDIA_TYPE_VIDEO, \ + .id = CODEC , \ + .priv_data_size = sizeof(V4L2m2mPriv), \ + .priv_class = &v4l2_m2m_ ## NAME ## _dec_class, \ + .init = v4l2_decode_init, \ + .receive_frame = v4l2_receive_frame, \ + .close = v4l2_decode_close, \ + .bsfs = bsf_name, \ + .capabilities = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING, \ + .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, \ + .wrapper_name = "v4l2m2m", \ + } M2MDEC(h264, "H.264", AV_CODEC_ID_H264, "h264_mp4toannexb"); M2MDEC(hevc, "HEVC", AV_CODEC_ID_HEVC, "hevc_mp4toannexb"); diff --git a/libavcodec/v4l2_m2m_enc.c b/libavcodec/v4l2_m2m_enc.c index 636e1a96dde..32321f392fc 100644 --- a/libavcodec/v4l2_m2m_enc.c +++ b/libavcodec/v4l2_m2m_enc.c @@ -25,11 +25,14 @@ #include #include #include "libavcodec/avcodec.h" +#include "libavcodec/internal.h" #include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" #include "libavutil/opt.h" +#include "profiles.h" #include "v4l2_context.h" #include "v4l2_m2m.h" +#include "v4l2_fmt.h" #define MPEG_CID(x) V4L2_CID_MPEG_VIDEO_##x #define MPEG_VIDEO(x) V4L2_MPEG_VIDEO_##x @@ -46,7 +49,7 @@ static inline void v4l2_set_timeperframe(V4L2m2mContext *s, unsigned int num, un av_log(s->avctx, AV_LOG_WARNING, "Failed to set timeperframe"); } -static inline void v4l2_set_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed int value, const char *name) +static inline void v4l2_set_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed int value, const char *name, int log_warning) { struct v4l2_ext_controls ctrls = { { 0 } }; struct v4l2_ext_control ctrl = { 0 }; @@ -58,15 +61,16 @@ static inline void v4l2_set_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed /* set ctrl*/ ctrl.value = value; - ctrl.id = id ; + ctrl.id = id; if (ioctl(s->fd, VIDIOC_S_EXT_CTRLS, &ctrls) < 0) - av_log(s->avctx, AV_LOG_WARNING, "Failed to set %s\n", name); + av_log(s->avctx, log_warning || errno != EINVAL ? AV_LOG_WARNING : AV_LOG_DEBUG, + "Failed to set %s: %s\n", name, strerror(errno)); else av_log(s->avctx, AV_LOG_DEBUG, "Encoder: %s = %d\n", name, value); } -static inline int v4l2_get_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed int *value, const char *name) +static inline int v4l2_get_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed int *value, const char *name, int log_warning) { struct v4l2_ext_controls ctrls = { { 0 } }; struct v4l2_ext_control ctrl = { 0 }; @@ -82,7 +86,8 @@ static inline int v4l2_get_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed i ret = ioctl(s->fd, VIDIOC_G_EXT_CTRLS, &ctrls); if (ret < 0) { - av_log(s->avctx, AV_LOG_WARNING, "Failed to set %s\n", name); + av_log(s->avctx, log_warning || errno != EINVAL ? AV_LOG_WARNING : AV_LOG_DEBUG, + "Failed to get %s\n", name); return ret; } @@ -144,8 +149,8 @@ static int v4l2_check_b_frame_support(V4L2m2mContext *s) if (s->avctx->max_b_frames) av_log(s->avctx, AV_LOG_WARNING, "Encoder does not support b-frames yet\n"); - v4l2_set_ext_ctrl(s, MPEG_CID(B_FRAMES), 0, "number of B-frames"); - v4l2_get_ext_ctrl(s, MPEG_CID(B_FRAMES), &s->avctx->max_b_frames, "number of B-frames"); + v4l2_set_ext_ctrl(s, MPEG_CID(B_FRAMES), 0, "number of B-frames", 0); + v4l2_get_ext_ctrl(s, MPEG_CID(B_FRAMES), &s->avctx->max_b_frames, "number of B-frames", 0); if (s->avctx->max_b_frames == 0) return 0; @@ -154,6 +159,17 @@ static int v4l2_check_b_frame_support(V4L2m2mContext *s) return AVERROR_PATCHWELCOME; } +static inline void v4l2_subscribe_eos_event(V4L2m2mContext *s) +{ + struct v4l2_event_subscription sub; + + memset(&sub, 0, sizeof(sub)); + sub.type = V4L2_EVENT_EOS; + if (ioctl(s->fd, VIDIOC_SUBSCRIBE_EVENT, &sub) < 0) + av_log(s->avctx, AV_LOG_WARNING, + "the v4l2 driver does not support end of stream VIDIOC_SUBSCRIBE_EVENT\n"); +} + static int v4l2_prepare_encoder(V4L2m2mContext *s) { AVCodecContext *avctx = s->avctx; @@ -163,6 +179,8 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s) /** * requirements */ + v4l2_subscribe_eos_event(s); + ret = v4l2_check_b_frame_support(s); if (ret) return ret; @@ -171,12 +189,13 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s) * settingss */ if (avctx->framerate.num || avctx->framerate.den) - v4l2_set_timeperframe(s, avctx->framerate.num, avctx->framerate.den); + v4l2_set_timeperframe(s, avctx->framerate.den, avctx->framerate.num); /* set ext ctrls */ - v4l2_set_ext_ctrl(s, MPEG_CID(HEADER_MODE), MPEG_VIDEO(HEADER_MODE_SEPARATE), "header mode"); - v4l2_set_ext_ctrl(s, MPEG_CID(BITRATE) , avctx->bit_rate, "bit rate"); - v4l2_set_ext_ctrl(s, MPEG_CID(GOP_SIZE), avctx->gop_size,"gop size"); + v4l2_set_ext_ctrl(s, MPEG_CID(HEADER_MODE), MPEG_VIDEO(HEADER_MODE_SEPARATE), "header mode", 0); + v4l2_set_ext_ctrl(s, MPEG_CID(BITRATE) , avctx->bit_rate, "bit rate", 1); + v4l2_set_ext_ctrl(s, MPEG_CID(FRAME_RC_ENABLE), 1, "frame level rate control", 0); + v4l2_set_ext_ctrl(s, MPEG_CID(GOP_SIZE), avctx->gop_size,"gop size", 1); av_log(avctx, AV_LOG_DEBUG, "Encoder Context: id (%d), profile (%d), frame rate(%d/%d), number b-frames (%d), " @@ -186,26 +205,30 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s) switch (avctx->codec_id) { case AV_CODEC_ID_H264: - val = v4l2_h264_profile_from_ff(avctx->profile); - if (val < 0) - av_log(avctx, AV_LOG_WARNING, "h264 profile not found\n"); - else - v4l2_set_ext_ctrl(s, MPEG_CID(H264_PROFILE), val, "h264 profile"); + if (avctx->profile != FF_PROFILE_UNKNOWN) { + val = v4l2_h264_profile_from_ff(avctx->profile); + if (val < 0) + av_log(avctx, AV_LOG_WARNING, "h264 profile not found\n"); + else + v4l2_set_ext_ctrl(s, MPEG_CID(H264_PROFILE), val, "h264 profile", 1); + } qmin_cid = MPEG_CID(H264_MIN_QP); qmax_cid = MPEG_CID(H264_MAX_QP); qmin = 0; qmax = 51; break; case AV_CODEC_ID_MPEG4: - val = v4l2_mpeg4_profile_from_ff(avctx->profile); - if (val < 0) - av_log(avctx, AV_LOG_WARNING, "mpeg4 profile not found\n"); - else - v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_PROFILE), val, "mpeg4 profile"); + if (avctx->profile != FF_PROFILE_UNKNOWN) { + val = v4l2_mpeg4_profile_from_ff(avctx->profile); + if (val < 0) + av_log(avctx, AV_LOG_WARNING, "mpeg4 profile not found\n"); + else + v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_PROFILE), val, "mpeg4 profile", 1); + } qmin_cid = MPEG_CID(MPEG4_MIN_QP); qmax_cid = MPEG_CID(MPEG4_MAX_QP); if (avctx->flags & AV_CODEC_FLAG_QPEL) - v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_QPEL), 1, "qpel"); + v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_QPEL), 1, "qpel", 1); qmin = 1; qmax = 31; break; @@ -231,11 +254,18 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s) return 0; } - if (qmin != avctx->qmin || qmax != avctx->qmax) - av_log(avctx, AV_LOG_WARNING, "Encoder adjusted: qmin (%d), qmax (%d)\n", qmin, qmax); + if (avctx->qmin >= 0 && avctx->qmax >= 0 && avctx->qmin > avctx->qmax) { + av_log(avctx, AV_LOG_WARNING, "Invalid qmin:%d qmax:%d. qmin should not " + "exceed qmax\n", avctx->qmin, avctx->qmax); + } else { + qmin = avctx->qmin >= 0 ? avctx->qmin : qmin; + qmax = avctx->qmax >= 0 ? avctx->qmax : qmax; + } - v4l2_set_ext_ctrl(s, qmin_cid, qmin, "minimum video quantizer scale"); - v4l2_set_ext_ctrl(s, qmax_cid, qmax, "maximum video quantizer scale"); + v4l2_set_ext_ctrl(s, qmin_cid, qmin, "minimum video quantizer scale", + avctx->qmin >= 0); + v4l2_set_ext_ctrl(s, qmax_cid, qmax, "maximum video quantizer scale", + avctx->qmax >= 0); return 0; } @@ -245,6 +275,11 @@ static int v4l2_send_frame(AVCodecContext *avctx, const AVFrame *frame) V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context; V4L2Context *const output = &s->output; +#ifdef V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME + if (frame && frame->pict_type == AV_PICTURE_TYPE_I) + v4l2_set_ext_ctrl(s, MPEG_CID(FORCE_KEY_FRAME), 0, "force key frame", 1); +#endif + return ff_v4l2_context_enqueue_frame(output, frame); } @@ -261,7 +296,7 @@ static int v4l2_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) if (!output->streamon) { ret = ff_v4l2_context_set_status(output, VIDIOC_STREAMON); if (ret) { - av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF failed on output context\n"); + av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON failed on output context\n"); return ret; } } @@ -282,9 +317,12 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx) { V4L2Context *capture, *output; V4L2m2mContext *s; + V4L2m2mPriv *priv = avctx->priv_data; + enum AVPixelFormat pix_fmt_output; + uint32_t v4l2_fmt_output; int ret; - ret = ff_v4l2_m2m_create_context(avctx, &s); + ret = ff_v4l2_m2m_create_context(priv, &s); if (ret < 0) return ret; @@ -303,50 +341,86 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx) capture->av_codec_id = avctx->codec_id; capture->av_pix_fmt = AV_PIX_FMT_NONE; - ret = ff_v4l2_m2m_codec_init(avctx); + s->avctx = avctx; + ret = ff_v4l2_m2m_codec_init(priv); if (ret) { av_log(avctx, AV_LOG_ERROR, "can't configure encoder\n"); return ret; } + if (V4L2_TYPE_IS_MULTIPLANAR(output->type)) + v4l2_fmt_output = output->format.fmt.pix_mp.pixelformat; + else + v4l2_fmt_output = output->format.fmt.pix.pixelformat; + + pix_fmt_output = ff_v4l2_format_v4l2_to_avfmt(v4l2_fmt_output, AV_CODEC_ID_RAWVIDEO); + if (pix_fmt_output != avctx->pix_fmt) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt_output); + av_log(avctx, AV_LOG_ERROR, "Encoder requires %s pixel format.\n", desc->name); + return AVERROR(EINVAL); + } + return v4l2_prepare_encoder(s); } +static av_cold int v4l2_encode_close(AVCodecContext *avctx) +{ + return ff_v4l2_m2m_codec_end(avctx->priv_data); +} + #define OFFSET(x) offsetof(V4L2m2mPriv, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM +#define V4L_M2M_CAPTURE_OPTS \ + V4L_M2M_DEFAULT_OPTS,\ + { "num_capture_buffers", "Number of buffers in the capture context", \ + OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 4 }, 4, INT_MAX, FLAGS } + +static const AVOption mpeg4_options[] = { + V4L_M2M_CAPTURE_OPTS, + FF_MPEG4_PROFILE_OPTS + { NULL }, +}; + static const AVOption options[] = { - V4L_M2M_DEFAULT_OPTS, - { "num_capture_buffers", "Number of buffers in the capture context", - OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 4 }, 4, INT_MAX, FLAGS }, + V4L_M2M_CAPTURE_OPTS, { NULL }, }; -#define M2MENC(NAME, LONGNAME, CODEC) \ -static const AVClass v4l2_m2m_ ## NAME ## _enc_class = {\ - .class_name = #NAME "_v4l2_m2m_encoder",\ - .item_name = av_default_item_name,\ - .option = options,\ - .version = LIBAVUTIL_VERSION_INT,\ -};\ -\ -AVCodec ff_ ## NAME ## _v4l2m2m_encoder = { \ - .name = #NAME "_v4l2m2m" ,\ - .long_name = NULL_IF_CONFIG_SMALL("V4L2 mem2mem " LONGNAME " encoder wrapper"),\ - .type = AVMEDIA_TYPE_VIDEO,\ - .id = CODEC ,\ - .priv_data_size = sizeof(V4L2m2mPriv),\ - .priv_class = &v4l2_m2m_ ## NAME ##_enc_class,\ - .init = v4l2_encode_init,\ - .send_frame = v4l2_send_frame,\ - .receive_packet = v4l2_receive_packet,\ - .close = ff_v4l2_m2m_codec_end,\ - .capabilities = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY, \ - .wrapper_name = "v4l2m2m", \ +static const AVCodecDefault v4l2_m2m_defaults[] = { + { "qmin", "-1" }, + { "qmax", "-1" }, + { NULL }, }; -M2MENC(mpeg4,"MPEG4", AV_CODEC_ID_MPEG4); -M2MENC(h263, "H.263", AV_CODEC_ID_H263); -M2MENC(h264, "H.264", AV_CODEC_ID_H264); -M2MENC(hevc, "HEVC", AV_CODEC_ID_HEVC); -M2MENC(vp8, "VP8", AV_CODEC_ID_VP8); +#define M2MENC_CLASS(NAME, OPTIONS_NAME) \ + static const AVClass v4l2_m2m_ ## NAME ## _enc_class = { \ + .class_name = #NAME "_v4l2m2m_encoder", \ + .item_name = av_default_item_name, \ + .option = OPTIONS_NAME, \ + .version = LIBAVUTIL_VERSION_INT, \ + }; + +#define M2MENC(NAME, LONGNAME, OPTIONS_NAME, CODEC) \ + M2MENC_CLASS(NAME, OPTIONS_NAME) \ + AVCodec ff_ ## NAME ## _v4l2m2m_encoder = { \ + .name = #NAME "_v4l2m2m" , \ + .long_name = NULL_IF_CONFIG_SMALL("V4L2 mem2mem " LONGNAME " encoder wrapper"), \ + .type = AVMEDIA_TYPE_VIDEO, \ + .id = CODEC , \ + .priv_data_size = sizeof(V4L2m2mPriv), \ + .priv_class = &v4l2_m2m_ ## NAME ##_enc_class, \ + .init = v4l2_encode_init, \ + .send_frame = v4l2_send_frame, \ + .receive_packet = v4l2_receive_packet, \ + .close = v4l2_encode_close, \ + .defaults = v4l2_m2m_defaults, \ + .capabilities = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY, \ + .wrapper_name = "v4l2m2m", \ + } + +M2MENC(mpeg4,"MPEG4", mpeg4_options, AV_CODEC_ID_MPEG4); +M2MENC(h263, "H.263", options, AV_CODEC_ID_H263); +M2MENC(h264, "H.264", options, AV_CODEC_ID_H264); +M2MENC(hevc, "HEVC", options, AV_CODEC_ID_HEVC); +M2MENC(vp8, "VP8", options, AV_CODEC_ID_VP8); diff --git a/libavcodec/vaapi_decode.c b/libavcodec/vaapi_decode.c index 69512e1d451..5e4f62baad0 100644 --- a/libavcodec/vaapi_decode.c +++ b/libavcodec/vaapi_decode.c @@ -24,6 +24,7 @@ #include "decode.h" #include "internal.h" #include "vaapi_decode.h" +#include "vaapi_hevc.h" int ff_vaapi_decode_make_param_buffer(AVCodecContext *avctx, @@ -255,6 +256,10 @@ static const struct { MAP(422H, YUV422P), #ifdef VA_FOURCC_YV16 MAP(YV16, YUV422P), +#endif + MAP(YUY2, YUYV422), +#ifdef VA_FOURCC_Y210 + MAP(Y210, Y210), #endif // 4:4:0 MAP(422V, YUV440P), @@ -364,8 +369,9 @@ static const struct { enum AVCodecID codec_id; int codec_profile; 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 } +#define MAP(c, p, v, ...) { AV_CODEC_ID_ ## c, FF_PROFILE_ ## p, VAProfile ## v, __VA_ARGS__ } MAP(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2Simple ), MAP(MPEG2VIDEO, MPEG2_MAIN, MPEG2Main ), MAP(H263, UNKNOWN, H263Baseline), @@ -380,6 +386,12 @@ static const struct { #if VA_CHECK_VERSION(0, 37, 0) MAP(HEVC, HEVC_MAIN, HEVCMain ), MAP(HEVC, HEVC_MAIN_10, HEVCMain10 ), + MAP(HEVC, HEVC_MAIN_STILL_PICTURE, + HEVCMain ), +#endif +#if VA_CHECK_VERSION(1, 2, 0) && CONFIG_HEVC_VAAPI_HWACCEL + MAP(HEVC, HEVC_REXT, None, + ff_vaapi_parse_hevc_rext_profile ), #endif MAP(MJPEG, MJPEG_HUFFMAN_BASELINE_DCT, JPEGBaseline), @@ -415,8 +427,8 @@ static int vaapi_decode_make_config(AVCodecContext *avctx, VAStatus vas; int err, i, j; const AVCodecDescriptor *codec_desc; - VAProfile *profile_list = NULL, matched_va_profile; - int profile_count, exact_match, matched_ff_profile; + VAProfile *profile_list = NULL, matched_va_profile, va_profile; + int profile_count, exact_match, matched_ff_profile, codec_profile; AVHWDeviceContext *device = (AVHWDeviceContext*)device_ref->data; AVVAAPIDeviceContext *hwctx = device->hwctx; @@ -454,15 +466,21 @@ static int vaapi_decode_make_config(AVCodecContext *avctx, if (avctx->profile == vaapi_profile_map[i].codec_profile || vaapi_profile_map[i].codec_profile == FF_PROFILE_UNKNOWN) profile_match = 1; + + va_profile = vaapi_profile_map[i].profile_parser ? + vaapi_profile_map[i].profile_parser(avctx) : + vaapi_profile_map[i].va_profile; + codec_profile = vaapi_profile_map[i].codec_profile; + for (j = 0; j < profile_count; j++) { - if (vaapi_profile_map[i].va_profile == profile_list[j]) { + if (va_profile == profile_list[j]) { exact_match = profile_match; break; } } if (j < profile_count) { - matched_va_profile = vaapi_profile_map[i].va_profile; - matched_ff_profile = vaapi_profile_map[i].codec_profile; + matched_va_profile = va_profile; + matched_ff_profile = codec_profile; if (exact_match) break; } diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c index dd2a24de046..cb05ebd774a 100644 --- a/libavcodec/vaapi_encode.c +++ b/libavcodec/vaapi_encode.c @@ -27,6 +27,11 @@ #include "vaapi_encode.h" #include "avcodec.h" +const AVCodecHWConfigInternal *ff_vaapi_encode_hw_configs[] = { + HW_CONFIG_ENCODER_FRAMES(VAAPI, VAAPI), + NULL, +}; + static const char * const picture_type_name[] = { "IDR", "I", "P", "B" }; static int vaapi_encode_make_packed_header(AVCodecContext *avctx, @@ -166,6 +171,7 @@ static int vaapi_encode_issue(AVCodecContext *avctx, int err, i; char data[MAX_PARAM_BUFFER_SIZE]; size_t bit_len; + av_unused AVFrameSideData *sd; av_log(avctx, AV_LOG_DEBUG, "Issuing encode for pic %"PRId64"/%"PRId64" " "as type %s.\n", pic->display_order, pic->encode_order, @@ -435,6 +441,71 @@ static int vaapi_encode_issue(AVCodecContext *avctx, } } +#if VA_CHECK_VERSION(1, 0, 0) + sd = av_frame_get_side_data(pic->input_image, + AV_FRAME_DATA_REGIONS_OF_INTEREST); + if (sd && ctx->roi_allowed) { + const AVRegionOfInterest *roi; + uint32_t roi_size; + VAEncMiscParameterBufferROI param_roi; + int nb_roi, i, v; + + roi = (const AVRegionOfInterest*)sd->data; + roi_size = roi->self_size; + av_assert0(roi_size && sd->size % roi_size == 0); + nb_roi = sd->size / roi_size; + if (nb_roi > ctx->roi_max_regions) { + if (!ctx->roi_warned) { + av_log(avctx, AV_LOG_WARNING, "More ROIs set than " + "supported by driver (%d > %d).\n", + nb_roi, ctx->roi_max_regions); + ctx->roi_warned = 1; + } + nb_roi = ctx->roi_max_regions; + } + + pic->roi = av_mallocz_array(nb_roi, sizeof(*pic->roi)); + if (!pic->roi) { + err = AVERROR(ENOMEM); + goto fail; + } + // For overlapping regions, the first in the array takes priority. + for (i = 0; i < nb_roi; i++) { + roi = (const AVRegionOfInterest*)(sd->data + roi_size * i); + + av_assert0(roi->qoffset.den != 0); + v = roi->qoffset.num * ctx->roi_quant_range / roi->qoffset.den; + av_log(avctx, AV_LOG_DEBUG, "ROI: (%d,%d)-(%d,%d) -> %+d.\n", + roi->top, roi->left, roi->bottom, roi->right, v); + + pic->roi[i] = (VAEncROI) { + .roi_rectangle = { + .x = roi->left, + .y = roi->top, + .width = roi->right - roi->left, + .height = roi->bottom - roi->top, + }, + .roi_value = av_clip_int8(v), + }; + } + + param_roi = (VAEncMiscParameterBufferROI) { + .num_roi = nb_roi, + .max_delta_qp = INT8_MAX, + .min_delta_qp = INT8_MIN, + .roi = pic->roi, + .roi_flags.bits.roi_value_is_qp_delta = 1, + }; + + err = vaapi_encode_make_misc_param_buffer(avctx, pic, + VAEncMiscParameterTypeROI, + ¶m_roi, + sizeof(param_roi)); + if (err < 0) + goto fail; + } +#endif + vas = vaBeginPicture(ctx->hwctx->display, ctx->va_context, pic->input_surface); if (vas != VA_STATUS_SUCCESS) { @@ -500,6 +571,7 @@ static int vaapi_encode_issue(AVCodecContext *avctx, av_freep(&pic->codec_picture_params); av_freep(&pic->param_buffers); av_freep(&pic->slices); + av_freep(&pic->roi); av_frame_free(&pic->recon_image); av_buffer_unref(&pic->output_buffer_ref); pic->output_buffer = VA_INVALID_ID; @@ -512,6 +584,8 @@ static int vaapi_encode_output(AVCodecContext *avctx, VAAPIEncodeContext *ctx = avctx->priv_data; VACodedBufferSegment *buf_list, *buf; VAStatus vas; + int total_size = 0; + uint8_t *ptr; int err; err = vaapi_encode_wait(avctx, pic); @@ -528,15 +602,21 @@ static int vaapi_encode_output(AVCodecContext *avctx, goto fail; } + for (buf = buf_list; buf; buf = buf->next) + total_size += buf->size; + + err = av_new_packet(pkt, total_size); + ptr = pkt->data; + + if (err < 0) + goto fail_mapped; + 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); - err = av_new_packet(pkt, buf->size); - if (err < 0) - goto fail_mapped; - - memcpy(pkt->data, buf->buf, buf->size); + memcpy(ptr, buf->buf, buf->size); + ptr += buf->size; } if (pic->type == PICTURE_TYPE_IDR) @@ -634,6 +714,7 @@ static int vaapi_encode_free(AVCodecContext *avctx, av_freep(&pic->priv_data); av_freep(&pic->codec_picture_params); + av_freep(&pic->roi); av_free(pic); @@ -948,6 +1029,17 @@ static int vaapi_encode_check_frame(AVCodecContext *avctx, ctx->crop_warned = 1; } + if (!ctx->roi_allowed) { + AVFrameSideData *sd = + av_frame_get_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST); + + if (sd && !ctx->roi_warned) { + av_log(avctx, AV_LOG_WARNING, "ROI side data on input " + "frames ignored due to lack of driver support.\n"); + ctx->roi_warned = 1; + } + } + return 0; } @@ -978,7 +1070,7 @@ int ff_vaapi_encode_send_frame(AVCodecContext *avctx, const AVFrame *frame) if (err < 0) goto fail; - if (ctx->input_order == 0) + if (ctx->input_order == 0 || frame->pict_type == AV_PICTURE_TYPE_I) pic->force_idr = 1; pic->input_surface = (VASurfaceID)(uintptr_t)frame->data[3]; @@ -1014,6 +1106,7 @@ int ff_vaapi_encode_send_frame(AVCodecContext *avctx, const AVFrame *frame) return 0; fail: + vaapi_encode_free(avctx, pic); return err; } @@ -1942,6 +2035,39 @@ static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx) return 0; } +static av_cold int vaapi_encode_init_roi(AVCodecContext *avctx) +{ +#if VA_CHECK_VERSION(1, 0, 0) + VAAPIEncodeContext *ctx = avctx->priv_data; + VAStatus vas; + VAConfigAttrib attr = { VAConfigAttribEncROI }; + + 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 ROI " + "config attribute: %d (%s).\n", vas, vaErrorStr(vas)); + return AVERROR_EXTERNAL; + } + + if (attr.value == VA_ATTRIB_NOT_SUPPORTED) { + ctx->roi_allowed = 0; + } else { + VAConfigAttribValEncROI roi = { + .value = attr.value, + }; + + ctx->roi_max_regions = roi.bits.num_roi_regions; + ctx->roi_allowed = ctx->roi_max_regions > 0 && + (ctx->va_rc_mode == VA_RC_CQP || + roi.bits.roi_rc_qp_delta_support); + } +#endif + return 0; +} + static void vaapi_encode_free_output_buffer(void *opaque, uint8_t *data) { @@ -2132,6 +2258,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx) if (err < 0) goto fail; + err = vaapi_encode_init_roi(avctx); + if (err < 0) + goto fail; + if (avctx->compression_level >= 0) { err = vaapi_encode_init_quality(avctx); if (err < 0) @@ -2236,7 +2366,6 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx) return 0; fail: - ff_vaapi_encode_close(avctx); return err; } diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h index eeec06036b2..1329f6428f4 100644 --- a/libavcodec/vaapi_encode.h +++ b/libavcodec/vaapi_encode.h @@ -31,6 +31,7 @@ #include "libavutil/hwcontext_vaapi.h" #include "avcodec.h" +#include "hwconfig.h" struct VAAPIEncodeType; struct VAAPIEncodePicture; @@ -44,6 +45,8 @@ enum { MAX_PARAM_BUFFER_SIZE = 1024, }; +extern const AVCodecHWConfigInternal *ff_vaapi_encode_hw_configs[]; + enum { PICTURE_TYPE_IDR = 0, PICTURE_TYPE_I = 1, @@ -69,6 +72,13 @@ typedef struct VAAPIEncodePicture { int64_t pts; int force_idr; +#if VA_CHECK_VERSION(1, 0, 0) + // ROI regions. + VAEncROI *roi; +#else + void *roi; +#endif + int type; int b_depth; int encode_issued; @@ -304,9 +314,20 @@ typedef struct VAAPIEncodeContext { int gop_counter; int end_of_stream; + // Whether the driver supports ROI at all. + int roi_allowed; + // Maximum number of regions supported by the driver. + int roi_max_regions; + // Quantisation range for offset calculations. Set by codec-specific + // code, as it may change based on parameters. + int roi_quant_range; + // The encoder does not support cropping information, so warn about // it the first time we encounter any nonzero crop fields. int crop_warned; + // If the driver does not support ROI then warn the first time we + // encounter a frame with ROI side data. + int roi_warned; } VAAPIEncodeContext; enum { diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c index d1427112ea9..e195650ef76 100644 --- a/libavcodec/vaapi_encode_h264.c +++ b/libavcodec/vaapi_encode_h264.c @@ -1130,6 +1130,8 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx) } } + ctx->roi_quant_range = 51 + 6 * (ctx->profile->depth - 8); + return 0; } @@ -1354,10 +1356,12 @@ AVCodec ff_h264_vaapi_encoder = { .close = &vaapi_encode_h264_close, .priv_class = &vaapi_encode_h264_class, .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .defaults = vaapi_encode_h264_defaults, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_VAAPI, AV_PIX_FMT_NONE, }, + .hw_configs = ff_vaapi_encode_hw_configs, .wrapper_name = "vaapi", }; diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c index 758bd40a37b..92e05109112 100644 --- a/libavcodec/vaapi_encode_h265.c +++ b/libavcodec/vaapi_encode_h265.c @@ -410,10 +410,10 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx) sps->conformance_window_flag = 1; sps->conf_win_left_offset = 0; sps->conf_win_right_offset = - (ctx->surface_width - avctx->width) / 2; + (ctx->surface_width - avctx->width) >> desc->log2_chroma_w; sps->conf_win_top_offset = 0; sps->conf_win_bottom_offset = - (ctx->surface_height - avctx->height) / 2; + (ctx->surface_height - avctx->height) >> desc->log2_chroma_h; } else { sps->conformance_window_flag = 0; } @@ -1102,6 +1102,8 @@ static av_cold int vaapi_encode_h265_configure(AVCodecContext *avctx) priv->fixed_qp_b = 30; } + ctx->roi_quant_range = 51 + 6 * (ctx->profile->depth - 8); + return 0; } @@ -1290,10 +1292,12 @@ AVCodec ff_hevc_vaapi_encoder = { .close = &vaapi_encode_h265_close, .priv_class = &vaapi_encode_h265_class, .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .defaults = vaapi_encode_h265_defaults, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_VAAPI, AV_PIX_FMT_NONE, }, + .hw_configs = ff_vaapi_encode_hw_configs, .wrapper_name = "vaapi", }; diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c index bd029cc9031..9f9ed811a43 100644 --- a/libavcodec/vaapi_encode_mjpeg.c +++ b/libavcodec/vaapi_encode_mjpeg.c @@ -563,12 +563,13 @@ AVCodec ff_mjpeg_vaapi_encoder = { .receive_packet = &ff_vaapi_encode_receive_packet, .close = &vaapi_encode_mjpeg_close, .priv_class = &vaapi_encode_mjpeg_class, - .capabilities = AV_CODEC_CAP_HARDWARE | - AV_CODEC_CAP_INTRA_ONLY, + .capabilities = AV_CODEC_CAP_HARDWARE, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .defaults = vaapi_encode_mjpeg_defaults, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_VAAPI, AV_PIX_FMT_NONE, }, + .hw_configs = ff_vaapi_encode_hw_configs, .wrapper_name = "vaapi", }; diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c index fb1ef71fdc1..02c76552eff 100644 --- a/libavcodec/vaapi_encode_mpeg2.c +++ b/libavcodec/vaapi_encode_mpeg2.c @@ -552,6 +552,8 @@ static av_cold int vaapi_encode_mpeg2_configure(AVCodecContext *avctx) ctx->nb_slices = ctx->slice_block_rows; ctx->slice_size = 1; + ctx->roi_quant_range = 31; + return 0; } @@ -700,10 +702,12 @@ AVCodec ff_mpeg2_vaapi_encoder = { .close = &vaapi_encode_mpeg2_close, .priv_class = &vaapi_encode_mpeg2_class, .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .defaults = vaapi_encode_mpeg2_defaults, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_VAAPI, AV_PIX_FMT_NONE, }, + .hw_configs = ff_vaapi_encode_hw_configs, .wrapper_name = "vaapi", }; diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c index ddbe4c90756..cff926baae8 100644 --- a/libavcodec/vaapi_encode_vp8.c +++ b/libavcodec/vaapi_encode_vp8.c @@ -173,6 +173,8 @@ static av_cold int vaapi_encode_vp8_configure(AVCodecContext *avctx) else priv->q_index_i = priv->q_index_p; + ctx->roi_quant_range = VP8_MAX_QUANT; + return 0; } @@ -255,10 +257,12 @@ AVCodec ff_vp8_vaapi_encoder = { .close = &ff_vaapi_encode_close, .priv_class = &vaapi_encode_vp8_class, .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .defaults = vaapi_encode_vp8_defaults, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_VAAPI, AV_PIX_FMT_NONE, }, + .hw_configs = ff_vaapi_encode_hw_configs, .wrapper_name = "vaapi", }; diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c index f89fd0d07a2..8514b859917 100644 --- a/libavcodec/vaapi_encode_vp9.c +++ b/libavcodec/vaapi_encode_vp9.c @@ -202,6 +202,8 @@ static av_cold int vaapi_encode_vp9_configure(AVCodecContext *avctx) priv->q_idx_idr = priv->q_idx_p = priv->q_idx_b = 100; } + ctx->roi_quant_range = VP9_MAX_QUANT; + return 0; } @@ -289,10 +291,12 @@ AVCodec ff_vp9_vaapi_encoder = { .close = &ff_vaapi_encode_close, .priv_class = &vaapi_encode_vp9_class, .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .defaults = vaapi_encode_vp9_defaults, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_VAAPI, AV_PIX_FMT_NONE, }, + .hw_configs = ff_vaapi_encode_hw_configs, .wrapper_name = "vaapi", }; diff --git a/libavcodec/vaapi_h264.c b/libavcodec/vaapi_h264.c index dd2a6571604..9332aa6f310 100644 --- a/libavcodec/vaapi_h264.c +++ b/libavcodec/vaapi_h264.c @@ -22,7 +22,7 @@ #include "h264dec.h" #include "h264_ps.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "vaapi_decode.h" /** diff --git a/libavcodec/vaapi_hevc.c b/libavcodec/vaapi_hevc.c index c69d63d8ec6..9083331c454 100644 --- a/libavcodec/vaapi_hevc.c +++ b/libavcodec/vaapi_hevc.c @@ -25,12 +25,19 @@ #include "avcodec.h" #include "hevcdec.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "vaapi_decode.h" +#include "vaapi_hevc.h" +#include "h265_profile_level.h" typedef struct VAAPIDecodePictureHEVC { +#if VA_CHECK_VERSION(1, 2, 0) + VAPictureParameterBufferHEVCExtension pic_param; + VASliceParameterBufferHEVCExtension last_slice_param; +#else VAPictureParameterBufferHEVC pic_param; VASliceParameterBufferHEVC last_slice_param; +#endif const uint8_t *last_buffer; size_t last_size; @@ -117,11 +124,13 @@ static int vaapi_hevc_start_frame(AVCodecContext *avctx, const HEVCPPS *pps = h->ps.pps; const ScalingList *scaling_list = NULL; - int err, i; + int pic_param_size, err, i; + + VAPictureParameterBufferHEVC *pic_param = (VAPictureParameterBufferHEVC *)&pic->pic_param; pic->pic.output_surface = ff_vaapi_get_surface_id(h->ref->frame); - pic->pic_param = (VAPictureParameterBufferHEVC) { + *pic_param = (VAPictureParameterBufferHEVC) { .pic_width_in_luma_samples = sps->width, .pic_height_in_luma_samples = sps->height, .log2_min_luma_coding_block_size_minus3 = sps->log2_min_cb_size - 3, @@ -188,29 +197,61 @@ static int vaapi_hevc_start_frame(AVCodecContext *avctx, }, }; - fill_vaapi_pic(&pic->pic_param.CurrPic, h->ref, 0); - fill_vaapi_reference_frames(h, &pic->pic_param); + fill_vaapi_pic(&pic_param->CurrPic, h->ref, 0); + fill_vaapi_reference_frames(h, pic_param); if (pps->tiles_enabled_flag) { - pic->pic_param.num_tile_columns_minus1 = pps->num_tile_columns - 1; - pic->pic_param.num_tile_rows_minus1 = pps->num_tile_rows - 1; + pic_param->num_tile_columns_minus1 = pps->num_tile_columns - 1; + pic_param->num_tile_rows_minus1 = pps->num_tile_rows - 1; for (i = 0; i < pps->num_tile_columns; i++) - pic->pic_param.column_width_minus1[i] = pps->column_width[i] - 1; + pic_param->column_width_minus1[i] = pps->column_width[i] - 1; for (i = 0; i < pps->num_tile_rows; i++) - pic->pic_param.row_height_minus1[i] = pps->row_height[i] - 1; + pic_param->row_height_minus1[i] = pps->row_height[i] - 1; } if (h->sh.short_term_ref_pic_set_sps_flag == 0 && h->sh.short_term_rps) { - pic->pic_param.st_rps_bits = h->sh.short_term_ref_pic_set_size; + pic_param->st_rps_bits = h->sh.short_term_ref_pic_set_size; } else { - pic->pic_param.st_rps_bits = 0; + pic_param->st_rps_bits = 0; + } + +#if VA_CHECK_VERSION(1, 2, 0) + if (avctx->profile == FF_PROFILE_HEVC_REXT) { + pic->pic_param.rext = (VAPictureParameterBufferHEVCRext) { + .range_extension_pic_fields.bits = { + .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, + .cross_component_prediction_enabled_flag = pps->cross_component_prediction_enabled_flag, + .chroma_qp_offset_list_enabled_flag = pps->chroma_qp_offset_list_enabled_flag, + }, + .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, + .log2_max_transform_skip_block_size_minus2 = pps->log2_max_transform_skip_block_size - 2, + }; + + for (i = 0; i < 6; i++) + pic->pic_param.rext.cb_qp_offset_list[i] = pps->cb_qp_offset_list[i]; + for (i = 0; i < 6; i++) + pic->pic_param.rext.cr_qp_offset_list[i] = pps->cr_qp_offset_list[i]; } +#endif + pic_param_size = avctx->profile == FF_PROFILE_HEVC_REXT ? + sizeof(pic->pic_param) : sizeof(VAPictureParameterBufferHEVC); err = ff_vaapi_decode_make_param_buffer(avctx, &pic->pic, VAPictureParameterBufferType, - &pic->pic_param, sizeof(pic->pic_param)); + &pic->pic_param, pic_param_size); if (err < 0) goto fail; @@ -255,12 +296,16 @@ static int vaapi_hevc_end_frame(AVCodecContext *avctx) { const HEVCContext *h = avctx->priv_data; VAAPIDecodePictureHEVC *pic = h->ref->hwaccel_picture_private; + VASliceParameterBufferHEVC *last_slice_param = (VASliceParameterBufferHEVC *)&pic->last_slice_param; int ret; + int slice_param_size = avctx->profile == FF_PROFILE_HEVC_REXT ? + sizeof(pic->last_slice_param) : sizeof(VASliceParameterBufferHEVC); + if (pic->last_size) { - pic->last_slice_param.LongSliceFlags.fields.LastSliceOfPic = 1; + last_slice_param->LongSliceFlags.fields.LastSliceOfPic = 1; ret = ff_vaapi_decode_make_slice_buffer(avctx, &pic->pic, - &pic->last_slice_param, sizeof(pic->last_slice_param), + &pic->last_slice_param, slice_param_size, pic->last_buffer, pic->last_size); if (ret < 0) goto fail; @@ -330,7 +375,7 @@ static void fill_pred_weight_table(const HEVCContext *h, static uint8_t get_ref_pic_index(const HEVCContext *h, const HEVCFrame *frame) { VAAPIDecodePictureHEVC *pic = h->ref->hwaccel_picture_private; - VAPictureParameterBufferHEVC *pp = &pic->pic_param; + VAPictureParameterBufferHEVC *pp = (VAPictureParameterBufferHEVC *)&pic->pic_param; uint8_t i; if (!frame) @@ -353,6 +398,10 @@ static int vaapi_hevc_decode_slice(AVCodecContext *avctx, const HEVCContext *h = avctx->priv_data; const SliceHeader *sh = &h->sh; 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 ? + sizeof(pic->last_slice_param) : sizeof(VASliceParameterBufferHEVC); int nb_list = (sh->slice_type == HEVC_SLICE_B) ? 2 : (sh->slice_type == HEVC_SLICE_I ? 0 : 1); @@ -361,7 +410,7 @@ static int vaapi_hevc_decode_slice(AVCodecContext *avctx, if (!sh->first_slice_in_pic_flag) { err = ff_vaapi_decode_make_slice_buffer(avctx, &pic->pic, - &pic->last_slice_param, sizeof(pic->last_slice_param), + &pic->last_slice_param, slice_param_size, pic->last_buffer, pic->last_size); pic->last_buffer = NULL; pic->last_size = 0; @@ -371,7 +420,7 @@ static int vaapi_hevc_decode_slice(AVCodecContext *avctx, } } - pic->last_slice_param = (VASliceParameterBufferHEVC) { + *last_slice_param = (VASliceParameterBufferHEVC) { .slice_data_size = size, .slice_data_offset = 0, .slice_data_flag = VA_SLICE_DATA_FLAG_ALL, @@ -404,16 +453,35 @@ static int vaapi_hevc_decode_slice(AVCodecContext *avctx, }, }; - memset(pic->last_slice_param.RefPicList, 0xFF, sizeof(pic->last_slice_param.RefPicList)); + memset(last_slice_param->RefPicList, 0xFF, sizeof(last_slice_param->RefPicList)); for (list_idx = 0; list_idx < nb_list; list_idx++) { RefPicList *rpl = &h->ref->refPicList[list_idx]; for (i = 0; i < rpl->nb_refs; i++) - pic->last_slice_param.RefPicList[list_idx][i] = get_ref_pic_index(h, rpl->ref[i]); + last_slice_param->RefPicList[list_idx][i] = get_ref_pic_index(h, rpl->ref[i]); } - fill_pred_weight_table(h, sh, &pic->last_slice_param); + fill_pred_weight_table(h, sh, last_slice_param); + +#if VA_CHECK_VERSION(1, 2, 0) + if (avctx->profile == FF_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, + }, + }; + + memcpy(pic->last_slice_param.rext.luma_offset_l0, pic->last_slice_param.base.luma_offset_l0, + sizeof(pic->last_slice_param.base.luma_offset_l0)); + memcpy(pic->last_slice_param.rext.luma_offset_l1, pic->last_slice_param.base.luma_offset_l1, + sizeof(pic->last_slice_param.base.luma_offset_l1)); + memcpy(pic->last_slice_param.rext.ChromaOffsetL0, pic->last_slice_param.base.ChromaOffsetL0, + sizeof(pic->last_slice_param.base.ChromaOffsetL0)); + memcpy(pic->last_slice_param.rext.ChromaOffsetL1, pic->last_slice_param.base.ChromaOffsetL1, + sizeof(pic->last_slice_param.base.ChromaOffsetL1)); + } +#endif pic->last_buffer = buffer; pic->last_size = size; @@ -421,6 +489,83 @@ static int vaapi_hevc_decode_slice(AVCodecContext *avctx, return 0; } +static int ptl_convert(const PTLCommon *general_ptl, H265RawProfileTierLevel *h265_raw_ptl) +{ + h265_raw_ptl->general_profile_space = general_ptl->profile_space; + h265_raw_ptl->general_tier_flag = general_ptl->tier_flag; + h265_raw_ptl->general_profile_idc = general_ptl->profile_idc; + + memcpy(h265_raw_ptl->general_profile_compatibility_flag, + general_ptl->profile_compatibility_flag, 32 * sizeof(uint8_t)); + +#define copy_field(name) h265_raw_ptl->general_ ## name = general_ptl->name + copy_field(progressive_source_flag); + copy_field(interlaced_source_flag); + copy_field(non_packed_constraint_flag); + copy_field(frame_only_constraint_flag); + copy_field(max_12bit_constraint_flag); + copy_field(max_10bit_constraint_flag); + copy_field(max_8bit_constraint_flag); + copy_field(max_422chroma_constraint_flag); + copy_field(max_420chroma_constraint_flag); + copy_field(max_monochrome_constraint_flag); + copy_field(intra_constraint_flag); + copy_field(one_picture_only_constraint_flag); + copy_field(lower_bit_rate_constraint_flag); + copy_field(max_14bit_constraint_flag); + copy_field(inbld_flag); + copy_field(level_idc); +#undef copy_field + + return 0; +} + +/* + * Find exact va_profile for HEVC Range Extension + */ +VAProfile ff_vaapi_parse_hevc_rext_profile(AVCodecContext *avctx) +{ + const HEVCContext *h = avctx->priv_data; + const HEVCSPS *sps = h->ps.sps; + const PTL *ptl = &sps->ptl; + const PTLCommon *general_ptl = &ptl->general_ptl; + const H265ProfileDescriptor *profile; + H265RawProfileTierLevel h265_raw_ptl = {0}; + + /* convert PTLCommon to H265RawProfileTierLevel */ + ptl_convert(general_ptl, &h265_raw_ptl); + + profile = ff_h265_get_profile(&h265_raw_ptl); + if (!profile) { + av_log(avctx, AV_LOG_WARNING, "HEVC profile is not found.\n"); + goto end; + } else { + av_log(avctx, AV_LOG_VERBOSE, "HEVC profile %s is found.\n", profile->name); + } + +#if VA_CHECK_VERSION(1, 2, 0) + if (!strcmp(profile->name, "Main 4:2:2 10") || + !strcmp(profile->name, "Main 4:2:2 10 Intra")) + return VAProfileHEVCMain422_10; + else if (!strcmp(profile->name, "Main 4:4:4") || + !strcmp(profile->name, "Main 4:4:4 Intra")) + return VAProfileHEVCMain444; + else if (!strcmp(profile->name, "Main 4:4:4 10") || + !strcmp(profile->name, "Main 4:4:4 10 Intra")) + return VAProfileHEVCMain444_10; +#else + av_log(avctx, AV_LOG_WARNING, "HEVC profile %s is " + "not supported with this VA version.\n", profile->name); +#endif + +end: + if (avctx->hwaccel_flags & AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH) { + // Default to selecting Main profile if profile mismatch is allowed + return VAProfileHEVCMain; + } else + return VAProfileNone; +} + const AVHWAccel ff_hevc_vaapi_hwaccel = { .name = "hevc_vaapi", .type = AVMEDIA_TYPE_VIDEO, diff --git a/libavfilter/scale.h b/libavcodec/vaapi_hevc.h similarity index 76% rename from libavfilter/scale.h rename to libavcodec/vaapi_hevc.h index dfe67d0be01..b3b0e6fc1e9 100644 --- a/libavfilter/scale.h +++ b/libavcodec/vaapi_hevc.h @@ -16,13 +16,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVFILTER_SCALE_H -#define AVFILTER_SCALE_H +#ifndef AVCODEC_VAAPI_HEVC_H +#define AVCODEC_VAAPI_HEVC_H -#include "avfilter.h" +#include +#include "avcodec.h" -int ff_scale_eval_dimensions(void *ctx, - const char *w_expr, const char *h_expr, - AVFilterLink *inlink, AVFilterLink *outlink, - int *ret_w, int *ret_h); -#endif +VAProfile ff_vaapi_parse_hevc_rext_profile(AVCodecContext *avctx); + +#endif /* AVCODEC_VAAPI_HEVC_H */ diff --git a/libavcodec/vaapi_mjpeg.c b/libavcodec/vaapi_mjpeg.c index 14e0206ae1f..81582114b64 100644 --- a/libavcodec/vaapi_mjpeg.c +++ b/libavcodec/vaapi_mjpeg.c @@ -19,7 +19,7 @@ #include #include -#include "hwaccel.h" +#include "hwconfig.h" #include "vaapi_decode.h" #include "mjpegdec.h" diff --git a/libavcodec/vaapi_mpeg2.c b/libavcodec/vaapi_mpeg2.c index aaed434c883..26e0cd827cb 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 "hwaccel.h" +#include "hwconfig.h" #include "mpegutils.h" #include "mpegvideo.h" #include "internal.h" diff --git a/libavcodec/vaapi_mpeg4.c b/libavcodec/vaapi_mpeg4.c index 11860ff7474..71e155154c1 100644 --- a/libavcodec/vaapi_mpeg4.c +++ b/libavcodec/vaapi_mpeg4.c @@ -21,7 +21,7 @@ */ #include "h263.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "internal.h" #include "mpeg4video.h" #include "mpegvideo.h" diff --git a/libavcodec/vaapi_vc1.c b/libavcodec/vaapi_vc1.c index 921ca6391b6..4e9607d9be1 100644 --- a/libavcodec/vaapi_vc1.c +++ b/libavcodec/vaapi_vc1.c @@ -20,7 +20,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "hwaccel.h" +#include "hwconfig.h" #include "internal.h" #include "vaapi_decode.h" #include "vc1.h" diff --git a/libavcodec/vaapi_vp8.c b/libavcodec/vaapi_vp8.c index 2426b30f13c..06c23e760bf 100644 --- a/libavcodec/vaapi_vp8.c +++ b/libavcodec/vaapi_vp8.c @@ -19,7 +19,7 @@ #include #include -#include "hwaccel.h" +#include "hwconfig.h" #include "vaapi_decode.h" #include "vp8.h" diff --git a/libavcodec/vaapi_vp9.c b/libavcodec/vaapi_vp9.c index f384ba7873a..776382f6837 100644 --- a/libavcodec/vaapi_vp9.c +++ b/libavcodec/vaapi_vp9.c @@ -22,7 +22,7 @@ #include "libavutil/pixdesc.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "vaapi_decode.h" #include "vp9shared.h" diff --git a/libavcodec/vble.c b/libavcodec/vble.c index c25ee986972..c48c13127ad 100644 --- a/libavcodec/vble.c +++ b/libavcodec/vble.c @@ -214,6 +214,5 @@ AVCodec ff_vble_decoder = { .close = vble_decode_close, .decode = vble_decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(vble_decode_init), .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, }; diff --git a/libavcodec/vc1.c b/libavcodec/vc1.c index e102b931d84..9df778bcab9 100644 --- a/libavcodec/vc1.c +++ b/libavcodec/vc1.c @@ -451,7 +451,11 @@ static int decode_sequence_header_adv(VC1Context *v, GetBitContext *gb) h = get_bits(gb, 8) + 1; v->s.avctx->sample_aspect_ratio = (AVRational){w, h}; } else { - av_reduce(&v->s.avctx->sample_aspect_ratio.num, + if (v->s.avctx->width > v->max_coded_width || + v->s.avctx->height > v->max_coded_height) { + avpriv_request_sample(v->s.avctx, "Huge resolution"); + } else + av_reduce(&v->s.avctx->sample_aspect_ratio.num, &v->s.avctx->sample_aspect_ratio.den, v->s.avctx->height * w, v->s.avctx->width * h, @@ -933,7 +937,9 @@ int ff_vc1_parse_frame_header_adv(VC1Context *v, GetBitContext* gb) else if ((v->s.pict_type != AV_PICTURE_TYPE_B) && (v->s.pict_type != AV_PICTURE_TYPE_BI)) { v->refdist = get_bits(gb, 2); if (v->refdist == 3) - v->refdist += get_unary(gb, 0, 16); + v->refdist += get_unary(gb, 0, 14); + if (v->refdist > 16) + return AVERROR_INVALIDDATA; } if ((v->s.pict_type == AV_PICTURE_TYPE_B) || (v->s.pict_type == AV_PICTURE_TYPE_BI)) { if (read_bfraction(v, gb) < 0) @@ -1313,16 +1319,17 @@ int ff_vc1_parse_frame_header_adv(VC1Context *v, GetBitContext* gb) break; } - if (v->fcm != PROGRESSIVE && !v->s.quarter_sample) { - v->range_x <<= 1; - v->range_y <<= 1; - } /* AC Syntax */ v->c_ac_table_index = decode012(gb); if (v->s.pict_type == AV_PICTURE_TYPE_I || v->s.pict_type == AV_PICTURE_TYPE_BI) { v->y_ac_table_index = decode012(gb); } + else if (v->fcm != PROGRESSIVE && !v->s.quarter_sample) { + v->range_x <<= 1; + v->range_y <<= 1; + } + /* DC Syntax */ v->s.dc_table_index = get_bits1(gb); if ((v->s.pict_type == AV_PICTURE_TYPE_I || v->s.pict_type == AV_PICTURE_TYPE_BI) diff --git a/libavcodec/vc1.h b/libavcodec/vc1.h index 69f6ca9e4d7..4559a06cb60 100644 --- a/libavcodec/vc1.h +++ b/libavcodec/vc1.h @@ -176,8 +176,6 @@ typedef struct VC1Context{ H264ChromaContext h264chroma; VC1DSPContext vc1dsp; - int bits; - /** Simple/Main Profile sequence header */ //@{ int res_sprite; ///< reserved, sprite mode diff --git a/libavcodec/vc1_block.c b/libavcodec/vc1_block.c index 514206f6d21..16542dba3a6 100644 --- a/libavcodec/vc1_block.c +++ b/libavcodec/vc1_block.c @@ -846,7 +846,7 @@ static int vc1_decode_i_block_adv(VC1Context *v, int16_t block[64], int n, q2 = FFABS(q2) * 2 + ((q2 < 0) ? 0 : v->halfpq) - 1; if (q2 && q1 != q2) { for (k = 1; k < 8; k++) - block[k << sh] += (ac_val[k] * q2 * ff_vc1_dqscale[q1 - 1] + 0x20000) >> 18; + block[k << sh] += (int)(ac_val[k] * (unsigned)q2 * ff_vc1_dqscale[q1 - 1] + 0x20000) >> 18; } else { for (k = 1; k < 8; k++) block[k << sh] += ac_val[k]; @@ -889,7 +889,7 @@ static int vc1_decode_i_block_adv(VC1Context *v, int16_t block[64], int n, q2 = FFABS(q2) * 2 + ((q2 < 0) ? 0 : v->halfpq) - 1; if (q2 && q1 != q2) { for (k = 1; k < 8; k++) - ac_val2[k] = (ac_val2[k] * q2 * ff_vc1_dqscale[q1 - 1] + 0x20000) >> 18; + ac_val2[k] = (int)(ac_val2[k] * q2 * (unsigned)ff_vc1_dqscale[q1 - 1] + 0x20000) >> 18; } for (k = 1; k < 8; k++) { block[k << sh] = ac_val2[k] * scale; @@ -1036,10 +1036,10 @@ static int vc1_decode_intra_block(VC1Context *v, int16_t block[64], int n, if (q2 && q1 != q2) { if (dc_pred_dir) { // left for (k = 1; k < 8; k++) - block[k << v->left_blk_sh] += (ac_val[k] * q2 * ff_vc1_dqscale[q1 - 1] + 0x20000) >> 18; + block[k << v->left_blk_sh] += (int)(ac_val[k] * q2 * (unsigned)ff_vc1_dqscale[q1 - 1] + 0x20000) >> 18; } else { //top for (k = 1; k < 8; k++) - block[k << v->top_blk_sh] += (ac_val[k + 8] * q2 * ff_vc1_dqscale[q1 - 1] + 0x20000) >> 18; + block[k << v->top_blk_sh] += (int)(ac_val[k + 8] * q2 * (unsigned)ff_vc1_dqscale[q1 - 1] + 0x20000) >> 18; } } else { if (dc_pred_dir) { // left @@ -1481,7 +1481,7 @@ static int vc1_decode_p_mb(VC1Context *v) v->vc1dsp.vc1_inv_trans_8x8(v->block[v->cur_blk_idx][block_map[i]]); if (v->rangeredfrm) for (j = 0; j < 64; j++) - v->block[v->cur_blk_idx][block_map[i]][j] <<= 1; + v->block[v->cur_blk_idx][block_map[i]][j] *= 2; block_cbp |= 0xF << (i << 2); block_intra |= 1 << i; } else if (is_coded[i]) { @@ -1997,7 +1997,7 @@ static int vc1_decode_b_mb(VC1Context *v) v->vc1dsp.vc1_inv_trans_8x8(s->block[i]); if (v->rangeredfrm) for (j = 0; j < 64; j++) - s->block[i][j] <<= 1; + s->block[i][j] *= 2; s->idsp.put_signed_pixels_clamped(s->block[i], s->dest[dst_idx] + off, i & 4 ? s->uvlinesize @@ -2632,10 +2632,10 @@ static void vc1_decode_i_blocks(VC1Context *v) if (v->s.loop_filter) ff_vc1_i_loop_filter(v); - if (get_bits_count(&s->gb) > v->bits) { + if (get_bits_left(&s->gb) < 0) { ff_er_add_slice(&s->er, 0, 0, s->mb_x, s->mb_y, ER_MB_ERROR); av_log(s->avctx, AV_LOG_ERROR, "Bits overconsumption: %i > %i\n", - get_bits_count(&s->gb), v->bits); + get_bits_count(&s->gb), s->gb.size_in_bits); return; } @@ -2661,7 +2661,7 @@ static void vc1_decode_i_blocks(VC1Context *v) /** Decode blocks of I-frame for advanced profile */ -static void vc1_decode_i_blocks_adv(VC1Context *v) +static int vc1_decode_i_blocks_adv(VC1Context *v) { int k; MpegEncContext *s = &v->s; @@ -2672,6 +2672,9 @@ static void vc1_decode_i_blocks_adv(VC1Context *v) int mqdiff; GetBitContext *gb = &s->gb; + if (get_bits_left(gb) <= 1) + return AVERROR_INVALIDDATA; + /* select coding mode used for VLC tables selection */ switch (v->y_ac_table_index) { case 0: @@ -2725,6 +2728,11 @@ static void vc1_decode_i_blocks_adv(VC1Context *v) // do actual MB decoding and displaying if (v->fieldtx_is_raw) v->fieldtx_plane[mb_pos] = get_bits1(&v->s.gb); + if (get_bits_left(&v->s.gb) <= 1) { + ff_er_add_slice(&s->er, 0, s->start_mb_y, s->mb_x, s->mb_y, ER_MB_ERROR); + return 0; + } + cbp = get_vlc2(&v->s.gb, ff_msmp4_mb_i_vlc.table, MB_INTRA_VLC_BITS, 2); if (v->acpred_is_raw) v->s.ac_pred = get_bits1(&v->s.gb); @@ -2770,12 +2778,12 @@ static void vc1_decode_i_blocks_adv(VC1Context *v) if (v->s.loop_filter) ff_vc1_i_loop_filter(v); - if (get_bits_count(&s->gb) > v->bits) { + if (get_bits_left(&s->gb) < 0) { // TODO: may need modification to handle slice coding ff_er_add_slice(&s->er, 0, s->start_mb_y, s->mb_x, s->mb_y, ER_MB_ERROR); av_log(s->avctx, AV_LOG_ERROR, "Bits overconsumption: %i > %i\n", - get_bits_count(&s->gb), v->bits); - return; + get_bits_count(&s->gb), s->gb.size_in_bits); + return 0; } inc_blk_idx(v->topleft_blk_idx); inc_blk_idx(v->top_blk_idx); @@ -2793,6 +2801,7 @@ static void vc1_decode_i_blocks_adv(VC1Context *v) ff_mpeg_draw_horiz_band(s, (s->end_mb_y - 1) * 16, 16); ff_er_add_slice(&s->er, 0, s->start_mb_y << v->field_mode, s->mb_width - 1, (s->end_mb_y << v->field_mode) - 1, ER_MB_END); + return 0; } static void vc1_decode_p_blocks(VC1Context *v) @@ -2834,6 +2843,12 @@ static void vc1_decode_p_blocks(VC1Context *v) for (; s->mb_x < s->mb_width; s->mb_x++) { ff_update_block_index(s); + if (v->fcm == ILACE_FIELD || (v->fcm == PROGRESSIVE && v->mv_type_is_raw) || v->skip_is_raw) + if (get_bits_left(&v->s.gb) <= 1) { + ff_er_add_slice(&s->er, 0, s->start_mb_y, s->mb_x, s->mb_y, ER_MB_ERROR); + return; + } + if (v->fcm == ILACE_FIELD) { vc1_decode_p_mb_intfi(v); if (apply_loop_filter) @@ -2847,11 +2862,11 @@ static void vc1_decode_p_blocks(VC1Context *v) if (apply_loop_filter) ff_vc1_p_loop_filter(v); } - if (get_bits_count(&s->gb) > v->bits || get_bits_count(&s->gb) < 0) { + if (get_bits_left(&s->gb) < 0 || get_bits_count(&s->gb) < 0) { // TODO: may need modification to handle slice coding ff_er_add_slice(&s->er, 0, s->start_mb_y, s->mb_x, s->mb_y, ER_MB_ERROR); av_log(s->avctx, AV_LOG_ERROR, "Bits overconsumption: %i > %i at %ix%i\n", - get_bits_count(&s->gb), v->bits, s->mb_x, s->mb_y); + get_bits_count(&s->gb), s->gb.size_in_bits, s->mb_x, s->mb_y); return; } inc_blk_idx(v->topleft_blk_idx); @@ -2917,6 +2932,12 @@ static void vc1_decode_b_blocks(VC1Context *v) for (; s->mb_x < s->mb_width; s->mb_x++) { ff_update_block_index(s); + if (v->fcm == ILACE_FIELD || v->skip_is_raw || v->dmb_is_raw) + if (get_bits_left(&v->s.gb) <= 1) { + ff_er_add_slice(&s->er, 0, s->start_mb_y, s->mb_x, s->mb_y, ER_MB_ERROR); + return; + } + if (v->fcm == ILACE_FIELD) { vc1_decode_b_mb_intfi(v); if (v->s.loop_filter) @@ -2930,11 +2951,11 @@ static void vc1_decode_b_blocks(VC1Context *v) if (v->s.loop_filter) ff_vc1_i_loop_filter(v); } - if (get_bits_count(&s->gb) > v->bits || get_bits_count(&s->gb) < 0) { + if (get_bits_left(&s->gb) < 0 || get_bits_count(&s->gb) < 0) { // TODO: may need modification to handle slice coding ff_er_add_slice(&s->er, 0, s->start_mb_y, s->mb_x, s->mb_y, ER_MB_ERROR); av_log(s->avctx, AV_LOG_ERROR, "Bits overconsumption: %i > %i at %ix%i\n", - get_bits_count(&s->gb), v->bits, s->mb_x, s->mb_y); + get_bits_count(&s->gb), s->gb.size_in_bits, s->mb_x, s->mb_y); return; } } diff --git a/libavcodec/vc1_pred.c b/libavcodec/vc1_pred.c index e1ad0e1d7df..f70956e7392 100644 --- a/libavcodec/vc1_pred.c +++ b/libavcodec/vc1_pred.c @@ -191,9 +191,9 @@ static av_always_inline int scaleforopp(VC1Context *v, int n /* MV */, n >>= hpel; if (v->s.pict_type == AV_PICTURE_TYPE_B && !v->second_field && dir == 1) { if (dim) - n = scaleforopp_y(v, n, dir) << hpel; + n = scaleforopp_y(v, n, dir) * (1 << hpel); else - n = scaleforopp_x(v, n) << hpel; + n = scaleforopp_x(v, n) * (1 << hpel); return n; } if (v->s.pict_type != AV_PICTURE_TYPE_B) diff --git a/libavcodec/vc1dec.c b/libavcodec/vc1dec.c index ac3198e4fd6..7809234ff79 100644 --- a/libavcodec/vc1dec.c +++ b/libavcodec/vc1dec.c @@ -29,7 +29,7 @@ #include "avcodec.h" #include "blockdsp.h" #include "get_bits.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "internal.h" #include "mpeg_er.h" #include "mpegvideo.h" @@ -431,7 +431,7 @@ static av_cold int vc1_decode_init(AVCodecContext *avctx) v->output_height = avctx->height; if (!avctx->extradata_size || !avctx->extradata) - return -1; + return AVERROR_INVALIDDATA; v->s.avctx = avctx; if ((ret = ff_vc1_init_common(v)) < 0) @@ -472,7 +472,7 @@ static av_cold int vc1_decode_init(AVCodecContext *avctx) if (avctx->extradata_size < 16) { av_log(avctx, AV_LOG_ERROR, "Extradata size too small: %i\n", avctx->extradata_size); - return -1; + return AVERROR_INVALIDDATA; } buf2 = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); @@ -508,7 +508,7 @@ static av_cold int vc1_decode_init(AVCodecContext *avctx) av_free(buf2); if (!seq_initialized || !ep_initialized) { av_log(avctx, AV_LOG_ERROR, "Incomplete extradata\n"); - return -1; + return AVERROR_INVALIDDATA; } v->res_sprite = (avctx->codec_id == AV_CODEC_ID_VC1IMAGE); } @@ -576,14 +576,21 @@ static av_cold int vc1_decode_init(AVCodecContext *avctx) if (v->sprite_width > 1 << 14 || v->sprite_height > 1 << 14 || v->output_width > 1 << 14 || - v->output_height > 1 << 14) return -1; + v->output_height > 1 << 14) { + ret = AVERROR_INVALIDDATA; + goto error; + } if ((v->sprite_width&1) || (v->sprite_height&1)) { avpriv_request_sample(avctx, "odd sprites support"); - return AVERROR_PATCHWELCOME; + ret = AVERROR_PATCHWELCOME; + goto error; } } return 0; +error: + av_frame_free(&v->sprite_output_frame); + return ret; } /** Close a VC1/WMV3 decoder @@ -688,13 +695,13 @@ static int vc1_decode_frame(AVCodecContext *avctx, void *data, int buf_size3; if (avctx->hwaccel) buf_start_second_field = start; - tmp = av_realloc_array(slices, sizeof(*slices), (n_slices+1)); + tmp = av_realloc_array(slices, sizeof(*slices), n_slices+1); if (!tmp) { ret = AVERROR(ENOMEM); goto err; } slices = tmp; - slices[n_slices].buf = av_mallocz(buf_size + AV_INPUT_BUFFER_PADDING_SIZE); + slices[n_slices].buf = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE); if (!slices[n_slices].buf) { ret = AVERROR(ENOMEM); goto err; @@ -717,13 +724,13 @@ static int vc1_decode_frame(AVCodecContext *avctx, void *data, break; case VC1_CODE_SLICE: { int buf_size3; - tmp = av_realloc_array(slices, sizeof(*slices), (n_slices+1)); + tmp = av_realloc_array(slices, sizeof(*slices), n_slices+1); if (!tmp) { ret = AVERROR(ENOMEM); goto err; } slices = tmp; - slices[n_slices].buf = av_mallocz(buf_size + AV_INPUT_BUFFER_PADDING_SIZE); + slices[n_slices].buf = av_mallocz(size + AV_INPUT_BUFFER_PADDING_SIZE); if (!slices[n_slices].buf) { ret = AVERROR(ENOMEM); goto err; @@ -752,7 +759,7 @@ static int vc1_decode_frame(AVCodecContext *avctx, void *data, } 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)); + tmp = av_realloc_array(slices, sizeof(*slices), n_slices+1); if (!tmp) { ret = AVERROR(ENOMEM); goto err; @@ -847,7 +854,12 @@ static int vc1_decode_frame(AVCodecContext *avctx, void *data, ret = AVERROR_INVALIDDATA; goto err; } - + if ((avctx->codec_id == AV_CODEC_ID_WMV3IMAGE || avctx->codec_id == AV_CODEC_ID_VC1IMAGE) + && v->field_mode) { + av_log(v->s.avctx, AV_LOG_ERROR, "Sprite decoder: expected Frames not Fields\n"); + ret = AVERROR_INVALIDDATA; + goto err; + } if ((s->mb_height >> v->field_mode) == 0) { av_log(v->s.avctx, AV_LOG_ERROR, "image too short\n"); ret = AVERROR_INVALIDDATA; @@ -1026,7 +1038,6 @@ static int vc1_decode_frame(AVCodecContext *avctx, void *data, ff_mpeg_er_frame_start(s); - v->bits = buf_size * 8; v->end_mb_x = s->mb_width; if (v->field_mode) { s->current_picture.f->linesize[0] <<= 1; @@ -1100,8 +1111,9 @@ static int vc1_decode_frame(AVCodecContext *avctx, void *data, continue; } ff_vc1_decode_blocks(v); - if (i != n_slices) + if (i != n_slices) { s->gb = slices[i].gb; + } } if (v->field_mode) { v->second_field = 0; diff --git a/libavcodec/vc2enc.c b/libavcodec/vc2enc.c index d0101e01e43..ba5a03e4ec7 100644 --- a/libavcodec/vc2enc.c +++ b/libavcodec/vc2enc.c @@ -867,6 +867,7 @@ static int dwt_plane(AVCodecContext *avctx, void *arg) for (x = 0; x < p->width; x++) { buf[x] = pix[x] - s->diff_offset; } + memset(&buf[x], 0, (p->coef_stride - p->width)*sizeof(dwtcoef)); buf += p->coef_stride; pix += pix_stride; } @@ -876,6 +877,7 @@ static int dwt_plane(AVCodecContext *avctx, void *arg) for (x = 0; x < p->width; x++) { buf[x] = pix[x] - s->diff_offset; } + memset(&buf[x], 0, (p->coef_stride - p->width)*sizeof(dwtcoef)); buf += p->coef_stride; pix += pix_stride; } diff --git a/libavcodec/vc2enc_dwt.c b/libavcodec/vc2enc_dwt.c index d22af8a3138..a8d3f1c6698 100644 --- a/libavcodec/vc2enc_dwt.c +++ b/libavcodec/vc2enc_dwt.c @@ -66,7 +66,7 @@ static void vc2_subband_dwt_97(VC2TransformContext *t, dwtcoef *data, */ for (y = 0; y < synth_height; y++) { for (x = 0; x < synth_width; x++) - synthl[x] = datal[x] << 1; + synthl[x] = datal[x] * 2; synthl += synth_width; datal += stride; } diff --git a/libavcodec/vdpau_h264.c b/libavcodec/vdpau_h264.c index 2a260f76aba..5ba73dafd4d 100644 --- a/libavcodec/vdpau_h264.c +++ b/libavcodec/vdpau_h264.c @@ -27,7 +27,7 @@ #include "internal.h" #include "h264dec.h" #include "h264_ps.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "mpegutils.h" #include "vdpau.h" #include "vdpau_internal.h" diff --git a/libavcodec/vdpau_hevc.c b/libavcodec/vdpau_hevc.c index 024ac6e2f22..29cb2da0788 100644 --- a/libavcodec/vdpau_hevc.c +++ b/libavcodec/vdpau_hevc.c @@ -26,7 +26,7 @@ #include "internal.h" #include "hevc_data.h" #include "hevcdec.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "vdpau.h" #include "vdpau_internal.h" diff --git a/libavcodec/vdpau_internal.h b/libavcodec/vdpau_internal.h index 1ee38dbc55b..b6ea078cb2f 100644 --- a/libavcodec/vdpau_internal.h +++ b/libavcodec/vdpau_internal.h @@ -54,6 +54,9 @@ union VDPAUPictureInfo { #ifdef VDP_YCBCR_FORMAT_Y_U_V_444 VdpPictureInfoHEVC444 hevc_444; #endif +#ifdef VDP_DECODER_PROFILE_VP9_PROFILE_0 + VdpPictureInfoVP9 vp9; +#endif }; typedef struct VDPAUHWContext { diff --git a/libavcodec/vdpau_mpeg12.c b/libavcodec/vdpau_mpeg12.c index d286e7e57da..72220ffb4ea 100644 --- a/libavcodec/vdpau_mpeg12.c +++ b/libavcodec/vdpau_mpeg12.c @@ -24,7 +24,7 @@ #include #include "avcodec.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "mpegvideo.h" #include "vdpau.h" #include "vdpau_internal.h" diff --git a/libavcodec/vdpau_mpeg4.c b/libavcodec/vdpau_mpeg4.c index 96f83026a8e..93b25beb1fe 100644 --- a/libavcodec/vdpau_mpeg4.c +++ b/libavcodec/vdpau_mpeg4.c @@ -24,7 +24,7 @@ #include #include "avcodec.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "mpeg4video.h" #include "vdpau.h" #include "vdpau_internal.h" diff --git a/libavcodec/vdpau_vc1.c b/libavcodec/vdpau_vc1.c index 671baf96b4d..96c91b58be0 100644 --- a/libavcodec/vdpau_vc1.c +++ b/libavcodec/vdpau_vc1.c @@ -24,7 +24,7 @@ #include #include "avcodec.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "vc1.h" #include "vdpau.h" #include "vdpau_internal.h" diff --git a/libavcodec/vdpau_vp9.c b/libavcodec/vdpau_vp9.c new file mode 100644 index 00000000000..a8609d647e3 --- /dev/null +++ b/libavcodec/vdpau_vp9.c @@ -0,0 +1,241 @@ +/* + * VP9 HW decode acceleration through VDPAU + * + * Copyright (c) 2019 Manoj Gupta Bonda + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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/pixdesc.h" +#include "avcodec.h" +#include "internal.h" +#include "vp9data.h" +#include "vp9dec.h" +#include "hwconfig.h" +#include "vdpau.h" +#include "vdpau_internal.h" + +static int vdpau_vp9_start_frame(AVCodecContext *avctx, + const uint8_t *buffer, uint32_t size) +{ + VP9Context *s = avctx->priv_data; + VP9SharedContext *h = &(s->s); + VP9Frame pic = h->frames[CUR_FRAME]; + struct vdpau_picture_context *pic_ctx = pic.hwaccel_picture_private; + int i; + + VdpPictureInfoVP9 *info = &pic_ctx->info.vp9; + const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(avctx->sw_pix_fmt); + if (!pixdesc) { + return AV_PIX_FMT_NONE; + } + + info->width = avctx->width; + info->height = avctx->height; + /* fill LvPictureInfoVP9 struct */ + info->lastReference = VDP_INVALID_HANDLE; + info->goldenReference = VDP_INVALID_HANDLE; + info->altReference = VDP_INVALID_HANDLE; + + if (h->refs[h->h.refidx[0]].f && h->refs[h->h.refidx[0]].f->private_ref) { + info->lastReference = ff_vdpau_get_surface_id(h->refs[h->h.refidx[0]].f); + } + if (h->refs[h->h.refidx[1]].f && h->refs[h->h.refidx[1]].f->private_ref) { + info->goldenReference = ff_vdpau_get_surface_id(h->refs[h->h.refidx[1]].f); + } + if (h->refs[h->h.refidx[2]].f && h->refs[h->h.refidx[2]].f->private_ref) { + info->altReference = ff_vdpau_get_surface_id(h->refs[h->h.refidx[2]].f); + } + + info->profile = h->h.profile; + info->frameContextIdx = h->h.framectxid; + info->keyFrame = h->h.keyframe; + info->showFrame = !h->h.invisible; + info->errorResilient = h->h.errorres; + info->frameParallelDecoding = h->h.parallelmode; + + info->subSamplingX = pixdesc->log2_chroma_w; + info->subSamplingY = pixdesc->log2_chroma_h; + + info->intraOnly = h->h.intraonly; + info->allowHighPrecisionMv = h->h.keyframe ? 0 : h->h.highprecisionmvs; + info->refreshEntropyProbs = h->h.refreshctx; + + info->bitDepthMinus8Luma = pixdesc->comp[0].depth - 8; + info->bitDepthMinus8Chroma = pixdesc->comp[1].depth - 8; + + info->loopFilterLevel = h->h.filter.level; + info->loopFilterSharpness = h->h.filter.sharpness; + info->modeRefLfEnabled = h->h.lf_delta.enabled; + + info->log2TileColumns = h->h.tiling.log2_tile_cols; + info->log2TileRows = h->h.tiling.log2_tile_rows; + + info->segmentEnabled = h->h.segmentation.enabled; + info->segmentMapUpdate = h->h.segmentation.update_map; + info->segmentMapTemporalUpdate = h->h.segmentation.temporal; + info->segmentFeatureMode = h->h.segmentation.absolute_vals; + + info->qpYAc = h->h.yac_qi; + info->qpYDc = h->h.ydc_qdelta; + info->qpChDc = h->h.uvdc_qdelta; + info->qpChAc = h->h.uvac_qdelta; + + info->resetFrameContext = h->h.resetctx; + info->mcompFilterType = h->h.filtermode ^ (h->h.filtermode <= 1); + info->uncompressedHeaderSize = h->h.uncompressed_header_size; + info->compressedHeaderSize = h->h.compressed_header_size; + info->refFrameSignBias[0] = 0; + + + for (i = 0; i < FF_ARRAY_ELEMS(info->mbModeLfDelta); i++) + info->mbModeLfDelta[i] = h->h.lf_delta.mode[i]; + + for (i = 0; i < FF_ARRAY_ELEMS(info->mbRefLfDelta); i++) + info->mbRefLfDelta[i] = h->h.lf_delta.ref[i]; + + for (i = 0; i < FF_ARRAY_ELEMS(info->mbSegmentTreeProbs); i++) + info->mbSegmentTreeProbs[i] = h->h.segmentation.prob[i]; + + for (i = 0; i < FF_ARRAY_ELEMS(info->activeRefIdx); i++) { + info->activeRefIdx[i] = h->h.refidx[i]; + info->segmentPredProbs[i] = h->h.segmentation.pred_prob[i]; + info->refFrameSignBias[i + 1] = h->h.signbias[i]; + } + + for (i = 0; i < FF_ARRAY_ELEMS(info->segmentFeatureEnable); i++) { + info->segmentFeatureEnable[i][0] = h->h.segmentation.feat[i].q_enabled; + info->segmentFeatureEnable[i][1] = h->h.segmentation.feat[i].lf_enabled; + info->segmentFeatureEnable[i][2] = h->h.segmentation.feat[i].ref_enabled; + info->segmentFeatureEnable[i][3] = h->h.segmentation.feat[i].skip_enabled; + + info->segmentFeatureData[i][0] = h->h.segmentation.feat[i].q_val; + info->segmentFeatureData[i][1] = h->h.segmentation.feat[i].lf_val; + info->segmentFeatureData[i][2] = h->h.segmentation.feat[i].ref_val; + info->segmentFeatureData[i][3] = 0; + } + + switch (avctx->colorspace) { + default: + case AVCOL_SPC_UNSPECIFIED: + info->colorSpace = 0; + break; + case AVCOL_SPC_BT470BG: + info->colorSpace = 1; + break; + case AVCOL_SPC_BT709: + info->colorSpace = 2; + break; + case AVCOL_SPC_SMPTE170M: + info->colorSpace = 3; + break; + case AVCOL_SPC_SMPTE240M: + info->colorSpace = 4; + break; + case AVCOL_SPC_BT2020_NCL: + info->colorSpace = 5; + break; + case AVCOL_SPC_RESERVED: + info->colorSpace = 6; + break; + case AVCOL_SPC_RGB: + info->colorSpace = 7; + break; + } + + return ff_vdpau_common_start_frame(pic_ctx, buffer, size); + +} + +static const uint8_t start_code_prefix[3] = { 0x00, 0x00, 0x01 }; + +static int vdpau_vp9_decode_slice(AVCodecContext *avctx, + const uint8_t *buffer, uint32_t size) +{ + VP9SharedContext *h = avctx->priv_data; + VP9Frame pic = h->frames[CUR_FRAME]; + struct vdpau_picture_context *pic_ctx = pic.hwaccel_picture_private; + + int val; + + val = ff_vdpau_add_buffer(pic_ctx, start_code_prefix, 3); + if (val) + return val; + + val = ff_vdpau_add_buffer(pic_ctx, buffer, size); + if (val) + return val; + + return 0; +} + +static int vdpau_vp9_end_frame(AVCodecContext *avctx) +{ + VP9SharedContext *h = avctx->priv_data; + VP9Frame pic = h->frames[CUR_FRAME]; + struct vdpau_picture_context *pic_ctx = pic.hwaccel_picture_private; + + int val; + + val = ff_vdpau_common_end_frame(avctx, pic.tf.f, pic_ctx); + if (val < 0) + return val; + + return 0; +} + +static int vdpau_vp9_init(AVCodecContext *avctx) +{ + VdpDecoderProfile profile; + uint32_t level = avctx->level; + + switch (avctx->profile) { + case FF_PROFILE_VP9_0: + profile = VDP_DECODER_PROFILE_VP9_PROFILE_0; + break; + case FF_PROFILE_VP9_1: + profile = VDP_DECODER_PROFILE_VP9_PROFILE_1; + break; + case FF_PROFILE_VP9_2: + profile = VDP_DECODER_PROFILE_VP9_PROFILE_2; + break; + case FF_PROFILE_VP9_3: + profile = VDP_DECODER_PROFILE_VP9_PROFILE_3; + break; + default: + return AVERROR(ENOTSUP); + } + + 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, + .start_frame = vdpau_vp9_start_frame, + .end_frame = vdpau_vp9_end_frame, + .decode_slice = vdpau_vp9_decode_slice, + .frame_priv_data_size = sizeof(struct vdpau_picture_context), + .init = vdpau_vp9_init, + .uninit = ff_vdpau_common_uninit, + .frame_params = ff_vdpau_common_frame_params, + .priv_data_size = sizeof(VDPAUContext), + .caps_internal = HWACCEL_CAP_ASYNC_SAFE, +}; diff --git a/libavcodec/version.h b/libavcodec/version.h index 3331d473001..85fbe24dc63 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,7 +28,7 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 58 -#define LIBAVCODEC_VERSION_MINOR 54 +#define LIBAVCODEC_VERSION_MINOR 91 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ @@ -135,6 +135,15 @@ #ifndef FF_API_UNSANITIZED_BITRATES #define FF_API_UNSANITIZED_BITRATES (LIBAVCODEC_VERSION_MAJOR < 59) #endif +#ifndef FF_API_OPENH264_SLICE_MODE +#define FF_API_OPENH264_SLICE_MODE (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_OPENH264_CABAC +#define FF_API_OPENH264_CABAC (LIBAVCODEC_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_UNUSED_CODEC_CAPS +#define FF_API_UNUSED_CODEC_CAPS (LIBAVCODEC_VERSION_MAJOR < 59) +#endif #endif /* AVCODEC_VERSION_H */ diff --git a/libavcodec/videotoolbox.c b/libavcodec/videotoolbox.c index c718e82cc56..8773de33930 100644 --- a/libavcodec/videotoolbox.c +++ b/libavcodec/videotoolbox.c @@ -91,6 +91,11 @@ static int videotoolbox_postproc_frame(void *avctx, AVFrame *frame) return AVERROR_EXTERNAL; } + frame->crop_right = 0; + frame->crop_left = 0; + frame->crop_top = 0; + frame->crop_bottom = 0; + frame->data[3] = (uint8_t*)ref->pixbuf; if (ref->hw_frames_ctx) { @@ -612,7 +617,7 @@ static void videotoolbox_decoder_callback(void *opaque, } if (!image_buffer) { - av_log(NULL, AV_LOG_DEBUG, "vt decoder cb: output image buffer is null\n"); + av_log(avctx, AV_LOG_DEBUG, "vt decoder cb: output image buffer is null\n"); return; } @@ -898,11 +903,6 @@ static int videotoolbox_common_end_frame(AVCodecContext *avctx, AVFrame *frame) AVVideotoolboxContext *videotoolbox = videotoolbox_get_context(avctx); VTContext *vtctx = avctx->internal->hwaccel_priv_data; - frame->crop_right = 0; - frame->crop_left = 0; - frame->crop_top = 0; - frame->crop_bottom = 0; - if (vtctx->reconfig_needed == true) { vtctx->reconfig_needed = false; av_log(avctx, AV_LOG_VERBOSE, "VideoToolbox decoder needs reconfig, restarting..\n"); @@ -1084,8 +1084,9 @@ static int videotoolbox_common_init(AVCodecContext *avctx) goto fail; } + bool full_range = avctx->color_range == AVCOL_RANGE_JPEG; vtctx->vt_ctx->cv_pix_fmt_type = - av_map_videotoolbox_format_from_pixfmt(hw_frames->sw_format); + av_map_videotoolbox_format_from_pixfmt2(hw_frames->sw_format, full_range); if (!vtctx->vt_ctx->cv_pix_fmt_type) { av_log(avctx, AV_LOG_ERROR, "Unknown sw_format.\n"); err = AVERROR(EINVAL); @@ -1143,7 +1144,7 @@ const AVHWAccel ff_hevc_videotoolbox_hwaccel = { .end_frame = videotoolbox_hevc_end_frame, .frame_params = videotoolbox_frame_params, .init = videotoolbox_common_init, - .uninit = ff_videotoolbox_uninit, + .uninit = videotoolbox_uninit, .priv_data_size = sizeof(VTContext), }; @@ -1208,14 +1209,15 @@ const AVHWAccel ff_mpeg4_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; -static AVVideotoolboxContext *av_videotoolbox_alloc_context_with_pix_fmt(enum AVPixelFormat pix_fmt) +static AVVideotoolboxContext *av_videotoolbox_alloc_context_with_pix_fmt(enum AVPixelFormat pix_fmt, + bool full_range) { AVVideotoolboxContext *ret = av_mallocz(sizeof(*ret)); if (ret) { ret->output_callback = videotoolbox_decoder_callback; - OSType cv_pix_fmt_type = av_map_videotoolbox_format_from_pixfmt(pix_fmt); + OSType cv_pix_fmt_type = av_map_videotoolbox_format_from_pixfmt2(pix_fmt, full_range); if (cv_pix_fmt_type == 0) { cv_pix_fmt_type = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; } @@ -1227,7 +1229,7 @@ static AVVideotoolboxContext *av_videotoolbox_alloc_context_with_pix_fmt(enum AV AVVideotoolboxContext *av_videotoolbox_alloc_context(void) { - return av_videotoolbox_alloc_context_with_pix_fmt(AV_PIX_FMT_NONE); + return av_videotoolbox_alloc_context_with_pix_fmt(AV_PIX_FMT_NONE, false); } int av_videotoolbox_default_init(AVCodecContext *avctx) @@ -1237,7 +1239,9 @@ int av_videotoolbox_default_init(AVCodecContext *avctx) int av_videotoolbox_default_init2(AVCodecContext *avctx, AVVideotoolboxContext *vtctx) { - avctx->hwaccel_context = vtctx ?: av_videotoolbox_alloc_context_with_pix_fmt(videotoolbox_best_pixel_format(avctx)); + enum AVPixelFormat pix_fmt = videotoolbox_best_pixel_format(avctx); + bool full_range = avctx->color_range == AVCOL_RANGE_JPEG; + avctx->hwaccel_context = vtctx ?: av_videotoolbox_alloc_context_with_pix_fmt(pix_fmt, full_range); if (!avctx->hwaccel_context) return AVERROR(ENOMEM); return videotoolbox_start(avctx); diff --git a/libavcodec/videotoolboxenc.c b/libavcodec/videotoolboxenc.c index ff33c279c3a..cc08cf6a500 100644 --- a/libavcodec/videotoolboxenc.c +++ b/libavcodec/videotoolboxenc.c @@ -80,6 +80,8 @@ static struct{ CFStringRef kVTProfileLevel_H264_High_5_1; CFStringRef kVTProfileLevel_H264_High_5_2; CFStringRef kVTProfileLevel_H264_High_AutoLevel; + CFStringRef kVTProfileLevel_H264_Extended_5_0; + CFStringRef kVTProfileLevel_H264_Extended_AutoLevel; CFStringRef kVTProfileLevel_HEVC_Main_AutoLevel; CFStringRef kVTProfileLevel_HEVC_Main10_AutoLevel; @@ -137,6 +139,8 @@ static void loadVTEncSymbols(){ GET_SYM(kVTProfileLevel_H264_High_5_1, "H264_High_5_1"); GET_SYM(kVTProfileLevel_H264_High_5_2, "H264_High_5_2"); 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_HEVC_Main_AutoLevel, "HEVC_Main_AutoLevel"); GET_SYM(kVTProfileLevel_HEVC_Main10_AutoLevel, "HEVC_Main10_AutoLevel"); @@ -154,6 +158,7 @@ typedef enum VT_H264Profile { H264_PROF_BASELINE, H264_PROF_MAIN, H264_PROF_HIGH, + H264_PROF_EXTENDED, H264_PROF_COUNT } VT_H264Profile; @@ -569,12 +574,16 @@ static void vtenc_output_callback( return; } - if (status || !sample_buffer) { + if (status) { av_log(avctx, AV_LOG_ERROR, "Error encoding frame: %d\n", (int)status); set_async_error(vtctx, AVERROR_EXTERNAL); return; } + if (!sample_buffer) { + return; + } + if (!avctx->extradata && (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)) { int set_status = set_extradata(avctx, sample_buffer); if (set_status) { @@ -704,6 +713,14 @@ static bool get_vt_h264_profile_level(AVCodecContext *avctx, compat_keys.kVTProfileLevel_H264_High_5_2; break; } break; + case H264_PROF_EXTENDED: + switch (vtctx->level) { + case 0: *profile_level_val = + compat_keys.kVTProfileLevel_H264_Extended_AutoLevel; break; + case 50: *profile_level_val = + compat_keys.kVTProfileLevel_H264_Extended_5_0; break; + } + break; } if (!*profile_level_val) { @@ -877,6 +894,14 @@ static int get_cv_color_primaries(AVCodecContext *avctx, *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; @@ -915,6 +940,22 @@ static int get_cv_transfer_function(AVCodecContext *avctx, *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; @@ -933,6 +974,7 @@ static int get_cv_transfer_function(AVCodecContext *avctx, break; default: + *transfer_fnc = NULL; av_log(avctx, AV_LOG_ERROR, "Transfer function %s is not supported.\n", av_color_transfer_name(trc)); return -1; } @@ -1078,7 +1120,7 @@ static int vtenc_create_encoder(AVCodecContext *avctx, kVTCompressionPropertyKey_ProfileLevel, profile_level); if (status) { - av_log(avctx, AV_LOG_ERROR, "Error setting profile/level property: %d\n", status); + av_log(avctx, AV_LOG_ERROR, "Error setting profile/level property: %d. Output will be encoded using a supported profile/level combination.\n", status); } } } @@ -2514,6 +2556,7 @@ static const AVOption h264_options[] = { { "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" }, diff --git a/libavcodec/vmdaudio.c b/libavcodec/vmdaudio.c index e8c8a064c7d..dfbd49fd840 100644 --- a/libavcodec/vmdaudio.c +++ b/libavcodec/vmdaudio.c @@ -76,7 +76,9 @@ static av_cold int vmdaudio_decode_init(AVCodecContext *avctx) av_log(avctx, AV_LOG_ERROR, "invalid number of channels\n"); return AVERROR(EINVAL); } - if (avctx->block_align < 1 || avctx->block_align % avctx->channels) { + if (avctx->block_align < 1 || avctx->block_align % avctx->channels || + avctx->block_align > INT_MAX - avctx->channels + ) { av_log(avctx, AV_LOG_ERROR, "invalid block align\n"); return AVERROR(EINVAL); } @@ -179,6 +181,9 @@ static int vmdaudio_decode_frame(AVCodecContext *avctx, void *data, /* drop incomplete chunks */ buf_size = audio_chunks * s->chunk_size; + if (silent_chunks + audio_chunks >= INT_MAX / avctx->block_align) + return AVERROR_INVALIDDATA; + /* get output buffer */ frame->nb_samples = ((silent_chunks + audio_chunks) * avctx->block_align) / avctx->channels; diff --git a/libavcodec/vmdvideo.c b/libavcodec/vmdvideo.c index b97032ff7e8..c1dc5b96963 100644 --- a/libavcodec/vmdvideo.c +++ b/libavcodec/vmdvideo.c @@ -226,7 +226,7 @@ static int vmd_decode(VmdVideoContext *s, AVFrame *frame) frame_y + frame_height > s->avctx->height) { av_log(s->avctx, AV_LOG_ERROR, "Invalid vertical range %d-%d\n", - frame_x, frame_width); + frame_y, frame_height); return AVERROR_INVALIDDATA; } diff --git a/libavcodec/vmnc.c b/libavcodec/vmnc.c index e2730433114..7f441bc4bd3 100644 --- a/libavcodec/vmnc.c +++ b/libavcodec/vmnc.c @@ -339,7 +339,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, if (12LL * chunks > bytestream2_get_bytes_left(gb)) return AVERROR_INVALIDDATA; - if ((ret = ff_reget_buffer(avctx, c->pic)) < 0) + if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; c->pic->key_frame = 0; diff --git a/libavcodec/vp3.c b/libavcodec/vp3.c index a2bd2ef07dd..6fe1ca46a3a 100644 --- a/libavcodec/vp3.c +++ b/libavcodec/vp3.c @@ -347,9 +347,6 @@ static av_cold int vp3_decode_end(AVCodecContext *avctx) av_frame_free(&s->last_frame.f); av_frame_free(&s->golden_frame.f); - if (avctx->internal->is_copy) - return 0; - for (i = 0; i < 16; i++) { ff_free_vlc(&s->dc_vlc[i]); ff_free_vlc(&s->ac_vlc_1[i]); @@ -2031,11 +2028,17 @@ static int vp4_mc_loop_filter(Vp3DecodeContext *s, int plane, int motion_x, int plane_width, plane_height); +#define safe_loop_filter(name, ptr, stride, bounding_values) \ + if ((uintptr_t)(ptr) & 7) \ + s->vp3dsp.name##_unaligned(ptr, stride, bounding_values); \ + else \ + s->vp3dsp.name(ptr, stride, bounding_values); + if (x_offset) - s->vp3dsp.h_loop_filter(loop + loop_stride + x_offset + 1, loop_stride, bounding_values); + safe_loop_filter(h_loop_filter, loop + loop_stride + x_offset + 1, loop_stride, bounding_values); if (y_offset) - s->vp3dsp.v_loop_filter(loop + (y_offset + 1)*loop_stride + 1, loop_stride, bounding_values); + safe_loop_filter(v_loop_filter, loop + (y_offset + 1)*loop_stride + 1, loop_stride, bounding_values); } for (i = 0; i < 9; i++) @@ -2324,8 +2327,6 @@ static av_cold int vp3_decode_init(AVCodecContext *avctx) if (ret < 0) return ret; - avctx->internal->allocate_progress = 1; - if (avctx->codec_tag == MKTAG('V', 'P', '4', '0')) s->version = 3; else if (avctx->codec_tag == MKTAG('V', 'P', '3', '0')) @@ -2416,32 +2417,32 @@ static av_cold int vp3_decode_init(AVCodecContext *avctx) /* init VLC tables */ if (s->version < 2) { - for (i = 0; i < 16; i++) { - /* DC histograms */ - init_vlc(&s->dc_vlc[i], 11, 32, - &dc_bias[i][0][1], 4, 2, - &dc_bias[i][0][0], 4, 2, 0); + for (i = 0; i < 16; i++) { + /* DC histograms */ + init_vlc(&s->dc_vlc[i], 11, 32, + &dc_bias[i][0][1], 4, 2, + &dc_bias[i][0][0], 4, 2, 0); - /* group 1 AC histograms */ - init_vlc(&s->ac_vlc_1[i], 11, 32, - &ac_bias_0[i][0][1], 4, 2, - &ac_bias_0[i][0][0], 4, 2, 0); + /* group 1 AC histograms */ + init_vlc(&s->ac_vlc_1[i], 11, 32, + &ac_bias_0[i][0][1], 4, 2, + &ac_bias_0[i][0][0], 4, 2, 0); - /* group 2 AC histograms */ - init_vlc(&s->ac_vlc_2[i], 11, 32, - &ac_bias_1[i][0][1], 4, 2, - &ac_bias_1[i][0][0], 4, 2, 0); + /* group 2 AC histograms */ + init_vlc(&s->ac_vlc_2[i], 11, 32, + &ac_bias_1[i][0][1], 4, 2, + &ac_bias_1[i][0][0], 4, 2, 0); - /* group 3 AC histograms */ - init_vlc(&s->ac_vlc_3[i], 11, 32, - &ac_bias_2[i][0][1], 4, 2, - &ac_bias_2[i][0][0], 4, 2, 0); + /* group 3 AC histograms */ + init_vlc(&s->ac_vlc_3[i], 11, 32, + &ac_bias_2[i][0][1], 4, 2, + &ac_bias_2[i][0][0], 4, 2, 0); - /* group 4 AC histograms */ - init_vlc(&s->ac_vlc_4[i], 11, 32, - &ac_bias_3[i][0][1], 4, 2, - &ac_bias_3[i][0][0], 4, 2, 0); - } + /* group 4 AC histograms */ + init_vlc(&s->ac_vlc_4[i], 11, 32, + &ac_bias_3[i][0][1], 4, 2, + &ac_bias_3[i][0][0], 4, 2, 0); + } #if CONFIG_VP4_DECODER } else { /* version >= 2 */ for (i = 0; i < 16; i++) { @@ -2589,10 +2590,6 @@ static int vp3_update_thread_context(AVCodecContext *dst, const AVCodecContext * Vp3DecodeContext *s = dst->priv_data, *s1 = src->priv_data; int qps_changed = 0, i, err; -#define copy_fields(to, from, start_field, end_field) \ - memcpy(&to->start_field, &from->start_field, \ - (char *) &to->end_field - (char *) &to->start_field) - if (!s1->current_frame.f->data[0] || s->width != s1->width || s->height != s1->height) { if (s != s1) @@ -2601,23 +2598,6 @@ static int vp3_update_thread_context(AVCodecContext *dst, const AVCodecContext * } if (s != s1) { - if (!s->current_frame.f) - return AVERROR(ENOMEM); - // init tables if the first frame hasn't been decoded - if (!s->current_frame.f->data[0]) { - int y_fragment_count, c_fragment_count; - s->avctx = dst; - err = allocate_tables(dst); - if (err) - return err; - y_fragment_count = s->fragment_width[0] * s->fragment_height[0]; - c_fragment_count = s->fragment_width[1] * s->fragment_height[1]; - memcpy(s->motion_val[0], s1->motion_val[0], - y_fragment_count * sizeof(*s->motion_val[0])); - memcpy(s->motion_val[1], s1->motion_val[1], - c_fragment_count * sizeof(*s->motion_val[1])); - } - // copy previous frame data if ((err = ref_frames(s, s1)) < 0) return err; @@ -2636,9 +2616,11 @@ static int vp3_update_thread_context(AVCodecContext *dst, const AVCodecContext * memcpy(&s->bounding_values_array, &s1->bounding_values_array, sizeof(s->bounding_values_array)); - if (qps_changed) - copy_fields(s, s1, qps, superblock_count); -#undef copy_fields + if (qps_changed) { + memcpy(s->qps, s1->qps, sizeof(s->qps)); + memcpy(s->last_qps, s1->last_qps, sizeof(s->last_qps)); + s->nqps = s1->nqps; + } } return update_frames(dst); @@ -2737,7 +2719,7 @@ static int vp3_decode_frame(AVCodecContext *avctx, 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 (ff_thread_get_buffer(avctx, &s->current_frame, AV_GET_BUFFER_FLAG_REF) < 0) + if ((ret = ff_thread_get_buffer(avctx, &s->current_frame, AV_GET_BUFFER_FLAG_REF)) < 0) goto error; if (!s->edge_emu_buffer) @@ -2789,8 +2771,8 @@ static int vp3_decode_frame(AVCodecContext *avctx, "vp3: first frame not a keyframe\n"); s->golden_frame.f->pict_type = AV_PICTURE_TYPE_I; - if (ff_thread_get_buffer(avctx, &s->golden_frame, - AV_GET_BUFFER_FLAG_REF) < 0) + if ((ret = ff_thread_get_buffer(avctx, &s->golden_frame, + AV_GET_BUFFER_FLAG_REF)) < 0) goto error; ff_thread_release_buffer(avctx, &s->last_frame); if ((ret = ff_thread_ref_frame(&s->last_frame, @@ -2804,39 +2786,39 @@ static int vp3_decode_frame(AVCodecContext *avctx, ff_thread_finish_setup(avctx); if (s->version < 2) { - if (unpack_superblocks(s, &gb)) { - av_log(s->avctx, AV_LOG_ERROR, "error in unpack_superblocks\n"); - goto error; - } + if ((ret = unpack_superblocks(s, &gb)) < 0) { + av_log(s->avctx, AV_LOG_ERROR, "error in unpack_superblocks\n"); + goto error; + } #if CONFIG_VP4_DECODER } else { - if (vp4_unpack_macroblocks(s, &gb)) { + if ((ret = vp4_unpack_macroblocks(s, &gb)) < 0) { av_log(s->avctx, AV_LOG_ERROR, "error in vp4_unpack_macroblocks\n"); goto error; } #endif } - if (unpack_modes(s, &gb)) { + if ((ret = unpack_modes(s, &gb)) < 0) { av_log(s->avctx, AV_LOG_ERROR, "error in unpack_modes\n"); goto error; } - if (unpack_vectors(s, &gb)) { + if (ret = unpack_vectors(s, &gb)) { av_log(s->avctx, AV_LOG_ERROR, "error in unpack_vectors\n"); goto error; } - if (unpack_block_qpis(s, &gb)) { + if ((ret = unpack_block_qpis(s, &gb)) < 0) { av_log(s->avctx, AV_LOG_ERROR, "error in unpack_block_qpis\n"); goto error; } if (s->version < 2) { - if (unpack_dct_coeffs(s, &gb)) { - av_log(s->avctx, AV_LOG_ERROR, "error in unpack_dct_coeffs\n"); - goto error; - } + if ((ret = unpack_dct_coeffs(s, &gb)) < 0) { + av_log(s->avctx, AV_LOG_ERROR, "error in unpack_dct_coeffs\n"); + goto error; + } #if CONFIG_VP4_DECODER } else { - if (vp4_unpack_dct_coeffs(s, &gb)) { + if ((ret = vp4_unpack_dct_coeffs(s, &gb)) < 0) { av_log(s->avctx, AV_LOG_ERROR, "error in vp4_unpack_dct_coeffs\n"); goto error; } @@ -2857,10 +2839,10 @@ static int vp3_decode_frame(AVCodecContext *avctx, // filter the last row if (s->version < 2) - for (i = 0; i < 3; i++) { - int row = (s->height >> (3 + (i && s->chroma_y_shift))) - 1; - apply_loop_filter(s, i, row, row + 1); - } + for (i = 0; i < 3; i++) { + int row = (s->height >> (3 + (i && s->chroma_y_shift))) - 1; + apply_loop_filter(s, i, row, row + 1); + } vp3_draw_horiz_band(s, s->height); /* output frame, offset as needed */ @@ -2888,7 +2870,7 @@ static int vp3_decode_frame(AVCodecContext *avctx, if (!HAVE_THREADS || !(s->avctx->active_thread_type & FF_THREAD_FRAME)) av_frame_unref(s->current_frame.f); - return -1; + return ret; } static int read_huffman_tree(AVCodecContext *avctx, GetBitContext *gb) @@ -2925,28 +2907,6 @@ static int read_huffman_tree(AVCodecContext *avctx, GetBitContext *gb) return 0; } -#if HAVE_THREADS -static int vp3_init_thread_copy(AVCodecContext *avctx) -{ - Vp3DecodeContext *s = avctx->priv_data; - - s->superblock_coding = NULL; - s->all_fragments = NULL; - s->coded_fragment_list[0] = NULL; - s-> kf_coded_fragment_list= NULL; - s->nkf_coded_fragment_list= NULL; - s->dct_tokens_base = NULL; - s->superblock_fragments = NULL; - s->macroblock_coding = NULL; - s->motion_val[0] = NULL; - s->motion_val[1] = NULL; - s->edge_emu_buffer = NULL; - s->dc_pred_row = NULL; - - return init_frames(s); -} -#endif - #if CONFIG_THEORA_DECODER static const enum AVPixelFormat theora_pix_fmts[4] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P @@ -2961,7 +2921,7 @@ static int theora_decode_header(AVCodecContext *avctx, GetBitContext *gb) AVRational fps, aspect; s->theora_header = 0; - s->theora = get_bits_long(gb, 24); + s->theora = get_bits(gb, 24); av_log(avctx, AV_LOG_DEBUG, "Theora bitstream version %X\n", s->theora); if (!s->theora) { s->theora = 1; @@ -2982,8 +2942,8 @@ static int theora_decode_header(AVCodecContext *avctx, GetBitContext *gb) s->height = get_bits(gb, 16) << 4; if (s->theora >= 0x030200) { - visible_width = get_bits_long(gb, 24); - visible_height = get_bits_long(gb, 24); + visible_width = get_bits(gb, 24); + visible_height = get_bits(gb, 24); offset_x = get_bits(gb, 8); /* offset x */ offset_y = get_bits(gb, 8); /* offset y, from bottom */ @@ -3011,8 +2971,8 @@ static int theora_decode_header(AVCodecContext *avctx, GetBitContext *gb) fps.den, fps.num, 1 << 30); } - aspect.num = get_bits_long(gb, 24); - aspect.den = get_bits_long(gb, 24); + aspect.num = get_bits(gb, 24); + aspect.den = get_bits(gb, 24); if (aspect.num && aspect.den) { av_reduce(&avctx->sample_aspect_ratio.num, &avctx->sample_aspect_ratio.den, @@ -3260,9 +3220,8 @@ AVCodec ff_theora_decoder = { .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_FRAME_THREADS, .flush = vp3_decode_flush, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(vp3_init_thread_copy), .update_thread_context = ONLY_IF_THREADS_ENABLED(vp3_update_thread_context), - .caps_internal = FF_CODEC_CAP_EXPORTS_CROPPING, + .caps_internal = FF_CODEC_CAP_EXPORTS_CROPPING | FF_CODEC_CAP_ALLOCATE_PROGRESS, }; #endif @@ -3278,8 +3237,8 @@ AVCodec ff_vp3_decoder = { .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_FRAME_THREADS, .flush = vp3_decode_flush, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(vp3_init_thread_copy), .update_thread_context = ONLY_IF_THREADS_ENABLED(vp3_update_thread_context), + .caps_internal = FF_CODEC_CAP_ALLOCATE_PROGRESS, }; #if CONFIG_VP4_DECODER @@ -3295,7 +3254,7 @@ AVCodec ff_vp4_decoder = { .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_FRAME_THREADS, .flush = vp3_decode_flush, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(vp3_init_thread_copy), .update_thread_context = ONLY_IF_THREADS_ENABLED(vp3_update_thread_context), + .caps_internal = FF_CODEC_CAP_ALLOCATE_PROGRESS, }; #endif diff --git a/libavcodec/vp3data.h b/libavcodec/vp3data.h index d520a10c768..3f24d5f7f68 100644 --- a/libavcodec/vp3data.h +++ b/libavcodec/vp3data.h @@ -26,7 +26,7 @@ /* these coefficients dequantize intraframe Y plane coefficients * (note: same as JPEG) */ -static const int8_t vp31_intra_y_dequant[64] = { +static const uint8_t vp31_intra_y_dequant[64] = { 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, @@ -39,7 +39,7 @@ static const int8_t vp31_intra_y_dequant[64] = { /* these coefficients dequantize intraframe C plane coefficients * (note: same as JPEG) */ -static const int8_t vp31_intra_c_dequant[64] = { +static const uint8_t vp31_intra_c_dequant[64] = { 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, @@ -51,7 +51,7 @@ static const int8_t vp31_intra_c_dequant[64] = { }; /* these coefficients dequantize interframe coefficients (all planes) */ -static const int8_t vp31_inter_dequant[64] = { +static const uint8_t vp31_inter_dequant[64] = { 16, 16, 16, 20, 24, 28, 32, 40, 16, 16, 20, 24, 28, 32, 40, 48, 16, 20, 24, 28, 32, 40, 48, 64, diff --git a/libavcodec/vp3dsp.c b/libavcodec/vp3dsp.c index ac4c57441c4..f485fba1f64 100644 --- a/libavcodec/vp3dsp.c +++ b/libavcodec/vp3dsp.c @@ -449,8 +449,8 @@ av_cold void ff_vp3dsp_init(VP3DSPContext *c, int flags) c->idct_put = vp3_idct_put_c; c->idct_add = vp3_idct_add_c; c->idct_dc_add = vp3_idct_dc_add_c; - c->v_loop_filter = vp3_v_loop_filter_8_c; - c->h_loop_filter = vp3_h_loop_filter_8_c; + c->v_loop_filter = c->v_loop_filter_unaligned = vp3_v_loop_filter_8_c; + c->h_loop_filter = c->h_loop_filter_unaligned = vp3_h_loop_filter_8_c; if (ARCH_ARM) ff_vp3dsp_init_arm(c, flags); diff --git a/libavcodec/vp3dsp.h b/libavcodec/vp3dsp.h index 32b2cad0ef2..3b849ec05d0 100644 --- a/libavcodec/vp3dsp.h +++ b/libavcodec/vp3dsp.h @@ -43,6 +43,8 @@ typedef struct VP3DSPContext { void (*idct_dc_add)(uint8_t *dest, ptrdiff_t stride, int16_t *block); void (*v_loop_filter)(uint8_t *src, ptrdiff_t stride, int *bounding_values); void (*h_loop_filter)(uint8_t *src, ptrdiff_t stride, int *bounding_values); + void (*v_loop_filter_unaligned)(uint8_t *src, ptrdiff_t stride, int *bounding_values); + void (*h_loop_filter_unaligned)(uint8_t *src, ptrdiff_t stride, int *bounding_values); } VP3DSPContext; void ff_vp3dsp_v_loop_filter_12(uint8_t *first_pixel, ptrdiff_t stride, int *bounding_values); diff --git a/libavcodec/vp5.c b/libavcodec/vp5.c index 0fca2829180..f3946f508c2 100644 --- a/libavcodec/vp5.c +++ b/libavcodec/vp5.c @@ -48,6 +48,8 @@ static int vp5_parse_header(VP56Context *s, const uint8_t *buf, int buf_size) ff_vp56_init_dequant(s, vp56_rac_gets(c, 6)); if (s->frames[VP56_FRAME_CURRENT]->key_frame) { + int render_x, render_y; + vp56_rac_gets(c, 8); if(vp56_rac_gets(c, 5) > 5) return AVERROR_INVALIDDATA; @@ -63,8 +65,11 @@ static int vp5_parse_header(VP56Context *s, const uint8_t *buf, int buf_size) cols << 4, rows << 4); return AVERROR_INVALIDDATA; } - vp56_rac_gets(c, 8); /* number of displayed macroblock rows */ - vp56_rac_gets(c, 8); /* number of displayed macroblock cols */ + render_y = vp56_rac_gets(c, 8); /* number of displayed macroblock rows */ + render_x = vp56_rac_gets(c, 8); /* number of displayed macroblock cols */ + if (render_x == 0 || render_x > cols || + render_y == 0 || render_y > rows) + return AVERROR_INVALIDDATA; vp56_rac_gets(c, 2); if (!s->macroblocks || /* first frame */ 16*cols != s->avctx->coded_width || diff --git a/libavcodec/vp56.h b/libavcodec/vp56.h index 84b2f6c94bb..65cf46870a1 100644 --- a/libavcodec/vp56.h +++ b/libavcodec/vp56.h @@ -89,6 +89,7 @@ typedef struct VP56RangeCoder { const uint8_t *buffer; const uint8_t *end; unsigned int code_word; + int end_reached; } VP56RangeCoder; typedef struct VP56RefDc { @@ -235,7 +236,9 @@ int ff_vp56_init_range_decoder(VP56RangeCoder *c, const uint8_t *buf, int buf_si */ static av_always_inline int vpX_rac_is_end(VP56RangeCoder *c) { - return c->end <= c->buffer && c->bits >= 0; + if (c->end <= c->buffer && c->bits >= 0) + c->end_reached ++; + return c->end_reached > 10; } static av_always_inline unsigned int vp56_rac_renorm(VP56RangeCoder *c) diff --git a/libavcodec/vp56rac.c b/libavcodec/vp56rac.c index e70302bf856..64fb6a99b4f 100644 --- a/libavcodec/vp56rac.c +++ b/libavcodec/vp56rac.c @@ -43,6 +43,7 @@ int ff_vp56_init_range_decoder(VP56RangeCoder *c, const uint8_t *buf, int buf_si c->bits = -16; c->buffer = buf; c->end = buf + buf_size; + c->end_reached = 0; if (buf_size < 1) return AVERROR_INVALIDDATA; c->code_word = bytestream_get_be24(&c->buffer); diff --git a/libavcodec/vp8.c b/libavcodec/vp8.c index 3ddc349a4d1..bab4223aca1 100644 --- a/libavcodec/vp8.c +++ b/libavcodec/vp8.c @@ -27,7 +27,7 @@ #include "libavutil/imgutils.h" #include "avcodec.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "internal.h" #include "mathops.h" #include "rectangle.h" @@ -501,15 +501,10 @@ static void fade(uint8_t *dst, ptrdiff_t dst_linesize, } } -static int vp7_fade_frame(VP8Context *s, VP56RangeCoder *c) +static int vp7_fade_frame(VP8Context *s, int alpha, int beta) { - int alpha = (int8_t) vp8_rac_get_uint(c, 8); - int beta = (int8_t) vp8_rac_get_uint(c, 8); int ret; - if (c->end <= c->buffer && c->bits >= 0) - return AVERROR_INVALIDDATA; - if (!s->keyframe && (alpha || beta)) { int width = s->mb_width * 16; int height = s->mb_height * 16; @@ -549,6 +544,8 @@ static int vp7_decode_frame_header(VP8Context *s, const uint8_t *buf, int buf_si int part1_size, hscale, vscale, i, j, ret; int width = s->avctx->width; int height = s->avctx->height; + int alpha = 0; + int beta = 0; if (buf_size < 4) { return AVERROR_INVALIDDATA; @@ -665,8 +662,8 @@ static int vp7_decode_frame_header(VP8Context *s, const uint8_t *buf, int buf_si return AVERROR_INVALIDDATA; /* E. Fading information for previous frame */ if (s->fade_present && vp8_rac_get(c)) { - if ((ret = vp7_fade_frame(s ,c)) < 0) - return ret; + alpha = (int8_t) vp8_rac_get_uint(c, 8); + beta = (int8_t) vp8_rac_get_uint(c, 8); } /* F. Loop filter type */ @@ -696,6 +693,12 @@ static int vp7_decode_frame_header(VP8Context *s, const uint8_t *buf, int buf_si vp78_update_pred16x16_pred8x8_mvc_probabilities(s, VP7_MVC_SIZE); } + if (vpX_rac_is_end(c)) + return AVERROR_INVALIDDATA; + + if ((ret = vp7_fade_frame(s, alpha, beta)) < 0) + return ret; + return 0; } @@ -2712,7 +2715,8 @@ int vp78_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, s->next_framep[VP56_FRAME_CURRENT] = curframe; - ff_thread_finish_setup(avctx); + if (avctx->codec->update_thread_context) + ff_thread_finish_setup(avctx); if (avctx->hwaccel) { ret = avctx->hwaccel->start_frame(avctx, avpkt->data, avpkt->size); @@ -2849,7 +2853,6 @@ int vp78_decode_init(AVCodecContext *avctx, int is_vp7) s->vp7 = avctx->codec->id == AV_CODEC_ID_VP7; s->pix_fmt = AV_PIX_FMT_NONE; avctx->pix_fmt = AV_PIX_FMT_YUV420P; - avctx->internal->allocate_progress = 1; ff_videodsp_init(&s->vdsp, 8); @@ -2891,21 +2894,6 @@ av_cold int ff_vp8_decode_init(AVCodecContext *avctx) #if CONFIG_VP8_DECODER #if HAVE_THREADS -static av_cold int vp8_decode_init_thread_copy(AVCodecContext *avctx) -{ - VP8Context *s = avctx->priv_data; - int ret; - - s->avctx = avctx; - - if ((ret = vp8_init_frames(s)) < 0) { - ff_vp8_decode_free(avctx); - return ret; - } - - return 0; -} - #define REBASE(pic) ((pic) ? (pic) - &s_src->frames[0] + &s->frames[0] : NULL) static int vp8_decode_update_thread_context(AVCodecContext *dst, @@ -2973,7 +2961,6 @@ AVCodec ff_vp8_decoder = { .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS, .flush = vp8_decode_flush, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(vp8_decode_init_thread_copy), .update_thread_context = ONLY_IF_THREADS_ENABLED(vp8_decode_update_thread_context), .hw_configs = (const AVCodecHWConfigInternal*[]) { #if CONFIG_VP8_VAAPI_HWACCEL @@ -2984,5 +2971,6 @@ AVCodec ff_vp8_decoder = { #endif NULL }, + .caps_internal = FF_CODEC_CAP_ALLOCATE_PROGRESS, }; #endif /* CONFIG_VP7_DECODER */ diff --git a/libavcodec/vp9.c b/libavcodec/vp9.c index f16462b1e9d..fd0bab14a23 100644 --- a/libavcodec/vp9.c +++ b/libavcodec/vp9.c @@ -23,7 +23,7 @@ #include "avcodec.h" #include "get_bits.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "internal.h" #include "profiles.h" #include "thread.h" @@ -34,6 +34,7 @@ #include "vp9dec.h" #include "libavutil/avassert.h" #include "libavutil/pixdesc.h" +#include "libavutil/video_enc_params.h" #define VP9_SYNCCODE 0x498342 @@ -93,6 +94,13 @@ static void vp9_free_entries(AVCodecContext *avctx) {} static int vp9_alloc_entries(AVCodecContext *avctx, int n) { return 0; } #endif +static void vp9_tile_data_free(VP9TileData *td) +{ + av_freep(&td->b_base); + av_freep(&td->block_base); + av_freep(&td->block_structure); +} + static void vp9_frame_unref(AVCodecContext *avctx, VP9Frame *f) { ff_thread_release_buffer(avctx, &f->tf); @@ -112,10 +120,20 @@ static int vp9_frame_alloc(AVCodecContext *avctx, VP9Frame *f) return ret; sz = 64 * s->sb_cols * s->sb_rows; - f->extradata = av_buffer_allocz(sz * (1 + sizeof(VP9mvrefPair))); + 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); + if (!s->frame_extradata_pool) { + s->frame_extradata_pool_size = 0; + goto fail; + } + s->frame_extradata_pool_size = sz; + } + f->extradata = av_buffer_pool_get(s->frame_extradata_pool); if (!f->extradata) { goto fail; } + memset(f->extradata->data, 0, f->extradata->size); f->segmentation_map = f->extradata->data; f->mv = (VP9mvrefPair *) (f->extradata->data + sz); @@ -173,7 +191,8 @@ static int update_size(AVCodecContext *avctx, int w, int h) #define HWACCEL_MAX (CONFIG_VP9_DXVA2_HWACCEL + \ CONFIG_VP9_D3D11VA_HWACCEL * 2 + \ CONFIG_VP9_NVDEC_HWACCEL + \ - CONFIG_VP9_VAAPI_HWACCEL) + CONFIG_VP9_VAAPI_HWACCEL + \ + CONFIG_VP9_VDPAU_HWACCEL) enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmtp = pix_fmts; VP9Context *s = avctx->priv_data; uint8_t *p; @@ -188,6 +207,9 @@ static int update_size(AVCodecContext *avctx, int w, int h) switch (s->pix_fmt) { case AV_PIX_FMT_YUV420P: +#if CONFIG_VP9_VDPAU_HWACCEL + *fmtp++ = AV_PIX_FMT_VDPAU; +#endif case AV_PIX_FMT_YUV420P10: #if CONFIG_VP9_DXVA2_HWACCEL *fmtp++ = AV_PIX_FMT_DXVA2_VLD; @@ -267,10 +289,8 @@ static int update_size(AVCodecContext *avctx, int w, int h) #undef assign if (s->td) { - for (i = 0; i < s->active_tile_cols; i++) { - av_freep(&s->td[i].b_base); - av_freep(&s->td[i].block_base); - } + for (i = 0; i < s->active_tile_cols; i++) + vp9_tile_data_free(&s->td[i]); } if (s->s.h.bpp != s->last_bpp) { @@ -292,8 +312,7 @@ static int update_block_buffers(AVCodecContext *avctx) if (td->b_base && td->block_base && s->block_alloc_using_2pass == s->s.frames[CUR_FRAME].uses_2pass) return 0; - av_free(td->b_base); - av_free(td->block_base); + vp9_tile_data_free(td); chroma_blocks = 64 * 64 >> (s->ss_h + s->ss_v); chroma_eobs = 16 * 16 >> (s->ss_h + s->ss_v); if (s->s.frames[CUR_FRAME].uses_2pass) { @@ -309,13 +328,16 @@ static int update_block_buffers(AVCodecContext *avctx) td->eob_base = (uint8_t *) (td->uvblock_base[1] + sbs * chroma_blocks * bytesperpixel); td->uveob_base[0] = td->eob_base + 16 * 16 * sbs; td->uveob_base[1] = td->uveob_base[0] + chroma_eobs * sbs; - } else { - for (i = 1; i < s->active_tile_cols; i++) { - if (s->td[i].b_base && s->td[i].block_base) { - av_free(s->td[i].b_base); - av_free(s->td[i].block_base); - } + + if (avctx->export_side_data & AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS) { + td->block_structure = av_malloc_array(s->cols * s->rows, sizeof(*td->block_structure)); + if (!td->block_structure) + return AVERROR(ENOMEM); } + } else { + for (i = 1; i < s->active_tile_cols; i++) + vp9_tile_data_free(&s->td[i]); + for (i = 0; i < s->active_tile_cols; i++) { s->td[i].b_base = av_malloc(sizeof(VP9Block)); s->td[i].block_base = av_mallocz((64 * 64 + 2 * chroma_blocks) * bytesperpixel * sizeof(int16_t) + @@ -327,6 +349,12 @@ static int update_block_buffers(AVCodecContext *avctx) s->td[i].eob_base = (uint8_t *) (s->td[i].uvblock_base[1] + chroma_blocks * bytesperpixel); s->td[i].uveob_base[0] = s->td[i].eob_base + 16 * 16; s->td[i].uveob_base[1] = s->td[i].uveob_base[0] + chroma_eobs; + + if (avctx->export_side_data & AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS) { + s->td[i].block_structure = av_malloc_array(s->cols * s->rows, sizeof(*td->block_structure)); + if (!s->td[i].block_structure) + return AVERROR(ENOMEM); + } } } s->block_alloc_using_2pass = s->s.frames[CUR_FRAME].uses_2pass; @@ -510,7 +538,7 @@ static int decode_frame_header(AVCodecContext *avctx, s->s.h.use_last_frame_mvs = !s->s.h.errorres && !last_invisible; if (s->s.h.keyframe) { - if (get_bits_long(&s->gb, 24) != VP9_SYNCCODE) { // synccode + if (get_bits(&s->gb, 24) != VP9_SYNCCODE) { // synccode av_log(avctx, AV_LOG_ERROR, "Invalid sync code\n"); return AVERROR_INVALIDDATA; } @@ -526,7 +554,7 @@ static int decode_frame_header(AVCodecContext *avctx, s->s.h.intraonly = s->s.h.invisible ? get_bits1(&s->gb) : 0; s->s.h.resetctx = s->s.h.errorres ? 0 : get_bits(&s->gb, 2); if (s->s.h.intraonly) { - if (get_bits_long(&s->gb, 24) != VP9_SYNCCODE) { // synccode + if (get_bits(&s->gb, 24) != VP9_SYNCCODE) { // synccode av_log(avctx, AV_LOG_ERROR, "Invalid sync code\n"); return AVERROR_INVALIDDATA; } @@ -759,10 +787,8 @@ static int decode_frame_header(AVCodecContext *avctx, VP56RangeCoder *rc; if (s->td) { - for (i = 0; i < s->active_tile_cols; i++) { - av_free(s->td[i].b_base); - av_free(s->td[i].block_base); - } + for (i = 0; i < s->active_tile_cols; i++) + vp9_tile_data_free(&s->td[i]); av_free(s->td); } @@ -790,6 +816,7 @@ static int decode_frame_header(AVCodecContext *avctx, /* check reference frames */ if (!s->s.h.keyframe && !s->s.h.intraonly) { + int valid_ref_frame = 0; for (i = 0; i < 3; i++) { AVFrame *ref = s->s.refs[s->s.h.refidx[i]].f; int refw = ref->width, refh = ref->height; @@ -803,17 +830,25 @@ static int decode_frame_header(AVCodecContext *avctx, } else if (refw == w && refh == h) { s->mvscale[i][0] = s->mvscale[i][1] = 0; } else { + /* Check to make sure at least one of frames that */ + /* this frame references has valid dimensions */ if (w * 2 < refw || h * 2 < refh || w > 16 * refw || h > 16 * refh) { - av_log(avctx, AV_LOG_ERROR, + av_log(avctx, AV_LOG_WARNING, "Invalid ref frame dimensions %dx%d for frame size %dx%d\n", refw, refh, w, h); - return AVERROR_INVALIDDATA; + s->mvscale[i][0] = s->mvscale[i][1] = REF_INVALID_SCALE; + continue; } s->mvscale[i][0] = (refw << 14) / w; s->mvscale[i][1] = (refh << 14) / h; s->mvstep[i][0] = 16 * s->mvscale[i][0] >> 14; s->mvstep[i][1] = 16 * s->mvscale[i][1] >> 14; } + valid_ref_frame++; + } + if (!valid_ref_frame) { + av_log(avctx, AV_LOG_ERROR, "No valid reference frame is found, bitstream not supported\n"); + return AVERROR_INVALIDDATA; } } @@ -859,6 +894,7 @@ static int decode_frame_header(AVCodecContext *avctx, } else { memset(&s->td[i].counts, 0, sizeof(s->td[0].counts)); } + s->td[i].nb_block_structure = 0; } /* FIXME is it faster to not copy here, but do it down in the fw updates @@ -1190,10 +1226,8 @@ static void free_buffers(VP9Context *s) int i; av_freep(&s->intra_pred_data[0]); - for (i = 0; i < s->active_tile_cols; i++) { - av_freep(&s->td[i].b_base); - av_freep(&s->td[i].block_base); - } + for (i = 0; i < s->active_tile_cols; i++) + vp9_tile_data_free(&s->td[i]); } static av_cold int vp9_decode_free(AVCodecContext *avctx) @@ -1202,16 +1236,14 @@ static av_cold int vp9_decode_free(AVCodecContext *avctx) int i; for (i = 0; i < 3; i++) { - if (s->s.frames[i].tf.f->buf[0]) - vp9_frame_unref(avctx, &s->s.frames[i]); + vp9_frame_unref(avctx, &s->s.frames[i]); av_frame_free(&s->s.frames[i].tf.f); } + av_buffer_pool_uninit(&s->frame_extradata_pool); for (i = 0; i < 8; i++) { - if (s->s.refs[i].f->buf[0]) - ff_thread_release_buffer(avctx, &s->s.refs[i]); + ff_thread_release_buffer(avctx, &s->s.refs[i]); av_frame_free(&s->s.refs[i].f); - if (s->next_refs[i].f->buf[0]) - ff_thread_release_buffer(avctx, &s->next_refs[i]); + ff_thread_release_buffer(avctx, &s->next_refs[i]); av_frame_free(&s->next_refs[i].f); } @@ -1464,6 +1496,58 @@ int loopfilter_proc(AVCodecContext *avctx) } #endif +static int vp9_export_enc_params(VP9Context *s, VP9Frame *frame) +{ + AVVideoEncParams *par; + unsigned int tile, nb_blocks = 0; + + if (s->s.h.segmentation.enabled) { + for (tile = 0; tile < s->active_tile_cols; tile++) + nb_blocks += s->td[tile].nb_block_structure; + } + + par = av_video_enc_params_create_side_data(frame->tf.f, + AV_VIDEO_ENC_PARAMS_VP9, nb_blocks); + if (!par) + return AVERROR(ENOMEM); + + par->qp = s->s.h.yac_qi; + par->delta_qp[0][0] = s->s.h.ydc_qdelta; + par->delta_qp[1][0] = s->s.h.uvdc_qdelta; + par->delta_qp[2][0] = s->s.h.uvdc_qdelta; + par->delta_qp[1][1] = s->s.h.uvac_qdelta; + par->delta_qp[2][1] = s->s.h.uvac_qdelta; + + if (nb_blocks) { + unsigned int block = 0; + unsigned int tile, block_tile; + + for (tile = 0; tile < s->active_tile_cols; tile++) { + VP9TileData *td = &s->td[tile]; + + for (block_tile = 0; block_tile < td->nb_block_structure; block_tile++) { + AVVideoBlockParams *b = av_video_enc_params_block(par, block++); + unsigned int row = td->block_structure[block_tile].row; + unsigned int col = td->block_structure[block_tile].col; + uint8_t seg_id = frame->segmentation_map[row * 8 * s->sb_cols + col]; + + b->src_x = col * 8; + b->src_y = row * 8; + b->w = 1 << (3 + td->block_structure[block_tile].block_size_idx_x); + b->h = 1 << (3 + td->block_structure[block_tile].block_size_idx_y); + + if (s->s.h.segmentation.feat[seg_id].q_enabled) { + b->delta_qp = s->s.h.segmentation.feat[seg_id].q_val; + if (s->s.h.segmentation.absolute_vals) + b->delta_qp -= par->qp; + } + } + } + } + + return 0; +} + static int vp9_decode_frame(AVCodecContext *avctx, void *frame, int *got_frame, AVPacket *pkt) { @@ -1610,6 +1694,7 @@ FF_ENABLE_DEPRECATION_WARNINGS s->td[i].eob = s->td[i].eob_base; s->td[i].uveob[0] = s->td[i].uveob_base[0]; s->td[i].uveob[1] = s->td[i].uveob_base[1]; + s->td[i].error_info = 0; } #if HAVE_THREADS @@ -1666,6 +1751,17 @@ FF_ENABLE_DEPRECATION_WARNINGS } while (s->pass++ == 1); ff_thread_report_progress(&s->s.frames[CUR_FRAME].tf, INT_MAX, 0); + if (s->td->error_info < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to decode tile data\n"); + s->td->error_info = 0; + return AVERROR_INVALIDDATA; + } + if (avctx->export_side_data & AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS) { + ret = vp9_export_enc_params(s, &s->s.frames[CUR_FRAME]); + if (ret < 0) + return ret; + } + finish: // ref frame setup for (i = 0; i < 8; i++) { @@ -1726,7 +1822,6 @@ static av_cold int vp9_decode_init(AVCodecContext *avctx) { VP9Context *s = avctx->priv_data; - avctx->internal->allocate_progress = 1; s->last_bpp = 0; s->s.h.filter.sharpness = -1; @@ -1734,11 +1829,6 @@ static av_cold int vp9_decode_init(AVCodecContext *avctx) } #if HAVE_THREADS -static av_cold int vp9_decode_init_thread_copy(AVCodecContext *avctx) -{ - return init_frames(avctx); -} - static int vp9_decode_update_thread_context(AVCodecContext *dst, const AVCodecContext *src) { int i, ret; @@ -1795,9 +1885,9 @@ AVCodec ff_vp9_decoder = { .close = vp9_decode_free, .decode = vp9_decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS, - .caps_internal = FF_CODEC_CAP_SLICE_THREAD_HAS_MF, + .caps_internal = FF_CODEC_CAP_SLICE_THREAD_HAS_MF | + FF_CODEC_CAP_ALLOCATE_PROGRESS, .flush = vp9_decode_flush, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(vp9_decode_init_thread_copy), .update_thread_context = ONLY_IF_THREADS_ENABLED(vp9_decode_update_thread_context), .profiles = NULL_IF_CONFIG_SMALL(ff_vp9_profiles), .bsfs = "vp9_superframe_split", @@ -1816,6 +1906,9 @@ AVCodec ff_vp9_decoder = { #endif #if CONFIG_VP9_VAAPI_HWACCEL HWACCEL_VAAPI(vp9), +#endif +#if CONFIG_VP9_VDPAU_HWACCEL + HWACCEL_VDPAU(vp9), #endif NULL }, diff --git a/libavcodec/vp9_metadata_bsf.c b/libavcodec/vp9_metadata_bsf.c index 1bde1b96aaf..2ca494e695d 100644 --- a/libavcodec/vp9_metadata_bsf.c +++ b/libavcodec/vp9_metadata_bsf.c @@ -21,6 +21,7 @@ #include "libavutil/opt.h" #include "bsf.h" +#include "bsf_internal.h" #include "cbs.h" #include "cbs_vp9.h" @@ -33,7 +34,7 @@ typedef struct VP9MetadataContext { int color_space; int color_range; - int color_range_rgb_warned; + int color_warnings; } VP9MetadataContext; @@ -56,20 +57,36 @@ static int vp9_metadata_filter(AVBSFContext *bsf, AVPacket *pkt) for (i = 0; i < frag->nb_units; i++) { VP9RawFrame *frame = frag->units[i].content; VP9RawFrameHeader *header = &frame->header; + int profile = (header->profile_high_bit << 1) + header->profile_low_bit; + + if (header->frame_type == VP9_KEY_FRAME || + header->intra_only && profile > 0) { + if (ctx->color_space >= 0) { + if (!(profile & 1) && ctx->color_space == VP9_CS_RGB) { + if (!(ctx->color_warnings & 2)) { + av_log(bsf, AV_LOG_WARNING, "Warning: RGB " + "incompatible with profiles 0 and 2.\n"); + ctx->color_warnings |= 2; + } + } else + header->color_space = ctx->color_space; + } - if (ctx->color_space >= 0) { - header->color_space = ctx->color_space; - } - if (ctx->color_range >= 0) { - if (ctx->color_range == 0 && - header->color_space == VP9_CS_RGB && - !ctx->color_range_rgb_warned) { - av_log(bsf, AV_LOG_WARNING, "Warning: color_range cannot " - "be set to limited in RGB streams.\n"); - ctx->color_range_rgb_warned = 1; - } else { + if (ctx->color_range >= 0) header->color_range = ctx->color_range; + if (header->color_space == VP9_CS_RGB) { + if (!(ctx->color_warnings & 1) && !header->color_range) { + av_log(bsf, AV_LOG_WARNING, "Warning: Color space RGB " + "implicitly sets color range to PC range.\n"); + ctx->color_warnings |= 1; + } + header->color_range = 1; } + } else if (!(ctx->color_warnings & 4) && header->intra_only && !profile && + ctx->color_space >= 0 && ctx->color_space != VP9_CS_BT_601) { + av_log(bsf, AV_LOG_WARNING, "Warning: Intra-only frames in " + "profile 0 are automatically BT.601.\n"); + ctx->color_warnings |= 4; } } diff --git a/libavcodec/vp9_raw_reorder_bsf.c b/libavcodec/vp9_raw_reorder_bsf.c index f19b4c71980..6562399159f 100644 --- a/libavcodec/vp9_raw_reorder_bsf.c +++ b/libavcodec/vp9_raw_reorder_bsf.c @@ -20,9 +20,9 @@ #include "libavutil/intmath.h" #include "libavutil/log.h" #include "libavutil/mem.h" -#include "libavutil/opt.h" #include "bsf.h" +#include "bsf_internal.h" #include "get_bits.h" #include "put_bits.h" @@ -292,7 +292,7 @@ static int vp9_raw_reorder_filter(AVBSFContext *bsf, AVPacket *out) return err; } - if (in->data[in->size - 1] & 0xe0 == 0xc0) { + if ((in->data[in->size - 1] & 0xe0) == 0xc0) { av_log(bsf, AV_LOG_ERROR, "Input in superframes is not " "supported.\n"); av_packet_free(&in); diff --git a/libavcodec/vp9_superframe_bsf.c b/libavcodec/vp9_superframe_bsf.c index 23933d41365..57681e29e42 100644 --- a/libavcodec/vp9_superframe_bsf.c +++ b/libavcodec/vp9_superframe_bsf.c @@ -20,8 +20,9 @@ */ #include "libavutil/avassert.h" -#include "avcodec.h" + #include "bsf.h" +#include "bsf_internal.h" #include "get_bits.h" #define MAX_CACHE 8 @@ -97,26 +98,25 @@ static int merge_superframe(AVPacket * const *in, int n_in, AVPacket *out) return 0; } -static int vp9_superframe_filter(AVBSFContext *ctx, AVPacket *out) +static int vp9_superframe_filter(AVBSFContext *ctx, AVPacket *pkt) { GetBitContext gb; VP9BSFContext *s = ctx->priv_data; - AVPacket *in; int res, invisible, profile, marker, uses_superframe_syntax = 0, n; - res = ff_bsf_get_packet(ctx, &in); + res = ff_bsf_get_packet_ref(ctx, pkt); if (res < 0) return res; - marker = in->data[in->size - 1]; + marker = pkt->data[pkt->size - 1]; if ((marker & 0xe0) == 0xc0) { int nbytes = 1 + ((marker >> 3) & 0x3); int n_frames = 1 + (marker & 0x7), idx_sz = 2 + n_frames * nbytes; - uses_superframe_syntax = in->size >= idx_sz && in->data[in->size - idx_sz] == marker; + uses_superframe_syntax = pkt->size >= idx_sz && pkt->data[pkt->size - idx_sz] == marker; } - if ((res = init_get_bits8(&gb, in->data, in->size)) < 0) + if ((res = init_get_bits8(&gb, pkt->data, pkt->size)) < 0) goto done; get_bits(&gb, 2); // frame marker @@ -138,8 +138,7 @@ static int vp9_superframe_filter(AVBSFContext *ctx, AVPacket *out) goto done; } else if ((!invisible || uses_superframe_syntax) && !s->n_cache) { // passthrough - av_packet_move_ref(out, in); - goto done; + return 0; } else if (s->n_cache + 1 >= MAX_CACHE) { av_log(ctx, AV_LOG_ERROR, "Too many invisible frames\n"); @@ -147,19 +146,18 @@ static int vp9_superframe_filter(AVBSFContext *ctx, AVPacket *out) goto done; } - av_packet_move_ref(s->cache[s->n_cache++], in); + av_packet_move_ref(s->cache[s->n_cache++], pkt); if (invisible) { - res = AVERROR(EAGAIN); - goto done; + return AVERROR(EAGAIN); } av_assert0(s->n_cache > 0); // build superframe - if ((res = merge_superframe(s->cache, s->n_cache, out)) < 0) + if ((res = merge_superframe(s->cache, s->n_cache, pkt)) < 0) goto done; - res = av_packet_copy_props(out, s->cache[s->n_cache - 1]); + res = av_packet_copy_props(pkt, s->cache[s->n_cache - 1]); if (res < 0) goto done; @@ -169,8 +167,7 @@ static int vp9_superframe_filter(AVBSFContext *ctx, AVPacket *out) done: if (res < 0) - av_packet_unref(out); - av_packet_free(&in); + av_packet_unref(pkt); return res; } diff --git a/libavcodec/vp9_superframe_split_bsf.c b/libavcodec/vp9_superframe_split_bsf.c index 13e85c3ca41..ed0444561ac 100644 --- a/libavcodec/vp9_superframe_split_bsf.c +++ b/libavcodec/vp9_superframe_split_bsf.c @@ -24,8 +24,8 @@ #include -#include "avcodec.h" #include "bsf.h" +#include "bsf_internal.h" #include "bytestream.h" #include "get_bits.h" diff --git a/libavcodec/vp9block.c b/libavcodec/vp9block.c index 1c3f7a72253..ec16e26c698 100644 --- a/libavcodec/vp9block.c +++ b/libavcodec/vp9block.c @@ -1290,6 +1290,14 @@ void ff_vp9_decode_block(VP9TileData *td, int row, int col, b->uvtx = b->tx - ((s->ss_h && w4 * 2 == (1 << b->tx)) || (s->ss_v && h4 * 2 == (1 << b->tx))); + if (td->block_structure) { + td->block_structure[td->nb_block_structure].row = row; + td->block_structure[td->nb_block_structure].col = col; + td->block_structure[td->nb_block_structure].block_size_idx_x = av_log2(w4); + td->block_structure[td->nb_block_structure].block_size_idx_y = av_log2(h4); + td->nb_block_structure++; + } + if (!b->skip) { int has_coeffs; diff --git a/libavcodec/vp9dec.h b/libavcodec/vp9dec.h index 66573edc791..cc2440b8542 100644 --- a/libavcodec/vp9dec.h +++ b/libavcodec/vp9dec.h @@ -36,6 +36,8 @@ #include "vp9dsp.h" #include "vp9shared.h" +#define REF_INVALID_SCALE 0xFFFF + enum MVJoint { MV_JOINT_ZERO, MV_JOINT_H, @@ -152,6 +154,10 @@ typedef struct VP9Context { int block_alloc_using_2pass; uint16_t mvscale[3][2]; uint8_t mvstep[3][2]; + + // frame specific buffer pools + AVBufferPool *frame_extradata_pool; + int frame_extradata_pool_size; } VP9Context; struct VP9TileData { @@ -217,6 +223,16 @@ struct VP9TileData { struct { int x, y; } min_mv, max_mv; int16_t *block_base, *block, *uvblock_base[2], *uvblock[2]; uint8_t *eob_base, *uveob_base[2], *eob, *uveob[2]; + + // error message + int error_info; + struct { + unsigned int row:13; + unsigned int col:13; + unsigned int block_size_idx_x:2; + unsigned int block_size_idx_y:2; + } *block_structure; + unsigned int nb_block_structure; }; void ff_vp9_fill_mv(VP9TileData *td, VP56mv *mv, int mode, int sb); diff --git a/libavcodec/vp9dsp_template.c b/libavcodec/vp9dsp_template.c index bb54561a60b..c6944f5ce38 100644 --- a/libavcodec/vp9dsp_template.c +++ b/libavcodec/vp9dsp_template.c @@ -1378,48 +1378,48 @@ static av_always_inline void iadst16_1d(const dctcoef *in, ptrdiff_t stride, dctint t0a, t1a, t2a, t3a, t4a, t5a, t6a, t7a; dctint t8a, t9a, t10a, t11a, t12a, t13a, t14a, t15a; - t0 = IN(15) * 16364 + IN(0) * 804; - t1 = IN(15) * 804 - IN(0) * 16364; - t2 = IN(13) * 15893 + IN(2) * 3981; - t3 = IN(13) * 3981 - IN(2) * 15893; - t4 = IN(11) * 14811 + IN(4) * 7005; - t5 = IN(11) * 7005 - IN(4) * 14811; - t6 = IN(9) * 13160 + IN(6) * 9760; - t7 = IN(9) * 9760 - IN(6) * 13160; - t8 = IN(7) * 11003 + IN(8) * 12140; - t9 = IN(7) * 12140 - IN(8) * 11003; - t10 = IN(5) * 8423 + IN(10) * 14053; - t11 = IN(5) * 14053 - IN(10) * 8423; - t12 = IN(3) * 5520 + IN(12) * 15426; - t13 = IN(3) * 15426 - IN(12) * 5520; - t14 = IN(1) * 2404 + IN(14) * 16207; - t15 = IN(1) * 16207 - IN(14) * 2404; - - t0a = (t0 + t8 + (1 << 13)) >> 14; - t1a = (t1 + t9 + (1 << 13)) >> 14; - t2a = (t2 + t10 + (1 << 13)) >> 14; - t3a = (t3 + t11 + (1 << 13)) >> 14; - t4a = (t4 + t12 + (1 << 13)) >> 14; - t5a = (t5 + t13 + (1 << 13)) >> 14; - t6a = (t6 + t14 + (1 << 13)) >> 14; - t7a = (t7 + t15 + (1 << 13)) >> 14; - t8a = (t0 - t8 + (1 << 13)) >> 14; - t9a = (t1 - t9 + (1 << 13)) >> 14; - t10a = (t2 - t10 + (1 << 13)) >> 14; - t11a = (t3 - t11 + (1 << 13)) >> 14; - t12a = (t4 - t12 + (1 << 13)) >> 14; - t13a = (t5 - t13 + (1 << 13)) >> 14; - t14a = (t6 - t14 + (1 << 13)) >> 14; - t15a = (t7 - t15 + (1 << 13)) >> 14; - - t8 = t8a * 16069 + t9a * 3196; - t9 = t8a * 3196 - t9a * 16069; - t10 = t10a * 9102 + t11a * 13623; - t11 = t10a * 13623 - t11a * 9102; - t12 = t13a * 16069 - t12a * 3196; - t13 = t13a * 3196 + t12a * 16069; - t14 = t15a * 9102 - t14a * 13623; - t15 = t15a * 13623 + t14a * 9102; + t0 = IN(15) * 16364U + IN(0) * 804U; + t1 = IN(15) * 804U - IN(0) * 16364U; + t2 = IN(13) * 15893U + IN(2) * 3981U; + t3 = IN(13) * 3981U - IN(2) * 15893U; + t4 = IN(11) * 14811U + IN(4) * 7005U; + t5 = IN(11) * 7005U - IN(4) * 14811U; + t6 = IN(9) * 13160U + IN(6) * 9760U; + t7 = IN(9) * 9760U - IN(6) * 13160U; + t8 = IN(7) * 11003U + IN(8) * 12140U; + t9 = IN(7) * 12140U - IN(8) * 11003U; + t10 = IN(5) * 8423U + IN(10) * 14053U; + t11 = IN(5) * 14053U - IN(10) * 8423U; + t12 = IN(3) * 5520U + IN(12) * 15426U; + t13 = IN(3) * 15426U - IN(12) * 5520U; + t14 = IN(1) * 2404U + IN(14) * 16207U; + t15 = IN(1) * 16207U - IN(14) * 2404U; + + t0a = (dctint)((1U << 13) + t0 + t8 ) >> 14; + t1a = (dctint)((1U << 13) + t1 + t9 ) >> 14; + t2a = (dctint)((1U << 13) + t2 + t10) >> 14; + t3a = (dctint)((1U << 13) + t3 + t11) >> 14; + t4a = (dctint)((1U << 13) + t4 + t12) >> 14; + t5a = (dctint)((1U << 13) + t5 + t13) >> 14; + t6a = (dctint)((1U << 13) + t6 + t14) >> 14; + t7a = (dctint)((1U << 13) + t7 + t15) >> 14; + t8a = (dctint)((1U << 13) + t0 - t8 ) >> 14; + t9a = (dctint)((1U << 13) + t1 - t9 ) >> 14; + t10a = (dctint)((1U << 13) + t2 - t10) >> 14; + t11a = (dctint)((1U << 13) + t3 - t11) >> 14; + t12a = (dctint)((1U << 13) + t4 - t12) >> 14; + t13a = (dctint)((1U << 13) + t5 - t13) >> 14; + t14a = (dctint)((1U << 13) + t6 - t14) >> 14; + t15a = (dctint)((1U << 13) + t7 - t15) >> 14; + + t8 = t8a * 16069U + t9a * 3196U; + t9 = t8a * 3196U - t9a * 16069U; + t10 = t10a * 9102U + t11a * 13623U; + t11 = t10a * 13623U - t11a * 9102U; + t12 = t13a * 16069U - t12a * 3196U; + t13 = t13a * 3196U + t12a * 16069U; + t14 = t15a * 9102U - t14a * 13623U; + t15 = t15a * 13623U + t14a * 9102U; t0 = t0a + t4a; t1 = t1a + t5a; @@ -1429,49 +1429,49 @@ static av_always_inline void iadst16_1d(const dctcoef *in, ptrdiff_t stride, t5 = t1a - t5a; t6 = t2a - t6a; t7 = t3a - t7a; - t8a = (t8 + t12 + (1 << 13)) >> 14; - t9a = (t9 + t13 + (1 << 13)) >> 14; - t10a = (t10 + t14 + (1 << 13)) >> 14; - t11a = (t11 + t15 + (1 << 13)) >> 14; - t12a = (t8 - t12 + (1 << 13)) >> 14; - t13a = (t9 - t13 + (1 << 13)) >> 14; - t14a = (t10 - t14 + (1 << 13)) >> 14; - t15a = (t11 - t15 + (1 << 13)) >> 14; - - t4a = t4 * 15137 + t5 * 6270; - t5a = t4 * 6270 - t5 * 15137; - t6a = t7 * 15137 - t6 * 6270; - t7a = t7 * 6270 + t6 * 15137; - t12 = t12a * 15137 + t13a * 6270; - t13 = t12a * 6270 - t13a * 15137; - t14 = t15a * 15137 - t14a * 6270; - t15 = t15a * 6270 + t14a * 15137; + t8a = (dctint)((1U << 13) + t8 + t12) >> 14; + t9a = (dctint)((1U << 13) + t9 + t13) >> 14; + t10a = (dctint)((1U << 13) + t10 + t14) >> 14; + t11a = (dctint)((1U << 13) + t11 + t15) >> 14; + t12a = (dctint)((1U << 13) + t8 - t12) >> 14; + t13a = (dctint)((1U << 13) + t9 - t13) >> 14; + t14a = (dctint)((1U << 13) + t10 - t14) >> 14; + t15a = (dctint)((1U << 13) + t11 - t15) >> 14; + + t4a = t4 * 15137U + t5 * 6270U; + t5a = t4 * 6270U - t5 * 15137U; + t6a = t7 * 15137U - t6 * 6270U; + t7a = t7 * 6270U + t6 * 15137U; + t12 = t12a * 15137U + t13a * 6270U; + t13 = t12a * 6270U - t13a * 15137U; + t14 = t15a * 15137U - t14a * 6270U; + t15 = t15a * 6270U + t14a * 15137U; out[ 0] = t0 + t2; out[15] = -(t1 + t3); t2a = t0 - t2; t3a = t1 - t3; - out[ 3] = -((t4a + t6a + (1 << 13)) >> 14); - out[12] = (t5a + t7a + (1 << 13)) >> 14; - t6 = (t4a - t6a + (1 << 13)) >> 14; - t7 = (t5a - t7a + (1 << 13)) >> 14; + out[ 3] = -((dctint)((1U << 13) + t4a + t6a) >> 14); + out[12] = (dctint)((1U << 13) + t5a + t7a) >> 14; + t6 = (dctint)((1U << 13) + t4a - t6a) >> 14; + t7 = (dctint)((1U << 13) + t5a - t7a) >> 14; out[ 1] = -(t8a + t10a); out[14] = t9a + t11a; t10 = t8a - t10a; t11 = t9a - t11a; - out[ 2] = (t12 + t14 + (1 << 13)) >> 14; - out[13] = -((t13 + t15 + (1 << 13)) >> 14); - t14a = (t12 - t14 + (1 << 13)) >> 14; - t15a = (t13 - t15 + (1 << 13)) >> 14; + out[ 2] = (dctint)((1U << 13) + t12 + t14) >> 14; + out[13] = -((dctint)((1U << 13) + t13 + t15) >> 14); + t14a = (dctint)((1U << 13) + t12 - t14) >> 14; + t15a = (dctint)((1U << 13) + t13 - t15) >> 14; - out[ 7] = ((t2a + t3a) * -11585 + (1 << 13)) >> 14; - out[ 8] = ((t2a - t3a) * 11585 + (1 << 13)) >> 14; - out[ 4] = ((t7 + t6) * 11585 + (1 << 13)) >> 14; - out[11] = ((t7 - t6) * 11585 + (1 << 13)) >> 14; - out[ 6] = ((t11 + t10) * 11585 + (1 << 13)) >> 14; - out[ 9] = ((t11 - t10) * 11585 + (1 << 13)) >> 14; - out[ 5] = ((t14a + t15a) * -11585 + (1 << 13)) >> 14; - out[10] = ((t14a - t15a) * 11585 + (1 << 13)) >> 14; + out[ 7] = (dctint)(-(t2a + t3a) * 11585U + (1 << 13)) >> 14; + out[ 8] = (dctint)( (t2a - t3a) * 11585U + (1 << 13)) >> 14; + out[ 4] = (dctint)( (t7 + t6) * 11585U + (1 << 13)) >> 14; + out[11] = (dctint)( (t7 - t6) * 11585U + (1 << 13)) >> 14; + out[ 6] = (dctint)( (t11 + t10) * 11585U + (1 << 13)) >> 14; + out[ 9] = (dctint)( (t11 - t10) * 11585U + (1 << 13)) >> 14; + out[ 5] = (dctint)(-(t14a + t15a) * 11585U + (1 << 13)) >> 14; + out[10] = (dctint)( (t14a - t15a) * 11585U + (1 << 13)) >> 14; } itxfm_wrap(16, 6) @@ -1479,38 +1479,38 @@ itxfm_wrap(16, 6) static av_always_inline void idct32_1d(const dctcoef *in, ptrdiff_t stride, dctcoef *out, int pass) { - dctint t0a = ((IN(0) + IN(16)) * 11585 + (1 << 13)) >> 14; - dctint t1a = ((IN(0) - IN(16)) * 11585 + (1 << 13)) >> 14; - dctint t2a = (IN( 8) * 6270 - IN(24) * 15137 + (1 << 13)) >> 14; - dctint t3a = (IN( 8) * 15137 + IN(24) * 6270 + (1 << 13)) >> 14; - dctint t4a = (IN( 4) * 3196 - IN(28) * 16069 + (1 << 13)) >> 14; - dctint t7a = (IN( 4) * 16069 + IN(28) * 3196 + (1 << 13)) >> 14; - dctint t5a = (IN(20) * 13623 - IN(12) * 9102 + (1 << 13)) >> 14; - dctint t6a = (IN(20) * 9102 + IN(12) * 13623 + (1 << 13)) >> 14; - dctint t8a = (IN( 2) * 1606 - IN(30) * 16305 + (1 << 13)) >> 14; - dctint t15a = (IN( 2) * 16305 + IN(30) * 1606 + (1 << 13)) >> 14; - dctint t9a = (IN(18) * 12665 - IN(14) * 10394 + (1 << 13)) >> 14; - dctint t14a = (IN(18) * 10394 + IN(14) * 12665 + (1 << 13)) >> 14; - dctint t10a = (IN(10) * 7723 - IN(22) * 14449 + (1 << 13)) >> 14; - dctint t13a = (IN(10) * 14449 + IN(22) * 7723 + (1 << 13)) >> 14; - dctint t11a = (IN(26) * 15679 - IN( 6) * 4756 + (1 << 13)) >> 14; - dctint t12a = (IN(26) * 4756 + IN( 6) * 15679 + (1 << 13)) >> 14; - dctint t16a = (IN( 1) * 804 - IN(31) * 16364 + (1 << 13)) >> 14; - dctint t31a = (IN( 1) * 16364 + IN(31) * 804 + (1 << 13)) >> 14; - dctint t17a = (IN(17) * 12140 - IN(15) * 11003 + (1 << 13)) >> 14; - dctint t30a = (IN(17) * 11003 + IN(15) * 12140 + (1 << 13)) >> 14; - dctint t18a = (IN( 9) * 7005 - IN(23) * 14811 + (1 << 13)) >> 14; - dctint t29a = (IN( 9) * 14811 + IN(23) * 7005 + (1 << 13)) >> 14; - dctint t19a = (IN(25) * 15426 - IN( 7) * 5520 + (1 << 13)) >> 14; - dctint t28a = (IN(25) * 5520 + IN( 7) * 15426 + (1 << 13)) >> 14; - dctint t20a = (IN( 5) * 3981 - IN(27) * 15893 + (1 << 13)) >> 14; - dctint t27a = (IN( 5) * 15893 + IN(27) * 3981 + (1 << 13)) >> 14; - dctint t21a = (IN(21) * 14053 - IN(11) * 8423 + (1 << 13)) >> 14; - dctint t26a = (IN(21) * 8423 + IN(11) * 14053 + (1 << 13)) >> 14; - dctint t22a = (IN(13) * 9760 - IN(19) * 13160 + (1 << 13)) >> 14; - dctint t25a = (IN(13) * 13160 + IN(19) * 9760 + (1 << 13)) >> 14; - dctint t23a = (IN(29) * 16207 - IN( 3) * 2404 + (1 << 13)) >> 14; - dctint t24a = (IN(29) * 2404 + IN( 3) * 16207 + (1 << 13)) >> 14; + dctint t0a = (dctint)((IN(0) + IN(16)) * 11585U + (1 << 13)) >> 14; + dctint t1a = (dctint)((IN(0) - IN(16)) * 11585U + (1 << 13)) >> 14; + dctint t2a = (dctint)(IN( 8) * 6270U - IN(24) * 15137U + (1 << 13)) >> 14; + dctint t3a = (dctint)(IN( 8) * 15137U + IN(24) * 6270U + (1 << 13)) >> 14; + dctint t4a = (dctint)(IN( 4) * 3196U - IN(28) * 16069U + (1 << 13)) >> 14; + dctint t7a = (dctint)(IN( 4) * 16069U + IN(28) * 3196U + (1 << 13)) >> 14; + dctint t5a = (dctint)(IN(20) * 13623U - IN(12) * 9102U + (1 << 13)) >> 14; + dctint t6a = (dctint)(IN(20) * 9102U + IN(12) * 13623U + (1 << 13)) >> 14; + dctint t8a = (dctint)(IN( 2) * 1606U - IN(30) * 16305U + (1 << 13)) >> 14; + dctint t15a = (dctint)(IN( 2) * 16305U + IN(30) * 1606U + (1 << 13)) >> 14; + dctint t9a = (dctint)(IN(18) * 12665U - IN(14) * 10394U + (1 << 13)) >> 14; + dctint t14a = (dctint)(IN(18) * 10394U + IN(14) * 12665U + (1 << 13)) >> 14; + dctint t10a = (dctint)(IN(10) * 7723U - IN(22) * 14449U + (1 << 13)) >> 14; + dctint t13a = (dctint)(IN(10) * 14449U + IN(22) * 7723U + (1 << 13)) >> 14; + dctint t11a = (dctint)(IN(26) * 15679U - IN( 6) * 4756U + (1 << 13)) >> 14; + dctint t12a = (dctint)(IN(26) * 4756U + IN( 6) * 15679U + (1 << 13)) >> 14; + dctint t16a = (dctint)(IN( 1) * 804U - IN(31) * 16364U + (1 << 13)) >> 14; + dctint t31a = (dctint)(IN( 1) * 16364U + IN(31) * 804U + (1 << 13)) >> 14; + dctint t17a = (dctint)(IN(17) * 12140U - IN(15) * 11003U + (1 << 13)) >> 14; + dctint t30a = (dctint)(IN(17) * 11003U + IN(15) * 12140U + (1 << 13)) >> 14; + dctint t18a = (dctint)(IN( 9) * 7005U - IN(23) * 14811U + (1 << 13)) >> 14; + dctint t29a = (dctint)(IN( 9) * 14811U + IN(23) * 7005U + (1 << 13)) >> 14; + dctint t19a = (dctint)(IN(25) * 15426U - IN( 7) * 5520U + (1 << 13)) >> 14; + dctint t28a = (dctint)(IN(25) * 5520U + IN( 7) * 15426U + (1 << 13)) >> 14; + dctint t20a = (dctint)(IN( 5) * 3981U - IN(27) * 15893U + (1 << 13)) >> 14; + dctint t27a = (dctint)(IN( 5) * 15893U + IN(27) * 3981U + (1 << 13)) >> 14; + dctint t21a = (dctint)(IN(21) * 14053U - IN(11) * 8423U + (1 << 13)) >> 14; + dctint t26a = (dctint)(IN(21) * 8423U + IN(11) * 14053U + (1 << 13)) >> 14; + dctint t22a = (dctint)(IN(13) * 9760U - IN(19) * 13160U + (1 << 13)) >> 14; + dctint t25a = (dctint)(IN(13) * 13160U + IN(19) * 9760U + (1 << 13)) >> 14; + dctint t23a = (dctint)(IN(29) * 16207U - IN( 3) * 2404U + (1 << 13)) >> 14; + dctint t24a = (dctint)(IN(29) * 2404U + IN( 3) * 16207U + (1 << 13)) >> 14; dctint t0 = t0a + t3a; dctint t1 = t1a + t2a; @@ -1545,20 +1545,20 @@ static av_always_inline void idct32_1d(const dctcoef *in, ptrdiff_t stride, dctint t30 = t31a - t30a; dctint t31 = t31a + t30a; - t5a = ((t6 - t5) * 11585 + (1 << 13)) >> 14; - t6a = ((t6 + t5) * 11585 + (1 << 13)) >> 14; - t9a = ( t14 * 6270 - t9 * 15137 + (1 << 13)) >> 14; - t14a = ( t14 * 15137 + t9 * 6270 + (1 << 13)) >> 14; - t10a = (-(t13 * 15137 + t10 * 6270) + (1 << 13)) >> 14; - t13a = ( t13 * 6270 - t10 * 15137 + (1 << 13)) >> 14; - t17a = ( t30 * 3196 - t17 * 16069 + (1 << 13)) >> 14; - t30a = ( t30 * 16069 + t17 * 3196 + (1 << 13)) >> 14; - t18a = (-(t29 * 16069 + t18 * 3196) + (1 << 13)) >> 14; - t29a = ( t29 * 3196 - t18 * 16069 + (1 << 13)) >> 14; - t21a = ( t26 * 13623 - t21 * 9102 + (1 << 13)) >> 14; - t26a = ( t26 * 9102 + t21 * 13623 + (1 << 13)) >> 14; - t22a = (-(t25 * 9102 + t22 * 13623) + (1 << 13)) >> 14; - t25a = ( t25 * 13623 - t22 * 9102 + (1 << 13)) >> 14; + t5a = (dctint)((t6 - t5) * 11585U + (1 << 13)) >> 14; + t6a = (dctint)((t6 + t5) * 11585U + (1 << 13)) >> 14; + t9a = (dctint)( t14 * 6270U - t9 * 15137U + (1 << 13)) >> 14; + t14a = (dctint)( t14 * 15137U + t9 * 6270U + (1 << 13)) >> 14; + t10a = (dctint)(-(t13 * 15137U + t10 * 6270U) + (1 << 13)) >> 14; + t13a = (dctint)( t13 * 6270U - t10 * 15137U + (1 << 13)) >> 14; + t17a = (dctint)( t30 * 3196U - t17 * 16069U + (1 << 13)) >> 14; + t30a = (dctint)( t30 * 16069U + t17 * 3196U + (1 << 13)) >> 14; + t18a = (dctint)(-(t29 * 16069U + t18 * 3196U) + (1 << 13)) >> 14; + t29a = (dctint)( t29 * 3196U - t18 * 16069U + (1 << 13)) >> 14; + t21a = (dctint)( t26 * 13623U - t21 * 9102U + (1 << 13)) >> 14; + t26a = (dctint)( t26 * 9102U + t21 * 13623U + (1 << 13)) >> 14; + t22a = (dctint)(-(t25 * 9102U + t22 * 13623U) + (1 << 13)) >> 14; + t25a = (dctint)( t25 * 13623U - t22 * 9102U + (1 << 13)) >> 14; t0a = t0 + t7; t1a = t1 + t6a; @@ -1593,18 +1593,18 @@ static av_always_inline void idct32_1d(const dctcoef *in, ptrdiff_t stride, t30 = t30a + t29a; t31a = t31 + t28; - t10a = ((t13 - t10) * 11585 + (1 << 13)) >> 14; - t13a = ((t13 + t10) * 11585 + (1 << 13)) >> 14; - t11 = ((t12a - t11a) * 11585 + (1 << 13)) >> 14; - t12 = ((t12a + t11a) * 11585 + (1 << 13)) >> 14; - t18a = ( t29 * 6270 - t18 * 15137 + (1 << 13)) >> 14; - t29a = ( t29 * 15137 + t18 * 6270 + (1 << 13)) >> 14; - t19 = ( t28a * 6270 - t19a * 15137 + (1 << 13)) >> 14; - t28 = ( t28a * 15137 + t19a * 6270 + (1 << 13)) >> 14; - t20 = (-(t27a * 15137 + t20a * 6270) + (1 << 13)) >> 14; - t27 = ( t27a * 6270 - t20a * 15137 + (1 << 13)) >> 14; - t21a = (-(t26 * 15137 + t21 * 6270) + (1 << 13)) >> 14; - t26a = ( t26 * 6270 - t21 * 15137 + (1 << 13)) >> 14; + t10a = (dctint)((t13 - t10) * 11585U + (1 << 13)) >> 14; + t13a = (dctint)((t13 + t10) * 11585U + (1 << 13)) >> 14; + t11 = (dctint)((t12a - t11a) * 11585U + (1 << 13)) >> 14; + t12 = (dctint)((t12a + t11a) * 11585U + (1 << 13)) >> 14; + t18a = (dctint)( t29 * 6270U - t18 * 15137U + (1 << 13)) >> 14; + t29a = (dctint)( t29 * 15137U + t18 * 6270U + (1 << 13)) >> 14; + t19 = (dctint)( t28a * 6270U - t19a * 15137U + (1 << 13)) >> 14; + t28 = (dctint)( t28a * 15137U + t19a * 6270U + (1 << 13)) >> 14; + t20 = (dctint)(-(t27a * 15137U + t20a * 6270U) + (1 << 13)) >> 14; + t27 = (dctint)( t27a * 6270U - t20a * 15137U + (1 << 13)) >> 14; + t21a = (dctint)(-(t26 * 15137U + t21 * 6270U) + (1 << 13)) >> 14; + t26a = (dctint)( t26 * 6270U - t21 * 15137U + (1 << 13)) >> 14; t0 = t0a + t15a; t1 = t1a + t14; @@ -1639,14 +1639,14 @@ static av_always_inline void idct32_1d(const dctcoef *in, ptrdiff_t stride, t30a = t30 + t25; t31 = t31a + t24a; - t20 = ((t27a - t20a) * 11585 + (1 << 13)) >> 14; - t27 = ((t27a + t20a) * 11585 + (1 << 13)) >> 14; - t21a = ((t26 - t21 ) * 11585 + (1 << 13)) >> 14; - t26a = ((t26 + t21 ) * 11585 + (1 << 13)) >> 14; - t22 = ((t25a - t22a) * 11585 + (1 << 13)) >> 14; - t25 = ((t25a + t22a) * 11585 + (1 << 13)) >> 14; - t23a = ((t24 - t23 ) * 11585 + (1 << 13)) >> 14; - t24a = ((t24 + t23 ) * 11585 + (1 << 13)) >> 14; + t20 = (dctint)((t27a - t20a) * 11585U + (1 << 13)) >> 14; + t27 = (dctint)((t27a + t20a) * 11585U + (1 << 13)) >> 14; + t21a = (dctint)((t26 - t21 ) * 11585U + (1 << 13)) >> 14; + t26a = (dctint)((t26 + t21 ) * 11585U + (1 << 13)) >> 14; + t22 = (dctint)((t25a - t22a) * 11585U + (1 << 13)) >> 14; + t25 = (dctint)((t25a + t22a) * 11585U + (1 << 13)) >> 14; + t23a = (dctint)((t24 - t23 ) * 11585U + (1 << 13)) >> 14; + t24a = (dctint)((t24 + t23 ) * 11585U + (1 << 13)) >> 14; out[ 0] = t0 + t31; out[ 1] = t1 + t30a; diff --git a/libavcodec/vp9recon.c b/libavcodec/vp9recon.c index 49bb04e1f40..9a4e7c7a039 100644 --- a/libavcodec/vp9recon.c +++ b/libavcodec/vp9recon.c @@ -572,6 +572,16 @@ static av_always_inline void inter_recon(VP9TileData *td, int bytesperpixel) VP9Block *b = td->b; int row = td->row, col = td->col; + if (s->mvscale[b->ref[0]][0] == REF_INVALID_SCALE || + (b->comp && s->mvscale[b->ref[1]][0] == REF_INVALID_SCALE)) { + if (!s->td->error_info) { + s->td->error_info = AVERROR_INVALIDDATA; + av_log(NULL, AV_LOG_ERROR, "Bitstream not supported, " + "reference frame has invalid dimensions\n"); + } + return; + } + if (s->mvscale[b->ref[0]][0] || (b->comp && s->mvscale[b->ref[1]][0])) { if (bytesperpixel == 1) { inter_pred_scaled_8bpp(td); diff --git a/libavcodec/vqavideo.c b/libavcodec/vqavideo.c index b9743abda95..f45390cfe54 100644 --- a/libavcodec/vqavideo.c +++ b/libavcodec/vqavideo.c @@ -637,6 +637,11 @@ static av_cold int vqa_decode_end(AVCodecContext *avctx) return 0; } +static const AVCodecDefault vqa_defaults[] = { + { "max_pixels", "320*240" }, + { NULL }, +}; + AVCodec ff_vqa_decoder = { .name = "vqavideo", .long_name = NULL_IF_CONFIG_SMALL("Westwood Studios VQA (Vector Quantized Animation) video"), @@ -647,4 +652,5 @@ AVCodec ff_vqa_decoder = { .close = vqa_decode_end, .decode = vqa_decode_frame, .capabilities = AV_CODEC_CAP_DR1, + .defaults = vqa_defaults, }; diff --git a/libavcodec/wavpack.c b/libavcodec/wavpack.c index d0242809fe0..f77548e5a55 100644 --- a/libavcodec/wavpack.c +++ b/libavcodec/wavpack.c @@ -1,6 +1,7 @@ /* * WavPack lossless audio decoder * Copyright (c) 2006,2011 Konstantin Shishkov + * Copyright (c) 2020 David Bryant * * This file is part of FFmpeg. * @@ -19,6 +20,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/buffer.h" #include "libavutil/channel_layout.h" #define BITSTREAM_READER_LE @@ -29,18 +31,37 @@ #include "thread.h" #include "unary.h" #include "wavpack.h" +#include "dsd.h" /** * @file * WavPack lossless audio decoder */ -typedef struct SavedContext { - int offset; - int size; - int bits_used; - uint32_t crc; -} SavedContext; +#define DSD_BYTE_READY(low,high) (!(((low) ^ (high)) & 0xff000000)) + +#define PTABLE_BITS 8 +#define PTABLE_BINS (1<> 8) @@ -365,12 +398,6 @@ static float wv_get_value_float(WavpackFrameContext *s, uint32_t *crc, int S) return value.f; } -static void wv_reset_saved_context(WavpackFrameContext *s) -{ - s->pos = 0; - s->sc.crc = s->extra_sc.crc = 0xFFFFFFFF; -} - static inline int wv_check_crc(WavpackFrameContext *s, uint32_t crc, uint32_t crc_extra_bits) { @@ -386,15 +413,372 @@ static inline int wv_check_crc(WavpackFrameContext *s, uint32_t crc, return 0; } +static void init_ptable(int *table, int rate_i, int rate_s) +{ + int value = 0x808000, rate = rate_i << 8; + + for (int c = (rate + 128) >> 8; c--;) + value += (DOWN - value) >> DECAY; + + for (int i = 0; i < PTABLE_BINS/2; i++) { + table[i] = value; + table[PTABLE_BINS-1-i] = 0x100ffff - value; + + if (value > 0x010000) { + rate += (rate * rate_s + 128) >> 8; + + for (int c = (rate + 64) >> 7; c--;) + value += (DOWN - value) >> DECAY; + } + } +} + +typedef struct { + int32_t value, fltr0, fltr1, fltr2, fltr3, fltr4, fltr5, fltr6, factor; + unsigned int byte; +} DSDfilters; + +static int wv_unpack_dsd_high(WavpackFrameContext *s, uint8_t *dst_left, uint8_t *dst_right) +{ + uint32_t checksum = 0xFFFFFFFF; + uint8_t *dst_l = dst_left, *dst_r = dst_right; + int total_samples = s->samples, stereo = dst_r ? 1 : 0; + DSDfilters filters[2], *sp = filters; + int rate_i, rate_s; + uint32_t low, high, value; + + if (bytestream2_get_bytes_left(&s->gbyte) < (stereo ? 20 : 13)) + return AVERROR_INVALIDDATA; + + rate_i = bytestream2_get_byte(&s->gbyte); + rate_s = bytestream2_get_byte(&s->gbyte); + + if (rate_s != RATE_S) + return AVERROR_INVALIDDATA; + + init_ptable(s->ptable, rate_i, rate_s); + + for (int channel = 0; channel < stereo + 1; channel++) { + DSDfilters *sp = filters + channel; + + sp->fltr1 = bytestream2_get_byte(&s->gbyte) << (PRECISION - 8); + sp->fltr2 = bytestream2_get_byte(&s->gbyte) << (PRECISION - 8); + sp->fltr3 = bytestream2_get_byte(&s->gbyte) << (PRECISION - 8); + sp->fltr4 = bytestream2_get_byte(&s->gbyte) << (PRECISION - 8); + sp->fltr5 = bytestream2_get_byte(&s->gbyte) << (PRECISION - 8); + sp->fltr6 = 0; + sp->factor = bytestream2_get_byte(&s->gbyte) & 0xff; + sp->factor |= (bytestream2_get_byte(&s->gbyte) << 8) & 0xff00; + sp->factor = (int32_t)((uint32_t)sp->factor << 16) >> 16; + } + + value = bytestream2_get_be32(&s->gbyte); + high = 0xffffffff; + low = 0x0; + + while (total_samples--) { + int bitcount = 8; + + sp[0].value = sp[0].fltr1 - sp[0].fltr5 + ((sp[0].fltr6 * sp[0].factor) >> 2); + + if (stereo) + sp[1].value = sp[1].fltr1 - sp[1].fltr5 + ((sp[1].fltr6 * sp[1].factor) >> 2); + + while (bitcount--) { + int32_t *pp = s->ptable + ((sp[0].value >> (PRECISION - PRECISION_USE)) & PTABLE_MASK); + uint32_t split = low + ((high - low) >> 8) * (*pp >> 16); + + if (value <= split) { + high = split; + *pp += (UP - *pp) >> DECAY; + sp[0].fltr0 = -1; + } else { + low = split + 1; + *pp += (DOWN - *pp) >> DECAY; + sp[0].fltr0 = 0; + } + + while (DSD_BYTE_READY(high, low) && bytestream2_get_bytes_left(&s->gbyte)) { + value = (value << 8) | bytestream2_get_byte(&s->gbyte); + high = (high << 8) | 0xff; + low <<= 8; + } + + sp[0].value += sp[0].fltr6 * 8; + sp[0].byte = (sp[0].byte << 1) | (sp[0].fltr0 & 1); + sp[0].factor += (((sp[0].value ^ sp[0].fltr0) >> 31) | 1) & + ((sp[0].value ^ (sp[0].value - (sp[0].fltr6 * 16))) >> 31); + sp[0].fltr1 += ((sp[0].fltr0 & VALUE_ONE) - sp[0].fltr1) >> 6; + sp[0].fltr2 += ((sp[0].fltr0 & VALUE_ONE) - sp[0].fltr2) >> 4; + sp[0].fltr3 += (sp[0].fltr2 - sp[0].fltr3) >> 4; + sp[0].fltr4 += (sp[0].fltr3 - sp[0].fltr4) >> 4; + sp[0].value = (sp[0].fltr4 - sp[0].fltr5) >> 4; + sp[0].fltr5 += sp[0].value; + sp[0].fltr6 += (sp[0].value - sp[0].fltr6) >> 3; + sp[0].value = sp[0].fltr1 - sp[0].fltr5 + ((sp[0].fltr6 * sp[0].factor) >> 2); + + if (!stereo) + continue; + + pp = s->ptable + ((sp[1].value >> (PRECISION - PRECISION_USE)) & PTABLE_MASK); + split = low + ((high - low) >> 8) * (*pp >> 16); + + if (value <= split) { + high = split; + *pp += (UP - *pp) >> DECAY; + sp[1].fltr0 = -1; + } else { + low = split + 1; + *pp += (DOWN - *pp) >> DECAY; + sp[1].fltr0 = 0; + } + + while (DSD_BYTE_READY(high, low) && bytestream2_get_bytes_left(&s->gbyte)) { + value = (value << 8) | bytestream2_get_byte(&s->gbyte); + high = (high << 8) | 0xff; + low <<= 8; + } + + sp[1].value += sp[1].fltr6 * 8; + sp[1].byte = (sp[1].byte << 1) | (sp[1].fltr0 & 1); + sp[1].factor += (((sp[1].value ^ sp[1].fltr0) >> 31) | 1) & + ((sp[1].value ^ (sp[1].value - (sp[1].fltr6 * 16))) >> 31); + sp[1].fltr1 += ((sp[1].fltr0 & VALUE_ONE) - sp[1].fltr1) >> 6; + sp[1].fltr2 += ((sp[1].fltr0 & VALUE_ONE) - sp[1].fltr2) >> 4; + sp[1].fltr3 += (sp[1].fltr2 - sp[1].fltr3) >> 4; + sp[1].fltr4 += (sp[1].fltr3 - sp[1].fltr4) >> 4; + sp[1].value = (sp[1].fltr4 - sp[1].fltr5) >> 4; + sp[1].fltr5 += sp[1].value; + sp[1].fltr6 += (sp[1].value - sp[1].fltr6) >> 3; + sp[1].value = sp[1].fltr1 - sp[1].fltr5 + ((sp[1].fltr6 * sp[1].factor) >> 2); + } + + checksum += (checksum << 1) + (*dst_l = sp[0].byte & 0xff); + sp[0].factor -= (sp[0].factor + 512) >> 10; + dst_l += 4; + + if (stereo) { + checksum += (checksum << 1) + (*dst_r = filters[1].byte & 0xff); + filters[1].factor -= (filters[1].factor + 512) >> 10; + dst_r += 4; + } + } + + if (wv_check_crc(s, checksum, 0)) { + if (s->avctx->err_recognition & AV_EF_CRCCHECK) + return AVERROR_INVALIDDATA; + + memset(dst_left, 0x69, s->samples * 4); + + if (dst_r) + memset(dst_right, 0x69, s->samples * 4); + } + + return 0; +} + +static int wv_unpack_dsd_fast(WavpackFrameContext *s, uint8_t *dst_left, uint8_t *dst_right) +{ + uint8_t *dst_l = dst_left, *dst_r = dst_right; + uint8_t history_bits, max_probability; + int total_summed_probabilities = 0; + int total_samples = s->samples; + uint8_t *vlb = s->value_lookup_buffer; + int history_bins, p0, p1, chan; + uint32_t checksum = 0xFFFFFFFF; + uint32_t low, high, value; + + if (!bytestream2_get_bytes_left(&s->gbyte)) + return AVERROR_INVALIDDATA; + + history_bits = bytestream2_get_byte(&s->gbyte); + + if (!bytestream2_get_bytes_left(&s->gbyte) || history_bits > MAX_HISTORY_BITS) + return AVERROR_INVALIDDATA; + + history_bins = 1 << history_bits; + max_probability = bytestream2_get_byte(&s->gbyte); + + if (max_probability < 0xff) { + uint8_t *outptr = (uint8_t *)s->probabilities; + uint8_t *outend = outptr + sizeof(*s->probabilities) * history_bins; + + while (outptr < outend && bytestream2_get_bytes_left(&s->gbyte)) { + int code = bytestream2_get_byte(&s->gbyte); + + if (code > max_probability) { + int zcount = code - max_probability; + + while (outptr < outend && zcount--) + *outptr++ = 0; + } else if (code) { + *outptr++ = code; + } + else { + break; + } + } + + if (outptr < outend || + (bytestream2_get_bytes_left(&s->gbyte) && bytestream2_get_byte(&s->gbyte))) + return AVERROR_INVALIDDATA; + } else if (bytestream2_get_bytes_left(&s->gbyte) > (int)sizeof(*s->probabilities) * history_bins) { + bytestream2_get_buffer(&s->gbyte, (uint8_t *)s->probabilities, + sizeof(*s->probabilities) * history_bins); + } else { + return AVERROR_INVALIDDATA; + } + + for (p0 = 0; p0 < history_bins; p0++) { + int32_t sum_values = 0; + + for (int i = 0; i < 256; i++) + s->summed_probabilities[p0][i] = sum_values += s->probabilities[p0][i]; + + if (sum_values) { + total_summed_probabilities += sum_values; + + if (total_summed_probabilities > history_bins * MAX_BIN_BYTES) + return AVERROR_INVALIDDATA; + + s->value_lookup[p0] = vlb; + + for (int i = 0; i < 256; i++) { + int c = s->probabilities[p0][i]; + + while (c--) + *vlb++ = i; + } + } + } + + if (bytestream2_get_bytes_left(&s->gbyte) < 4) + return AVERROR_INVALIDDATA; + + chan = p0 = p1 = 0; + low = 0; high = 0xffffffff; + value = bytestream2_get_be32(&s->gbyte); + + if (dst_r) + total_samples *= 2; + + while (total_samples--) { + unsigned int mult, index, code; + + if (!s->summed_probabilities[p0][255]) + return AVERROR_INVALIDDATA; + + mult = (high - low) / s->summed_probabilities[p0][255]; + + if (!mult) { + if (bytestream2_get_bytes_left(&s->gbyte) >= 4) + value = bytestream2_get_be32(&s->gbyte); + + low = 0; + high = 0xffffffff; + mult = high / s->summed_probabilities[p0][255]; + + if (!mult) + return AVERROR_INVALIDDATA; + } + + index = (value - low) / mult; + + if (index >= s->summed_probabilities[p0][255]) + return AVERROR_INVALIDDATA; + + if (!dst_r) { + if ((*dst_l = code = s->value_lookup[p0][index])) + low += s->summed_probabilities[p0][code-1] * mult; + + dst_l += 4; + } else { + if ((code = s->value_lookup[p0][index])) + low += s->summed_probabilities[p0][code-1] * mult; + + if (chan) { + *dst_r = code; + dst_r += 4; + } + else { + *dst_l = code; + dst_l += 4; + } + + chan ^= 1; + } + + high = low + s->probabilities[p0][code] * mult - 1; + checksum += (checksum << 1) + code; + + if (!dst_r) { + p0 = code & (history_bins-1); + } else { + p0 = p1; + p1 = code & (history_bins-1); + } + + while (DSD_BYTE_READY(high, low) && bytestream2_get_bytes_left(&s->gbyte)) { + value = (value << 8) | bytestream2_get_byte(&s->gbyte); + high = (high << 8) | 0xff; + low <<= 8; + } + } + + if (wv_check_crc(s, checksum, 0)) { + if (s->avctx->err_recognition & AV_EF_CRCCHECK) + return AVERROR_INVALIDDATA; + + memset(dst_left, 0x69, s->samples * 4); + + if (dst_r) + memset(dst_right, 0x69, s->samples * 4); + } + + return 0; +} + +static int wv_unpack_dsd_copy(WavpackFrameContext *s, uint8_t *dst_left, uint8_t *dst_right) +{ + uint8_t *dst_l = dst_left, *dst_r = dst_right; + int total_samples = s->samples; + uint32_t checksum = 0xFFFFFFFF; + + if (bytestream2_get_bytes_left(&s->gbyte) != total_samples * (dst_r ? 2 : 1)) + return AVERROR_INVALIDDATA; + + while (total_samples--) { + checksum += (checksum << 1) + (*dst_l = bytestream2_get_byte(&s->gbyte)); + dst_l += 4; + + if (dst_r) { + checksum += (checksum << 1) + (*dst_r = bytestream2_get_byte(&s->gbyte)); + dst_r += 4; + } + } + + if (wv_check_crc(s, checksum, 0)) { + if (s->avctx->err_recognition & AV_EF_CRCCHECK) + return AVERROR_INVALIDDATA; + + memset(dst_left, 0x69, s->samples * 4); + + if (dst_r) + memset(dst_right, 0x69, s->samples * 4); + } + + return 0; +} + static inline int wv_unpack_stereo(WavpackFrameContext *s, GetBitContext *gb, void *dst_l, void *dst_r, const int type) { int i, j, count = 0; int last, t; int A, B, L, L2, R, R2; - int pos = s->pos; - uint32_t crc = s->sc.crc; - uint32_t crc_extra_bits = s->extra_sc.crc; + int pos = 0; + uint32_t crc = 0xFFFFFFFF; + uint32_t crc_extra_bits = 0xFFFFFFFF; int16_t *dst16_l = dst_l; int16_t *dst16_r = dst_r; int32_t *dst32_l = dst_l; @@ -504,8 +888,6 @@ static inline int wv_unpack_stereo(WavpackFrameContext *s, GetBitContext *gb, count++; } while (!last && count < s->samples); - wv_reset_saved_context(s); - if (last && count < s->samples) { int size = av_get_bytes_per_sample(type); memset((uint8_t*)dst_l + count*size, 0, (s->samples-count)*size); @@ -525,9 +907,9 @@ static inline int wv_unpack_mono(WavpackFrameContext *s, GetBitContext *gb, int i, j, count = 0; int last, t; int A, S, T; - int pos = s->pos; - uint32_t crc = s->sc.crc; - uint32_t crc_extra_bits = s->extra_sc.crc; + int pos = 0; + uint32_t crc = 0xFFFFFFFF; + uint32_t crc_extra_bits = 0xFFFFFFFF; int16_t *dst16 = dst; int32_t *dst32 = dst; float *dstfl = dst; @@ -572,8 +954,6 @@ static inline int wv_unpack_mono(WavpackFrameContext *s, GetBitContext *gb, count++; } while (!last && count < s->samples); - wv_reset_saved_context(s); - if (last && count < s->samples) { int size = av_get_bytes_per_sample(type); memset((uint8_t*)dst + count*size, 0, (s->samples-count)*size); @@ -598,16 +978,63 @@ static av_cold int wv_alloc_frame_context(WavpackContext *c) return -1; c->fdec_num++; c->fdec[c->fdec_num - 1]->avctx = c->avctx; - wv_reset_saved_context(c->fdec[c->fdec_num - 1]); + + return 0; +} + +static int wv_dsd_reset(WavpackContext *s, int channels) +{ + int i; + + s->dsdctx = NULL; + s->dsd_channels = 0; + av_buffer_unref(&s->dsd_ref); + + if (!channels) + return 0; + + if (channels > INT_MAX / sizeof(*s->dsdctx)) + return AVERROR(EINVAL); + + s->dsd_ref = av_buffer_allocz(channels * sizeof(*s->dsdctx)); + if (!s->dsd_ref) + return AVERROR(ENOMEM); + s->dsdctx = (DSDContext*)s->dsd_ref->data; + s->dsd_channels = channels; + + for (i = 0; i < channels; i++) + memset(s->dsdctx[i].buf, 0x69, sizeof(s->dsdctx[i].buf)); return 0; } #if HAVE_THREADS -static int init_thread_copy(AVCodecContext *avctx) +static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src) { - WavpackContext *s = avctx->priv_data; - s->avctx = avctx; + WavpackContext *fsrc = src->priv_data; + WavpackContext *fdst = dst->priv_data; + int ret; + + if (dst == src) + return 0; + + ff_thread_release_buffer(dst, &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; + } + + av_buffer_unref(&fdst->dsd_ref); + fdst->dsdctx = NULL; + fdst->dsd_channels = 0; + if (fsrc->dsd_ref) { + fdst->dsd_ref = av_buffer_ref(fsrc->dsd_ref); + if (!fdst->dsd_ref) + return AVERROR(ENOMEM); + fdst->dsdctx = (DSDContext*)fdst->dsd_ref->data; + fdst->dsd_channels = fsrc->dsd_channels; + } + return 0; } #endif @@ -620,35 +1047,52 @@ static av_cold int wavpack_decode_init(AVCodecContext *avctx) s->fdec_num = 0; + s->curr_frame.f = av_frame_alloc(); + s->prev_frame.f = av_frame_alloc(); + + if (!s->curr_frame.f || !s->prev_frame.f) + return AVERROR(ENOMEM); + + ff_init_dsd_data(); + return 0; } static av_cold int wavpack_decode_end(AVCodecContext *avctx) { WavpackContext *s = avctx->priv_data; - int i; - for (i = 0; i < s->fdec_num; i++) + for (int i = 0; i < s->fdec_num; i++) av_freep(&s->fdec[i]); s->fdec_num = 0; + ff_thread_release_buffer(avctx, &s->curr_frame); + av_frame_free(&s->curr_frame.f); + + ff_thread_release_buffer(avctx, &s->prev_frame); + av_frame_free(&s->prev_frame.f); + + av_buffer_unref(&s->dsd_ref); + return 0; } static int wavpack_decode_block(AVCodecContext *avctx, int block_no, - AVFrame *frame, const uint8_t *buf, int buf_size) + const uint8_t *buf, int buf_size) { WavpackContext *wc = avctx->priv_data; - ThreadFrame tframe = { .f = frame }; WavpackFrameContext *s; GetByteContext gb; + enum AVSampleFormat sample_fmt; void *samples_l = NULL, *samples_r = NULL; int ret; int got_terms = 0, got_weights = 0, got_samples = 0, - got_entropy = 0, got_bs = 0, got_float = 0, got_hybrid = 0; + got_entropy = 0, got_pcm = 0, got_float = 0, got_hybrid = 0; + int got_dsd = 0; int i, j, id, size, ssize, weights, t; - int bpp, chan = 0, chmask = 0, orig_bpp, sample_rate = 0; + int bpp, chan = 0, orig_bpp, sample_rate = 0, rate_x = 1, dsd_mode = 0; int multiblock; + uint64_t chmask = 0; if (block_no >= wc->fdec_num && wv_alloc_frame_context(wc) < 0) { av_log(avctx, AV_LOG_ERROR, "Error creating frame decode context\n"); @@ -677,7 +1121,18 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no, return AVERROR_INVALIDDATA; } s->frame_flags = bytestream2_get_le32(&gb); - bpp = av_get_bytes_per_sample(avctx->sample_fmt); + + if (s->frame_flags & (WV_FLOAT_DATA | WV_DSD_DATA)) + sample_fmt = AV_SAMPLE_FMT_FLTP; + else if ((s->frame_flags & 0x03) <= 1) + sample_fmt = AV_SAMPLE_FMT_S16P; + else + sample_fmt = AV_SAMPLE_FMT_S32P; + + if (wc->ch_offset && avctx->sample_fmt != sample_fmt) + return AVERROR_INVALIDDATA; + + bpp = av_get_bytes_per_sample(sample_fmt); orig_bpp = ((s->frame_flags & 0x03) + 1) << 3; multiblock = (s->frame_flags & WV_SINGLE_BLOCK) != WV_SINGLE_BLOCK; @@ -698,10 +1153,8 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no, while (bytestream2_get_bytes_left(&gb)) { id = bytestream2_get_byte(&gb); size = bytestream2_get_byte(&gb); - if (id & WP_IDF_LONG) { - size |= (bytestream2_get_byte(&gb)) << 8; - size |= (bytestream2_get_byte(&gb)) << 16; - } + if (id & WP_IDF_LONG) + size |= (bytestream2_get_le16u(&gb)) << 8; size <<= 1; // size is specified in words ssize = size; if (id & WP_IDF_ODD) @@ -897,13 +1350,31 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no, bytestream2_skip(&gb, 1); break; case WP_ID_DATA: - s->sc.offset = bytestream2_tell(&gb); - s->sc.size = size * 8; if ((ret = init_get_bits8(&s->gb, gb.buffer, size)) < 0) return ret; - s->data_size = size * 8; bytestream2_skip(&gb, size); - got_bs = 1; + got_pcm = 1; + break; + case WP_ID_DSD_DATA: + if (size < 2) { + av_log(avctx, AV_LOG_ERROR, "Invalid DSD_DATA, size = %i\n", + size); + bytestream2_skip(&gb, ssize); + continue; + } + rate_x = bytestream2_get_byte(&gb); + if (rate_x > 30) + return AVERROR_INVALIDDATA; + rate_x = 1 << rate_x; + dsd_mode = bytestream2_get_byte(&gb); + if (dsd_mode && dsd_mode != 1 && dsd_mode != 3) { + av_log(avctx, AV_LOG_ERROR, "Invalid DSD encoding mode: %d\n", + dsd_mode); + return AVERROR_INVALIDDATA; + } + bytestream2_init(&s->gbyte, gb.buffer, size-2); + bytestream2_skip(&gb, size-2); + got_dsd = 1; break; case WP_ID_EXTRABITS: if (size <= 4) { @@ -912,8 +1383,6 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no, bytestream2_skip(&gb, size); continue; } - s->extra_sc.offset = bytestream2_tell(&gb); - s->extra_sc.size = size * 8; if ((ret = init_get_bits8(&s->gb_extra_bits, gb.buffer, size)) < 0) return ret; s->crc_extra_bits = get_bits_long(&s->gb_extra_bits, 32); @@ -979,70 +1448,115 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no, bytestream2_skip(&gb, 1); } - if (!got_terms) { - av_log(avctx, AV_LOG_ERROR, "No block with decorrelation terms\n"); - return AVERROR_INVALIDDATA; - } - if (!got_weights) { - av_log(avctx, AV_LOG_ERROR, "No block with decorrelation weights\n"); - return AVERROR_INVALIDDATA; - } - if (!got_samples) { - av_log(avctx, AV_LOG_ERROR, "No block with decorrelation samples\n"); - return AVERROR_INVALIDDATA; - } - if (!got_entropy) { - av_log(avctx, AV_LOG_ERROR, "No block with entropy info\n"); - return AVERROR_INVALIDDATA; - } - if (s->hybrid && !got_hybrid) { - av_log(avctx, AV_LOG_ERROR, "Hybrid config not found\n"); - return AVERROR_INVALIDDATA; + if (got_pcm) { + if (!got_terms) { + av_log(avctx, AV_LOG_ERROR, "No block with decorrelation terms\n"); + return AVERROR_INVALIDDATA; + } + if (!got_weights) { + av_log(avctx, AV_LOG_ERROR, "No block with decorrelation weights\n"); + return AVERROR_INVALIDDATA; + } + if (!got_samples) { + av_log(avctx, AV_LOG_ERROR, "No block with decorrelation samples\n"); + return AVERROR_INVALIDDATA; + } + if (!got_entropy) { + av_log(avctx, AV_LOG_ERROR, "No block with entropy info\n"); + return AVERROR_INVALIDDATA; + } + if (s->hybrid && !got_hybrid) { + av_log(avctx, AV_LOG_ERROR, "Hybrid config not found\n"); + return AVERROR_INVALIDDATA; + } + if (!got_float && sample_fmt == AV_SAMPLE_FMT_FLTP) { + av_log(avctx, AV_LOG_ERROR, "Float information not found\n"); + return AVERROR_INVALIDDATA; + } + if (s->got_extra_bits && sample_fmt != AV_SAMPLE_FMT_FLTP) { + const int size = get_bits_left(&s->gb_extra_bits); + const int wanted = s->samples * s->extra_bits << s->stereo_in; + if (size < wanted) { + av_log(avctx, AV_LOG_ERROR, "Too small EXTRABITS\n"); + s->got_extra_bits = 0; + } + } } - if (!got_bs) { + + if (!got_pcm && !got_dsd) { av_log(avctx, AV_LOG_ERROR, "Packed samples not found\n"); return AVERROR_INVALIDDATA; } - if (!got_float && avctx->sample_fmt == AV_SAMPLE_FMT_FLTP) { - av_log(avctx, AV_LOG_ERROR, "Float information not found\n"); - return AVERROR_INVALIDDATA; - } - if (s->got_extra_bits && avctx->sample_fmt != AV_SAMPLE_FMT_FLTP) { - const int size = get_bits_left(&s->gb_extra_bits); - const int wanted = s->samples * s->extra_bits << s->stereo_in; - if (size < wanted) { - av_log(avctx, AV_LOG_ERROR, "Too small EXTRABITS\n"); - s->got_extra_bits = 0; - } + + if ((got_pcm && wc->modulation != MODULATION_PCM) || + (got_dsd && wc->modulation != MODULATION_DSD)) { + av_log(avctx, AV_LOG_ERROR, "Invalid PCM/DSD mix encountered\n"); + return AVERROR_INVALIDDATA; } if (!wc->ch_offset) { + int new_channels = avctx->channels; + uint64_t new_chmask = avctx->channel_layout; + int new_samplerate; int sr = (s->frame_flags >> 23) & 0xf; if (sr == 0xf) { if (!sample_rate) { av_log(avctx, AV_LOG_ERROR, "Custom sample rate missing.\n"); return AVERROR_INVALIDDATA; } - avctx->sample_rate = sample_rate; + new_samplerate = sample_rate; } else - avctx->sample_rate = wv_rates[sr]; + new_samplerate = wv_rates[sr]; + + if (new_samplerate * (uint64_t)rate_x > INT_MAX) + return AVERROR_INVALIDDATA; + new_samplerate *= rate_x; if (multiblock) { if (chan) - avctx->channels = chan; + new_channels = chan; if (chmask) - avctx->channel_layout = chmask; + new_chmask = chmask; } else { - avctx->channels = s->stereo ? 2 : 1; - avctx->channel_layout = s->stereo ? AV_CH_LAYOUT_STEREO : - AV_CH_LAYOUT_MONO; + new_channels = s->stereo ? 2 : 1; + new_chmask = s->stereo ? AV_CH_LAYOUT_STEREO : + AV_CH_LAYOUT_MONO; } + if (new_chmask && + av_get_channel_layout_nb_channels(new_chmask) != new_channels) { + av_log(avctx, AV_LOG_ERROR, "Channel mask does not match the channel count\n"); + return AVERROR_INVALIDDATA; + } + + /* clear DSD state if stream properties change */ + if (new_channels != wc->dsd_channels || + new_chmask != avctx->channel_layout || + new_samplerate != avctx->sample_rate || + !!got_dsd != !!wc->dsdctx) { + ret = wv_dsd_reset(wc, got_dsd ? new_channels : 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error reinitializing the DSD context\n"); + return ret; + } + ff_thread_release_buffer(avctx, &wc->curr_frame); + } + avctx->channels = new_channels; + avctx->channel_layout = new_chmask; + avctx->sample_rate = new_samplerate; + avctx->sample_fmt = sample_fmt; + avctx->bits_per_raw_sample = orig_bpp; + + ff_thread_release_buffer(avctx, &wc->prev_frame); + FFSWAP(ThreadFrame, wc->curr_frame, wc->prev_frame); + /* get output buffer */ - frame->nb_samples = s->samples + 1; - if ((ret = ff_thread_get_buffer(avctx, &tframe, 0)) < 0) + wc->curr_frame.f->nb_samples = s->samples; + if ((ret = ff_thread_get_buffer(avctx, &wc->curr_frame, AV_GET_BUFFER_FLAG_REF)) < 0) return ret; - frame->nb_samples = s->samples; + + wc->frame = wc->curr_frame.f; + ff_thread_finish_setup(avctx); } if (wc->ch_offset + s->stereo >= avctx->channels) { @@ -1050,18 +1564,38 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no, return ((avctx->err_recognition & AV_EF_EXPLODE) || !wc->ch_offset) ? AVERROR_INVALIDDATA : 0; } - samples_l = frame->extended_data[wc->ch_offset]; + samples_l = wc->frame->extended_data[wc->ch_offset]; if (s->stereo) - samples_r = frame->extended_data[wc->ch_offset + 1]; + samples_r = wc->frame->extended_data[wc->ch_offset + 1]; wc->ch_offset += 1 + s->stereo; if (s->stereo_in) { - ret = wv_unpack_stereo(s, &s->gb, samples_l, samples_r, avctx->sample_fmt); + if (got_dsd) { + if (dsd_mode == 3) { + ret = wv_unpack_dsd_high(s, samples_l, samples_r); + } else if (dsd_mode == 1) { + ret = wv_unpack_dsd_fast(s, samples_l, samples_r); + } else { + ret = wv_unpack_dsd_copy(s, samples_l, samples_r); + } + } else { + ret = wv_unpack_stereo(s, &s->gb, samples_l, samples_r, avctx->sample_fmt); + } if (ret < 0) return ret; } else { - ret = wv_unpack_mono(s, &s->gb, samples_l, avctx->sample_fmt); + if (got_dsd) { + if (dsd_mode == 3) { + ret = wv_unpack_dsd_high(s, samples_l, NULL); + } else if (dsd_mode == 1) { + ret = wv_unpack_dsd_fast(s, samples_l, NULL); + } else { + ret = wv_unpack_dsd_copy(s, samples_l, NULL); + } + } else { + ret = wv_unpack_mono(s, &s->gb, samples_l, avctx->sample_fmt); + } if (ret < 0) return ret; @@ -1075,10 +1609,20 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no, static void wavpack_decode_flush(AVCodecContext *avctx) { WavpackContext *s = avctx->priv_data; - int i; - for (i = 0; i < s->fdec_num; i++) - wv_reset_saved_context(s->fdec[i]); + wv_dsd_reset(s, 0); +} + +static int dsd_channel(AVCodecContext *avctx, void *frmptr, int jobnr, int threadnr) +{ + WavpackContext *s = avctx->priv_data; + AVFrame *frame = frmptr; + + ff_dsd2pcm_translate (&s->dsdctx [jobnr], s->samples, 0, + (uint8_t *)frame->extended_data[jobnr], 4, + (float *)frame->extended_data[jobnr], 1); + + return 0; } static int wavpack_decode_frame(AVCodecContext *avctx, void *data, @@ -1087,12 +1631,12 @@ static int wavpack_decode_frame(AVCodecContext *avctx, void *data, WavpackContext *s = avctx->priv_data; const uint8_t *buf = avpkt->data; int buf_size = avpkt->size; - AVFrame *frame = data; int frame_size, ret, frame_flags; if (avpkt->size <= WV_HEADER_SIZE) return AVERROR_INVALIDDATA; + s->frame = NULL; s->block = 0; s->ch_offset = 0; @@ -1105,18 +1649,9 @@ static int wavpack_decode_frame(AVCodecContext *avctx, void *data, return AVERROR_INVALIDDATA; } - if (frame_flags & 0x80) { - avctx->sample_fmt = AV_SAMPLE_FMT_FLTP; - } else if ((frame_flags & 0x03) <= 1) { - avctx->sample_fmt = AV_SAMPLE_FMT_S16P; - } else { - avctx->sample_fmt = AV_SAMPLE_FMT_S32P; - avctx->bits_per_raw_sample = ((frame_flags & 0x03) + 1) << 3; - } + s->modulation = (frame_flags & WV_DSD_DATA) ? MODULATION_DSD : MODULATION_PCM; - while (buf_size > 0) { - if (buf_size <= WV_HEADER_SIZE) - break; + while (buf_size > WV_HEADER_SIZE) { frame_size = AV_RL32(buf + 4) - 12; buf += 20; buf_size -= 20; @@ -1124,14 +1659,11 @@ static int wavpack_decode_frame(AVCodecContext *avctx, void *data, av_log(avctx, AV_LOG_ERROR, "Block %d has invalid size (size %d vs. %d bytes left)\n", s->block, frame_size, buf_size); - wavpack_decode_flush(avctx); - return AVERROR_INVALIDDATA; - } - if ((ret = wavpack_decode_block(avctx, s->block, - frame, buf, frame_size)) < 0) { - wavpack_decode_flush(avctx); - return ret; + ret = AVERROR_INVALIDDATA; + goto error; } + if ((ret = wavpack_decode_block(avctx, s->block, buf, frame_size)) < 0) + goto error; s->block++; buf += frame_size; buf_size -= frame_size; @@ -1139,12 +1671,33 @@ static int wavpack_decode_frame(AVCodecContext *avctx, void *data, if (s->ch_offset != avctx->channels) { av_log(avctx, AV_LOG_ERROR, "Not enough channels coded in a packet.\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto error; } + ff_thread_await_progress(&s->prev_frame, INT_MAX, 0); + ff_thread_release_buffer(avctx, &s->prev_frame); + + if (s->modulation == MODULATION_DSD) + avctx->execute2(avctx, dsd_channel, s->frame, NULL, avctx->channels); + + ff_thread_report_progress(&s->curr_frame, INT_MAX, 0); + + if ((ret = av_frame_ref(data, s->frame)) < 0) + return ret; + *got_frame_ptr = 1; return avpkt->size; + +error: + if (s->frame) { + ff_thread_await_progress(&s->prev_frame, INT_MAX, 0); + ff_thread_release_buffer(avctx, &s->prev_frame); + ff_thread_report_progress(&s->curr_frame, INT_MAX, 0); + } + + return ret; } AVCodec ff_wavpack_decoder = { @@ -1157,6 +1710,8 @@ AVCodec ff_wavpack_decoder = { .close = wavpack_decode_end, .decode = wavpack_decode_frame, .flush = wavpack_decode_flush, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(init_thread_copy), - .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, + .update_thread_context = ONLY_IF_THREADS_ENABLED(update_thread_context), + .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_SLICE_THREADS, + .caps_internal = FF_CODEC_CAP_ALLOCATE_PROGRESS, }; diff --git a/libavcodec/wavpack.h b/libavcodec/wavpack.h index 6caad038e9d..43aaac815fb 100644 --- a/libavcodec/wavpack.h +++ b/libavcodec/wavpack.h @@ -35,6 +35,7 @@ #define WV_FLOAT_DATA 0x00000080 #define WV_INT32_DATA 0x00000100 #define WV_FALSE_STEREO 0x40000000 +#define WV_DSD_DATA 0x80000000 #define WV_HYBRID_MODE 0x00000008 #define WV_HYBRID_SHAPE 0x00000008 @@ -77,6 +78,7 @@ enum WP_ID { WP_ID_CORR, WP_ID_EXTRABITS, WP_ID_CHANINFO, + WP_ID_DSD_DATA, WP_ID_SAMPLE_RATE = 0x27, }; diff --git a/libavcodec/wavpackenc.c b/libavcodec/wavpackenc.c index 95f4b6530c2..0c85fbe374f 100644 --- a/libavcodec/wavpackenc.c +++ b/libavcodec/wavpackenc.c @@ -529,9 +529,9 @@ static int8_t store_weight(int weight) static int restore_weight(int8_t weight) { - int result; + int result = 8 * weight; - if ((result = (int) weight << 3) > 0) + if (result > 0) result += (result + 64) >> 7; return result; @@ -2557,7 +2557,7 @@ static int wavpack_encode_block(WavPackEncodeContext *s, ret = wv_mono(s, samples_l, !s->num_terms, 1); } else { for (i = 0; i < nb_samples; i++) - crc += (crc << 3) + (samples_l[i] << 1) + samples_l[i] + samples_r[i]; + crc += (crc << 3) + ((uint32_t)samples_l[i] << 1) + samples_l[i] + samples_r[i]; if (s->num_passes) ret = wv_stereo(s, samples_l, samples_r, !s->num_terms, 1); diff --git a/libavcodec/wcmv.c b/libavcodec/wcmv.c index 0d60b9fe1fc..d7a3cbd1b71 100644 --- a/libavcodec/wcmv.c +++ b/libavcodec/wcmv.c @@ -45,7 +45,7 @@ static int decode_frame(AVCodecContext *avctx, { WCMVContext *s = avctx->priv_data; AVFrame *frame = data; - int skip, blocks, zret, ret, intra = 0, bpp = s->bpp; + int skip, blocks, zret, ret, intra = 0, flags = 0, bpp = s->bpp; GetByteContext gb; uint8_t *dst; @@ -58,9 +58,9 @@ static int decode_frame(AVCodecContext *avctx, bytestream2_init(&gb, avpkt->data, avpkt->size); blocks = bytestream2_get_le16(&gb); if (!blocks) - return avpkt->size; + flags |= FF_REGET_BUFFER_FLAG_READONLY; - if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0) + if ((ret = ff_reget_buffer(avctx, s->prev_frame, flags)) < 0) return ret; if (blocks > 5) { @@ -157,13 +157,9 @@ static int decode_frame(AVCodecContext *avctx, if (bytestream2_get_bytes_left(&gb) < 8LL * blocks) return AVERROR_INVALIDDATA; - if (s->prev_frame->data[0]) { - ret = av_frame_copy(frame, s->prev_frame); - if (ret < 0) - return ret; - } else { - ptrdiff_t linesize[4] = { frame->linesize[0], 0, 0, 0 }; - av_image_fill_black(frame->data, linesize, avctx->pix_fmt, 0, + if (!avctx->frame_number) { + ptrdiff_t linesize[4] = { s->prev_frame->linesize[0], 0, 0, 0 }; + av_image_fill_black(s->prev_frame->data, linesize, avctx->pix_fmt, 0, avctx->width, avctx->height); } @@ -184,7 +180,7 @@ static int decode_frame(AVCodecContext *avctx, if (w > avctx->width || h > avctx->height) return AVERROR_INVALIDDATA; - dst = frame->data[0] + (avctx->height - y - 1) * frame->linesize[0] + x * bpp; + dst = s->prev_frame->data[0] + (avctx->height - y - 1) * s->prev_frame->linesize[0] + x * bpp; for (int i = 0; i < h; i++) { s->zstream.next_out = dst; s->zstream.avail_out = w * bpp; @@ -196,15 +192,14 @@ static int decode_frame(AVCodecContext *avctx, return AVERROR_INVALIDDATA; } - dst -= frame->linesize[0]; + dst -= s->prev_frame->linesize[0]; } } - frame->key_frame = intra; - frame->pict_type = intra ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + s->prev_frame->key_frame = intra; + s->prev_frame->pict_type = intra ? 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_ref(frame, s->prev_frame)) < 0) return ret; *got_frame = 1; diff --git a/libavcodec/webp.c b/libavcodec/webp.c index 077bb06f85a..c6d02068463 100644 --- a/libavcodec/webp.c +++ b/libavcodec/webp.c @@ -1412,8 +1412,11 @@ static int webp_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, return AVERROR_INVALIDDATA; chunk_size += chunk_size & 1; - if (bytestream2_get_bytes_left(&gb) < chunk_size) - return AVERROR_INVALIDDATA; + if (bytestream2_get_bytes_left(&gb) < chunk_size) { + /* we seem to be running out of data, but it could also be that the + bitstream has trailing junk leading to bogus chunk_size. */ + break; + } switch (chunk_type) { case MKTAG('V', 'P', '8', ' '): diff --git a/libavcodec/webp_parser.c b/libavcodec/webp_parser.c new file mode 100644 index 00000000000..fdb7c383504 --- /dev/null +++ b/libavcodec/webp_parser.c @@ -0,0 +1,112 @@ +/* + * WebP parser + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 + * WebP parser + */ + +#include "libavutil/bswap.h" +#include "libavutil/common.h" + +#include "parser.h" + +typedef struct WebPParseContext { + ParseContext pc; + uint32_t fsize; + uint32_t remaining_size; +} WebPParseContext; + +static int webp_parse(AVCodecParserContext *s, AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) +{ + WebPParseContext *ctx = s->priv_data; + uint64_t state = ctx->pc.state64; + int next = END_NOT_FOUND; + int i = 0; + + *poutbuf = NULL; + *poutbuf_size = 0; + +restart: + if (ctx->pc.frame_start_found <= 8) { + for (; i < buf_size; i++) { + state = (state << 8) | buf[i]; + if (ctx->pc.frame_start_found == 0) { + if ((state >> 32) == MKBETAG('R', 'I', 'F', 'F')) { + ctx->fsize = av_bswap32(state); + if (ctx->fsize > 15 && ctx->fsize <= UINT32_MAX - 10) { + ctx->pc.frame_start_found = 1; + ctx->fsize += 8; + } + } + } else if (ctx->pc.frame_start_found == 8) { + if ((state >> 32) != MKBETAG('W', 'E', 'B', 'P')) { + ctx->pc.frame_start_found = 0; + continue; + } + ctx->pc.frame_start_found++; + ctx->remaining_size = ctx->fsize + i - 15; + if (ctx->pc.index + i > 15) { + next = i - 15; + state = 0; + break; + } else { + ctx->pc.state64 = 0; + goto restart; + } + } else if (ctx->pc.frame_start_found) + ctx->pc.frame_start_found++; + } + ctx->pc.state64 = state; + } else { + if (ctx->remaining_size) { + i = FFMIN(ctx->remaining_size, buf_size); + ctx->remaining_size -= i; + if (ctx->remaining_size) + goto flush; + + ctx->pc.frame_start_found = 0; + goto restart; + } + } + +flush: + if (ff_combine_frame(&ctx->pc, next, &buf, &buf_size) < 0) + return buf_size; + + if (next != END_NOT_FOUND && next < 0) + ctx->pc.frame_start_found = FFMAX(ctx->pc.frame_start_found - i - 1, 0); + else + ctx->pc.frame_start_found = 0; + + *poutbuf = buf; + *poutbuf_size = buf_size; + + return next; +} + +AVCodecParser ff_webp_parser = { + .codec_ids = { AV_CODEC_ID_WEBP }, + .priv_data_size = sizeof(WebPParseContext), + .parser_parse = webp_parse, + .parser_close = ff_parse_close, +}; diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c index c84bbf4b4e1..febf6ee370a 100644 --- a/libavcodec/webvttenc.c +++ b/libavcodec/webvttenc.c @@ -168,7 +168,7 @@ static int webvtt_encode_frame(AVCodecContext *avctx, if (sub->rects[i]->type != SUBTITLE_ASS) { av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); - return AVERROR(ENOSYS); + return AVERROR(EINVAL); } #if FF_API_ASS_TIMING @@ -200,7 +200,7 @@ static int webvtt_encode_frame(AVCodecContext *avctx, if (s->buffer.len > bufsize) { av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); - return -1; + return AVERROR_BUFFER_TOO_SMALL; } memcpy(buf, s->buffer.str, s->buffer.len); diff --git a/libavcodec/wma.h b/libavcodec/wma.h index 325f03c44b7..c7fcf5047cc 100644 --- a/libavcodec/wma.h +++ b/libavcodec/wma.h @@ -123,6 +123,7 @@ typedef struct WMACodecContext { uint8_t last_superframe[MAX_CODED_SUPERFRAME_SIZE + AV_INPUT_BUFFER_PADDING_SIZE]; /* padding added */ int last_bitoffset; int last_superframe_len; + int exponents_initialized[MAX_CHANNELS]; float noise_table[NOISE_TAB_SIZE]; int noise_index; float noise_mult; /* XXX: suppress that and integrate it in the noise array */ diff --git a/libavcodec/wmadec.c b/libavcodec/wmadec.c index 78b51e5871b..07fd960f7f9 100644 --- a/libavcodec/wmadec.c +++ b/libavcodec/wmadec.c @@ -585,10 +585,16 @@ static int wma_decode_block(WMACodecContext *s) decode_exp_lsp(s, ch); } s->exponents_bsize[ch] = bsize; + s->exponents_initialized[ch] = 1; } } } + for (ch = 0; ch < s->avctx->channels; ch++) { + if (s->channel_coded[ch] && !s->exponents_initialized[ch]) + return AVERROR_INVALIDDATA; + } + /* parse spectral coefficients : just RLE encoding */ for (ch = 0; ch < s->avctx->channels; ch++) { if (s->channel_coded[ch]) { @@ -889,11 +895,11 @@ static int wma_decode_superframe(AVCodecContext *avctx, void *data, q = s->last_superframe + s->last_superframe_len; len = bit_offset; while (len > 7) { - *q++ = (get_bits) (&s->gb, 8); + *q++ = get_bits(&s->gb, 8); len -= 8; } if (len > 0) - *q++ = (get_bits) (&s->gb, len) << (8 - len); + *q++ = get_bits(&s->gb, len) << (8 - len); memset(q, 0, AV_INPUT_BUFFER_PADDING_SIZE); /* XXX: bit_offset bits into last frame */ diff --git a/libavcodec/wmalosslessdec.c b/libavcodec/wmalosslessdec.c index eb1db615ae1..725e811070d 100644 --- a/libavcodec/wmalosslessdec.c +++ b/libavcodec/wmalosslessdec.c @@ -164,7 +164,7 @@ typedef struct WmallDecodeCtx { int transient_pos[WMALL_MAX_CHANNELS]; int seekable_tile; - int ave_sum[WMALL_MAX_CHANNELS]; + unsigned ave_sum[WMALL_MAX_CHANNELS]; int channel_residues[WMALL_MAX_CHANNELS][WMALL_BLOCK_MAX_SIZE]; @@ -184,11 +184,18 @@ static av_cold int decode_init(AVCodecContext *avctx) unsigned int channel_mask; int i, log2_max_num_subframes; - if (!avctx->block_align) { - av_log(avctx, AV_LOG_ERROR, "block_align is not set\n"); + if (avctx->block_align <= 0 || avctx->block_align > (1<<21)) { + av_log(avctx, AV_LOG_ERROR, "block_align is not set or invalid\n"); return AVERROR(EINVAL); } + av_assert0(avctx->channels >= 0); + if (avctx->channels > WMALL_MAX_CHANNELS) { + avpriv_request_sample(avctx, + "More than " AV_STRINGIFY(WMALL_MAX_CHANNELS) " channels"); + return AVERROR_PATCHWELCOME; + } + s->max_frame_size = MAX_FRAMESIZE * avctx->channels; s->frame_data = av_mallocz(s->max_frame_size + AV_INPUT_BUFFER_PADDING_SIZE); if (!s->frame_data) @@ -267,16 +274,6 @@ static av_cold int decode_init(AVCodecContext *avctx) ++s->lfe_channel; } - if (s->num_channels < 0) { - av_log(avctx, AV_LOG_ERROR, "invalid number of channels %"PRId8"\n", - s->num_channels); - return AVERROR_INVALIDDATA; - } else if (s->num_channels > WMALL_MAX_CHANNELS) { - avpriv_request_sample(avctx, - "More than %d channels", WMALL_MAX_CHANNELS); - return AVERROR_PATCHWELCOME; - } - s->frame = av_frame_alloc(); if (!s->frame) return AVERROR(ENOMEM); @@ -535,7 +532,8 @@ static int decode_channel_residues(WmallDecodeCtx *s, int ch, int tile_size) i++; } for (; i < tile_size; i++) { - int quo = 0, rem, rem_bits, residue; + int rem, rem_bits; + unsigned quo = 0, residue; while(get_bits1(&s->gb)) { quo++; if (get_bits_left(&s->gb) <= 0) @@ -628,7 +626,7 @@ static void mclms_update(WmallDecodeCtx *s, int icoef, int *pred) int range = 1 << (s->bits_per_sample - 1); for (ich = 0; ich < num_channels; ich++) { - pred_error = s->channel_residues[ich][icoef] - pred[ich]; + pred_error = s->channel_residues[ich][icoef] - (unsigned)pred[ich]; if (pred_error > 0) { for (i = 0; i < order * num_channels; i++) s->mclms_coeffs[i + ich * order * num_channels] += @@ -678,9 +676,9 @@ static void mclms_predict(WmallDecodeCtx *s, int icoef, int *pred) for (i = 0; i < ich; i++) pred[ich] += (uint32_t)s->channel_residues[i][icoef] * s->mclms_coeffs_cur[i + num_channels * ich]; - pred[ich] += 1 << s->mclms_scaling - 1; + pred[ich] += (1U << s->mclms_scaling) >> 1; pred[ich] >>= s->mclms_scaling; - s->channel_residues[ich][icoef] += pred[ich]; + s->channel_residues[ich][icoef] += (unsigned)pred[ich]; } } @@ -760,13 +758,14 @@ static void lms_update ## bits (WmallDecodeCtx *s, int ich, int ilms, int input) static void revert_cdlms ## bits (WmallDecodeCtx *s, int ch, \ int coef_begin, int coef_end) \ { \ - int icoef, pred, ilms, num_lms, residue, input; \ + int icoef, ilms, num_lms, residue, input; \ + unsigned pred;\ \ num_lms = s->cdlms_ttl[ch]; \ for (ilms = num_lms - 1; ilms >= 0; ilms--) { \ for (icoef = coef_begin; icoef < coef_end; icoef++) { \ int##bits##_t *prevvalues = (int##bits##_t *)s->cdlms[ch][ilms].lms_prevvalues; \ - pred = 1 << (s->cdlms[ch][ilms].scaling - 1); \ + pred = (1 << s->cdlms[ch][ilms].scaling) >> 1; \ residue = s->channel_residues[ch][icoef]; \ pred += s->dsp.scalarproduct_and_madd_int## bits (s->cdlms[ch][ilms].coefs, \ prevvalues + s->cdlms[ch][ilms].recent, \ @@ -774,7 +773,7 @@ static void revert_cdlms ## bits (WmallDecodeCtx *s, int ch, \ s->cdlms[ch][ilms].recent, \ FFALIGN(s->cdlms[ch][ilms].order, ROUND), \ WMASIGN(residue)); \ - input = residue + (pred >> s->cdlms[ch][ilms].scaling); \ + input = residue + (unsigned)((int)pred >> s->cdlms[ch][ilms].scaling); \ lms_update ## bits(s, ch, ilms, input); \ s->channel_residues[ch][icoef] = input; \ } \ @@ -792,8 +791,8 @@ static void revert_inter_ch_decorr(WmallDecodeCtx *s, int tile_size) else if (s->is_channel_coded[0] || s->is_channel_coded[1]) { int icoef; for (icoef = 0; icoef < tile_size; icoef++) { - s->channel_residues[0][icoef] -= s->channel_residues[1][icoef] >> 1; - s->channel_residues[1][icoef] += s->channel_residues[0][icoef]; + s->channel_residues[0][icoef] -= (unsigned)(s->channel_residues[1][icoef] >> 1); + s->channel_residues[1][icoef] += (unsigned) s->channel_residues[0][icoef]; } } } @@ -811,22 +810,25 @@ static void revert_acfilter(WmallDecodeCtx *s, int tile_size) pred = 0; for (j = 0; j < order; j++) { if (i <= j) - pred += filter_coeffs[j] * prevvalues[j - i]; + pred += (uint32_t)filter_coeffs[j] * prevvalues[j - i]; else - pred += s->channel_residues[ich][i - j - 1] * filter_coeffs[j]; + pred += (uint32_t)s->channel_residues[ich][i - j - 1] * filter_coeffs[j]; } pred >>= scaling; - s->channel_residues[ich][i] += pred; + s->channel_residues[ich][i] += (unsigned)pred; } for (i = order; i < tile_size; i++) { pred = 0; for (j = 0; j < order; j++) pred += (uint32_t)s->channel_residues[ich][i - j - 1] * filter_coeffs[j]; pred >>= scaling; - s->channel_residues[ich][i] += pred; + s->channel_residues[ich][i] += (unsigned)pred; } - for (j = 0; j < order; j++) - prevvalues[j] = s->channel_residues[ich][tile_size - j - 1]; + for (j = order - 1; j >= 0; j--) + if (tile_size <= j) { + prevvalues[j] = prevvalues[j - tile_size]; + }else + prevvalues[j] = s->channel_residues[ich][tile_size - j - 1]; } } @@ -950,6 +952,8 @@ static int decode_subframe(WmallDecodeCtx *s) for (j = 0; j < subframe_len; j++) s->channel_residues[i][j] = get_sbits_long(&s->gb, bits); } else { + if (s->bits_per_sample < padding_zeroes) + return AVERROR_INVALIDDATA; for (i = 0; i < s->num_channels; i++) { if (s->is_channel_coded[i]) { decode_channel_residues(s, i, subframe_len); @@ -977,7 +981,7 @@ static int decode_subframe(WmallDecodeCtx *s) if (s->quant_stepsize != 1) for (i = 0; i < s->num_channels; i++) for (j = 0; j < subframe_len; j++) - s->channel_residues[i][j] *= s->quant_stepsize; + s->channel_residues[i][j] *= (unsigned)s->quant_stepsize; } /* Write to proper output buffer depending on bit-depth */ @@ -987,9 +991,9 @@ static int decode_subframe(WmallDecodeCtx *s) for (j = 0; j < subframe_len; j++) { if (s->bits_per_sample == 16) { - *s->samples_16[c]++ = (int16_t) s->channel_residues[c][j] << padding_zeroes; + *s->samples_16[c]++ = (int16_t) s->channel_residues[c][j] * (1 << padding_zeroes); } else { - *s->samples_32[c]++ = s->channel_residues[c][j] << (padding_zeroes + 8); + *s->samples_32[c]++ = s->channel_residues[c][j] * (256U << padding_zeroes); } } } @@ -1327,6 +1331,7 @@ AVCodec ff_wmalossless_decoder = { .decode = decode_packet, .flush = flush, .capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_NONE }, diff --git a/libavcodec/wmaprodec.c b/libavcodec/wmaprodec.c index d0fa974c80b..cbf5fa7fd55 100644 --- a/libavcodec/wmaprodec.c +++ b/libavcodec/wmaprodec.c @@ -437,7 +437,7 @@ static av_cold int decode_init(WMAProDecodeCtx *s, AVCodecContext *avctx, int nu av_log(avctx, AV_LOG_ERROR, "invalid number of channels per XMA stream %d\n", s->nb_channels); return AVERROR_INVALIDDATA; - } else if (s->nb_channels > WMAPRO_MAX_CHANNELS) { + } else if (s->nb_channels > WMAPRO_MAX_CHANNELS || s->nb_channels > avctx->channels) { avpriv_request_sample(avctx, "More than %d channels", WMAPRO_MAX_CHANNELS); return AVERROR_PATCHWELCOME; @@ -544,7 +544,7 @@ static av_cold int decode_init(WMAProDecodeCtx *s, AVCodecContext *avctx, int nu for (i = 0; i < WMAPRO_BLOCK_SIZES; i++) ff_mdct_init(&s->mdct_ctx[i], WMAPRO_BLOCK_MIN_BITS+1+i, 1, 1.0 / (1 << (WMAPRO_BLOCK_MIN_BITS + i - 1)) - / (1 << (s->bits_per_sample - 1))); + / (1ll << (s->bits_per_sample - 1))); /** init MDCT windows: simple sine window */ for (i = 0; i < WMAPRO_BLOCK_SIZES; i++) { @@ -1565,9 +1565,9 @@ static void save_bits(WMAProDecodeCtx *s, GetBitContext* gb, int len, s->frame_offset = get_bits_count(gb) & 7; s->num_saved_bits = s->frame_offset; init_put_bits(&s->pb, s->frame_data, MAX_FRAMESIZE); - } - - buflen = (put_bits_count(&s->pb) + len + 8) >> 3; + buflen = (s->num_saved_bits + len + 7) >> 3; + } else + buflen = (put_bits_count(&s->pb) + len + 7) >> 3; if (len <= 0 || buflen > MAX_FRAMESIZE) { avpriv_request_sample(s->avctx, "Too small input buffer"); @@ -1644,6 +1644,7 @@ static int decode_packet(AVCodecContext *avctx, WMAProDecodeCtx *s, if (avctx->codec_id == AV_CODEC_ID_WMAPRO && buf_size < avctx->block_align) { av_log(avctx, AV_LOG_ERROR, "Input packet too small (%d < %d)\n", buf_size, avctx->block_align); + s->packet_loss = 1; return AVERROR_INVALIDDATA; } @@ -1793,10 +1794,21 @@ static int xma_decode_packet(AVCodecContext *avctx, void *data, AVFrame *frame = data; int i, ret, offset = INT_MAX; + if (!s->frames[s->current_stream]->data[0]) { + s->frames[s->current_stream]->nb_samples = 512; + if ((ret = ff_get_buffer(avctx, s->frames[s->current_stream], 0)) < 0) { + return ret; + } + } /* decode current stream packet */ ret = decode_packet(avctx, &s->xma[s->current_stream], s->frames[s->current_stream], &got_stream_frame_ptr, avpkt); + if (got_stream_frame_ptr && s->offset[s->current_stream] >= 64) { + got_stream_frame_ptr = 0; + ret = AVERROR_INVALIDDATA; + } + /* copy stream samples (1/2ch) to sample buffer (Nch) */ if (got_stream_frame_ptr) { int start_ch = s->start_channel[s->current_stream]; @@ -1888,12 +1900,14 @@ static av_cold int xma_decode_init(AVCodecContext *avctx) s->num_streams = avctx->extradata[1]; if (avctx->extradata_size != (32 + ((avctx->extradata[0]==3)?0:8) + 4*s->num_streams)) { av_log(avctx, AV_LOG_ERROR, "Incorrect XMA2 extradata size\n"); + s->num_streams = 0; return AVERROR(EINVAL); } } else if (avctx->codec_id == AV_CODEC_ID_XMA1 && avctx->extradata_size >= 4) { /* XMAWAVEFORMAT */ s->num_streams = avctx->extradata[4]; if (avctx->extradata_size != (8 + 20*s->num_streams)) { av_log(avctx, AV_LOG_ERROR, "Incorrect XMA1 extradata size\n"); + s->num_streams = 0; return AVERROR(EINVAL); } } else { @@ -1902,8 +1916,11 @@ static av_cold int xma_decode_init(AVCodecContext *avctx) } /* encoder supports up to 64 streams / 64*2 channels (would have to alloc arrays) */ - if (avctx->channels > XMA_MAX_CHANNELS || s->num_streams > XMA_MAX_STREAMS) { + if (avctx->channels > XMA_MAX_CHANNELS || s->num_streams > XMA_MAX_STREAMS || + s->num_streams <= 0 + ) { avpriv_request_sample(avctx, "More than %d channels in %d streams", XMA_MAX_CHANNELS, s->num_streams); + s->num_streams = 0; return AVERROR_PATCHWELCOME; } @@ -1915,14 +1932,12 @@ static av_cold int xma_decode_init(AVCodecContext *avctx) s->frames[i] = av_frame_alloc(); if (!s->frames[i]) return AVERROR(ENOMEM); - s->frames[i]->nb_samples = 512; - if ((ret = ff_get_buffer(avctx, s->frames[i], 0)) < 0) { - return AVERROR(ENOMEM); - } s->start_channel[i] = start_channels; start_channels += s->xma[i].nb_channels; } + if (start_channels != avctx->channels) + return AVERROR_INVALIDDATA; return ret; } @@ -1936,6 +1951,7 @@ static av_cold int xma_decode_end(AVCodecContext *avctx) decode_end(&s->xma[i]); av_frame_free(&s->frames[i]); } + s->num_streams = 0; return 0; } @@ -1991,6 +2007,7 @@ AVCodec ff_wmapro_decoder = { .close = wmapro_decode_end, .decode = wmapro_decode_packet, .capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .flush = wmapro_flush, .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, @@ -2006,6 +2023,7 @@ AVCodec ff_xma1_decoder = { .close = xma_decode_end, .decode = xma_decode_packet, .capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, }; @@ -2021,6 +2039,7 @@ AVCodec ff_xma2_decoder = { .decode = xma_decode_packet, .flush = xma_flush, .capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, }; diff --git a/libavcodec/wmavoice.c b/libavcodec/wmavoice.c index 68bb65986e3..f6550c6a711 100644 --- a/libavcodec/wmavoice.c +++ b/libavcodec/wmavoice.c @@ -386,7 +386,7 @@ static av_cold int wmavoice_decode_init(AVCodecContext *ctx) ctx->extradata_size); return AVERROR_INVALIDDATA; } - if (ctx->block_align <= 0) { + if (ctx->block_align <= 0 || ctx->block_align > (1<<22)) { av_log(ctx, AV_LOG_ERROR, "Invalid block alignment %d.\n", ctx->block_align); return AVERROR_INVALIDDATA; } @@ -433,6 +433,9 @@ static av_cold int wmavoice_decode_init(AVCodecContext *ctx) return AVERROR_INVALIDDATA; } + if (ctx->sample_rate >= INT_MAX / (256 * 37)) + return AVERROR_INVALIDDATA; + s->min_pitch_val = ((ctx->sample_rate << 8) / 400 + 50) >> 8; s->max_pitch_val = ((ctx->sample_rate << 8) * 37 / 2000 + 50) >> 8; pitch_range = s->max_pitch_val - s->min_pitch_val; @@ -633,12 +636,14 @@ static void calc_input_response(WMAVoiceContext *s, float *lpcs, for (n = 0; n <= 64; n++) { float pwr; - idx = FFMAX(0, lrint((max - lpcs[n]) * irange) - 1); + idx = lrint((max - lpcs[n]) * irange - 1); + idx = FFMAX(0, idx); pwr = wmavoice_denoise_power_table[s->denoise_strength][idx]; lpcs[n] = angle_mul * pwr; /* 70.57 =~ 1/log10(1.0331663) */ - idx = (pwr * gain_mul - 0.0295) * 70.570526123; + idx = av_clipf((pwr * gain_mul - 0.0295) * 70.570526123, 0, INT_MAX / 2); + if (idx > 127) { // fall back if index falls outside table range coeffs[n] = wmavoice_energy_table[127] * powf(1.0331663, idx - 127); @@ -1520,7 +1525,7 @@ static int synth_frame(AVCodecContext *ctx, GetBitContext *gb, int frame_idx, /* "pitch-diff-per-sample" for calculation of pitch per sample */ s->pitch_diff_sh16 = - ((cur_pitch_val - s->last_pitch_val) << 16) / MAX_FRAMESIZE; + (cur_pitch_val - s->last_pitch_val) * (1 << 16) / MAX_FRAMESIZE; } /* Global gain (if silence) and pitch-adaptive window coordinates */ @@ -1840,6 +1845,9 @@ static int parse_packet_header(WMAVoiceContext *s) skip_bits(gb, 4); // packet sequence number s->has_residual_lsps = get_bits1(gb); do { + if (get_bits_left(gb) < 6 + s->spillover_bitsize) + return AVERROR_INVALIDDATA; + res = get_bits(gb, 6); // number of superframes per packet // (minus first one if there is spillover) n_superframes += res; @@ -1998,5 +2006,6 @@ AVCodec ff_wmavoice_decoder = { .close = wmavoice_decode_end, .decode = wmavoice_decode_packet, .capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .flush = wmavoice_flush, }; diff --git a/libavcodec/wmv2enc.c b/libavcodec/wmv2enc.c index 74ae12bbf74..312dbc08341 100644 --- a/libavcodec/wmv2enc.c +++ b/libavcodec/wmv2enc.c @@ -231,6 +231,7 @@ AVCodec ff_wmv2_encoder = { .init = wmv2_encode_init, .encode2 = ff_mpv_encode_picture, .close = ff_mpv_encode_end, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, }; diff --git a/libavcodec/x86/diracdsp.asm b/libavcodec/x86/diracdsp.asm index cc8a26fca5f..17145baf874 100644 --- a/libavcodec/x86/diracdsp.asm +++ b/libavcodec/x86/diracdsp.asm @@ -274,7 +274,7 @@ cglobal dequant_subband_32, 7, 7, 4, src, dst, stride, qf, qs, tot_v, tot_h movd m3, qsd SPLATD m2 SPLATD m3 - mov r4, tot_hq + mov r4d, tot_hd mov r3, dstq .loop_v: @@ -294,8 +294,9 @@ cglobal dequant_subband_32, 7, 7, 4, src, dst, stride, qf, qs, tot_v, tot_h add srcq, mmsize add dstq, mmsize - sub tot_hd, 4 + sub tot_hq, 4 jg .loop_h + lea srcq, [srcq + 4*tot_hq] add r3, strideq dec tot_vd diff --git a/libavcodec/x86/hevc_add_res.asm b/libavcodec/x86/hevc_add_res.asm index 36d4d8e2e2e..c6c40078a0f 100644 --- a/libavcodec/x86/hevc_add_res.asm +++ b/libavcodec/x86/hevc_add_res.asm @@ -30,27 +30,26 @@ cextern pw_1023 %macro ADD_RES_MMX_4_8 0 mova m0, [r1] mova m2, [r1+8] - pxor m1, m1 - pxor m3, m3 - psubw m1, m0 - psubw m3, m2 - packuswb m0, m2 - packuswb m1, m3 - - movd m2, [r0] + + movd m1, [r0] movd m3, [r0+r2] - punpckldq m2, m3 - paddusb m0, m2 - psubusb m0, m1 + punpcklbw m1, m4 + punpcklbw m3, m4 + + paddsw m0, m1 + paddsw m2, m3 + packuswb m0, m4 + packuswb m2, m4 + movd [r0], m0 - psrlq m0, 32 - movd [r0+r2], m0 + movd [r0+r2], m2 %endmacro INIT_MMX mmxext ; void ff_hevc_add_residual_4_8_mmxext(uint8_t *dst, int16_t *res, ptrdiff_t stride) cglobal hevc_add_residual_4_8, 3, 3, 6 + pxor m4, m4 ADD_RES_MMX_4_8 add r1, 16 lea r0, [r0+r2*2] @@ -58,69 +57,70 @@ cglobal hevc_add_residual_4_8, 3, 3, 6 RET %macro ADD_RES_SSE_8_8 0 - pxor m3, m3 - mova m4, [r1] - mova m6, [r1+16] - mova m0, [r1+32] - mova m2, [r1+48] - psubw m5, m3, m4 - psubw m7, m3, m6 - psubw m1, m3, m0 - packuswb m4, m0 - packuswb m5, m1 - psubw m3, m2 - packuswb m6, m2 - packuswb m7, m3 - movq m0, [r0] movq m1, [r0+r2] - movhps m0, [r0+r2*2] - movhps m1, [r0+r3] - paddusb m0, m4 - paddusb m1, m6 - psubusb m0, m5 - psubusb m1, m7 + punpcklbw m0, m4 + punpcklbw m1, m4 + mova m2, [r1] + mova m3, [r1+16] + paddsw m0, m2 + paddsw m1, m3 + packuswb m0, m1 + + movq m2, [r0+r2*2] + movq m3, [r0+r3] + punpcklbw m2, m4 + punpcklbw m3, m4 + mova m6, [r1+32] + mova m7, [r1+48] + paddsw m2, m6 + paddsw m3, m7 + packuswb m2, m3 + movq [r0], m0 - movq [r0+r2], m1 - movhps [r0+2*r2], m0 - movhps [r0+r3], m1 + movhps [r0+r2], m0 + movq [r0+r2*2], m2 + movhps [r0+r3], m2 %endmacro %macro ADD_RES_SSE_16_32_8 3 - mova xm2, [r1+%1] + mova m1, [%2] + mova m2, m1 + punpcklbw m1, m0 + punpckhbw m2, m0 + mova xm5, [r1+%1] mova xm6, [r1+%1+16] %if cpuflag(avx2) - vinserti128 m2, m2, [r1+%1+32], 1 + vinserti128 m5, m5, [r1+%1+32], 1 vinserti128 m6, m6, [r1+%1+48], 1 %endif - psubw m1, m0, m2 - psubw m5, m0, m6 - packuswb m2, m6 - packuswb m1, m5 - - mova xm4, [r1+%1+mmsize*2] + paddsw m1, m5 + paddsw m2, m6 + + mova m3, [%3] + mova m4, m3 + punpcklbw m3, m0 + punpckhbw m4, m0 + mova xm5, [r1+%1+mmsize*2] mova xm6, [r1+%1+mmsize*2+16] %if cpuflag(avx2) - vinserti128 m4, m4, [r1+%1+96 ], 1 + vinserti128 m5, m5, [r1+%1+96], 1 vinserti128 m6, m6, [r1+%1+112], 1 %endif - psubw m3, m0, m4 - psubw m5, m0, m6 - packuswb m4, m6 - packuswb m3, m5 - - paddusb m2, [%2] - paddusb m4, [%3] - psubusb m2, m1 - psubusb m4, m3 - mova [%2], m2 - mova [%3], m4 + paddsw m3, m5 + paddsw m4, m6 + + packuswb m1, m2 + packuswb m3, m4 + mova [%2], m1 + mova [%3], m3 %endmacro %macro TRANSFORM_ADD_8 0 ; void ff_hevc_add_residual_8_8_(uint8_t *dst, int16_t *res, ptrdiff_t stride) cglobal hevc_add_residual_8_8, 3, 4, 8 + pxor m4, m4 lea r3, [r2*3] ADD_RES_SSE_8_8 add r1, 64 diff --git a/libavcodec/x86/opus_pvq_search.asm b/libavcodec/x86/opus_pvq_search.asm deleted file mode 100644 index 5c1e6d6174c..00000000000 --- a/libavcodec/x86/opus_pvq_search.asm +++ /dev/null @@ -1,385 +0,0 @@ -;****************************************************************************** -;* SIMD optimized Opus encoder DSP function -;* -;* Copyright (C) 2017 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 -;****************************************************************************** - -%include "config.asm" -%include "libavutil/x86/x86util.asm" - -%ifdef __NASM_VER__ -%use "smartalign" -ALIGNMODE p6 -%endif - -SECTION_RODATA 64 - -const_float_abs_mask: times 8 dd 0x7fffffff -const_align_abs_edge: times 8 dd 0 - -const_float_0_5: times 8 dd 0.5 -const_float_1: times 8 dd 1.0 -const_float_sign_mask: times 8 dd 0x80000000 - -const_int32_offsets: - %rep 8 - dd $-const_int32_offsets - %endrep -SECTION .text - -; -; Setup High Register to be used -; for holding memory constants -; -; %1 - the register to be used, assmues it is >= mm8 -; %2 - name of the constant. -; -; Subsequent opcodes are going to use the constant in the form -; "addps m0, mm_const_name" and it would be turned into: -; "addps m0, [const_name]" on 32 bit arch or -; "addps m0, m8" on 64 bit arch -%macro SET_HI_REG_MM_CONSTANT 3 ; movop, reg, const_name -%if num_mmregs > 8 - %define mm_%3 %2 - %{1} %2, [%3] ; movaps m8, [const_name] -%else - %define mm_%3 [%3] -%endif -%endmacro - -; -; Set Position Independent Code -; Base address of a constant -; %1 - the register to be used, if PIC is set -; %2 - name of the constant. -; -; Subsequent opcode are going to use the base address in the form -; "movaps m0, [pic_base_constant_name+r4]" and it would be turned into -; "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 - %{1} %2, [%3] ; lea r5, [rip+const] - %define pic_base_%3 %2 -%else - %define pic_base_%3 %3 -%endif -%endmacro - -%macro PULSES_SEARCH 1 -; m6 Syy_norm -; m7 Sxy_norm - addps m6, mm_const_float_0_5 ; Syy_norm += 1.0/2 - pxor m1, m1 ; max_idx - xorps m3, m3 ; p_max - xor r4d, r4d -align 16 -%%distortion_search: - movd xm2, dword r4d ; movd zero extends -%ifidn %1,add - movaps m4, [tmpY + r4] ; y[i] - movaps m5, [tmpX + r4] ; X[i] - - %if USE_APPROXIMATION == 1 - xorps m0, m0 - cmpps m0, m0, m5, 4 ; m0 = (X[i] != 0.0) - %endif - - addps m4, m6 ; m4 = Syy_new = y[i] + Syy_norm - addps m5, m7 ; m5 = Sxy_new = X[i] + Sxy_norm - - %if USE_APPROXIMATION == 1 - andps m5, m0 ; if(X[i] == 0) Sxy_new = 0; Prevent aproximation error from setting pulses in array padding. - %endif - -%else - movaps m5, [tmpY + r4] ; m5 = y[i] - - xorps m0, m0 ; m0 = 0; - cmpps m0, m0, m5, 1 ; m0 = (0 p_max) - maxps m3, m5 ; m3=max(p_max,p) - ; maxps here is faster than blendvps, despite blend having lower latency. - - pand m2, m0 ; This version seems faster than sse41 pblendvb - pmaxsw m1, m2 ; SSE2 signed word, so it would work for N < 32768/4 - - add r4d, mmsize - cmp r4d, Nd - jb %%distortion_search - - por m1, mm_const_int32_offsets ; max_idx offsets per individual lane (skipped in the inner loop) - movdqa m4, m1 ; needed for the aligned y[max_idx]+=1; processing - -%if mmsize >= 32 -; Merge parallel maximums round 8 (4 vs 4) - - vextractf128 xm5, ym3, 1 ; xmm5 = ymm3[1x128] = ymm3[255..128b] - cmpps xm0, xm3, xm5, 1 ; m0 = (m3 < m5) = ( p[0x128] < p[1x128] ) - - vextracti128 xm2, ym1, 1 ; xmm2 = ymm1[1x128] = ymm1[255..128b] - BLENDVPS xm3, xm5, xm0 ; max_idx = m0 ? max_idx[1x128] : max_idx[0x128] - PBLENDVB xm1, xm2, xm0 ; p = m0 ? p[1x128] : p[0x128] -%endif - -; Merge parallel maximums round 4 (2 vs 2) - ; m3=p[3210] - movhlps xm5, xm3 ; m5=p[xx32] - cmpps xm0, xm3, xm5, 1 ; m0 = (m3 < m5) = ( p[1,0] < p[3,2] ) - - pshufd xm2, xm1, q3232 - BLENDVPS xm3, xm5, xm0 ; max_idx = m0 ? max_idx[3,2] : max_idx[1,0] - PBLENDVB xm1, xm2, xm0 ; p = m0 ? p[3,2] : p[1,0] - -; Merge parallel maximums final round (1 vs 1) - shufps xm0, xm3, xm3, q1111 ; m0 = m3[1] = p[1] - cmpss xm0, xm3, 5 ; m0 = !(m0 >= m3) = !( p[1] >= p[0] ) - - pshufd xm2, xm1, q1111 - PBLENDVB xm1, xm2, xm0 - - movd dword r4d, xm1 ; zero extends to the rest of r4q - - VBROADCASTSS m3, [tmpX + r4] - %{1}ps m7, m3 ; Sxy += X[max_idx] - - VBROADCASTSS m5, [tmpY + r4] - %{1}ps m6, m5 ; Syy += Y[max_idx] - - ; We have to update a single element in Y[i] - ; However writing 4 bytes and then doing 16 byte load in the inner loop - ; could cause a stall due to breaking write forwarding. - VPBROADCASTD m1, xm1 - pcmpeqd m1, m1, m4 ; exactly 1 element matches max_idx and this finds it - - and r4d, ~(mmsize-1) ; align address down, so the value pointed by max_idx is inside a mmsize load - movaps m5, [tmpY + r4] ; m5 = Y[y3...ym...y0] - andps m1, mm_const_float_1 ; m1 = [ 0...1.0...0] - %{1}ps m5, m1 ; m5 = Y[y3...ym...y0] +/- [0...1.0...0] - movaps [tmpY + r4], m5 ; Y[max_idx] +-= 1.0; -%endmacro - -; -; We need one more register for -; PIC relative addressing. Use this -; to count it in cglobal -; -%ifdef PIC - %define num_pic_regs 1 -%else - %define num_pic_regs 0 -%endif - -; -; Pyramid Vector Quantization Search implementation -; -; float * inX - Unaligned (SIMD) access, it will be overread, -; but extra data is masked away. -; int32 * outY - Should be aligned and padded buffer. -; It is used as temp buffer. -; uint32 K - Number of pulses to have after quantizations. -; uint32 N - Number of vector elements. Must be 0 < N < 256 -; -%macro PVQ_FAST_SEARCH 1 -cglobal pvq_search%1, 4, 5+num_pic_regs, 11, 256*4, inX, outY, K, N -%define tmpX rsp -%define tmpY outYq - - movaps m0, [const_float_abs_mask] - shl Nd, 2 ; N *= sizeof(float); also 32 bit operation zeroes the high 32 bits in 64 bit mode. - mov r4d, Nd - - neg r4d - and r4d, mmsize-1 - - SET_PIC_BASE lea, r5, const_align_abs_edge ; rip+const - movups m2, [pic_base_const_align_abs_edge + r4 - mmsize] - - add Nd, r4d ; N = align(N, mmsize) - - lea r4d, [Nd - mmsize] ; N is rounded up (aligned up) to mmsize, so r4 can't become negative here, unless N=0. - movups m1, [inXq + r4] - andps m1, m2 - movaps [tmpX + r4], m1 ; Sx = abs( X[N-1] ) - -align 16 -%%loop_abs_sum: - sub r4d, mmsize - jc %%end_loop_abs_sum - - movups m2, [inXq + r4] - andps m2, m0 - - movaps [tmpX + r4], m2 ; tmpX[i]=abs(X[i]) - addps m1, m2 ; Sx += abs(X[i]) - jmp %%loop_abs_sum - -align 16 -%%end_loop_abs_sum: - - HSUMPS m1, m2 ; m1 = Sx - - xorps m0, m0 - comiss xm0, xm1 ; - jz %%zero_input ; if (Sx==0) goto zero_input - - cvtsi2ss xm0, dword Kd ; m0 = K -%if USE_APPROXIMATION == 1 - rcpss xm1, xm1 ; m1 = approx(1/Sx) - mulss xm0, xm1 ; m0 = K*(1/Sx) -%else - divss xm0, xm1 ; b = K/Sx - ; b = K/max_x -%endif - - VBROADCASTSS m0, xm0 - - lea r4d, [Nd - mmsize] - pxor m5, m5 ; Sy ( Sum of abs( y[i]) ) - xorps m6, m6 ; Syy ( Sum of y[i]*y[i] ) - xorps m7, m7 ; Sxy ( Sum of X[i]*y[i] ) -align 16 -%%loop_guess: - movaps m1, [tmpX + r4] ; m1 = X[i] - mulps m2, m0, m1 ; m2 = res*X[i] - cvtps2dq m2, m2 ; yt = (int)lrintf( res*X[i] ) - paddd m5, m2 ; Sy += yt - cvtdq2ps m2, m2 ; yt = (float)yt - mulps m1, m2 ; m1 = X[i]*yt - movaps [tmpY + r4], m2 ; y[i] = m2 - addps m7, m1 ; Sxy += m1; - mulps m2, m2 ; m2 = yt*yt - addps m6, m2 ; Syy += m2 - - sub r4d, mmsize - jnc %%loop_guess - - HSUMPS m6, m1 ; Syy_norm - HADDD m5, m4 ; pulses - - movd dword r4d, xm5 ; zero extends to the rest of r4q - - sub Kd, r4d ; K -= pulses , also 32 bit operation zeroes high 32 bit in 64 bit mode. - jz %%finish ; K - pulses == 0 - - SET_HI_REG_MM_CONSTANT movaps, m8, const_float_0_5 - SET_HI_REG_MM_CONSTANT movaps, m9, const_float_1 - SET_HI_REG_MM_CONSTANT movdqa, m10, const_int32_offsets - ; Use Syy/2 in distortion parameter calculations. - ; Saves pre and post-caclulation to correct Y[] values. - ; Same precision, since float mantisa is normalized. - ; The SQRT approximation does differ. - HSUMPS m7, m0 ; Sxy_norm - mulps m6, mm_const_float_0_5 - - jc %%remove_pulses_loop ; K - pulses < 0 - -align 16 ; K - pulses > 0 -%%add_pulses_loop: - - PULSES_SEARCH add ; m6 Syy_norm ; m7 Sxy_norm - - sub Kd, 1 - jnz %%add_pulses_loop - - addps m6, m6 ; Syy*=2 - - jmp %%finish - -align 16 -%%remove_pulses_loop: - - PULSES_SEARCH sub ; m6 Syy_norm ; m7 Sxy_norm - - add Kd, 1 - jnz %%remove_pulses_loop - - addps m6, m6 ; Syy*=2 - -align 16 -%%finish: - lea r4d, [Nd - mmsize] - movaps m2, [const_float_sign_mask] - -align 16 -%%restore_sign_loop: - movaps m0, [tmpY + r4] ; m0 = Y[i] - movups m1, [inXq + r4] ; m1 = X[i] - andps m1, m2 ; m1 = sign(X[i]) - orps m0, m1 ; m0 = Y[i]*sign - cvtps2dq m3, m0 ; m3 = (int)m0 - movaps [outYq + r4], m3 - - sub r4d, mmsize - jnc %%restore_sign_loop -%%return: - -%if ARCH_X86_64 == 0 ; sbrdsp - movss r0m, xm6 ; return (float)Syy_norm - fld dword r0m -%else - movaps m0, m6 ; return (float)Syy_norm -%endif - - RET - -align 16 -%%zero_input: - lea r4d, [Nd - mmsize] - xorps m0, m0 -%%zero_loop: - movaps [outYq + r4], m0 - sub r4d, mmsize - jnc %%zero_loop - - movaps m6, [const_float_1] - jmp %%return -%endmacro - -; if 1, use a float op that give half precision but execute for around 3 cycles. -; On Skylake & Ryzen the division is much faster (around 11c/3), -; that makes the full precision code about 2% slower. -; Opus also does use rsqrt approximation in their intrinsics code. -%define USE_APPROXIMATION 1 - -INIT_XMM sse2 -PVQ_FAST_SEARCH _approx - -INIT_XMM sse4 -PVQ_FAST_SEARCH _approx - -%define USE_APPROXIMATION 0 - -INIT_XMM avx -PVQ_FAST_SEARCH _exact diff --git a/libavcodec/x86/opusdsp.asm b/libavcodec/x86/opusdsp.asm index f5d206a8b15..418cc163303 100644 --- a/libavcodec/x86/opusdsp.asm +++ b/libavcodec/x86/opusdsp.asm @@ -64,7 +64,7 @@ cglobal opus_deemphasis, 4, 4, 8, out, in, coeff, len add inq, mmsize add outq, mmsize - sub lenq, mmsize >> 2 + sub lend, mmsize >> 2 jg .loop %if ARCH_X86_64 == 0 @@ -80,7 +80,8 @@ cglobal opus_postfilter, 4, 4, 8, data, period, gains, len VBROADCASTSS m1, [gainsq + 4] VBROADCASTSS m2, [gainsq + 8] - lea periodq, [periodq*4 + 8] + shl periodd, 2 + add periodq, 8 neg periodq movups m3, [dataq + periodq] @@ -104,7 +105,7 @@ cglobal opus_postfilter, 4, 4, 8, data, period, gains, len movaps [dataq], m5 add dataq, mmsize - sub lenq, mmsize >> 2 + sub lend, mmsize >> 2 jg .loop RET diff --git a/libavcodec/x86/opusdsp_init.c b/libavcodec/x86/opusdsp_init.c index 6834c4e6a49..5c8a4c22e96 100644 --- a/libavcodec/x86/opusdsp_init.c +++ b/libavcodec/x86/opusdsp_init.c @@ -28,7 +28,7 @@ av_cold void ff_opus_dsp_init_x86(OpusDSP *ctx) { int cpu_flags = av_get_cpu_flags(); - if (EXTERNAL_FMA3_FAST(cpu_flags)) { + if (EXTERNAL_FMA3(cpu_flags)) { ctx->postfilter = ff_opus_postfilter_fma3; ctx->deemphasis = ff_opus_deemphasis_fma3; } diff --git a/libavcodec/x86/pixblockdsp_init.c b/libavcodec/x86/pixblockdsp_init.c index ade55e01a3a..3a5eb6959c2 100644 --- a/libavcodec/x86/pixblockdsp_init.c +++ b/libavcodec/x86/pixblockdsp_init.c @@ -37,15 +37,19 @@ av_cold void ff_pixblockdsp_init_x86(PixblockDSPContext *c, int cpu_flags = av_get_cpu_flags(); if (EXTERNAL_MMX(cpu_flags)) { - if (!high_bit_depth) + if (!high_bit_depth) { + c->get_pixels_unaligned = c->get_pixels = ff_get_pixels_mmx; + } c->diff_pixels_unaligned = c->diff_pixels = ff_diff_pixels_mmx; } if (EXTERNAL_SSE2(cpu_flags)) { - if (!high_bit_depth) + if (!high_bit_depth) { + c->get_pixels_unaligned = c->get_pixels = ff_get_pixels_sse2; + } c->diff_pixels_unaligned = c->diff_pixels = ff_diff_pixels_sse2; } diff --git a/libavcodec/x86/vp3dsp_init.c b/libavcodec/x86/vp3dsp_init.c index 1ba9576431a..ba47e1c6cd9 100644 --- a/libavcodec/x86/vp3dsp_init.c +++ b/libavcodec/x86/vp3dsp_init.c @@ -59,8 +59,8 @@ av_cold void ff_vp3dsp_init_x86(VP3DSPContext *c, int flags) c->idct_dc_add = ff_vp3_idct_dc_add_mmxext; if (!(flags & AV_CODEC_FLAG_BITEXACT)) { - c->v_loop_filter = ff_vp3_v_loop_filter_mmxext; - c->h_loop_filter = ff_vp3_h_loop_filter_mmxext; + 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; } } diff --git a/libavcodec/xfaceenc.c b/libavcodec/xfaceenc.c index bfb9fb9ece8..dd5bb689c26 100644 --- a/libavcodec/xfaceenc.c +++ b/libavcodec/xfaceenc.c @@ -219,5 +219,4 @@ AVCodec ff_xface_encoder = { .priv_data_size = sizeof(XFaceContext), .encode2 = xface_encode_frame, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_MONOWHITE, AV_PIX_FMT_NONE }, - .capabilities = AV_CODEC_CAP_INTRA_ONLY, }; diff --git a/libavcodec/xiph.c b/libavcodec/xiph.c index d072224b4a5..3073315e634 100644 --- a/libavcodec/xiph.c +++ b/libavcodec/xiph.c @@ -35,7 +35,7 @@ int avpriv_split_xiph_headers(const uint8_t *extradata, int extradata_size, header_start[i] = extradata; extradata += header_len[i]; if (overall_len > extradata_size - header_len[i]) - return -1; + return AVERROR_INVALIDDATA; overall_len += header_len[i]; } } else if (extradata_size >= 3 && extradata_size < INT_MAX - 0x1ff && extradata[0] == 2) { @@ -50,7 +50,7 @@ int avpriv_split_xiph_headers(const uint8_t *extradata, int extradata_size, header_len[i] += *extradata; overall_len += *extradata; if (overall_len > extradata_size) - return -1; + return AVERROR_INVALIDDATA; } header_len[2] = extradata_size - overall_len; header_start[0] = extradata; diff --git a/libavcodec/xsubdec.c b/libavcodec/xsubdec.c index 93fd0f4d50e..c1c6e11dc31 100644 --- a/libavcodec/xsubdec.c +++ b/libavcodec/xsubdec.c @@ -46,7 +46,7 @@ static int64_t parse_timecode(const uint8_t *buf, int64_t packet_time) { return ms - packet_time; } -static int decode_frame(AVCodecContext *avctx, void *data, int *data_size, +static int decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr, AVPacket *avpkt) { const uint8_t *buf = avpkt->data; int buf_size = avpkt->size; @@ -130,7 +130,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *data_size, ((uint32_t *)sub->rects[0]->data[1])[i] |= 0xff000000; } else { for (i = 0; i < sub->rects[0]->nb_colors; i++) - ((uint32_t *)sub->rects[0]->data[1])[i] |= *buf++ << 24; + ((uint32_t *)sub->rects[0]->data[1])[i] |= (unsigned)*buf++ << 24; } #if FF_API_AVPICTURE @@ -169,7 +169,7 @@ FF_ENABLE_DEPRECATION_WARNINGS bitmap += w; align_get_bits(&gb); } - *data_size = 1; + *got_sub_ptr = 1; return buf_size; } diff --git a/libavcodec/xsubenc.c b/libavcodec/xsubenc.c index b3da909679d..4d58e0f3b58 100644 --- a/libavcodec/xsubenc.c +++ b/libavcodec/xsubenc.c @@ -63,7 +63,7 @@ static int xsub_encode_rle(PutBitContext *pb, const uint8_t *bitmap, while (x0 < w) { // Make sure we have enough room for at least one run and padding if (pb->size_in_bits - put_bits_count(pb) < 7*8) - return -1; + return AVERROR_BUFFER_TOO_SMALL; x1 = x0; color = bitmap[x1++] & 3; @@ -124,7 +124,7 @@ static int xsub_encode(AVCodecContext *avctx, unsigned char *buf, if (bufsize < 27 + 7*2 + 4*3) { av_log(avctx, AV_LOG_ERROR, "Buffer too small for XSUB header.\n"); - return -1; + return AVERROR_BUFFER_TOO_SMALL; } // TODO: support multiple rects @@ -147,7 +147,7 @@ FF_ENABLE_DEPRECATION_WARNINGS // TODO: render text-based subtitles into bitmaps if (!h->rects[0]->data[0] || !h->rects[0]->data[1]) { av_log(avctx, AV_LOG_WARNING, "No subtitle bitmap available.\n"); - return -1; + return AVERROR(EINVAL); } // TODO: color reduction, similar to dvdsub encoder @@ -160,7 +160,7 @@ FF_ENABLE_DEPRECATION_WARNINGS if (make_tc(startTime, start_tc) || make_tc(endTime, end_tc)) { av_log(avctx, AV_LOG_WARNING, "Time code >= 100 hours.\n"); - return -1; + return AVERROR(EINVAL); } snprintf(buf, 28, @@ -195,13 +195,13 @@ FF_ENABLE_DEPRECATION_WARNINGS if (xsub_encode_rle(&pb, h->rects[0]->data[0], h->rects[0]->linesize[0] * 2, h->rects[0]->w, (h->rects[0]->h + 1) >> 1)) - return -1; + return AVERROR_BUFFER_TOO_SMALL; bytestream_put_le16(&rlelenptr, put_bits_count(&pb) >> 3); // Length of first field if (xsub_encode_rle(&pb, h->rects[0]->data[0] + h->rects[0]->linesize[0], h->rects[0]->linesize[0] * 2, h->rects[0]->w, h->rects[0]->h >> 1)) - return -1; + return AVERROR_BUFFER_TOO_SMALL; // Enforce total height to be a multiple of 2 if (h->rects[0]->h & 1) { diff --git a/libavcodec/xvididct.c b/libavcodec/xvididct.c index d8f3dd70726..360deb3244c 100644 --- a/libavcodec/xvididct.c +++ b/libavcodec/xvididct.c @@ -115,24 +115,24 @@ static int idct_row(short *in, const int *const tab, int rnd) in[6] = a1; } else { const int k = c4 * in[0] + rnd; - const int a0 = k + c2 * in[2] + c4 * in[4] + c6 * in[6]; - const int a1 = k + c6 * in[2] - c4 * in[4] - c2 * in[6]; - const int a2 = k - c6 * in[2] - c4 * in[4] + c2 * in[6]; - const int a3 = k - c2 * in[2] + c4 * in[4] - c6 * in[6]; - - const int b0 = c1 * in[1] + c3 * in[3] + c5 * in[5] + c7 * in[7]; - const int b1 = c3 * in[1] - c7 * in[3] - c1 * in[5] - c5 * in[7]; - const int b2 = c5 * in[1] - c1 * in[3] + c7 * in[5] + c3 * in[7]; - const int b3 = c7 * in[1] - c5 * in[3] + c3 * in[5] - c1 * in[7]; - - in[0] = (a0 + b0) >> ROW_SHIFT; - in[1] = (a1 + b1) >> ROW_SHIFT; - in[2] = (a2 + b2) >> ROW_SHIFT; - in[3] = (a3 + b3) >> ROW_SHIFT; - in[4] = (a3 - b3) >> ROW_SHIFT; - in[5] = (a2 - b2) >> ROW_SHIFT; - in[6] = (a1 - b1) >> ROW_SHIFT; - in[7] = (a0 - b0) >> ROW_SHIFT; + const unsigned int a0 = k + c2 * in[2] + c4 * in[4] + c6 * in[6]; + const unsigned int a1 = k + c6 * in[2] - c4 * in[4] - c2 * in[6]; + const unsigned int a2 = k - c6 * in[2] - c4 * in[4] + c2 * in[6]; + const unsigned int a3 = k - c2 * in[2] + c4 * in[4] - c6 * in[6]; + + const unsigned int b0 = c1 * in[1] + c3 * in[3] + c5 * in[5] + c7 * in[7]; + const unsigned int b1 = c3 * in[1] - c7 * in[3] - c1 * in[5] - c5 * in[7]; + const unsigned int b2 = c5 * in[1] - c1 * in[3] + c7 * in[5] + c3 * in[7]; + const unsigned int b3 = c7 * in[1] - c5 * in[3] + c3 * in[5] - c1 * in[7]; + + in[0] = (int)(a0 + b0) >> ROW_SHIFT; + in[1] = (int)(a1 + b1) >> ROW_SHIFT; + in[2] = (int)(a2 + b2) >> ROW_SHIFT; + in[3] = (int)(a3 + b3) >> ROW_SHIFT; + in[4] = (int)(a3 - b3) >> ROW_SHIFT; + in[5] = (int)(a2 - b2) >> ROW_SHIFT; + in[6] = (int)(a1 - b1) >> ROW_SHIFT; + in[7] = (int)(a0 - b0) >> ROW_SHIFT; } return 1; } @@ -142,7 +142,7 @@ static int idct_row(short *in, const int *const tab, int rnd) #define TAN3 0xAB0E #define SQRT2 0x5A82 -#define MULT(c, x, n) (((c) * (x)) >> (n)) +#define MULT(c, x, n) ((unsigned)((int)((c) * (unsigned)(x)) >> (n))) // 12b version => #define MULT(c,x, n) ((((c) >> 3) * (x)) >> ((n) - 3)) // 12b zero-testing version: diff --git a/libavcodec/xxan.c b/libavcodec/xxan.c index 8bb7087af90..afe60e15647 100644 --- a/libavcodec/xxan.c +++ b/libavcodec/xxan.c @@ -410,7 +410,7 @@ static int xan_decode_frame(AVCodecContext *avctx, int ftype; int ret; - if ((ret = ff_reget_buffer(avctx, s->pic)) < 0) + if ((ret = ff_reget_buffer(avctx, s->pic, 0)) < 0) return ret; bytestream2_init(&s->gb, avpkt->data, avpkt->size); diff --git a/libavcodec/y41penc.c b/libavcodec/y41penc.c index ca94a3c171a..63752e2b449 100644 --- a/libavcodec/y41penc.c +++ b/libavcodec/y41penc.c @@ -90,5 +90,4 @@ AVCodec ff_y41p_encoder = { .close = y41p_encode_close, .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV411P, AV_PIX_FMT_NONE }, - .capabilities = AV_CODEC_CAP_INTRA_ONLY, }; diff --git a/libavcodec/ylc.c b/libavcodec/ylc.c index 11333222b94..2afe3fc9d5c 100644 --- a/libavcodec/ylc.c +++ b/libavcodec/ylc.c @@ -453,24 +453,6 @@ static int decode_frame(AVCodecContext *avctx, return avpkt->size; } -#if HAVE_THREADS -static int init_thread_copy(AVCodecContext *avctx) -{ - YLCContext *s = avctx->priv_data; - - memset(&s->vlc[0], 0, sizeof(VLC)); - memset(&s->vlc[1], 0, sizeof(VLC)); - memset(&s->vlc[2], 0, sizeof(VLC)); - memset(&s->vlc[3], 0, sizeof(VLC)); - s->table_bits = NULL; - s->table_bits_size = 0; - s->bitstream_bits = NULL; - s->bitstream_bits_size = 0; - - return 0; -} -#endif - static av_cold int decode_end(AVCodecContext *avctx) { YLCContext *s = avctx->priv_data; @@ -494,7 +476,6 @@ AVCodec ff_ylc_decoder = { .id = AV_CODEC_ID_YLC, .priv_data_size = sizeof(YLCContext), .init = decode_init, - .init_thread_copy = ONLY_IF_THREADS_ENABLED(init_thread_copy), .close = decode_end, .decode = decode_frame, .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, diff --git a/libavcodec/yop.c b/libavcodec/yop.c index 32cfea200a1..aff28ef3fcd 100644 --- a/libavcodec/yop.c +++ b/libavcodec/yop.c @@ -204,7 +204,7 @@ static int yop_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, return AVERROR_INVALIDDATA; } - if ((ret = ff_reget_buffer(avctx, frame)) < 0) + if ((ret = ff_reget_buffer(avctx, frame, 0)) < 0) return ret; if (!avctx->frame_number) diff --git a/libavcodec/yuv4enc.c b/libavcodec/yuv4enc.c index cc8846d7e52..f21b1f36ce0 100644 --- a/libavcodec/yuv4enc.c +++ b/libavcodec/yuv4enc.c @@ -76,5 +76,4 @@ AVCodec ff_yuv4_encoder = { .encode2 = yuv4_encode_frame, .close = yuv4_encode_close, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }, - .capabilities = AV_CODEC_CAP_INTRA_ONLY, }; diff --git a/libavcodec/zerocodec.c b/libavcodec/zerocodec.c index e67eee4f7d8..1051fdaa45d 100644 --- a/libavcodec/zerocodec.c +++ b/libavcodec/zerocodec.c @@ -131,14 +131,19 @@ static av_cold int zerocodec_decode_init(AVCodecContext *avctx) } zc->previous_frame = av_frame_alloc(); - if (!zc->previous_frame) { - zerocodec_decode_close(avctx); + if (!zc->previous_frame) return AVERROR(ENOMEM); - } return 0; } +static void zerocodec_decode_flush(AVCodecContext *avctx) +{ + ZeroCodecContext *zc = avctx->priv_data; + + av_frame_unref(zc->previous_frame); +} + AVCodec ff_zerocodec_decoder = { .type = AVMEDIA_TYPE_VIDEO, .name = "zerocodec", @@ -147,7 +152,9 @@ AVCodec ff_zerocodec_decoder = { .priv_data_size = sizeof(ZeroCodecContext), .init = zerocodec_decode_init, .decode = zerocodec_decode_frame, + .flush = zerocodec_decode_flush, .close = zerocodec_decode_close, .capabilities = AV_CODEC_CAP_DR1, - .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | + FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/zmbv.c b/libavcodec/zmbv.c index 99e735cfd93..e3ccc16d0bc 100644 --- a/libavcodec/zmbv.c +++ b/libavcodec/zmbv.c @@ -69,8 +69,8 @@ typedef struct ZmbvContext { int stride; int bw, bh, bx, by; int decomp_len; + int got_keyframe; z_stream zstream; - int (*decode_intra)(struct ZmbvContext *c); int (*decode_xor)(struct ZmbvContext *c); } ZmbvContext; @@ -425,8 +425,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac c->flags = buf[0]; buf++; len--; if (c->flags & ZMBV_KEYFRAME) { - void *decode_intra = NULL; - c->decode_intra= NULL; + c->got_keyframe = 0; if (len < 6) return AVERROR_INVALIDDATA; @@ -436,7 +435,6 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac c->fmt = buf[3]; c->bw = buf[4]; c->bh = buf[5]; - c->decode_intra = NULL; c->decode_xor = NULL; buf += 6; @@ -460,7 +458,6 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac switch (c->fmt) { case ZMBV_FMT_8BPP: c->bpp = 8; - decode_intra = zmbv_decode_intra; c->decode_xor = zmbv_decode_xor_8; avctx->pix_fmt = AV_PIX_FMT_PAL8; c->stride = c->width; @@ -468,7 +465,6 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac case ZMBV_FMT_15BPP: case ZMBV_FMT_16BPP: c->bpp = 16; - decode_intra = zmbv_decode_intra; c->decode_xor = zmbv_decode_xor_16; if (c->fmt == ZMBV_FMT_15BPP) avctx->pix_fmt = AV_PIX_FMT_RGB555LE; @@ -479,7 +475,6 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac #ifdef ZMBV_ENABLE_24BPP case ZMBV_FMT_24BPP: c->bpp = 24; - decode_intra = zmbv_decode_intra; c->decode_xor = zmbv_decode_xor_24; avctx->pix_fmt = AV_PIX_FMT_BGR24; c->stride = c->width * 3; @@ -487,7 +482,6 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac #endif //ZMBV_ENABLE_24BPP case ZMBV_FMT_32BPP: c->bpp = 32; - decode_intra = zmbv_decode_intra; c->decode_xor = zmbv_decode_xor_32; avctx->pix_fmt = AV_PIX_FMT_BGR0; c->stride = c->width * 4; @@ -517,7 +511,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac } memset(c->cur, 0, avctx->width * avctx->height * (c->bpp / 8)); memset(c->prev, 0, avctx->width * avctx->height * (c->bpp / 8)); - c->decode_intra= decode_intra; + c->got_keyframe = 1; } if (c->flags & ZMBV_KEYFRAME) { expected_size = avctx->width * avctx->height * (c->bpp / 8); @@ -528,7 +522,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac (c->flags & (ZMBV_DELTAPAL | ZMBV_KEYFRAME))) expected_size += 768; - if (!c->decode_intra) { + if (!c->got_keyframe) { av_log(avctx, AV_LOG_ERROR, "Error! Got no format or no keyframe!\n"); return AVERROR_INVALIDDATA; } @@ -542,7 +536,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac c->decomp_len = len; } else { // ZLIB-compressed data c->zstream.total_in = c->zstream.total_out = 0; - c->zstream.next_in = (uint8_t*)buf; + c->zstream.next_in = buf; c->zstream.avail_in = len; c->zstream.next_out = c->decomp_buf; c->zstream.avail_out = c->decomp_size; @@ -564,7 +558,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPac if (c->flags & ZMBV_KEYFRAME) { frame->key_frame = 1; frame->pict_type = AV_PICTURE_TYPE_I; - c->decode_intra(c); + zmbv_decode_intra(c); } else { frame->key_frame = 0; frame->pict_type = AV_PICTURE_TYPE_P; diff --git a/libavcodec/zmbvenc.c b/libavcodec/zmbvenc.c index 48871758e0a..319381dd48f 100644 --- a/libavcodec/zmbvenc.c +++ b/libavcodec/zmbvenc.c @@ -240,8 +240,8 @@ FF_ENABLE_DEPRECATION_WARNINGS tprev = prev + x * c->bypp; zmbv_me(c, tsrc, p->linesize[0], tprev, c->pstride, x, y, &mx, &my, &xored); - mv[0] = (mx << 1) | !!xored; - mv[1] = my << 1; + mv[0] = (mx * 2) | !!xored; + mv[1] = my * 2; tprev += mx * c->bypp + my * c->pstride; if(xored){ for(j = 0; j < bh2; j++){ @@ -409,7 +409,7 @@ static av_cold int encode_init(AVCodecContext *avctx) */ c->pstride = FFALIGN((avctx->width + c->lrange) * c->bypp, 16); prev_size = FFALIGN(c->lrange * c->bypp, 16) + c->pstride * (c->lrange + avctx->height + c->urange); - prev_offset = FFALIGN(c->lrange, 16) + c->pstride * c->lrange; + prev_offset = FFALIGN(c->lrange * c->bypp, 16) + c->pstride * c->lrange; if (!(c->prev_buf = av_mallocz(prev_size))) { av_log(avctx, AV_LOG_ERROR, "Can't allocate picture.\n"); return AVERROR(ENOMEM); diff --git a/libavdevice/.gitignore b/libavdevice/.gitignore new file mode 100644 index 00000000000..08ac3eb86a8 --- /dev/null +++ b/libavdevice/.gitignore @@ -0,0 +1,2 @@ +/indev_list.c +/outdev_list.c diff --git a/libavdevice/alsa_dec.c b/libavdevice/alsa_dec.c index c50ce715064..36494e921cc 100644 --- a/libavdevice/alsa_dec.c +++ b/libavdevice/alsa_dec.c @@ -148,7 +148,7 @@ static const AVOption options[] = { }; static const AVClass alsa_demuxer_class = { - .class_name = "ALSA demuxer", + .class_name = "ALSA indev", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT, diff --git a/libavdevice/alsa_enc.c b/libavdevice/alsa_enc.c index 0bef625ea46..1a6d01e3b1b 100644 --- a/libavdevice/alsa_enc.c +++ b/libavdevice/alsa_enc.c @@ -151,7 +151,7 @@ static int audio_get_device_list(AVFormatContext *h, AVDeviceInfoList *device_li } static const AVClass alsa_muxer_class = { - .class_name = "ALSA muxer", + .class_name = "ALSA outdev", .item_name = av_default_item_name, .version = LIBAVUTIL_VERSION_INT, .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT, diff --git a/libavdevice/avdevice.c b/libavdevice/avdevice.c index 72e1b67887f..3d03d89f048 100644 --- a/libavdevice/avdevice.c +++ b/libavdevice/avdevice.c @@ -75,7 +75,7 @@ const char * avdevice_configuration(void) const char * avdevice_license(void) { #define LICENSE_PREFIX "libavdevice license: " - return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1; + return &LICENSE_PREFIX FFMPEG_LICENSE[sizeof(LICENSE_PREFIX) - 1]; } static void *device_next(void *prev, int output, diff --git a/libavdevice/avfoundation.m b/libavdevice/avfoundation.m index 08deecfeeab..59d5b0af4f4 100644 --- a/libavdevice/avfoundation.m +++ b/libavdevice/avfoundation.m @@ -88,7 +88,6 @@ int64_t first_pts; int64_t first_audio_pts; pthread_mutex_t frame_lock; - pthread_cond_t frame_wait_cond; id avf_delegate; id avf_audio_delegate; @@ -98,7 +97,9 @@ int capture_cursor; int capture_mouse_clicks; int capture_raw_data; + int drop_late_frames; int video_is_muxed; + int video_is_screen; int list_devices; int video_device_index; @@ -129,6 +130,12 @@ AVCaptureAudioDataOutput *audio_output; CMSampleBufferRef current_frame; CMSampleBufferRef current_audio_frame; + + AVCaptureDevice *observed_device; +#if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + AVCaptureDeviceTransportControlsPlaybackMode observed_mode; +#endif + int observed_quit; } AVFContext; static void lock_frames(AVFContext* ctx) @@ -162,10 +169,58 @@ - (id)initWithContext:(AVFContext*)context { if (self = [super init]) { _context = context; + + // start observing if a device is set for it +#if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + if (_context->observed_device) { + NSString *keyPath = NSStringFromSelector(@selector(transportControlsPlaybackMode)); + NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew; + + [_context->observed_device addObserver: self + forKeyPath: keyPath + options: options + context: _context]; + } +#endif } return self; } +- (void)dealloc { + // stop observing if a device is set for it +#if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + if (_context->observed_device) { + NSString *keyPath = NSStringFromSelector(@selector(transportControlsPlaybackMode)); + [_context->observed_device removeObserver: self forKeyPath: keyPath]; + } +#endif + [super dealloc]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + if (context == _context) { +#if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + AVCaptureDeviceTransportControlsPlaybackMode mode = + [change[NSKeyValueChangeNewKey] integerValue]; + + if (mode != _context->observed_mode) { + if (mode == AVCaptureDeviceTransportControlsNotPlayingMode) { + _context->observed_quit = 1; + } + _context->observed_mode = mode; + } +#endif + } else { + [super observeValueForKeyPath: keyPath + ofObject: object + change: change + context: context]; + } +} + - (void) captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)videoFrame fromConnection:(AVCaptureConnection *)connection @@ -178,8 +233,6 @@ - (void) captureOutput:(AVCaptureOutput *)captureOutput _context->current_frame = (CMSampleBufferRef)CFRetain(videoFrame); - pthread_cond_signal(&_context->frame_wait_cond); - unlock_frames(_context); ++_context->frames_captured; @@ -224,8 +277,6 @@ - (void) captureOutput:(AVCaptureOutput *)captureOutput _context->current_audio_frame = (CMSampleBufferRef)CFRetain(audioFrame); - pthread_cond_signal(&_context->frame_wait_cond); - unlock_frames(_context); ++_context->audio_frames_captured; @@ -252,7 +303,6 @@ static void destroy_context(AVFContext* ctx) av_freep(&ctx->audio_buffer); pthread_mutex_destroy(&ctx->frame_lock); - pthread_cond_destroy(&ctx->frame_wait_cond); if (ctx->current_frame) { CFRelease(ctx->current_frame); @@ -496,7 +546,20 @@ static int add_video_device(AVFormatContext *s, AVCaptureDevice *video_device) [ctx->video_output setVideoSettings:capture_dict]; } - [ctx->video_output setAlwaysDiscardsLateVideoFrames:YES]; + [ctx->video_output setAlwaysDiscardsLateVideoFrames:ctx->drop_late_frames]; + +#if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 + // check for transport control support and set observer device if supported + if (!ctx->video_is_screen) { + int trans_ctrl = [video_device transportControlsSupported]; + AVCaptureDeviceTransportControlsPlaybackMode trans_mode = [video_device transportControlsPlaybackMode]; + + if (trans_ctrl) { + ctx->observed_mode = trans_mode; + ctx->observed_device = video_device; + } + } +#endif ctx->avf_delegate = [[AVFFrameReceiver alloc] initWithContext:ctx]; @@ -694,7 +757,6 @@ static int get_audio_config(AVFormatContext *s) static int avf_read_header(AVFormatContext *s) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - int capture_screen = 0; uint32_t num_screens = 0; AVFContext *ctx = (AVFContext*)s->priv_data; AVCaptureDevice *video_device = nil; @@ -708,7 +770,6 @@ static int avf_read_header(AVFormatContext *s) ctx->first_audio_pts = av_gettime(); pthread_mutex_init(&ctx->frame_lock, NULL); - pthread_cond_init(&ctx->frame_wait_cond, NULL); #if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 CGGetActiveDisplayList(0, NULL, &num_screens); @@ -792,7 +853,7 @@ static int avf_read_header(AVFormatContext *s) } video_device = (AVCaptureDevice*) capture_screen_input; - capture_screen = 1; + ctx->video_is_screen = 1; #endif } else { av_log(ctx, AV_LOG_ERROR, "Invalid device index\n"); @@ -829,7 +890,7 @@ static int avf_read_header(AVFormatContext *s) AVCaptureScreenInput* capture_screen_input = [[[AVCaptureScreenInput alloc] initWithDisplayID:screens[idx]] autorelease]; video_device = (AVCaptureDevice*) capture_screen_input; ctx->video_device_index = ctx->num_video_devices + idx; - capture_screen = 1; + ctx->video_is_screen = 1; if (ctx->framerate.num > 0) { capture_screen_input.minFrameDuration = CMTimeMake(ctx->framerate.den, ctx->framerate.num); @@ -920,7 +981,7 @@ static int avf_read_header(AVFormatContext *s) /* Unlock device configuration only after the session is started so it * does not reset the capture formats */ - if (!capture_screen) { + if (!ctx->video_is_screen) { [video_device unlockForConfiguration]; } @@ -1109,7 +1170,12 @@ static int avf_read_packet(AVFormatContext *s, AVPacket *pkt) ctx->current_audio_frame = nil; } else { pkt->data = NULL; - pthread_cond_wait(&ctx->frame_wait_cond, &ctx->frame_lock); + unlock_frames(ctx); + if (ctx->observed_quit) { + return AVERROR_EOF; + } else { + return AVERROR(EAGAIN); + } } unlock_frames(ctx); @@ -1135,12 +1201,13 @@ static int avf_close(AVFormatContext *s) { "capture_cursor", "capture the screen cursor", offsetof(AVFContext, capture_cursor), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, { "capture_mouse_clicks", "capture the screen mouse clicks", offsetof(AVFContext, capture_mouse_clicks), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, { "capture_raw_data", "capture the raw data from device connection", offsetof(AVFContext, capture_raw_data), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { "drop_late_frames", "drop frames that are available later than expected", offsetof(AVFContext, drop_late_frames), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, { NULL }, }; static const AVClass avf_class = { - .class_name = "AVFoundation input device", + .class_name = "AVFoundation indev", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT, diff --git a/libavdevice/bktr.c b/libavdevice/bktr.c index 993cc19ac7c..2601adbba8b 100644 --- a/libavdevice/bktr.c +++ b/libavdevice/bktr.c @@ -341,7 +341,7 @@ static const AVOption options[] = { }; static const AVClass bktr_class = { - .class_name = "BKTR grab interface", + .class_name = "BKTR grab indev", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT, diff --git a/libavdevice/caca.c b/libavdevice/caca.c index 47de8247dcf..be3ff79736b 100644 --- a/libavdevice/caca.c +++ b/libavdevice/caca.c @@ -220,7 +220,7 @@ static const AVOption options[] = { }; static const AVClass caca_class = { - .class_name = "caca_outdev", + .class_name = "caca outdev", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT, diff --git a/libavdevice/decklink_common.cpp b/libavdevice/decklink_common.cpp index 659aa9be3f4..53b57ffe4e2 100644 --- a/libavdevice/decklink_common.cpp +++ b/libavdevice/decklink_common.cpp @@ -200,7 +200,7 @@ int ff_decklink_set_format(AVFormatContext *avctx, int width, int height, int tb_num, int tb_den, enum AVFieldOrder field_order, - decklink_direction_t direction, int num) + decklink_direction_t direction) { struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx; @@ -214,8 +214,8 @@ int ff_decklink_set_format(AVFormatContext *avctx, int i = 1; HRESULT res; - av_log(avctx, AV_LOG_DEBUG, "Trying to find mode for frame size %dx%d, frame timing %d/%d, field order %d, direction %d, mode number %d, format code %s\n", - width, height, tb_num, tb_den, field_order, direction, num, (cctx->format_code) ? cctx->format_code : "(unset)"); + av_log(avctx, AV_LOG_DEBUG, "Trying to find mode for frame size %dx%d, frame timing %d/%d, field order %d, direction %d, format code %s\n", + width, height, tb_num, tb_den, field_order, direction, cctx->format_code ? cctx->format_code : "(unset)"); if (direction == DIRECTION_IN) { res = ctx->dli->GetDisplayModeIterator (&itermode); @@ -248,7 +248,6 @@ int ff_decklink_set_format(AVFormatContext *avctx, bmd_height == height && !av_cmp_q(mode_tb, target_tb) && field_order_eq(field_order, bmd_field_dominance)) - || i == num || target_mode == bmd_mode) { ctx->bmd_mode = bmd_mode; ctx->bmd_width = bmd_width; @@ -270,7 +269,22 @@ int ff_decklink_set_format(AVFormatContext *avctx, if (ctx->bmd_mode == bmdModeUnknown) return -1; -#if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000 +#if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b050000 + if (direction == DIRECTION_IN) { + BMDDisplayMode actualMode = ctx->bmd_mode; + if (ctx->dli->DoesSupportVideoMode(ctx->video_input, ctx->bmd_mode, (BMDPixelFormat) cctx->raw_format, + bmdNoVideoInputConversion, bmdSupportedVideoModeDefault, + &actualMode, &support) != S_OK || !support || ctx->bmd_mode != actualMode) + return -1; + } else { + BMDDisplayMode actualMode = ctx->bmd_mode; + if (ctx->dlo->DoesSupportVideoMode(bmdVideoConnectionUnspecified, ctx->bmd_mode, ctx->raw_format, + bmdNoVideoOutputConversion, bmdSupportedVideoModeDefault, + &actualMode, &support) != S_OK || !support || ctx->bmd_mode != actualMode) + return -1; + } + return 0; +#elif BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000 if (direction == DIRECTION_IN) { if (ctx->dli->DoesSupportVideoMode(ctx->video_input, ctx->bmd_mode, (BMDPixelFormat) cctx->raw_format, bmdSupportedVideoModeDefault, @@ -314,8 +328,8 @@ int ff_decklink_set_format(AVFormatContext *avctx, return -1; } -int ff_decklink_set_format(AVFormatContext *avctx, decklink_direction_t direction, int num) { - return ff_decklink_set_format(avctx, 0, 0, 0, 0, AV_FIELD_UNKNOWN, direction, num); +int ff_decklink_set_format(AVFormatContext *avctx, decklink_direction_t direction) { + return ff_decklink_set_format(avctx, 0, 0, 0, 0, AV_FIELD_UNKNOWN, direction); } int ff_decklink_list_devices(AVFormatContext *avctx, diff --git a/libavdevice/decklink_common.h b/libavdevice/decklink_common.h index 921818ba416..27ce6a8a402 100644 --- a/libavdevice/decklink_common.h +++ b/libavdevice/decklink_common.h @@ -115,7 +115,6 @@ struct decklink_ctx { /* Status */ int playback_started; - int capture_started; int64_t last_pts; unsigned long frameCount; unsigned int dropped; @@ -149,6 +148,7 @@ struct decklink_ctx { int channels; int audio_depth; + unsigned long tc_seen; // used with option wait_for_tc }; typedef enum { DIRECTION_IN, DIRECTION_OUT} decklink_direction_t; @@ -196,8 +196,8 @@ static const BMDTimecodeFormat decklink_timecode_format_map[] = { }; int ff_decklink_set_configs(AVFormatContext *avctx, decklink_direction_t direction); -int ff_decklink_set_format(AVFormatContext *avctx, int width, int height, int tb_num, int tb_den, enum AVFieldOrder field_order, decklink_direction_t direction = DIRECTION_OUT, int num = 0); -int ff_decklink_set_format(AVFormatContext *avctx, decklink_direction_t direction, int num); +int ff_decklink_set_format(AVFormatContext *avctx, int width, int height, int tb_num, int tb_den, enum AVFieldOrder field_order, decklink_direction_t direction = DIRECTION_OUT); +int ff_decklink_set_format(AVFormatContext *avctx, decklink_direction_t direction); int ff_decklink_list_devices(AVFormatContext *avctx, struct AVDeviceInfoList *device_list, int show_inputs, int show_outputs); void ff_decklink_list_devices_legacy(AVFormatContext *avctx, int show_inputs, int show_outputs); int ff_decklink_list_formats(AVFormatContext *avctx, decklink_direction_t direction = DIRECTION_OUT); diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h index ca85ec2504c..88b1eae18d0 100644 --- a/libavdevice/decklink_common_c.h +++ b/libavdevice/decklink_common_c.h @@ -42,7 +42,6 @@ struct decklink_cctx { int list_formats; int64_t teletext_lines; double preroll; - int v210; int audio_channels; int audio_depth; int duplex_mode; @@ -58,6 +57,7 @@ struct decklink_cctx { int copyts; int64_t timestamp_align; int timing_offset; + int wait_for_tc; }; #endif /* AVDEVICE_DECKLINK_COMMON_C_H */ diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp index 4da9122bff5..82106aa69e7 100644 --- a/libavdevice/decklink_dec.cpp +++ b/libavdevice/decklink_dec.cpp @@ -784,6 +784,8 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( if (packed_metadata) { if (av_packet_add_side_data(&pkt, AV_PKT_DATA_STRINGS_METADATA, packed_metadata, metadata_len) < 0) av_freep(&packed_metadata); + else if (!ctx->tc_seen) + ctx->tc_seen = ctx->frameCount; } } } @@ -793,6 +795,14 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( } } + if (ctx->tc_format && cctx->wait_for_tc && !ctx->tc_seen) { + + av_log(avctx, AV_LOG_WARNING, "No TC detected yet. wait_for_tc set. Dropping. \n"); + av_log(avctx, AV_LOG_WARNING, "Frame received (#%lu) - " + "- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped); + return S_OK; + } + pkt.pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->video_pts_source, ctx->video_st->time_base, &initial_video_pts, cctx->copyts); pkt.dts = pkt.pts; @@ -940,8 +950,8 @@ static int decklink_autodetect(struct decklink_cctx *cctx) { return -1; } - // 1 second timeout - for (i = 0; i < 10; i++) { + // 3 second timeout + for (i = 0; i < 30; i++) { av_usleep(100000); /* Sometimes VideoInputFrameArrived is called without the * bmdFrameHasNoInputSource flag before VideoInputFormatChanged. @@ -973,7 +983,7 @@ av_cold int ff_decklink_read_close(AVFormatContext *avctx) struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx; - if (ctx->capture_started) { + if (ctx->dli) { ctx->dli->StopStreams(); ctx->dli->DisableVideoInput(); ctx->dli->DisableAudioInput(); @@ -995,9 +1005,6 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) class decklink_input_callback *input_callback; AVStream *st; HRESULT result; - char fname[1024]; - char *tmp; - int mode_num = 0; int ret; ctx = (struct decklink_ctx *) av_mallocz(sizeof(struct decklink_ctx)); @@ -1043,24 +1050,12 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) /* List available devices. */ if (ctx->list_devices) { + av_log(avctx, AV_LOG_WARNING, "The -list_devices option is deprecated and will be removed. Please use ffmpeg -sources decklink instead.\n"); ff_decklink_list_devices_legacy(avctx, 1, 0); return AVERROR_EXIT; } - if (cctx->v210) { - av_log(avctx, AV_LOG_WARNING, "The bm_v210 option is deprecated and will be removed. Please use the -raw_format yuv422p10.\n"); - cctx->raw_format = MKBETAG('v','2','1','0'); - } - - av_strlcpy(fname, avctx->url, sizeof(fname)); - tmp=strchr (fname, '@'); - if (tmp != NULL) { - av_log(avctx, AV_LOG_WARNING, "The @mode syntax is deprecated and will be removed. Please use the -format_code option.\n"); - mode_num = atoi (tmp+1); - *tmp = 0; - } - - ret = ff_decklink_init_device(avctx, fname); + ret = ff_decklink_init_device(avctx, avctx->url); if (ret < 0) return ret; @@ -1101,7 +1096,7 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) goto error; } - if (mode_num == 0 && !cctx->format_code) { + if (!cctx->format_code) { if (decklink_autodetect(cctx) < 0) { av_log(avctx, AV_LOG_ERROR, "Cannot Autodetect input stream or No signal\n"); ret = AVERROR(EIO); @@ -1109,9 +1104,9 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) } av_log(avctx, AV_LOG_INFO, "Autodetected the input mode\n"); } - if (ff_decklink_set_format(avctx, DIRECTION_IN, mode_num) < 0) { - av_log(avctx, AV_LOG_ERROR, "Could not set mode number %d or format code %s for %s\n", - mode_num, (cctx->format_code) ? cctx->format_code : "(unset)", fname); + if (ff_decklink_set_format(avctx, DIRECTION_IN) < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not set format code %s for %s\n", + cctx->format_code ? cctx->format_code : "(unset)", avctx->url); ret = AVERROR(EIO); goto error; } diff --git a/libavdevice/decklink_dec_c.c b/libavdevice/decklink_dec_c.c index 91d2839c57e..b59876994a8 100644 --- a/libavdevice/decklink_dec_c.c +++ b/libavdevice/decklink_dec_c.c @@ -33,7 +33,6 @@ static const AVOption options[] = { { "list_devices", "list available devices" , OFFSET(list_devices), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, DEC }, { "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 }, - { "bm_v210", "v210 10 bit per channel" , OFFSET(v210), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, DEC }, { "raw_format", "pixel format to be returned by the card when capturing" , OFFSET(raw_format), AV_OPT_TYPE_INT, { .i64 = MKBETAG('2','v','u','y')}, 0, UINT_MAX, DEC, "raw_format" }, { "uyvy422", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MKBETAG('2','v','u','y') }, 0, 0, DEC, "raw_format"}, { "yuv422p10", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MKBETAG('v','2','1','0') }, 0, 0, DEC, "raw_format"}, @@ -85,11 +84,12 @@ static const AVOption options[] = { { "audio_depth", "audio bitdepth (16 or 32)", OFFSET(audio_depth), AV_OPT_TYPE_INT, { .i64 = 16}, 16, 32, DEC }, { "decklink_copyts", "copy timestamps, do not remove the initial offset", OFFSET(copyts), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC }, { "timestamp_align", "capture start time alignment (in seconds)", OFFSET(timestamp_align), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT_MAX, DEC }, + { "wait_for_tc", "drop frames till a frame with timecode is received. TC format must be set", OFFSET(wait_for_tc), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC }, { NULL }, }; static const AVClass decklink_demuxer_class = { - .class_name = "Blackmagic DeckLink demuxer", + .class_name = "Blackmagic DeckLink indev", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT, diff --git a/libavdevice/decklink_enc.cpp b/libavdevice/decklink_enc.cpp index 04b06aee3ab..883fdeadfb8 100644 --- a/libavdevice/decklink_enc.cpp +++ b/libavdevice/decklink_enc.cpp @@ -568,6 +568,7 @@ av_cold int ff_decklink_write_header(AVFormatContext *avctx) /* List available devices and exit. */ if (ctx->list_devices) { + av_log(avctx, AV_LOG_WARNING, "The -list_devices option is deprecated and will be removed. Please use ffmpeg -sinks decklink instead.\n"); ff_decklink_list_devices_legacy(avctx, 0, 1); return AVERROR_EXIT; } diff --git a/libavdevice/decklink_enc_c.c b/libavdevice/decklink_enc_c.c index 63cbd39ecd5..682c7147d64 100644 --- a/libavdevice/decklink_enc_c.c +++ b/libavdevice/decklink_enc_c.c @@ -41,7 +41,7 @@ static const AVOption options[] = { }; static const AVClass decklink_muxer_class = { - .class_name = "Blackmagic DeckLink muxer", + .class_name = "Blackmagic DeckLink outdev", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT, diff --git a/libavdevice/lavfi.c b/libavdevice/lavfi.c index ca8f05f3f72..c949ff7e124 100644 --- a/libavdevice/lavfi.c +++ b/libavdevice/lavfi.c @@ -302,9 +302,13 @@ av_cold static int lavfi_read_header(AVFormatContext *avctx) if (lavfi->dump_graph) { char *dump = avfilter_graph_dump(lavfi->graph, lavfi->dump_graph); - fputs(dump, stderr); - fflush(stderr); - av_free(dump); + if (dump != NULL) { + fputs(dump, stderr); + fflush(stderr); + av_free(dump); + } else { + FAIL(AVERROR(ENOMEM)); + } } /* fill each stream with the information in the corresponding sink */ diff --git a/libavdevice/libndi_newtek_dec.c b/libavdevice/libndi_newtek_dec.c deleted file mode 100644 index 4fb719770e5..00000000000 --- a/libavdevice/libndi_newtek_dec.c +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Newtek NDI input - * Copyright (c) 2017 Maksym Veremeyenko - * - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General 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 "libavformat/avformat.h" -#include "libavformat/internal.h" -#include "libavutil/opt.h" -#include "libavutil/imgutils.h" - -#include "libndi_newtek_common.h" - -struct NDIContext { - const AVClass *cclass; - - /* Options */ - int find_sources; - int64_t wait_sources; - int allow_video_fields; - - /* Runtime */ - NDIlib_recv_create_t *recv; - NDIlib_find_instance_t ndi_find; - - /* Streams */ - AVStream *video_st, *audio_st; -}; - -static int ndi_set_video_packet(AVFormatContext *avctx, NDIlib_video_frame_t *v, AVPacket *pkt) -{ - int ret; - struct NDIContext *ctx = avctx->priv_data; - - ret = av_new_packet(pkt, v->yres * v->line_stride_in_bytes); - if (ret < 0) - return ret; - - pkt->dts = pkt->pts = av_rescale_q(v->timecode, NDI_TIME_BASE_Q, ctx->video_st->time_base); - pkt->duration = av_rescale_q(1, (AVRational){v->frame_rate_D, v->frame_rate_N}, ctx->video_st->time_base); - - av_log(avctx, AV_LOG_DEBUG, "%s: pkt->dts = pkt->pts = %"PRId64", duration=%"PRId64", timecode=%"PRId64"\n", - __func__, pkt->dts, pkt->duration, v->timecode); - - pkt->flags |= AV_PKT_FLAG_KEY; - pkt->stream_index = ctx->video_st->index; - - memcpy(pkt->data, v->p_data, pkt->size); - - return 0; -} - -static int ndi_set_audio_packet(AVFormatContext *avctx, NDIlib_audio_frame_t *a, AVPacket *pkt) -{ - int ret; - struct NDIContext *ctx = avctx->priv_data; - - NDIlib_audio_frame_interleaved_16s_t dst; - - ret = av_new_packet(pkt, 2 * a->no_samples * a->no_channels); - if (ret < 0) - return ret; - - pkt->dts = pkt->pts = av_rescale_q(a->timecode, NDI_TIME_BASE_Q, ctx->audio_st->time_base); - pkt->duration = av_rescale_q(1, (AVRational){a->no_samples, a->sample_rate}, ctx->audio_st->time_base); - - av_log(avctx, AV_LOG_DEBUG, "%s: pkt->dts = pkt->pts = %"PRId64", duration=%"PRId64", timecode=%"PRId64"\n", - __func__, pkt->dts, pkt->duration, a->timecode); - - pkt->flags |= AV_PKT_FLAG_KEY; - pkt->stream_index = ctx->audio_st->index; - - dst.reference_level = 0; - dst.p_data = (short *)pkt->data; - NDIlib_util_audio_to_interleaved_16s(a, &dst); - - return 0; -} - -static int ndi_find_sources(AVFormatContext *avctx, const char *name, NDIlib_source_t *source_to_connect_to) -{ - int j = AVERROR(ENODEV); - unsigned int n, i; - struct NDIContext *ctx = avctx->priv_data; - const NDIlib_source_t *ndi_srcs = NULL; - const NDIlib_find_create_t find_create_desc = { .show_local_sources = true, - .p_groups = NULL, .p_extra_ips = NULL }; - - if (!ctx->ndi_find) - ctx->ndi_find = NDIlib_find_create2(&find_create_desc); - if (!ctx->ndi_find) { - av_log(avctx, AV_LOG_ERROR, "NDIlib_find_create failed.\n"); - return AVERROR(EIO); - } - - while (1) - { - int f, t = ctx->wait_sources / 1000; - av_log(avctx, AV_LOG_DEBUG, "Waiting for sources %d miliseconds\n", t); - f = NDIlib_find_wait_for_sources(ctx->ndi_find, t); - av_log(avctx, AV_LOG_DEBUG, "NDIlib_find_wait_for_sources returns %d\n", f); - if (!f) - break; - }; - - ndi_srcs = NDIlib_find_get_current_sources(ctx->ndi_find, &n); - - if (ctx->find_sources) - av_log(avctx, AV_LOG_INFO, "Found %d NDI sources:\n", n); - - for (i = 0; i < n; i++) { - if (ctx->find_sources) - av_log(avctx, AV_LOG_INFO, "\t'%s'\t'%s'\n", ndi_srcs[i].p_ndi_name, ndi_srcs[i].p_ip_address); - - if (!strcmp(name, ndi_srcs[i].p_ndi_name)) { - *source_to_connect_to = ndi_srcs[i]; - j = i; - } - } - - return j; -} - -static int ndi_read_header(AVFormatContext *avctx) -{ - int ret; - NDIlib_recv_create_t recv_create_desc; - const NDIlib_tally_t tally_state = { .on_program = true, .on_preview = false }; - struct NDIContext *ctx = avctx->priv_data; - - if (!NDIlib_initialize()) { - av_log(avctx, AV_LOG_ERROR, "NDIlib_initialize failed.\n"); - return AVERROR_EXTERNAL; - } - - /* Find available sources. */ - ret = ndi_find_sources(avctx, avctx->url, &recv_create_desc.source_to_connect_to); - if (ctx->find_sources) { - return AVERROR_EXIT; - } - if (ret < 0) - return ret; - - /* Create receiver description */ - recv_create_desc.color_format = NDIlib_recv_color_format_e_UYVY_RGBA; - recv_create_desc.bandwidth = NDIlib_recv_bandwidth_highest; - recv_create_desc.allow_video_fields = ctx->allow_video_fields; - - /* Create the receiver */ - ctx->recv = NDIlib_recv_create(&recv_create_desc); - if (!ctx->recv) { - av_log(avctx, AV_LOG_ERROR, "NDIlib_recv_create2 failed.\n"); - return AVERROR(EIO); - } - - /* Set tally */ - NDIlib_recv_set_tally(ctx->recv, &tally_state); - - avctx->ctx_flags |= AVFMTCTX_NOHEADER; - - return 0; -} - -static int ndi_create_video_stream(AVFormatContext *avctx, NDIlib_video_frame_t *v) -{ - AVStream *st; - AVRational tmp; - struct NDIContext *ctx = avctx->priv_data; - - st = avformat_new_stream(avctx, NULL); - if (!st) { - av_log(avctx, AV_LOG_ERROR, "Cannot add video stream\n"); - return AVERROR(ENOMEM); - } - - st->time_base = NDI_TIME_BASE_Q; - st->r_frame_rate = av_make_q(v->frame_rate_N, v->frame_rate_D); - - tmp = av_mul_q(av_d2q(v->picture_aspect_ratio, INT_MAX), (AVRational){v->yres, v->xres}); - av_reduce(&st->sample_aspect_ratio.num, &st->sample_aspect_ratio.den, tmp.num, tmp.den, 1000); - st->codecpar->sample_aspect_ratio = st->sample_aspect_ratio; - - st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; - st->codecpar->width = v->xres; - st->codecpar->height = v->yres; - st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO; - st->codecpar->bit_rate = av_rescale(v->xres * v->yres * 16, v->frame_rate_N, v->frame_rate_D); - st->codecpar->field_order = v->frame_format_type == NDIlib_frame_format_type_progressive - ? AV_FIELD_PROGRESSIVE : AV_FIELD_TT; - - if (NDIlib_FourCC_type_UYVY == v->FourCC || NDIlib_FourCC_type_UYVA == v->FourCC) { - st->codecpar->format = AV_PIX_FMT_UYVY422; - st->codecpar->codec_tag = MKTAG('U', 'Y', 'V', 'Y'); - if (NDIlib_FourCC_type_UYVA == v->FourCC) - av_log(avctx, AV_LOG_WARNING, "Alpha channel ignored\n"); - } else if (NDIlib_FourCC_type_BGRA == v->FourCC) { - st->codecpar->format = AV_PIX_FMT_BGRA; - st->codecpar->codec_tag = MKTAG('B', 'G', 'R', 'A'); - } else if (NDIlib_FourCC_type_BGRX == v->FourCC) { - st->codecpar->format = AV_PIX_FMT_BGR0; - st->codecpar->codec_tag = MKTAG('B', 'G', 'R', '0'); - } else if (NDIlib_FourCC_type_RGBA == v->FourCC) { - st->codecpar->format = AV_PIX_FMT_RGBA; - st->codecpar->codec_tag = MKTAG('R', 'G', 'B', 'A'); - } else if (NDIlib_FourCC_type_RGBX == v->FourCC) { - st->codecpar->format = AV_PIX_FMT_RGB0; - st->codecpar->codec_tag = MKTAG('R', 'G', 'B', '0'); - } else { - av_log(avctx, AV_LOG_ERROR, "Unsupported video stream format, v->FourCC=%d\n", v->FourCC); - return AVERROR(EINVAL); - } - - avpriv_set_pts_info(st, 64, 1, NDI_TIME_BASE); - - ctx->video_st = st; - - return 0; -} - -static int ndi_create_audio_stream(AVFormatContext *avctx, NDIlib_audio_frame_t *a) -{ - AVStream *st; - struct NDIContext *ctx = avctx->priv_data; - - st = avformat_new_stream(avctx, NULL); - if (!st) { - av_log(avctx, AV_LOG_ERROR, "Cannot add audio stream\n"); - return AVERROR(ENOMEM); - } - - st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; - st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; - st->codecpar->sample_rate = a->sample_rate; - st->codecpar->channels = a->no_channels; - - avpriv_set_pts_info(st, 64, 1, NDI_TIME_BASE); - - ctx->audio_st = st; - - return 0; -} - -static int ndi_read_packet(AVFormatContext *avctx, AVPacket *pkt) -{ - int ret = 0; - struct NDIContext *ctx = avctx->priv_data; - - while (!ret) { - NDIlib_video_frame_t v; - NDIlib_audio_frame_t a; - NDIlib_metadata_frame_t m; - NDIlib_frame_type_e t; - - av_log(avctx, AV_LOG_DEBUG, "NDIlib_recv_capture...\n"); - t = NDIlib_recv_capture(ctx->recv, &v, &a, &m, 40); - av_log(avctx, AV_LOG_DEBUG, "NDIlib_recv_capture=%d\n", t); - - if (t == NDIlib_frame_type_video) { - if (!ctx->video_st) - ret = ndi_create_video_stream(avctx, &v); - if (!ret) - ret = ndi_set_video_packet(avctx, &v, pkt); - NDIlib_recv_free_video(ctx->recv, &v); - break; - } - else if (t == NDIlib_frame_type_audio) { - if (!ctx->audio_st) - ret = ndi_create_audio_stream(avctx, &a); - if (!ret) - ret = ndi_set_audio_packet(avctx, &a, pkt); - NDIlib_recv_free_audio(ctx->recv, &a); - break; - } - else if (t == NDIlib_frame_type_metadata) - NDIlib_recv_free_metadata(ctx->recv, &m); - else if (t == NDIlib_frame_type_error){ - av_log(avctx, AV_LOG_ERROR, "NDIlib_recv_capture failed with error\n"); - ret = AVERROR(EIO); - } - }; - - return ret; -} - -static int ndi_read_close(AVFormatContext *avctx) -{ - struct NDIContext *ctx = (struct NDIContext *)avctx->priv_data; - - if (ctx->recv) - NDIlib_recv_destroy(ctx->recv); - - if (ctx->ndi_find) - NDIlib_find_destroy(ctx->ndi_find); - - return 0; -} - -#define OFFSET(x) offsetof(struct NDIContext, x) -#define DEC AV_OPT_FLAG_DECODING_PARAM - -static const AVOption options[] = { - { "find_sources", "Find available sources" , OFFSET(find_sources), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC }, - { "wait_sources", "Time to wait until the number of online sources have changed" , OFFSET(wait_sources), AV_OPT_TYPE_DURATION, { .i64 = 1000000 }, 100000, 20000000, DEC }, - { "allow_video_fields", "When this flag is FALSE, all video that you receive will be progressive" , OFFSET(allow_video_fields), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, DEC }, - { NULL }, -}; - -static const AVClass libndi_newtek_demuxer_class = { - .class_name = "NDI demuxer", - .item_name = av_default_item_name, - .option = options, - .version = LIBAVUTIL_VERSION_INT, - .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, -}; - -AVInputFormat ff_libndi_newtek_demuxer = { - .name = "libndi_newtek", - .long_name = NULL_IF_CONFIG_SMALL("Network Device Interface (NDI) input using NewTek library"), - .flags = AVFMT_NOFILE, - .priv_class = &libndi_newtek_demuxer_class, - .priv_data_size = sizeof(struct NDIContext), - .read_header = ndi_read_header, - .read_packet = ndi_read_packet, - .read_close = ndi_read_close, -}; diff --git a/libavdevice/libndi_newtek_enc.c b/libavdevice/libndi_newtek_enc.c deleted file mode 100644 index f3603f5a3ad..00000000000 --- a/libavdevice/libndi_newtek_enc.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * NewTek NDI output - * Copyright (c) 2017 Maksym Veremeyenko - * - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General 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 "libavformat/avformat.h" -#include "libavformat/internal.h" -#include "libavutil/opt.h" -#include "libavutil/imgutils.h" - -#include "libndi_newtek_common.h" - -struct NDIContext { - const AVClass *cclass; - - /* Options */ - int reference_level; - int clock_video, clock_audio; - - NDIlib_video_frame_t *video; - NDIlib_audio_frame_interleaved_16s_t *audio; - NDIlib_send_instance_t ndi_send; - AVFrame *last_avframe; -}; - -static int ndi_write_trailer(AVFormatContext *avctx) -{ - struct NDIContext *ctx = avctx->priv_data; - - if (ctx->ndi_send) { - NDIlib_send_destroy(ctx->ndi_send); - av_frame_free(&ctx->last_avframe); - } - - av_freep(&ctx->video); - av_freep(&ctx->audio); - - return 0; -} - -static int ndi_write_video_packet(AVFormatContext *avctx, AVStream *st, AVPacket *pkt) -{ - struct NDIContext *ctx = avctx->priv_data; - AVFrame *avframe, *tmp = (AVFrame *)pkt->data; - - if (tmp->format != AV_PIX_FMT_UYVY422 && tmp->format != AV_PIX_FMT_BGRA && - tmp->format != AV_PIX_FMT_BGR0 && tmp->format != AV_PIX_FMT_RGBA && - tmp->format != AV_PIX_FMT_RGB0) { - av_log(avctx, AV_LOG_ERROR, "Got a frame with invalid pixel format.\n"); - return AVERROR(EINVAL); - } - - if (tmp->linesize[0] < 0) { - av_log(avctx, AV_LOG_ERROR, "Got a frame with negative linesize.\n"); - return AVERROR(EINVAL); - } - - if (tmp->width != ctx->video->xres || - tmp->height != ctx->video->yres) { - av_log(avctx, AV_LOG_ERROR, "Got a frame with invalid dimension.\n"); - av_log(avctx, AV_LOG_ERROR, "tmp->width=%d, tmp->height=%d, ctx->video->xres=%d, ctx->video->yres=%d\n", - tmp->width, tmp->height, ctx->video->xres, ctx->video->yres); - return AVERROR(EINVAL); - } - - avframe = av_frame_clone(tmp); - if (!avframe) - return AVERROR(ENOMEM); - - ctx->video->timecode = av_rescale_q(pkt->pts, st->time_base, NDI_TIME_BASE_Q); - - ctx->video->line_stride_in_bytes = avframe->linesize[0]; - ctx->video->p_data = (void *)(avframe->data[0]); - - av_log(avctx, AV_LOG_DEBUG, "%s: pkt->pts=%"PRId64", timecode=%"PRId64", st->time_base=%d/%d\n", - __func__, pkt->pts, ctx->video->timecode, st->time_base.num, st->time_base.den); - - /* asynchronous for one frame, but will block if a second frame - is given before the first one has been sent */ - NDIlib_send_send_video_async(ctx->ndi_send, ctx->video); - - av_frame_free(&ctx->last_avframe); - ctx->last_avframe = avframe; - - return 0; -} - -static int ndi_write_audio_packet(AVFormatContext *avctx, AVStream *st, AVPacket *pkt) -{ - struct NDIContext *ctx = avctx->priv_data; - - ctx->audio->p_data = (short *)pkt->data; - ctx->audio->timecode = av_rescale_q(pkt->pts, st->time_base, NDI_TIME_BASE_Q); - ctx->audio->no_samples = pkt->size / (ctx->audio->no_channels << 1); - - av_log(avctx, AV_LOG_DEBUG, "%s: pkt->pts=%"PRId64", timecode=%"PRId64", st->time_base=%d/%d\n", - __func__, pkt->pts, ctx->audio->timecode, st->time_base.num, st->time_base.den); - - NDIlib_util_send_send_audio_interleaved_16s(ctx->ndi_send, ctx->audio); - - return 0; -} - -static int ndi_write_packet(AVFormatContext *avctx, AVPacket *pkt) -{ - AVStream *st = avctx->streams[pkt->stream_index]; - - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) - return ndi_write_video_packet(avctx, st, pkt); - else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) - return ndi_write_audio_packet(avctx, st, pkt); - - return AVERROR_BUG; -} - -static int ndi_setup_audio(AVFormatContext *avctx, AVStream *st) -{ - struct NDIContext *ctx = avctx->priv_data; - AVCodecParameters *c = st->codecpar; - - if (ctx->audio) { - av_log(avctx, AV_LOG_ERROR, "Only one audio stream is supported!\n"); - return AVERROR(EINVAL); - } - - ctx->audio = av_mallocz(sizeof(NDIlib_audio_frame_interleaved_16s_t)); - if (!ctx->audio) - return AVERROR(ENOMEM); - - ctx->audio->sample_rate = c->sample_rate; - ctx->audio->no_channels = c->channels; - ctx->audio->reference_level = ctx->reference_level; - - avpriv_set_pts_info(st, 64, 1, NDI_TIME_BASE); - - return 0; -} - -static int ndi_setup_video(AVFormatContext *avctx, AVStream *st) -{ - struct NDIContext *ctx = avctx->priv_data; - AVCodecParameters *c = st->codecpar; - - if (ctx->video) { - av_log(avctx, AV_LOG_ERROR, "Only one video stream is supported!\n"); - return AVERROR(EINVAL); - } - - if (c->codec_id != AV_CODEC_ID_WRAPPED_AVFRAME) { - av_log(avctx, AV_LOG_ERROR, "Unsupported codec format!" - " Only AV_CODEC_ID_WRAPPED_AVFRAME is supported (-vcodec wrapped_avframe).\n"); - return AVERROR(EINVAL); - } - - if (c->format != AV_PIX_FMT_UYVY422 && c->format != AV_PIX_FMT_BGRA && - c->format != AV_PIX_FMT_BGR0 && c->format != AV_PIX_FMT_RGBA && - c->format != AV_PIX_FMT_RGB0) { - av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format!" - " Only AV_PIX_FMT_UYVY422, AV_PIX_FMT_BGRA, AV_PIX_FMT_BGR0," - " AV_PIX_FMT_RGBA, AV_PIX_FMT_RGB0 is supported.\n"); - return AVERROR(EINVAL); - } - - if (c->field_order == AV_FIELD_BB || c->field_order == AV_FIELD_BT) { - av_log(avctx, AV_LOG_ERROR, "Lower field-first disallowed"); - return AVERROR(EINVAL); - } - - ctx->video = av_mallocz(sizeof(NDIlib_video_frame_t)); - if (!ctx->video) - return AVERROR(ENOMEM); - - switch(c->format) { - case AV_PIX_FMT_UYVY422: - ctx->video->FourCC = NDIlib_FourCC_type_UYVY; - break; - case AV_PIX_FMT_BGRA: - ctx->video->FourCC = NDIlib_FourCC_type_BGRA; - break; - case AV_PIX_FMT_BGR0: - ctx->video->FourCC = NDIlib_FourCC_type_BGRX; - break; - case AV_PIX_FMT_RGBA: - ctx->video->FourCC = NDIlib_FourCC_type_RGBA; - break; - case AV_PIX_FMT_RGB0: - ctx->video->FourCC = NDIlib_FourCC_type_RGBX; - break; - } - - ctx->video->xres = c->width; - ctx->video->yres = c->height; - ctx->video->frame_rate_N = st->avg_frame_rate.num; - ctx->video->frame_rate_D = st->avg_frame_rate.den; - ctx->video->frame_format_type = c->field_order == AV_FIELD_PROGRESSIVE - ? NDIlib_frame_format_type_progressive - : NDIlib_frame_format_type_interleaved; - - if (st->sample_aspect_ratio.num) { - AVRational display_aspect_ratio; - av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, - st->codecpar->width * (int64_t)st->sample_aspect_ratio.num, - st->codecpar->height * (int64_t)st->sample_aspect_ratio.den, - 1024 * 1024); - ctx->video->picture_aspect_ratio = av_q2d(display_aspect_ratio); - } - else - ctx->video->picture_aspect_ratio = (double)st->codecpar->width/st->codecpar->height; - - avpriv_set_pts_info(st, 64, 1, NDI_TIME_BASE); - - return 0; -} - -static int ndi_write_header(AVFormatContext *avctx) -{ - int ret = 0; - unsigned int n; - struct NDIContext *ctx = avctx->priv_data; - const NDIlib_send_create_t ndi_send_desc = { .p_ndi_name = avctx->url, - .p_groups = NULL, .clock_video = ctx->clock_video, .clock_audio = ctx->clock_audio }; - - if (!NDIlib_initialize()) { - av_log(avctx, AV_LOG_ERROR, "NDIlib_initialize failed.\n"); - return AVERROR_EXTERNAL; - } - - /* check if streams compatible */ - for (n = 0; n < avctx->nb_streams; n++) { - AVStream *st = avctx->streams[n]; - AVCodecParameters *c = st->codecpar; - if (c->codec_type == AVMEDIA_TYPE_AUDIO) { - if ((ret = ndi_setup_audio(avctx, st))) - goto error; - } else if (c->codec_type == AVMEDIA_TYPE_VIDEO) { - if ((ret = ndi_setup_video(avctx, st))) - goto error; - } else { - av_log(avctx, AV_LOG_ERROR, "Unsupported stream type.\n"); - ret = AVERROR(EINVAL); - goto error; - } - } - - ctx->ndi_send = NDIlib_send_create(&ndi_send_desc); - if (!ctx->ndi_send) { - av_log(avctx, AV_LOG_ERROR, "Failed to create NDI output %s\n", avctx->url); - ret = AVERROR_EXTERNAL; - } - -error: - return ret; -} - -#define OFFSET(x) offsetof(struct NDIContext, x) -static const AVOption options[] = { - { "reference_level", "The audio reference level in dB" , OFFSET(reference_level), AV_OPT_TYPE_INT, { .i64 = 0 }, -20, 20, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM}, - { "clock_video", "These specify whether video 'clock' themselves" , OFFSET(clock_video), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM }, - { "clock_audio", "These specify whether audio 'clock' themselves" , OFFSET(clock_audio), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM }, - { NULL }, -}; - -static const AVClass libndi_newtek_muxer_class = { - .class_name = "NDI muxer", - .item_name = av_default_item_name, - .option = options, - .version = LIBAVUTIL_VERSION_INT, - .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT, -}; - -AVOutputFormat ff_libndi_newtek_muxer = { - .name = "libndi_newtek", - .long_name = NULL_IF_CONFIG_SMALL("Network Device Interface (NDI) output using NewTek library"), - .audio_codec = AV_CODEC_ID_PCM_S16LE, - .video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, - .subtitle_codec = AV_CODEC_ID_NONE, - .flags = AVFMT_NOFILE, - .priv_class = &libndi_newtek_muxer_class, - .priv_data_size = sizeof(struct NDIContext), - .write_header = ndi_write_header, - .write_packet = ndi_write_packet, - .write_trailer = ndi_write_trailer, -}; diff --git a/libavdevice/openal-dec.c b/libavdevice/openal-dec.c index c19048e15d3..57de665eb69 100644 --- a/libavdevice/openal-dec.c +++ b/libavdevice/openal-dec.c @@ -241,7 +241,7 @@ static const AVOption options[] = { }; static const AVClass class = { - .class_name = "openal", + .class_name = "openal indev", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT, diff --git a/libavdevice/opengl_enc.c b/libavdevice/opengl_enc.c index fd0bb177d9e..2bdb8da732b 100644 --- a/libavdevice/opengl_enc.c +++ b/libavdevice/opengl_enc.c @@ -568,8 +568,9 @@ static void opengl_make_ortho(float matrix[16], float left, float right, matrix[15] = 1.0f; } -static av_cold int opengl_read_limits(OpenGLContext *opengl) +static av_cold int opengl_read_limits(AVFormatContext *h) { + OpenGLContext *opengl = h->priv_data; static const struct{ const char *extension; int major; @@ -587,17 +588,21 @@ static av_cold int opengl_read_limits(OpenGLContext *opengl) version = glGetString(GL_VERSION); extensions = glGetString(GL_EXTENSIONS); + if (!version || !extensions) { + av_log(h, AV_LOG_ERROR, "No OpenGL context initialized for the current thread\n"); + return AVERROR(ENOSYS); + } - av_log(opengl, AV_LOG_DEBUG, "OpenGL version: %s\n", version); + av_log(h, AV_LOG_DEBUG, "OpenGL version: %s\n", version); sscanf(version, "%d.%d", &major, &minor); for (i = 0; required_extensions[i].extension; i++) { if (major < required_extensions[i].major && (major == required_extensions[i].major && minor < required_extensions[i].minor) && !strstr(extensions, required_extensions[i].extension)) { - av_log(opengl, AV_LOG_ERROR, "Required extension %s is not supported.\n", + av_log(h, AV_LOG_ERROR, "Required extension %s is not supported.\n", required_extensions[i].extension); - av_log(opengl, AV_LOG_DEBUG, "Supported extensions are: %s\n", extensions); + av_log(h, AV_LOG_DEBUG, "Supported extensions are: %s\n", extensions); return AVERROR(ENOSYS); } } @@ -610,10 +615,10 @@ static av_cold int opengl_read_limits(OpenGLContext *opengl) opengl->unpack_subimage = 1; #endif - av_log(opengl, AV_LOG_DEBUG, "Non Power of 2 textures support: %s\n", opengl->non_pow_2_textures ? "Yes" : "No"); - av_log(opengl, AV_LOG_DEBUG, "Unpack Subimage extension support: %s\n", opengl->unpack_subimage ? "Yes" : "No"); - av_log(opengl, AV_LOG_DEBUG, "Max texture size: %dx%d\n", opengl->max_texture_size, opengl->max_texture_size); - av_log(opengl, AV_LOG_DEBUG, "Max viewport size: %dx%d\n", + av_log(h, AV_LOG_DEBUG, "Non Power of 2 textures support: %s\n", opengl->non_pow_2_textures ? "Yes" : "No"); + av_log(h, AV_LOG_DEBUG, "Unpack Subimage extension support: %s\n", opengl->unpack_subimage ? "Yes" : "No"); + av_log(h, AV_LOG_DEBUG, "Max texture size: %dx%d\n", opengl->max_texture_size, opengl->max_texture_size); + av_log(h, AV_LOG_DEBUG, "Max viewport size: %dx%d\n", opengl->max_viewport_width, opengl->max_viewport_height); OPENGL_ERROR_CHECK(opengl); @@ -1050,13 +1055,14 @@ static av_cold int opengl_init_context(OpenGLContext *opengl) static av_cold int opengl_write_header(AVFormatContext *h) { OpenGLContext *opengl = h->priv_data; + AVCodecParameters *par = h->streams[0]->codecpar; AVStream *st; int ret; if (h->nb_streams != 1 || - h->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO || - h->streams[0]->codecpar->codec_id != AV_CODEC_ID_RAWVIDEO) { - av_log(opengl, AV_LOG_ERROR, "Only a single video stream is supported.\n"); + par->codec_type != AVMEDIA_TYPE_VIDEO || + (par->codec_id != AV_CODEC_ID_WRAPPED_AVFRAME && par->codec_id != AV_CODEC_ID_RAWVIDEO)) { + av_log(opengl, AV_LOG_ERROR, "Only a single raw or wrapped avframe video stream is supported.\n"); return AVERROR(EINVAL); } st = h->streams[0]; @@ -1074,7 +1080,7 @@ static av_cold int opengl_write_header(AVFormatContext *h) if ((ret = opengl_create_window(h))) goto fail; - if ((ret = opengl_read_limits(opengl)) < 0) + if ((ret = opengl_read_limits(h)) < 0) goto fail; if (opengl->width > opengl->max_texture_size || opengl->height > opengl->max_texture_size) { @@ -1251,7 +1257,13 @@ static int opengl_draw(AVFormatContext *h, void *input, int repaint, int is_pkt) static int opengl_write_packet(AVFormatContext *h, AVPacket *pkt) { - return opengl_draw(h, pkt, 0, 1); + AVCodecParameters *par = h->streams[0]->codecpar; + if (par->codec_id == AV_CODEC_ID_WRAPPED_AVFRAME) { + AVFrame *frame = (AVFrame *)pkt->data; + return opengl_draw(h, frame, 0, 0); + } else { + return opengl_draw(h, pkt, 0, 1); + } } static int opengl_write_frame(AVFormatContext *h, int stream_index, @@ -1265,7 +1277,7 @@ static int opengl_write_frame(AVFormatContext *h, int stream_index, #define OFFSET(x) offsetof(OpenGLContext, x) #define ENC AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "background", "set background color", OFFSET(background), AV_OPT_TYPE_COLOR, {.str = "black"}, CHAR_MIN, CHAR_MAX, ENC }, + { "background", "set background color", OFFSET(background), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, ENC }, { "no_window", "disable default window", OFFSET(no_window), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, ENC }, { "window_title", "set window title", OFFSET(window_title), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, ENC }, { "window_size", "set window size", OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, ENC }, @@ -1285,7 +1297,7 @@ AVOutputFormat ff_opengl_muxer = { .long_name = NULL_IF_CONFIG_SMALL("OpenGL output"), .priv_data_size = sizeof(OpenGLContext), .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_RAWVIDEO, + .video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, .write_header = opengl_write_header, .write_packet = opengl_write_packet, .write_uncoded_frame = opengl_write_frame, diff --git a/libavdevice/oss_dec.c b/libavdevice/oss_dec.c index d0dc327dc62..13ace7000d1 100644 --- a/libavdevice/oss_dec.c +++ b/libavdevice/oss_dec.c @@ -125,7 +125,7 @@ static const AVOption options[] = { }; static const AVClass oss_demuxer_class = { - .class_name = "OSS demuxer", + .class_name = "OSS indev", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT, diff --git a/libavdevice/oss_enc.c b/libavdevice/oss_enc.c index e3172afaa47..274c7601aa4 100644 --- a/libavdevice/oss_enc.c +++ b/libavdevice/oss_enc.c @@ -90,7 +90,7 @@ static int audio_write_trailer(AVFormatContext *s1) } static const AVClass oss_muxer_class = { - .class_name = "OSS muxer", + .class_name = "OSS outdev", .item_name = av_default_item_name, .version = LIBAVUTIL_VERSION_INT, .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT, diff --git a/libavdevice/pulse_audio_dec.c b/libavdevice/pulse_audio_dec.c index 042fe76d435..50a3c971aee 100644 --- a/libavdevice/pulse_audio_dec.c +++ b/libavdevice/pulse_audio_dec.c @@ -359,7 +359,7 @@ static const AVOption options[] = { }; static const AVClass pulse_demuxer_class = { - .class_name = "Pulse demuxer", + .class_name = "Pulse indev", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT, diff --git a/libavdevice/pulse_audio_enc.c b/libavdevice/pulse_audio_enc.c index d430b772726..e0a631b227d 100644 --- a/libavdevice/pulse_audio_enc.c +++ b/libavdevice/pulse_audio_enc.c @@ -771,7 +771,7 @@ static const AVOption options[] = { }; static const AVClass pulse_muxer_class = { - .class_name = "PulseAudio muxer", + .class_name = "PulseAudio outdev", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT, diff --git a/libavdevice/tests/.gitignore b/libavdevice/tests/.gitignore new file mode 100644 index 00000000000..b4a2281a70d --- /dev/null +++ b/libavdevice/tests/.gitignore @@ -0,0 +1 @@ +/timefilter diff --git a/libavdevice/v4l2.c b/libavdevice/v4l2.c index a9a0ed324d4..365bacd7714 100644 --- a/libavdevice/v4l2.c +++ b/libavdevice/v4l2.c @@ -538,11 +538,10 @@ static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt) s->frame_size = buf.bytesused; if (s->frame_size > 0 && buf.bytesused != s->frame_size) { - av_log(ctx, AV_LOG_ERROR, + av_log(ctx, AV_LOG_WARNING, "Dequeued v4l2 buffer contains %d bytes, but %d were expected. Flags: 0x%08X.\n", buf.bytesused, s->frame_size, buf.flags); - enqueue_buffer(s, &buf); - return AVERROR_INVALIDDATA; + buf.bytesused = 0; } } @@ -812,7 +811,8 @@ static int device_try_init(AVFormatContext *ctx, } *codec_id = ff_fmt_v4l2codec(*desired_format); - av_assert0(*codec_id != AV_CODEC_ID_NONE); + if (*codec_id == AV_CODEC_ID_NONE) + av_assert0(ret == AVERROR(EINVAL)); return ret; } diff --git a/libavdevice/v4l2enc.c b/libavdevice/v4l2enc.c index 1c36f81f90b..6e5cb884914 100644 --- a/libavdevice/v4l2enc.c +++ b/libavdevice/v4l2enc.c @@ -47,8 +47,7 @@ static av_cold int write_header(AVFormatContext *s1) } if (s1->nb_streams != 1 || - s1->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO || - s1->streams[0]->codecpar->codec_id != AV_CODEC_ID_RAWVIDEO) { + s1->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) { av_log(s1, AV_LOG_ERROR, "V4L2 output device supports only a single raw video stream\n"); return AVERROR(EINVAL); @@ -56,7 +55,12 @@ static av_cold int write_header(AVFormatContext *s1) par = s1->streams[0]->codecpar; - v4l2_pixfmt = ff_fmt_ff2v4l(par->format, AV_CODEC_ID_RAWVIDEO); + if(par->codec_id == AV_CODEC_ID_RAWVIDEO) { + v4l2_pixfmt = ff_fmt_ff2v4l(par->format, AV_CODEC_ID_RAWVIDEO); + } else { + v4l2_pixfmt = ff_fmt_ff2v4l(AV_PIX_FMT_NONE, par->codec_id); + } + if (!v4l2_pixfmt) { // XXX: try to force them one by one? av_log(s1, AV_LOG_ERROR, "Unknown V4L2 pixel format equivalent for %s\n", av_get_pix_fmt_name(par->format)); diff --git a/libavdevice/version.h b/libavdevice/version.h index 840cefad0f7..9ee3d3db4a2 100644 --- a/libavdevice/version.h +++ b/libavdevice/version.h @@ -28,7 +28,7 @@ #include "libavutil/version.h" #define LIBAVDEVICE_VERSION_MAJOR 58 -#define LIBAVDEVICE_VERSION_MINOR 8 +#define LIBAVDEVICE_VERSION_MINOR 10 #define LIBAVDEVICE_VERSION_MICRO 100 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \ diff --git a/libavdevice/xcbgrab.c b/libavdevice/xcbgrab.c index b7e689343e0..6f6b2dbf157 100644 --- a/libavdevice/xcbgrab.c +++ b/libavdevice/xcbgrab.c @@ -49,16 +49,15 @@ typedef struct XCBGrabContext { const AVClass *class; - uint8_t *buffer; - xcb_connection_t *conn; xcb_screen_t *screen; xcb_window_t window; #if CONFIG_LIBXCB_SHM - xcb_shm_seg_t segment; + AVBufferPool *shm_pool; #endif int64_t time_frame; AVRational time_base; + int64_t frame_duration; int x, y; int width, height; @@ -71,7 +70,6 @@ typedef struct XCBGrabContext { int region_border; int centered; - const char *video_size; const char *framerate; int has_shm; @@ -86,7 +84,7 @@ static const AVOption options[] = { { "y", "Initial y coordinate.", OFFSET(y), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D }, { "grab_x", "Initial x coordinate.", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D }, { "grab_y", "Initial y coordinate.", OFFSET(y), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D }, - { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = "vga" }, 0, 0, D }, + { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL }, 0, 0, D }, { "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.", @@ -146,6 +144,11 @@ static int xcbgrab_reposition(AVFormatContext *s, return 0; } +static void xcbgrab_image_reply_free(void *opaque, uint8_t *data) +{ + free(opaque); +} + static int xcbgrab_frame(AVFormatContext *s, AVPacket *pkt) { XCBGrabContext *c = s->priv_data; @@ -154,7 +157,7 @@ static int xcbgrab_frame(AVFormatContext *s, AVPacket *pkt) xcb_drawable_t drawable = c->screen->root; xcb_generic_error_t *e = NULL; uint8_t *data; - int length, ret; + int length; iq = xcb_get_image(c->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable, c->x, c->y, c->width, c->height, ~0); @@ -168,6 +171,7 @@ static int xcbgrab_frame(AVFormatContext *s, AVPacket *pkt) "sequence:%u resource_id:%u minor_code:%u major_code:%u.\n", e->response_type, e->error_code, e->sequence, e->resource_id, e->minor_code, e->major_code); + free(e); return AVERROR(EACCES); } @@ -177,23 +181,26 @@ static int xcbgrab_frame(AVFormatContext *s, AVPacket *pkt) data = xcb_get_image_data(img); length = xcb_get_image_data_length(img); - ret = av_new_packet(pkt, length); + av_init_packet(pkt); - if (!ret) - memcpy(pkt->data, data, length); + pkt->buf = av_buffer_create(data, length, xcbgrab_image_reply_free, img, 0); + if (!pkt->buf) { + free(img); + return AVERROR(ENOMEM); + } - free(img); + pkt->data = data; + pkt->size = length; - return ret; + return 0; } -static void wait_frame(AVFormatContext *s, AVPacket *pkt) +static int64_t wait_frame(AVFormatContext *s, AVPacket *pkt) { XCBGrabContext *c = s->priv_data; int64_t curtime, delay; - int64_t frame_time = av_rescale_q(1, c->time_base, AV_TIME_BASE_Q); - c->time_frame += frame_time; + c->time_frame += c->frame_duration; for (;;) { curtime = av_gettime(); @@ -203,7 +210,7 @@ static void wait_frame(AVFormatContext *s, AVPacket *pkt) av_usleep(delay); } - pkt->pts = curtime; + return curtime; } #if CONFIG_LIBXCB_SHM @@ -221,31 +228,35 @@ static int check_shm(xcb_connection_t *conn) return 0; } -static int allocate_shm(AVFormatContext *s) +static void free_shm_buffer(void *opaque, uint8_t *data) { - XCBGrabContext *c = s->priv_data; - int size = c->frame_size + AV_INPUT_BUFFER_PADDING_SIZE; + shmdt(data); +} + +static AVBufferRef *allocate_shm_buffer(void *opaque, int size) +{ + xcb_connection_t *conn = opaque; + xcb_shm_seg_t segment; + AVBufferRef *ref; uint8_t *data; int id; - if (c->buffer) - return 0; id = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777); - if (id == -1) { - char errbuf[1024]; - int err = AVERROR(errno); - av_strerror(err, errbuf, sizeof(errbuf)); - av_log(s, AV_LOG_ERROR, "Cannot get %d bytes of shared memory: %s.\n", - size, errbuf); - return err; - } - xcb_shm_attach(c->conn, c->segment, id, 0); + if (id == -1) + return NULL; + + segment = xcb_generate_id(conn); + xcb_shm_attach(conn, segment, id, 0); data = shmat(id, NULL, 0); shmctl(id, IPC_RMID, 0); if ((intptr_t)data == -1 || !data) - return AVERROR(errno); - c->buffer = data; - return 0; + return NULL; + + ref = av_buffer_create(data, size, free_shm_buffer, (void *)(ptrdiff_t)segment, 0); + if (!ref) + shmdt(data); + + return ref; } static int xcbgrab_frame_shm(AVFormatContext *s, AVPacket *pkt) @@ -255,15 +266,19 @@ static int xcbgrab_frame_shm(AVFormatContext *s, AVPacket *pkt) xcb_shm_get_image_reply_t *img; xcb_drawable_t drawable = c->screen->root; xcb_generic_error_t *e = NULL; - int ret; + AVBufferRef *buf; + xcb_shm_seg_t segment; - ret = allocate_shm(s); - if (ret < 0) - return ret; + buf = av_buffer_pool_get(c->shm_pool); + if (!buf) { + av_log(s, AV_LOG_ERROR, "Could not get shared memory buffer.\n"); + return AVERROR(ENOMEM); + } + segment = (xcb_shm_seg_t)av_buffer_pool_buffer_get_opaque(buf); iq = xcb_shm_get_image(c->conn, drawable, c->x, c->y, c->width, c->height, ~0, - XCB_IMAGE_FORMAT_Z_PIXMAP, c->segment, 0); + XCB_IMAGE_FORMAT_Z_PIXMAP, segment, 0); img = xcb_shm_get_image_reply(c->conn, iq, &e); xcb_flush(c->conn); @@ -276,12 +291,17 @@ static int xcbgrab_frame_shm(AVFormatContext *s, AVPacket *pkt) e->response_type, e->error_code, e->sequence, e->resource_id, e->minor_code, e->major_code); + free(e); + av_buffer_unref(&buf); return AVERROR(EACCES); } free(img); - pkt->data = c->buffer; + av_init_packet(pkt); + + pkt->buf = buf; + pkt->data = buf->data; pkt->size = c->frame_size; return 0; @@ -397,8 +417,9 @@ static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt) xcb_query_pointer_reply_t *p = NULL; xcb_get_geometry_reply_t *geo = NULL; int ret = 0; + int64_t pts; - wait_frame(s, pkt); + pts = wait_frame(s, pkt); if (c->follow_mouse || c->draw_mouse) { pc = xcb_query_pointer(c->conn, c->screen->root); @@ -414,11 +435,15 @@ static int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt) xcbgrab_update_region(s); #if CONFIG_LIBXCB_SHM - if (c->has_shm && xcbgrab_frame_shm(s, pkt) < 0) + if (c->has_shm && xcbgrab_frame_shm(s, pkt) < 0) { + av_log(s, AV_LOG_WARNING, "Continuing without shared memory.\n"); c->has_shm = 0; + } #endif if (!c->has_shm) ret = xcbgrab_frame(s, pkt); + pkt->dts = pkt->pts = pts; + pkt->duration = c->frame_duration; #if CONFIG_LIBXCB_XFIXES if (ret >= 0 && c->draw_mouse && p->same_screen) @@ -436,9 +461,7 @@ static av_cold int xcbgrab_read_close(AVFormatContext *s) XCBGrabContext *ctx = s->priv_data; #if CONFIG_LIBXCB_SHM - if (ctx->buffer) { - shmdt(ctx->buffer); - } + av_buffer_pool_uninit(&ctx->shm_pool); #endif xcb_disconnect(ctx->conn); @@ -464,7 +487,7 @@ static xcb_screen_t *get_screen(const xcb_setup_t *setup, int screen_num) } static int pixfmt_from_pixmap_format(AVFormatContext *s, int depth, - int *pix_fmt) + int *pix_fmt, int *bpp) { XCBGrabContext *c = s->priv_data; const xcb_setup_t *setup = xcb_get_setup(c->conn); @@ -502,8 +525,7 @@ static int pixfmt_from_pixmap_format(AVFormatContext *s, int depth, } if (*pix_fmt) { - c->bpp = fmt->bits_per_pixel; - c->frame_size = c->width * c->height * fmt->bits_per_pixel / 8; + *bpp = fmt->bits_per_pixel; return 0; } @@ -520,15 +542,12 @@ static int create_stream(AVFormatContext *s) AVStream *st = avformat_new_stream(s, NULL); xcb_get_geometry_cookie_t gc; xcb_get_geometry_reply_t *geo; + int64_t frame_size_bits; int ret; if (!st) return AVERROR(ENOMEM); - ret = av_parse_video_size(&c->width, &c->height, c->video_size); - if (ret < 0) - return ret; - ret = av_parse_video_rate(&st->avg_frame_rate, c->framerate); if (ret < 0) return ret; @@ -537,6 +556,13 @@ static int create_stream(AVFormatContext *s) gc = xcb_get_geometry(c->conn, c->screen->root); geo = xcb_get_geometry_reply(c->conn, gc, NULL); + if (!geo) + return AVERROR_EXTERNAL; + + if (!c->width || !c->height) { + c->width = geo->width; + c->height = geo->height; + } if (c->x + c->width > geo->width || c->y + c->height > geo->height) { @@ -546,21 +572,39 @@ static int create_stream(AVFormatContext *s) c->width, c->height, c->x, c->y, geo->width, geo->height); + free(geo); return AVERROR(EINVAL); } c->time_base = (AVRational){ st->avg_frame_rate.den, st->avg_frame_rate.num }; + c->frame_duration = av_rescale_q(1, c->time_base, AV_TIME_BASE_Q); c->time_frame = av_gettime(); + ret = pixfmt_from_pixmap_format(s, geo->depth, &st->codecpar->format, &c->bpp); + free(geo); + if (ret < 0) + return ret; + + frame_size_bits = (int64_t)c->width * c->height * c->bpp; + if (frame_size_bits / 8 + AV_INPUT_BUFFER_PADDING_SIZE > INT_MAX) { + av_log(s, AV_LOG_ERROR, "Captured area is too large\n"); + return AVERROR_PATCHWELCOME; + } + c->frame_size = frame_size_bits / 8; + +#if CONFIG_LIBXCB_SHM + c->shm_pool = av_buffer_pool_init2(c->frame_size + AV_INPUT_BUFFER_PADDING_SIZE, + c->conn, allocate_shm_buffer, NULL); + if (!c->shm_pool) + return AVERROR(ENOMEM); +#endif + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO; st->codecpar->width = c->width; st->codecpar->height = c->height; - - ret = pixfmt_from_pixmap_format(s, geo->depth, &st->codecpar->format); - - free(geo); + st->codecpar->bit_rate = av_rescale(frame_size_bits, st->avg_frame_rate.num, st->avg_frame_rate.den); return ret; } @@ -666,8 +710,7 @@ static av_cold int xcbgrab_read_header(AVFormatContext *s) } #if CONFIG_LIBXCB_SHM - if ((c->has_shm = check_shm(c->conn))) - c->segment = xcb_generate_id(c->conn); + c->has_shm = check_shm(c->conn); #endif #if CONFIG_LIBXCB_XFIXES diff --git a/libavdevice/xv.c b/libavdevice/xv.c index c3ed2e48bd2..50dc4e0d041 100644 --- a/libavdevice/xv.c +++ b/libavdevice/xv.c @@ -113,8 +113,8 @@ static int xv_write_header(AVFormatContext *s) if ( s->nb_streams > 1 || par->codec_type != AVMEDIA_TYPE_VIDEO - || par->codec_id != AV_CODEC_ID_RAWVIDEO) { - av_log(s, AV_LOG_ERROR, "Only supports one rawvideo stream\n"); + || (par->codec_id != AV_CODEC_ID_WRAPPED_AVFRAME && par->codec_id != AV_CODEC_ID_RAWVIDEO)) { + av_log(s, AV_LOG_ERROR, "Only a single raw or wrapped avframe video stream is supported.\n"); return AVERROR(EINVAL); } @@ -322,12 +322,18 @@ static int write_picture(AVFormatContext *s, uint8_t *input_data[4], static int xv_write_packet(AVFormatContext *s, AVPacket *pkt) { AVCodecParameters *par = s->streams[0]->codecpar; - uint8_t *data[4]; - int linesize[4]; - av_image_fill_arrays(data, linesize, pkt->data, par->format, - par->width, par->height, 1); - return write_picture(s, data, linesize); + if (par->codec_id == AV_CODEC_ID_WRAPPED_AVFRAME) { + AVFrame *frame = (AVFrame *)pkt->data; + return write_picture(s, frame->data, frame->linesize); + } else { + uint8_t *data[4]; + int linesize[4]; + + av_image_fill_arrays(data, linesize, pkt->data, par->format, + par->width, par->height, 1); + return write_picture(s, data, linesize); + } } static int xv_write_frame(AVFormatContext *s, int stream_index, AVFrame **frame, @@ -375,7 +381,7 @@ AVOutputFormat ff_xv_muxer = { .long_name = NULL_IF_CONFIG_SMALL("XV (XVideo) output device"), .priv_data_size = sizeof(XVContext), .audio_codec = AV_CODEC_ID_NONE, - .video_codec = AV_CODEC_ID_RAWVIDEO, + .video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, .write_header = xv_write_header, .write_packet = xv_write_packet, .write_uncoded_frame = xv_write_frame, diff --git a/libavfilter/.gitignore b/libavfilter/.gitignore new file mode 100644 index 00000000000..26bddebc933 --- /dev/null +++ b/libavfilter/.gitignore @@ -0,0 +1 @@ +/filter_list.c diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 455c809b151..51235406530 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -26,9 +26,8 @@ OBJS-$(HAVE_THREADS) += pthread.o # subsystems OBJS-$(CONFIG_QSVVPP) += qsvvpp.o -DNN-OBJS-$(CONFIG_LIBTENSORFLOW) += dnn_backend_tf.o -OBJS-$(CONFIG_DNN) += dnn_interface.o dnn_backend_native.o $(DNN-OBJS-yes) OBJS-$(CONFIG_SCENE_SAD) += scene_sad.o +include $(SRC_PATH)/libavfilter/dnn/Makefile # audio filters OBJS-$(CONFIG_ABENCH_FILTER) += f_bench.o @@ -64,6 +63,7 @@ OBJS-$(CONFIG_AMIX_FILTER) += af_amix.o OBJS-$(CONFIG_AMULTIPLY_FILTER) += af_amultiply.o OBJS-$(CONFIG_ANEQUALIZER_FILTER) += af_anequalizer.o OBJS-$(CONFIG_ANLMDN_FILTER) += af_anlmdn.o +OBJS-$(CONFIG_ANLMS_FILTER) += af_anlms.o OBJS-$(CONFIG_ANULL_FILTER) += af_anull.o OBJS-$(CONFIG_APAD_FILTER) += af_apad.o OBJS-$(CONFIG_APERMS_FILTER) += f_perms.o @@ -72,6 +72,7 @@ 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_ARNNDN_FILTER) += af_arnndn.o OBJS-$(CONFIG_ASELECT_FILTER) += f_select.o OBJS-$(CONFIG_ASENDCMD_FILTER) += f_sendcmd.o OBJS-$(CONFIG_ASETNSAMPLES_FILTER) += af_asetnsamples.o @@ -85,8 +86,10 @@ OBJS-$(CONFIG_ASPLIT_FILTER) += split.o OBJS-$(CONFIG_ASR_FILTER) += af_asr.o OBJS-$(CONFIG_ASTATS_FILTER) += af_astats.o OBJS-$(CONFIG_ASTREAMSELECT_FILTER) += f_streamselect.o framesync.o +OBJS-$(CONFIG_ASUBBOOST_FILTER) += af_asubboost.o OBJS-$(CONFIG_ATEMPO_FILTER) += af_atempo.o OBJS-$(CONFIG_ATRIM_FILTER) += trim.o +OBJS-$(CONFIG_AXCORRELATE_FILTER) += af_axcorrelate.o OBJS-$(CONFIG_AZMQ_FILTER) += f_zmq.o OBJS-$(CONFIG_BANDPASS_FILTER) += af_biquads.o OBJS-$(CONFIG_BANDREJECT_FILTER) += af_biquads.o @@ -142,6 +145,7 @@ OBJS-$(CONFIG_VOLUME_FILTER) += af_volume.o OBJS-$(CONFIG_VOLUMEDETECT_FILTER) += af_volumedetect.o OBJS-$(CONFIG_AEVALSRC_FILTER) += aeval.o +OBJS-$(CONFIG_AFIRSRC_FILTER) += asrc_afirsrc.o OBJS-$(CONFIG_ANOISESRC_FILTER) += asrc_anoisesrc.o OBJS-$(CONFIG_ANULLSRC_FILTER) += asrc_anullsrc.o OBJS-$(CONFIG_FLITE_FILTER) += asrc_flite.o @@ -152,6 +156,7 @@ OBJS-$(CONFIG_SINE_FILTER) += asrc_sine.o OBJS-$(CONFIG_ANULLSINK_FILTER) += asink_anullsink.o # video filters +OBJS-$(CONFIG_ADDROI_FILTER) += vf_addroi.o OBJS-$(CONFIG_ALPHAEXTRACT_FILTER) += vf_extractplanes.o OBJS-$(CONFIG_ALPHAMERGE_FILTER) += vf_alphamerge.o OBJS-$(CONFIG_AMPLIFY_FILTER) += vf_amplify.o @@ -160,17 +165,21 @@ OBJS-$(CONFIG_ATADENOISE_FILTER) += vf_atadenoise.o OBJS-$(CONFIG_AVGBLUR_FILTER) += vf_avgblur.o OBJS-$(CONFIG_AVGBLUR_OPENCL_FILTER) += vf_avgblur_opencl.o opencl.o \ opencl/avgblur.o boxblur.o +OBJS-$(CONFIG_AVGBLUR_VULKAN_FILTER) += vf_avgblur_vulkan.o vulkan.o OBJS-$(CONFIG_BBOX_FILTER) += bbox.o vf_bbox.o OBJS-$(CONFIG_BENCH_FILTER) += f_bench.o +OBJS-$(CONFIG_BILATERAL_FILTER) += vf_bilateral.o OBJS-$(CONFIG_BITPLANENOISE_FILTER) += vf_bitplanenoise.o OBJS-$(CONFIG_BLACKDETECT_FILTER) += vf_blackdetect.o OBJS-$(CONFIG_BLACKFRAME_FILTER) += vf_blackframe.o OBJS-$(CONFIG_BLEND_FILTER) += vf_blend.o framesync.o -OBJS-$(CONFIG_BM3D_FILTER) += vf_bm3d.o +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_CAS_FILTER) += vf_cas.o +OBJS-$(CONFIG_CHROMABER_VULKAN_FILTER) += vf_chromaber_vulkan.o vulkan.o OBJS-$(CONFIG_CHROMAHOLD_FILTER) += vf_chromakey.o OBJS-$(CONFIG_CHROMAKEY_FILTER) += vf_chromakey.o OBJS-$(CONFIG_CHROMASHIFT_FILTER) += vf_chromashift.o @@ -197,6 +206,7 @@ OBJS-$(CONFIG_CROPDETECT_FILTER) += vf_cropdetect.o OBJS-$(CONFIG_CUE_FILTER) += f_cue.o OBJS-$(CONFIG_CURVES_FILTER) += vf_curves.o OBJS-$(CONFIG_DATASCOPE_FILTER) += vf_datascope.o +OBJS-$(CONFIG_DBLUR_FILTER) += vf_dblur.o OBJS-$(CONFIG_DCTDNOIZ_FILTER) += vf_dctdnoiz.o OBJS-$(CONFIG_DEBAND_FILTER) += vf_deband.o OBJS-$(CONFIG_DEBLOCK_FILTER) += vf_deblock.o @@ -211,6 +221,8 @@ OBJS-$(CONFIG_DEINTERLACE_VAAPI_FILTER) += vf_deinterlace_vaapi.o vaapi_vpp OBJS-$(CONFIG_DEJUDDER_FILTER) += vf_dejudder.o OBJS-$(CONFIG_DELOGO_FILTER) += vf_delogo.o OBJS-$(CONFIG_DENOISE_VAAPI_FILTER) += vf_misc_vaapi.o vaapi_vpp.o +OBJS-$(CONFIG_DESHAKE_OPENCL_FILTER) += vf_deshake_opencl.o opencl.o \ + opencl/deshake.o OBJS-$(CONFIG_DESHAKE_FILTER) += vf_deshake.o OBJS-$(CONFIG_DESPILL_FILTER) += vf_despill.o OBJS-$(CONFIG_DETELECINE_FILTER) += vf_detelecine.o @@ -218,6 +230,7 @@ OBJS-$(CONFIG_DILATION_FILTER) += vf_neighbor.o OBJS-$(CONFIG_DILATION_OPENCL_FILTER) += vf_neighbor_opencl.o opencl.o \ opencl/neighbor.o OBJS-$(CONFIG_DISPLACE_FILTER) += vf_displace.o framesync.o +OBJS-$(CONFIG_DNN_PROCESSING_FILTER) += vf_dnn_processing.o OBJS-$(CONFIG_DOUBLEWEAVE_FILTER) += vf_weave.o OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o OBJS-$(CONFIG_DRAWGRAPH_FILTER) += f_drawgraph.o @@ -247,6 +260,7 @@ OBJS-$(CONFIG_FRAMEPACK_FILTER) += vf_framepack.o OBJS-$(CONFIG_FRAMERATE_FILTER) += vf_framerate.o OBJS-$(CONFIG_FRAMESTEP_FILTER) += vf_framestep.o 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 OBJS-$(CONFIG_GBLUR_FILTER) += vf_gblur.o @@ -283,19 +297,23 @@ OBJS-$(CONFIG_LUMAKEY_FILTER) += vf_lumakey.o OBJS-$(CONFIG_LUT1D_FILTER) += vf_lut3d.o OBJS-$(CONFIG_LUT_FILTER) += vf_lut.o OBJS-$(CONFIG_LUT2_FILTER) += vf_lut2.o framesync.o -OBJS-$(CONFIG_LUT3D_FILTER) += vf_lut3d.o +OBJS-$(CONFIG_LUT3D_FILTER) += vf_lut3d.o framesync.o OBJS-$(CONFIG_LUTRGB_FILTER) += vf_lut.o OBJS-$(CONFIG_LUTYUV_FILTER) += vf_lut.o OBJS-$(CONFIG_MASKEDCLAMP_FILTER) += vf_maskedclamp.o framesync.o +OBJS-$(CONFIG_MASKEDMAX_FILTER) += vf_maskedminmax.o framesync.o OBJS-$(CONFIG_MASKEDMERGE_FILTER) += vf_maskedmerge.o framesync.o +OBJS-$(CONFIG_MASKEDMIN_FILTER) += vf_maskedminmax.o framesync.o +OBJS-$(CONFIG_MASKEDTHRESHOLD_FILTER) += vf_maskedthreshold.o framesync.o OBJS-$(CONFIG_MASKFUN_FILTER) += vf_maskfun.o OBJS-$(CONFIG_MCDEINT_FILTER) += vf_mcdeint.o +OBJS-$(CONFIG_MEDIAN_FILTER) += vf_median.o OBJS-$(CONFIG_MERGEPLANES_FILTER) += vf_mergeplanes.o framesync.o OBJS-$(CONFIG_MESTIMATE_FILTER) += vf_mestimate.o motion_estimation.o OBJS-$(CONFIG_METADATA_FILTER) += f_metadata.o OBJS-$(CONFIG_MIDEQUALIZER_FILTER) += vf_midequalizer.o framesync.o OBJS-$(CONFIG_MINTERPOLATE_FILTER) += vf_minterpolate.o motion_estimation.o -OBJS-$(CONFIG_MIX_FILTER) += vf_mix.o +OBJS-$(CONFIG_MIX_FILTER) += vf_mix.o framesync.o OBJS-$(CONFIG_MPDECIMATE_FILTER) += vf_mpdecimate.o OBJS-$(CONFIG_NEGATE_FILTER) += vf_lut.o OBJS-$(CONFIG_NLMEANS_FILTER) += vf_nlmeans.o @@ -309,16 +327,20 @@ OBJS-$(CONFIG_OCR_FILTER) += vf_ocr.o OBJS-$(CONFIG_OCV_FILTER) += vf_libopencv.o OBJS-$(CONFIG_OSCILLOSCOPE_FILTER) += vf_datascope.o OBJS-$(CONFIG_OVERLAY_FILTER) += vf_overlay.o framesync.o +OBJS-$(CONFIG_OVERLAY_CUDA_FILTER) += vf_overlay_cuda.o framesync.o vf_overlay_cuda.ptx.o OBJS-$(CONFIG_OVERLAY_OPENCL_FILTER) += vf_overlay_opencl.o opencl.o \ opencl/overlay.o framesync.o OBJS-$(CONFIG_OVERLAY_QSV_FILTER) += vf_overlay_qsv.o framesync.o +OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER) += vf_overlay_vulkan.o vulkan.o OBJS-$(CONFIG_OWDENOISE_FILTER) += vf_owdenoise.o OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o +OBJS-$(CONFIG_PAD_OPENCL_FILTER) += vf_pad_opencl.o opencl.o opencl/pad.o OBJS-$(CONFIG_PALETTEGEN_FILTER) += vf_palettegen.o OBJS-$(CONFIG_PALETTEUSE_FILTER) += vf_paletteuse.o framesync.o OBJS-$(CONFIG_PERMS_FILTER) += f_perms.o OBJS-$(CONFIG_PERSPECTIVE_FILTER) += vf_perspective.o OBJS-$(CONFIG_PHASE_FILTER) += vf_phase.o +OBJS-$(CONFIG_PHOTOSENSITIVITY_FILTER) += vf_photosensitivity.o OBJS-$(CONFIG_PIXDESCTEST_FILTER) += vf_pixdesctest.o OBJS-$(CONFIG_PIXSCOPE_FILTER) += vf_datascope.o OBJS-$(CONFIG_PP_FILTER) += vf_pp.o @@ -348,12 +370,15 @@ OBJS-$(CONFIG_ROBERTS_OPENCL_FILTER) += vf_convolution_opencl.o opencl.o opencl/convolution.o OBJS-$(CONFIG_ROTATE_FILTER) += vf_rotate.o OBJS-$(CONFIG_SAB_FILTER) += vf_sab.o -OBJS-$(CONFIG_SCALE_FILTER) += vf_scale.o scale.o -OBJS-$(CONFIG_SCALE_CUDA_FILTER) += vf_scale_cuda.o vf_scale_cuda.ptx.o -OBJS-$(CONFIG_SCALE_NPP_FILTER) += vf_scale_npp.o scale.o +OBJS-$(CONFIG_SCALE_FILTER) += vf_scale.o scale_eval.o +OBJS-$(CONFIG_SCALE_CUDA_FILTER) += vf_scale_cuda.o vf_scale_cuda.ptx.o scale_eval.o +OBJS-$(CONFIG_SCALE_NPP_FILTER) += vf_scale_npp.o scale_eval.o OBJS-$(CONFIG_SCALE_QSV_FILTER) += vf_scale_qsv.o -OBJS-$(CONFIG_SCALE_VAAPI_FILTER) += vf_scale_vaapi.o scale.o vaapi_vpp.o -OBJS-$(CONFIG_SCALE2REF_FILTER) += vf_scale.o scale.o +OBJS-$(CONFIG_SCALE_VAAPI_FILTER) += vf_scale_vaapi.o scale_eval.o vaapi_vpp.o +OBJS-$(CONFIG_SCALE_VULKAN_FILTER) += vf_scale_vulkan.o vulkan.o +OBJS-$(CONFIG_SCALE2REF_FILTER) += vf_scale.o scale_eval.o +OBJS-$(CONFIG_SCDET_FILTER) += vf_scdet.o +OBJS-$(CONFIG_SCROLL_FILTER) += vf_scroll.o OBJS-$(CONFIG_SELECT_FILTER) += f_select.o OBJS-$(CONFIG_SELECTIVECOLOR_FILTER) += vf_selectivecolor.o OBJS-$(CONFIG_SENDCMD_FILTER) += f_sendcmd.o @@ -389,16 +414,19 @@ OBJS-$(CONFIG_SWAPRECT_FILTER) += vf_swaprect.o OBJS-$(CONFIG_SWAPUV_FILTER) += vf_swapuv.o OBJS-$(CONFIG_TBLEND_FILTER) += vf_blend.o framesync.o OBJS-$(CONFIG_TELECINE_FILTER) += vf_telecine.o +OBJS-$(CONFIG_THISTOGRAM_FILTER) += vf_histogram.o OBJS-$(CONFIG_THRESHOLD_FILTER) += vf_threshold.o framesync.o OBJS-$(CONFIG_THUMBNAIL_FILTER) += vf_thumbnail.o OBJS-$(CONFIG_THUMBNAIL_CUDA_FILTER) += vf_thumbnail_cuda.o vf_thumbnail_cuda.ptx.o OBJS-$(CONFIG_TILE_FILTER) += vf_tile.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 OBJS-$(CONFIG_TMIX_FILTER) += vf_mix.o framesync.o OBJS-$(CONFIG_TONEMAP_FILTER) += vf_tonemap.o colorspace.o OBJS-$(CONFIG_TONEMAP_OPENCL_FILTER) += vf_tonemap_opencl.o colorspace.o opencl.o \ opencl/tonemap.o opencl/colorspace_common.o +OBJS-$(CONFIG_TONEMAP_VAAPI_FILTER) += vf_tonemap_vaapi.o vaapi_vpp.o OBJS-$(CONFIG_TPAD_FILTER) += vf_tpad.o OBJS-$(CONFIG_TRANSPOSE_FILTER) += vf_transpose.o OBJS-$(CONFIG_TRANSPOSE_NPP_FILTER) += vf_transpose_npp.o @@ -409,7 +437,9 @@ OBJS-$(CONFIG_UNPREMULTIPLY_FILTER) += vf_premultiply.o framesync.o OBJS-$(CONFIG_UNSHARP_FILTER) += vf_unsharp.o OBJS-$(CONFIG_UNSHARP_OPENCL_FILTER) += vf_unsharp_opencl.o opencl.o \ opencl/unsharp.o +OBJS-$(CONFIG_UNTILE_FILTER) += vf_untile.o OBJS-$(CONFIG_USPP_FILTER) += vf_uspp.o +OBJS-$(CONFIG_V360_FILTER) += vf_v360.o OBJS-$(CONFIG_VAGUEDENOISER_FILTER) += vf_vaguedenoiser.o OBJS-$(CONFIG_VECTORSCOPE_FILTER) += vf_vectorscope.o OBJS-$(CONFIG_VFLIP_FILTER) += vf_vflip.o @@ -425,11 +455,14 @@ OBJS-$(CONFIG_W3FDIF_FILTER) += vf_w3fdif.o OBJS-$(CONFIG_WAVEFORM_FILTER) += vf_waveform.o OBJS-$(CONFIG_WEAVE_FILTER) += vf_weave.o OBJS-$(CONFIG_XBR_FILTER) += vf_xbr.o +OBJS-$(CONFIG_XFADE_FILTER) += vf_xfade.o +OBJS-$(CONFIG_XFADE_OPENCL_FILTER) += vf_xfade_opencl.o opencl.o opencl/xfade.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 OBJS-$(CONFIG_YADIF_CUDA_FILTER) += vf_yadif_cuda.o vf_yadif_cuda.ptx.o \ yadif_common.o +OBJS-$(CONFIG_YAEPBLUR_FILTER) += vf_yaepblur.o OBJS-$(CONFIG_ZMQ_FILTER) += f_zmq.o OBJS-$(CONFIG_ZOOMPAN_FILTER) += vf_zoompan.o OBJS-$(CONFIG_ZSCALE_FILTER) += vf_zscale.o @@ -440,6 +473,7 @@ OBJS-$(CONFIG_CELLAUTO_FILTER) += vsrc_cellauto.o OBJS-$(CONFIG_COLOR_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_COREIMAGESRC_FILTER) += vf_coreimage.o OBJS-$(CONFIG_FREI0R_SRC_FILTER) += vf_frei0r.o +OBJS-$(CONFIG_GRADIENTS_FILTER) += vsrc_gradients.o OBJS-$(CONFIG_HALDCLUTSRC_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_LIFE_FILTER) += vsrc_life.o OBJS-$(CONFIG_MANDELBROT_FILTER) += vsrc_mandelbrot.o @@ -449,6 +483,7 @@ 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_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_TESTSRC_FILTER) += vsrc_testsrc.o @@ -489,6 +524,9 @@ OBJS-$(CONFIG_SHARED) += log2_tab.o SKIPHEADERS-$(CONFIG_QSVVPP) += qsvvpp.h SKIPHEADERS-$(CONFIG_OPENCL) += opencl.h SKIPHEADERS-$(CONFIG_VAAPI) += vaapi_vpp.h +SKIPHEADERS-$(CONFIG_VULKAN) += vulkan.h + +OBJS-$(CONFIG_LIBGLSLANG) += glslang.o TOOLS = graph2dot TESTPROGS = drawutils filtfmts formats integral @@ -496,7 +534,7 @@ TESTPROGS = drawutils filtfmts formats integral TOOLS-$(CONFIG_LIBZMQ) += zmqsend clean:: - $(RM) $(CLEANSUFFIXES:%=libavfilter/libmpcodecs/%) + $(RM) $(CLEANSUFFIXES:%=libavfilter/dnn/%) OPENCL = $(subst $(SRC_PATH)/,,$(wildcard $(SRC_PATH)/libavfilter/opencl/*.cl)) .SECONDARY: $(OPENCL:.cl=.c) diff --git a/libavfilter/aeval.c b/libavfilter/aeval.c index cdddbaf31d4..32cd6de0435 100644 --- a/libavfilter/aeval.c +++ b/libavfilter/aeval.c @@ -89,8 +89,8 @@ static const AVOption aevalsrc_options[]= { { "exprs", "set the '|'-separated list of channels expressions", OFFSET(exprs), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS }, { "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 0, INT_MAX, FLAGS }, { "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 0, INT_MAX, FLAGS }, - { "sample_rate", "set the sample rate", OFFSET(sample_rate_str), AV_OPT_TYPE_STRING, {.str = "44100"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "s", "set the sample rate", OFFSET(sample_rate_str), AV_OPT_TYPE_STRING, {.str = "44100"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "sample_rate", "set the sample rate", OFFSET(sample_rate_str), AV_OPT_TYPE_STRING, {.str = "44100"}, 0, 0, FLAGS }, + { "s", "set the sample rate", OFFSET(sample_rate_str), AV_OPT_TYPE_STRING, {.str = "44100"}, 0, 0, FLAGS }, { "duration", "set audio duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS }, { "d", "set audio duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS }, { "channel_layout", "set channel layout", OFFSET(chlayout_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, diff --git a/libavfilter/af_acopy.c b/libavfilter/af_acopy.c index d8490609663..2d915019db1 100644 --- a/libavfilter/af_acopy.c +++ b/libavfilter/af_acopy.c @@ -24,15 +24,25 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterLink *outlink = inlink->dst->outputs[0]; AVFrame *out = ff_get_audio_buffer(outlink, in->nb_samples); + int ret; if (!out) { - av_frame_free(&in); - return AVERROR(ENOMEM); + ret = AVERROR(ENOMEM); + goto fail; } - av_frame_copy_props(out, in); - av_frame_copy(out, in); + + ret = av_frame_copy_props(out, in); + if (ret < 0) + goto fail; + ret = av_frame_copy(out, in); + if (ret < 0) + goto fail; av_frame_free(&in); return ff_filter_frame(outlink, out); +fail: + av_frame_free(&in); + av_frame_free(&out); + return ret; } static const AVFilterPad acopy_inputs[] = { diff --git a/libavfilter/af_acrossover.c b/libavfilter/af_acrossover.c index 3ccc4d72f60..002f378c3de 100644 --- a/libavfilter/af_acrossover.c +++ b/libavfilter/af_acrossover.c @@ -61,6 +61,9 @@ typedef struct AudioCrossoverContext { float *splits; CrossoverChannel *xover; + + AVFrame *input_frame; + AVFrame *frames[MAX_BANDS]; } AudioCrossoverContext; #define OFFSET(x) offsetof(AudioCrossoverContext, x) @@ -96,7 +99,10 @@ static av_cold int init(AVFilterContext *ctx) p = NULL; - av_sscanf(arg, "%f", &freq); + if (av_sscanf(arg, "%f", &freq) != 1) { + av_log(ctx, AV_LOG_ERROR, "Invalid syntax for frequency[%d].\n", i); + return AVERROR(EINVAL); + } if (freq <= 0) { av_log(ctx, AV_LOG_ERROR, "Frequency %f must be positive number.\n", freq); return AVERROR(EINVAL); @@ -131,21 +137,22 @@ static av_cold int init(AVFilterContext *ctx) return ret; } -static void set_lp(BiquadContext *b, float fc, float q, float sr) +static void set_lp(BiquadContext *b, double fc, double q, double sr) { - double omega = (2.0 * M_PI * fc / sr); + double omega = 2.0 * M_PI * fc / sr; double sn = sin(omega); double cs = cos(omega); - double alpha = (sn / (2 * q)); - double inv = (1.0 / (1.0 + alpha)); + double alpha = sn / (2. * q); + double inv = 1.0 / (1.0 + alpha); - b->a2 = b->a0 = (inv * (1.0 - cs) * 0.5); - b->a1 = b->a0 + b->a0; + b->a0 = (1. - cs) * 0.5 * inv; + b->a1 = (1. - cs) * inv; + b->a2 = b->a0; b->b1 = -2. * cs * inv; b->b2 = (1. - alpha) * inv; } -static void set_hp(BiquadContext *b, float fc, float q, float sr) +static void set_hp(BiquadContext *b, double fc, double q, double sr) { double omega = 2 * M_PI * fc / sr; double sn = sin(omega); @@ -250,12 +257,53 @@ static double biquad_process(BiquadContext *b, double in) return out; } +static int filter_channels(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + AudioCrossoverContext *s = ctx->priv; + AVFrame *in = s->input_frame; + AVFrame **frames = s->frames; + const int start = (in->channels * jobnr) / nb_jobs; + const int end = (in->channels * (jobnr+1)) / nb_jobs; + int f, band; + + for (int ch = start; ch < end; ch++) { + const double *src = (const double *)in->extended_data[ch]; + CrossoverChannel *xover = &s->xover[ch]; + + for (int i = 0; i < in->nb_samples; i++) { + double sample = src[i], lo, hi; + + for (band = 0; band < ctx->nb_outputs; band++) { + double *dst = (double *)frames[band]->extended_data[ch]; + + lo = sample; + hi = sample; + for (f = 0; band + 1 < ctx->nb_outputs && f < s->filter_count; f++) { + BiquadContext *lp = &xover->lp[band][f]; + lo = biquad_process(lp, lo); + } + + for (f = 0; band + 1 < ctx->nb_outputs && f < s->filter_count; f++) { + BiquadContext *hp = &xover->hp[band][f]; + hi = biquad_process(hp, hi); + } + + dst[i] = lo; + + sample = hi; + } + } + } + + return 0; +} + static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; AudioCrossoverContext *s = ctx->priv; - AVFrame *frames[MAX_BANDS] = { NULL }; - int i, f, ch, band, ret = 0; + AVFrame **frames = s->frames; + int i, ret = 0; for (i = 0; i < ctx->nb_outputs; i++) { frames[i] = ff_get_audio_buffer(ctx->outputs[i], in->nb_samples); @@ -271,39 +319,22 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) if (ret < 0) goto fail; - for (ch = 0; ch < inlink->channels; ch++) { - const double *src = (const double *)in->extended_data[ch]; - CrossoverChannel *xover = &s->xover[ch]; - - for (band = 0; band < ctx->nb_outputs; band++) { - double *dst = (double *)frames[band]->extended_data[ch]; - - for (i = 0; i < in->nb_samples; i++) { - dst[i] = src[i]; - - for (f = 0; f < s->filter_count; f++) { - if (band + 1 < ctx->nb_outputs) { - BiquadContext *lp = &xover->lp[band][f]; - dst[i] = biquad_process(lp, dst[i]); - } - - if (band - 1 >= 0) { - BiquadContext *hp = &xover->hp[band - 1][f]; - dst[i] = biquad_process(hp, dst[i]); - } - } - } - } - } + s->input_frame = in; + ctx->internal->execute(ctx, filter_channels, NULL, NULL, FFMIN(inlink->channels, + ff_filter_get_nb_threads(ctx))); for (i = 0; i < ctx->nb_outputs; i++) { ret = ff_filter_frame(ctx->outputs[i], frames[i]); + frames[i] = NULL; if (ret < 0) break; } fail: + for (i = 0; i < ctx->nb_outputs; i++) + av_frame_free(&frames[i]); av_frame_free(&in); + s->input_frame = NULL; return ret; } @@ -314,6 +345,7 @@ static av_cold void uninit(AVFilterContext *ctx) int i; av_freep(&s->splits); + av_freep(&s->xover); for (i = 0; i < ctx->nb_outputs; i++) av_freep(&ctx->output_pads[i].name); @@ -339,5 +371,6 @@ AVFilter ff_af_acrossover = { .query_formats = query_formats, .inputs = inputs, .outputs = NULL, - .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS, + .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS | + AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/af_adeclick.c b/libavfilter/af_adeclick.c index 50eef749218..e86a1f7bef2 100644 --- a/libavfilter/af_adeclick.c +++ b/libavfilter/af_adeclick.c @@ -63,6 +63,7 @@ typedef struct AudioDeclickContext { int hop_size; int overlap_skip; + AVFrame *enabled; AVFrame *in; AVFrame *out; AVFrame *buffer; @@ -77,6 +78,7 @@ typedef struct AudioDeclickContext { int samples_left; int eof; + AVAudioFifo *efifo; AVAudioFifo *fifo; double *window_func_lut; @@ -159,13 +161,17 @@ static int config_input(AVFilterLink *inlink) av_frame_free(&s->out); av_frame_free(&s->buffer); av_frame_free(&s->is); + s->enabled = ff_get_audio_buffer(inlink, s->window_size); s->in = ff_get_audio_buffer(inlink, s->window_size); s->out = ff_get_audio_buffer(inlink, s->window_size); s->buffer = ff_get_audio_buffer(inlink, s->window_size * 2); s->is = ff_get_audio_buffer(inlink, s->window_size); - if (!s->in || !s->out || !s->buffer || !s->is) + if (!s->in || !s->out || !s->buffer || !s->is || !s->enabled) return AVERROR(ENOMEM); + s->efifo = av_audio_fifo_alloc(inlink->format, 1, s->window_size); + if (!s->efifo) + return AVERROR(ENOMEM); s->fifo = av_audio_fifo_alloc(inlink->format, inlink->channels, s->window_size); if (!s->fifo) return AVERROR(ENOMEM); @@ -513,14 +519,20 @@ static int filter_channel(AVFilterContext *ctx, void *arg, int ch, int nb_jobs) nb_errors = s->detector(s, c, sigmae, c->detection, c->acoefficients, c->click, index, src, dst); if (nb_errors > 0) { + double *enabled = (double *)s->enabled->extended_data[0]; + ret = interpolation(c, src, s->ar_order, c->acoefficients, index, nb_errors, c->auxiliary, interpolated); if (ret < 0) return ret; + av_audio_fifo_peek(s->efifo, (void**)s->enabled->extended_data, s->window_size); + for (j = 0; j < nb_errors; j++) { - dst[index[j]] = interpolated[j]; - is[index[j]] = 1; + if (enabled[index[j]]) { + dst[index[j]] = interpolated[j]; + is[index[j]] = 1; + } } } } else { @@ -580,19 +592,20 @@ static int filter_frame(AVFilterLink *inlink) } av_audio_fifo_drain(s->fifo, s->hop_size); + av_audio_fifo_drain(s->efifo, s->hop_size); if (s->samples_left > 0) out->nb_samples = FFMIN(s->hop_size, s->samples_left); out->pts = s->pts; - s->pts += s->hop_size; + s->pts += av_rescale_q(s->hop_size, (AVRational){1, outlink->sample_rate}, outlink->time_base); s->detected_errors += detected_errors; s->nb_samples += out->nb_samples * inlink->channels; ret = ff_filter_frame(outlink, out); if (ret < 0) - goto fail; + return ret; if (s->samples_left > 0) { s->samples_left -= s->hop_size; @@ -621,11 +634,17 @@ static int activate(AVFilterContext *ctx) if (ret < 0) return ret; if (ret > 0) { + double *e = (double *)s->enabled->extended_data[0]; + if (s->pts == AV_NOPTS_VALUE) s->pts = in->pts; ret = av_audio_fifo_write(s->fifo, (void **)in->extended_data, in->nb_samples); + for (int i = 0; i < in->nb_samples; i++) + e[i] = !ctx->is_disabled; + + av_audio_fifo_write(s->efifo, (void**)s->enabled->extended_data, in->nb_samples); av_frame_free(&in); if (ret < 0) return ret; @@ -684,7 +703,9 @@ static av_cold void uninit(AVFilterContext *ctx) s->nb_samples, 100. * s->detected_errors / s->nb_samples); av_audio_fifo_free(s->fifo); + av_audio_fifo_free(s->efifo); av_freep(&s->window_func_lut); + av_frame_free(&s->enabled); av_frame_free(&s->in); av_frame_free(&s->out); av_frame_free(&s->buffer); @@ -744,7 +765,7 @@ AVFilter ff_af_adeclick = { .uninit = uninit, .inputs = inputs, .outputs = outputs, - .flags = AVFILTER_FLAG_SLICE_THREADS, + .flags = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, }; static const AVOption adeclip_options[] = { @@ -772,5 +793,5 @@ AVFilter ff_af_adeclip = { .uninit = uninit, .inputs = inputs, .outputs = outputs, - .flags = AVFILTER_FLAG_SLICE_THREADS, + .flags = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, }; diff --git a/libavfilter/af_adelay.c b/libavfilter/af_adelay.c index eb97039566d..6ac81c2a3e0 100644 --- a/libavfilter/af_adelay.c +++ b/libavfilter/af_adelay.c @@ -36,6 +36,7 @@ typedef struct ChanDelay { typedef struct AudioDelayContext { const AVClass *class; + int all; char *delays; ChanDelay *chandelay; int nb_delays; @@ -54,6 +55,7 @@ typedef struct AudioDelayContext { static const AVOption adelay_options[] = { { "delays", "set list of delays for each channel", OFFSET(delays), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, A }, + { "all", "use last available delay for remained channels", OFFSET(all), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, A }, { NULL } }; @@ -153,7 +155,10 @@ static int config_input(AVFilterLink *inlink) ret = av_sscanf(arg, "%d%c", &d->delay, &type); if (ret != 2 || type != 'S') { div = type == 's' ? 1.0 : 1000.0; - av_sscanf(arg, "%f", &delay); + if (av_sscanf(arg, "%f", &delay) != 1) { + av_log(ctx, AV_LOG_ERROR, "Invalid syntax for delay.\n"); + return AVERROR(EINVAL); + } d->delay = delay * inlink->sample_rate / div; } @@ -163,6 +168,11 @@ static int config_input(AVFilterLink *inlink) } } + if (s->all && i) { + for (int j = i; j < s->nb_delays; j++) + s->chandelay[j].delay = s->chandelay[i-1].delay; + } + s->padding = s->chandelay[0].delay; for (i = 1; i < s->nb_delays; i++) { ChanDelay *d = &s->chandelay[i]; diff --git a/libavfilter/af_aecho.c b/libavfilter/af_aecho.c index 876a149df4b..7a7b8bccf24 100644 --- a/libavfilter/af_aecho.c +++ b/libavfilter/af_aecho.c @@ -24,6 +24,7 @@ #include "libavutil/samplefmt.h" #include "avfilter.h" #include "audio.h" +#include "filters.h" #include "internal.h" typedef struct AudioEchoContext { @@ -36,6 +37,7 @@ typedef struct AudioEchoContext { uint8_t **delayptrs; int max_samples, fade_out; int *samples; + int eof; int64_t next_pts; void (*echo_samples)(struct AudioEchoContext *ctx, uint8_t **delayptrs, @@ -302,42 +304,65 @@ static int request_frame(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; AudioEchoContext *s = ctx->priv; - int ret; + int nb_samples = FFMIN(s->fade_out, 2048); + AVFrame *frame = ff_get_audio_buffer(outlink, nb_samples); - ret = ff_request_frame(ctx->inputs[0]); + if (!frame) + return AVERROR(ENOMEM); + s->fade_out -= nb_samples; - if (ret == AVERROR_EOF && !ctx->is_disabled && s->fade_out) { - int nb_samples = FFMIN(s->fade_out, 2048); - AVFrame *frame; + av_samples_set_silence(frame->extended_data, 0, + frame->nb_samples, + outlink->channels, + frame->format); - frame = ff_get_audio_buffer(outlink, nb_samples); - if (!frame) - return AVERROR(ENOMEM); - s->fade_out -= nb_samples; + s->echo_samples(s, s->delayptrs, frame->extended_data, frame->extended_data, + frame->nb_samples, outlink->channels); - av_samples_set_silence(frame->extended_data, 0, - frame->nb_samples, - outlink->channels, - frame->format); + 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->echo_samples(s, s->delayptrs, frame->extended_data, frame->extended_data, - frame->nb_samples, outlink->channels); + return ff_filter_frame(outlink, frame); +} + +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + AudioEchoContext *s = ctx->priv; + AVFrame *in; + int ret, status; + int64_t pts; - 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); + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); - return ff_filter_frame(outlink, frame); + ret = ff_inlink_consume_frame(inlink, &in); + if (ret < 0) + return ret; + if (ret > 0) + return filter_frame(inlink, in); + + if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) { + if (status == AVERROR_EOF) + s->eof = 1; } - return ret; + if (s->eof && s->fade_out <= 0) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->next_pts); + return 0; + } + + if (!s->eof) + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return request_frame(outlink); } static const AVFilterPad aecho_inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = filter_frame, }, { NULL } }; @@ -345,7 +370,6 @@ static const AVFilterPad aecho_inputs[] = { static const AVFilterPad aecho_outputs[] = { { .name = "default", - .request_frame = request_frame, .config_props = config_output, .type = AVMEDIA_TYPE_AUDIO, }, @@ -359,6 +383,7 @@ AVFilter ff_af_aecho = { .priv_size = sizeof(AudioEchoContext), .priv_class = &aecho_class, .init = init, + .activate = activate, .uninit = uninit, .inputs = aecho_inputs, .outputs = aecho_outputs, diff --git a/libavfilter/af_afade.c b/libavfilter/af_afade.c index 195fb65ab51..4edfd27a3c6 100644 --- a/libavfilter/af_afade.c +++ b/libavfilter/af_afade.c @@ -454,21 +454,22 @@ static int activate(AVFilterContext *ctx) if (s->crossfade_is_over) { ret = ff_inlink_consume_frame(ctx->inputs[1], &in); - if (ret < 0) { + if (ret > 0) { + in->pts = s->pts; + s->pts += av_rescale_q(in->nb_samples, + (AVRational){ 1, outlink->sample_rate }, outlink->time_base); + return ff_filter_frame(outlink, in); + } 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); return 0; - } else { - if (ff_outlink_frame_wanted(ctx->outputs[0]) && !in) { + } else if (!ret) { + if (ff_outlink_frame_wanted(ctx->outputs[0])) { ff_inlink_request_frame(ctx->inputs[1]); return 0; } } - in->pts = s->pts; - s->pts += av_rescale_q(in->nb_samples, - (AVRational){ 1, outlink->sample_rate }, outlink->time_base); - return ff_filter_frame(outlink, in); } if (ff_inlink_queued_samples(ctx->inputs[0]) > s->nb_samples) { @@ -483,7 +484,8 @@ 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 (ff_inlink_queued_samples(ctx->inputs[1]) >= s->nb_samples) { + } else if (ff_inlink_queued_samples(ctx->inputs[0]) >= s->nb_samples && + ff_inlink_queued_samples(ctx->inputs[1]) >= s->nb_samples && s->cf0_eof) { if (s->overlap) { out = ff_get_audio_buffer(outlink, s->nb_samples); if (!out) diff --git a/libavfilter/af_afftdn.c b/libavfilter/af_afftdn.c index 9619aadbee0..bde5ff66858 100644 --- a/libavfilter/af_afftdn.c +++ b/libavfilter/af_afftdn.c @@ -141,24 +141,25 @@ typedef struct AudioFFTDeNoiseContext { } AudioFFTDeNoiseContext; #define OFFSET(x) offsetof(AudioFFTDeNoiseContext, x) -#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define AFR AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption afftdn_options[] = { - { "nr", "set the noise reduction", OFFSET(noise_reduction), AV_OPT_TYPE_FLOAT, {.dbl = 12}, .01, 97, A }, - { "nf", "set the noise floor", OFFSET(noise_floor), AV_OPT_TYPE_FLOAT, {.dbl =-50}, -80,-20, A }, - { "nt", "set the noise type", OFFSET(noise_type), AV_OPT_TYPE_INT, {.i64 = WHITE_NOISE}, WHITE_NOISE, NB_NOISE-1, A, "type" }, - { "w", "white noise", 0, AV_OPT_TYPE_CONST, {.i64 = WHITE_NOISE}, 0, 0, A, "type" }, - { "v", "vinyl noise", 0, AV_OPT_TYPE_CONST, {.i64 = VINYL_NOISE}, 0, 0, A, "type" }, - { "s", "shellac noise", 0, AV_OPT_TYPE_CONST, {.i64 = SHELLAC_NOISE}, 0, 0, A, "type" }, - { "c", "custom noise", 0, AV_OPT_TYPE_CONST, {.i64 = CUSTOM_NOISE}, 0, 0, A, "type" }, - { "bn", "set the custom bands noise", OFFSET(band_noise_str), AV_OPT_TYPE_STRING, {.str = 0}, 0, 0, A }, - { "rf", "set the residual floor", OFFSET(residual_floor), AV_OPT_TYPE_FLOAT, {.dbl =-38}, -80,-20, A }, - { "tn", "track noise", OFFSET(track_noise), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, A }, - { "tr", "track residual", OFFSET(track_residual), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, A }, - { "om", "set output mode", OFFSET(output_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" }, - { "n", "noise", 0, AV_OPT_TYPE_CONST, {.i64 = NOISE_MODE}, 0, 0, A, "mode" }, + { "nr", "set the noise reduction", OFFSET(noise_reduction), AV_OPT_TYPE_FLOAT, {.dbl = 12}, .01, 97, AFR }, + { "nf", "set the noise floor", OFFSET(noise_floor), AV_OPT_TYPE_FLOAT, {.dbl =-50}, -80,-20, AFR }, + { "nt", "set the noise type", OFFSET(noise_type), AV_OPT_TYPE_INT, {.i64 = WHITE_NOISE}, WHITE_NOISE, NB_NOISE-1, AF, "type" }, + { "w", "white noise", 0, AV_OPT_TYPE_CONST, {.i64 = WHITE_NOISE}, 0, 0, AF, "type" }, + { "v", "vinyl noise", 0, AV_OPT_TYPE_CONST, {.i64 = VINYL_NOISE}, 0, 0, AF, "type" }, + { "s", "shellac noise", 0, AV_OPT_TYPE_CONST, {.i64 = SHELLAC_NOISE}, 0, 0, AF, "type" }, + { "c", "custom noise", 0, AV_OPT_TYPE_CONST, {.i64 = CUSTOM_NOISE}, 0, 0, AF, "type" }, + { "bn", "set the custom bands noise", OFFSET(band_noise_str), AV_OPT_TYPE_STRING, {.str = 0}, 0, 0, AF }, + { "rf", "set the residual floor", OFFSET(residual_floor), AV_OPT_TYPE_FLOAT, {.dbl =-38}, -80,-20, AFR }, + { "tn", "track noise", OFFSET(track_noise), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AFR }, + { "tr", "track residual", OFFSET(track_residual), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AFR }, + { "om", "set output mode", OFFSET(output_mode), AV_OPT_TYPE_INT, {.i64 = OUT_MODE}, 0, NB_MODES-1, AFR, "mode" }, + { "i", "input", 0, AV_OPT_TYPE_CONST, {.i64 = IN_MODE}, 0, 0, AFR, "mode" }, + { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64 = OUT_MODE}, 0, 0, AFR, "mode" }, + { "n", "noise", 0, AV_OPT_TYPE_CONST, {.i64 = NOISE_MODE}, 0, 0, AFR, "mode" }, { NULL } }; @@ -1260,7 +1261,7 @@ static int output_frame(AVFilterLink *inlink) ret = ff_filter_frame(outlink, out); if (ret < 0) goto end; - s->pts += s->sample_advance; + s->pts += av_rescale_q(s->sample_advance, (AVRational){1, outlink->sample_rate}, outlink->time_base); end: av_frame_free(&in); @@ -1375,6 +1376,7 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar { AudioFFTDeNoiseContext *s = ctx->priv; int need_reset = 0; + int ret = 0; if (!strcmp(cmd, "sample_noise") || !strcmp(cmd, "sn")) { @@ -1386,31 +1388,11 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar s->sample_noise_start = 0; s->sample_noise_end = 1; } - } else if (!strcmp(cmd, "nr") || - !strcmp(cmd, "noise_reduction")) { - float nr; - - if (av_sscanf(args, "%f", &nr) == 1) { - s->noise_reduction = av_clipf(nr, 0.01, 97); - need_reset = 1; - } - } else if (!strcmp(cmd, "nf") || - !strcmp(cmd, "noise_floor")) { - float nf; - - if (av_sscanf(args, "%f", &nf) == 1) { - s->noise_floor = av_clipf(nf, -80, -20); - need_reset = 1; - } - } else if (!strcmp(cmd, "output_mode") || - !strcmp(cmd, "om")) { - if (!strcmp(args, "i")) { - s->output_mode = IN_MODE; - } else if (!strcmp(args, "o")) { - s->output_mode = OUT_MODE; - } else if (!strcmp(args, "n")) { - s->output_mode = NOISE_MODE; - } + } else { + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + need_reset = 1; } if (need_reset) diff --git a/libavfilter/af_afftfilt.c b/libavfilter/af_afftfilt.c index 86278ef35b9..a6156bf852d 100644 --- a/libavfilter/af_afftfilt.c +++ b/libavfilter/af_afftfilt.c @@ -40,6 +40,7 @@ typedef struct AFFTFiltContext { FFTComplex **fft_data; FFTComplex **fft_temp; int nb_exprs; + int channels; int window_size; AVExpr **real; AVExpr **imag; @@ -129,6 +130,7 @@ static int config_input(AVFilterLink *inlink) char *args; const char *last_expr = "1"; + s->channels = inlink->channels; s->pts = AV_NOPTS_VALUE; s->fft_bits = av_log2(s->fft_size); s->fft = av_fft_init(s->fft_bits, 0); @@ -176,30 +178,32 @@ static int config_input(AVFilterLink *inlink) ret = av_expr_parse(&s->real[ch], arg ? arg : last_expr, var_names, NULL, NULL, func2_names, func2, 0, ctx); if (ret < 0) - break; + goto fail; if (arg) last_expr = arg; s->nb_exprs++; } - av_free(args); + av_freep(&args); args = av_strdup(s->img_str ? s->img_str : s->real_str); if (!args) return AVERROR(ENOMEM); + saveptr = NULL; + last_expr = "1"; for (ch = 0; ch < inlink->channels; ch++) { char *arg = av_strtok(ch == 0 ? args : NULL, "|", &saveptr); ret = av_expr_parse(&s->imag[ch], arg ? arg : last_expr, var_names, NULL, NULL, func2_names, func2, 0, ctx); if (ret < 0) - break; + goto fail; if (arg) last_expr = arg; } - av_free(args); + av_freep(&args); s->fifo = av_audio_fifo_alloc(inlink->format, inlink->channels, s->window_size); if (!s->fifo) @@ -221,6 +225,9 @@ static int config_input(AVFilterLink *inlink) if (!s->buffer) return AVERROR(ENOMEM); +fail: + av_freep(&args); + return ret; } @@ -313,7 +320,7 @@ static int filter_frame(AVFilterLink *inlink) } out->pts = s->pts; - s->pts += s->hop_size; + s->pts += av_rescale_q(s->hop_size, (AVRational){1, outlink->sample_rate}, outlink->time_base); for (ch = 0; ch < inlink->channels; ch++) { float *dst = (float *)out->extended_data[ch]; @@ -430,7 +437,7 @@ static av_cold void uninit(AVFilterContext *ctx) av_fft_end(s->fft); av_fft_end(s->ifft); - for (i = 0; i < s->nb_exprs; i++) { + for (i = 0; i < s->channels; i++) { if (s->fft_data) av_freep(&s->fft_data[i]); if (s->fft_temp) diff --git a/libavfilter/af_afir.c b/libavfilter/af_afir.c index 31919f62e9c..5ba880f10b8 100644 --- a/libavfilter/af_afir.c +++ b/libavfilter/af_afir.c @@ -25,6 +25,7 @@ #include +#include "libavutil/avstring.h" #include "libavutil/common.h" #include "libavutil/float_dsp.h" #include "libavutil/intreadwrite.h" @@ -56,10 +57,17 @@ static void fcmul_add_c(float *sum, const float *t, const float *c, ptrdiff_t le sum[2 * n] += t[2 * n] * c[2 * n]; } +static void direct(const float *in, const FFTComplex *ir, int len, float *out) +{ + for (int n = 0; n < len; n++) + for (int m = 0; m <= n; m++) + out[n] += ir[m].re * in[n - m]; +} + static int fir_quantum(AVFilterContext *ctx, AVFrame *out, int ch, int offset) { AudioFIRContext *s = ctx->priv; - const float *in = (const float *)s->in[0]->extended_data[ch] + offset; + const float *in = (const float *)s->in->extended_data[ch] + offset; float *block, *buf, *ptr = (float *)out->extended_data[ch] + offset; const int nb_samples = FFMIN(s->min_part_size, out->nb_samples - offset); int n, i, j; @@ -70,8 +78,13 @@ static int fir_quantum(AVFilterContext *ctx, AVFrame *out, int ch, int offset) float *dst = (float *)seg->output->extended_data[ch]; float *sum = (float *)seg->sum->extended_data[ch]; - s->fdsp->vector_fmul_scalar(src + seg->input_offset, in, s->dry_gain, FFALIGN(nb_samples, 4)); - emms_c(); + if (s->min_part_size >= 8) { + s->fdsp->vector_fmul_scalar(src + seg->input_offset, in, s->dry_gain, FFALIGN(nb_samples, 4)); + emms_c(); + } else { + for (n = 0; n < nb_samples; n++) + src[seg->input_offset + n] = in[n] * s->dry_gain; + } seg->output_offset[ch] += s->min_part_size; if (seg->output_offset[ch] == seg->part_size) { @@ -86,6 +99,32 @@ static int fir_quantum(AVFilterContext *ctx, AVFrame *out, int ch, int offset) continue; } + if (seg->part_size < 8) { + memset(dst, 0, sizeof(*dst) * seg->part_size * seg->nb_partitions); + + j = seg->part_index[ch]; + + for (i = 0; i < seg->nb_partitions; i++) { + const int coffset = j * seg->coeff_size; + const FFTComplex *coeff = (const FFTComplex *)seg->coeff->extended_data[ch * !s->one2many] + coffset; + + direct(src, coeff, nb_samples, dst); + + if (j == 0) + j = seg->nb_partitions; + j--; + } + + seg->part_index[ch] = (seg->part_index[ch] + 1) % seg->nb_partitions; + + memmove(src, src + s->min_part_size, (seg->input_size - s->min_part_size) * sizeof(*src)); + + for (n = 0; n < nb_samples; n++) { + ptr[n] += dst[n]; + } + continue; + } + memset(sum, 0, sizeof(*sum) * seg->fft_length); block = (float *)seg->block->extended_data[ch] + seg->part_index[ch] * seg->block_size; memset(block + seg->part_size, 0, sizeof(*block) * (seg->fft_length - seg->part_size)); @@ -132,8 +171,13 @@ static int fir_quantum(AVFilterContext *ctx, AVFrame *out, int ch, int offset) } } - s->fdsp->vector_fmul_scalar(ptr, ptr, s->wet_gain, FFALIGN(nb_samples, 4)); - emms_c(); + if (s->min_part_size >= 8) { + s->fdsp->vector_fmul_scalar(ptr, ptr, s->wet_gain, FFALIGN(nb_samples, 4)); + emms_c(); + } else { + for (n = 0; n < nb_samples; n++) + ptr[n] *= s->wet_gain; + } return 0; } @@ -175,7 +219,7 @@ static int fir_frame(AudioFIRContext *s, AVFrame *in, AVFilterLink *outlink) if (s->pts == AV_NOPTS_VALUE) s->pts = in->pts; - s->in[0] = in; + s->in = in; ctx->internal->execute(ctx, fir_channels, out, NULL, FFMIN(outlink->channels, ff_filter_get_nb_threads(ctx))); @@ -184,7 +228,7 @@ static int fir_frame(AudioFIRContext *s, AVFrame *in, AVFilterLink *outlink) s->pts += av_rescale_q(out->nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base); av_frame_free(&in); - s->in[0] = NULL; + s->in = NULL; return ff_filter_frame(outlink, out); } @@ -255,9 +299,9 @@ static void draw_response(AVFilterContext *ctx, AVFrame *out) if (!mag || !phase || !delay) goto end; - channel = av_clip(s->ir_channel, 0, s->in[1]->channels - 1); + channel = av_clip(s->ir_channel, 0, s->ir[s->selir]->channels - 1); for (i = 0; i < s->w; i++) { - const float *src = (const float *)s->in[1]->extended_data[channel]; + const float *src = (const float *)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.; @@ -350,7 +394,7 @@ static int init_segment(AVFilterContext *ctx, AudioFIRSegment *seg, if (!seg->part_index || !seg->output_offset) return AVERROR(ENOMEM); - for (int ch = 0; ch < ctx->inputs[0]->channels; ch++) { + for (int ch = 0; ch < ctx->inputs[0]->channels && part_size >= 8; ch++) { seg->rdft[ch] = av_rdft_init(av_log2(2 * part_size), DFT_R2C); seg->irdft[ch] = av_rdft_init(av_log2(2 * part_size), IDFT_C2R); if (!seg->rdft[ch] || !seg->irdft[ch]) @@ -360,7 +404,7 @@ static int init_segment(AVFilterContext *ctx, AudioFIRSegment *seg, seg->sum = ff_get_audio_buffer(ctx->inputs[0], seg->fft_length); seg->block = ff_get_audio_buffer(ctx->inputs[0], seg->nb_partitions * seg->block_size); seg->buffer = ff_get_audio_buffer(ctx->inputs[0], seg->part_size); - seg->coeff = ff_get_audio_buffer(ctx->inputs[1], seg->nb_partitions * seg->coeff_size * 2); + seg->coeff = ff_get_audio_buffer(ctx->inputs[1 + s->selir], seg->nb_partitions * seg->coeff_size * 2); seg->input = ff_get_audio_buffer(ctx->inputs[0], seg->input_size); seg->output = ff_get_audio_buffer(ctx->inputs[0], seg->part_size); if (!seg->buffer || !seg->sum || !seg->block || !seg->coeff || !seg->input || !seg->output) @@ -369,79 +413,116 @@ static int init_segment(AVFilterContext *ctx, AudioFIRSegment *seg, return 0; } +static void uninit_segment(AVFilterContext *ctx, AudioFIRSegment *seg) +{ + AudioFIRContext *s = ctx->priv; + + if (seg->rdft) { + for (int ch = 0; ch < s->nb_channels; ch++) { + av_rdft_end(seg->rdft[ch]); + } + } + av_freep(&seg->rdft); + + if (seg->irdft) { + for (int ch = 0; ch < s->nb_channels; ch++) { + av_rdft_end(seg->irdft[ch]); + } + } + av_freep(&seg->irdft); + + av_freep(&seg->output_offset); + av_freep(&seg->part_index); + + av_frame_free(&seg->block); + av_frame_free(&seg->sum); + av_frame_free(&seg->buffer); + av_frame_free(&seg->coeff); + av_frame_free(&seg->input); + av_frame_free(&seg->output); + seg->input_size = 0; +} + static int convert_coeffs(AVFilterContext *ctx) { AudioFIRContext *s = ctx->priv; - int left, offset = 0, part_size, max_part_size; - int ret, i, ch, n; + int ret, i, ch, n, cur_nb_taps; float power = 0; - s->nb_taps = ff_inlink_queued_samples(ctx->inputs[1]); - if (s->nb_taps <= 0) - return AVERROR(EINVAL); + if (!s->nb_taps) { + int part_size, max_part_size; + int left, offset = 0; - if (s->minp > s->maxp) { - s->maxp = s->minp; - } + s->nb_taps = ff_inlink_queued_samples(ctx->inputs[1 + s->selir]); + if (s->nb_taps <= 0) + return AVERROR(EINVAL); + + if (s->minp > s->maxp) { + s->maxp = s->minp; + } - left = s->nb_taps; - part_size = 1 << av_log2(s->minp); - max_part_size = 1 << av_log2(s->maxp); + left = s->nb_taps; + part_size = 1 << av_log2(s->minp); + max_part_size = 1 << av_log2(s->maxp); - s->min_part_size = part_size; + s->min_part_size = part_size; - for (i = 0; left > 0; i++) { - int step = part_size == max_part_size ? INT_MAX : 1 + (i == 0); - int nb_partitions = FFMIN(step, (left + part_size - 1) / part_size); + for (i = 0; left > 0; i++) { + 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], offset, nb_partitions, part_size); + s->nb_segments = i + 1; + ret = init_segment(ctx, &s->seg[i], offset, nb_partitions, part_size); + if (ret < 0) + return ret; + offset += nb_partitions * part_size; + left -= nb_partitions * part_size; + part_size *= 2; + part_size = FFMIN(part_size, max_part_size); + } + } + + if (!s->ir[s->selir]) { + ret = ff_inlink_consume_samples(ctx->inputs[1 + s->selir], s->nb_taps, s->nb_taps, &s->ir[s->selir]); if (ret < 0) return ret; - offset += nb_partitions * part_size; - left -= nb_partitions * part_size; - part_size *= 2; - part_size = FFMIN(part_size, max_part_size); + if (ret == 0) + return AVERROR_BUG; } - ret = ff_inlink_consume_samples(ctx->inputs[1], s->nb_taps, s->nb_taps, &s->in[1]); - if (ret < 0) - return ret; - if (ret == 0) - return AVERROR_BUG; - if (s->response) draw_response(ctx, s->video); s->gain = 1; + cur_nb_taps = s->ir[s->selir]->nb_samples; switch (s->gtype) { case -1: /* nothing to do */ break; case 0: - for (ch = 0; ch < ctx->inputs[1]->channels; ch++) { - float *time = (float *)s->in[1]->extended_data[!s->one2many * ch]; + for (ch = 0; ch < ctx->inputs[1 + s->selir]->channels; ch++) { + float *time = (float *)s->ir[s->selir]->extended_data[!s->one2many * ch]; - for (i = 0; i < s->nb_taps; i++) + for (i = 0; i < cur_nb_taps; i++) power += FFABS(time[i]); } - s->gain = ctx->inputs[1]->channels / power; + s->gain = ctx->inputs[1 + s->selir]->channels / power; break; case 1: - for (ch = 0; ch < ctx->inputs[1]->channels; ch++) { - float *time = (float *)s->in[1]->extended_data[!s->one2many * ch]; + for (ch = 0; ch < ctx->inputs[1 + s->selir]->channels; ch++) { + float *time = (float *)s->ir[s->selir]->extended_data[!s->one2many * ch]; - for (i = 0; i < s->nb_taps; i++) + for (i = 0; i < cur_nb_taps; i++) power += time[i]; } - s->gain = ctx->inputs[1]->channels / power; + s->gain = ctx->inputs[1 + s->selir]->channels / power; break; case 2: - for (ch = 0; ch < ctx->inputs[1]->channels; ch++) { - float *time = (float *)s->in[1]->extended_data[!s->one2many * ch]; + for (ch = 0; ch < ctx->inputs[1 + s->selir]->channels; ch++) { + float *time = (float *)s->ir[s->selir]->extended_data[!s->one2many * ch]; - for (i = 0; i < s->nb_taps; i++) + for (i = 0; i < cur_nb_taps; i++) power += time[i] * time[i]; } s->gain = sqrtf(ch / power); @@ -452,17 +533,17 @@ static int convert_coeffs(AVFilterContext *ctx) s->gain = FFMIN(s->gain * s->ir_gain, 1.f); av_log(ctx, AV_LOG_DEBUG, "power %f, gain %f\n", power, s->gain); - for (ch = 0; ch < ctx->inputs[1]->channels; ch++) { - float *time = (float *)s->in[1]->extended_data[!s->one2many * ch]; + for (ch = 0; ch < ctx->inputs[1 + s->selir]->channels; ch++) { + float *time = (float *)s->ir[s->selir]->extended_data[!s->one2many * ch]; - s->fdsp->vector_fmul_scalar(time, time, s->gain, FFALIGN(s->nb_taps, 4)); + s->fdsp->vector_fmul_scalar(time, time, s->gain, FFALIGN(cur_nb_taps, 4)); } - av_log(ctx, AV_LOG_DEBUG, "nb_taps: %d\n", s->nb_taps); + 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); - for (ch = 0; ch < ctx->inputs[1]->channels; ch++) { - float *time = (float *)s->in[1]->extended_data[!s->one2many * ch]; + for (ch = 0; ch < ctx->inputs[1 + s->selir]->channels; ch++) { + float *time = (float *)s->ir[s->selir]->extended_data[!s->one2many * ch]; int toffset = 0; for (i = FFMAX(1, s->length * s->nb_taps); i < s->nb_taps; i++) @@ -483,6 +564,14 @@ static int convert_coeffs(AVFilterContext *ctx) const int remaining = s->nb_taps - toffset; const int size = remaining >= seg->part_size ? seg->part_size : remaining; + if (size < 8) { + for (n = 0; n < size; n++) + coeff[coffset + n].re = time[toffset + n]; + + toffset += size; + continue; + } + memset(block, 0, sizeof(*block) * seg->fft_length); memcpy(block, time + toffset, size * sizeof(*block)); @@ -510,7 +599,6 @@ static int convert_coeffs(AVFilterContext *ctx) } } - av_frame_free(&s->in[1]); s->have_coeffs = 1; return 0; @@ -543,26 +631,26 @@ static int activate(AVFilterContext *ctx) 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) { + if (!s->eof_coeffs[s->selir]) { AVFrame *ir = NULL; - ret = check_ir(ctx->inputs[1], ir); + ret = check_ir(ctx->inputs[1 + s->selir], ir); if (ret < 0) return ret; - if (ff_outlink_get_status(ctx->inputs[1]) == AVERROR_EOF) - s->eof_coeffs = 1; + if (ff_outlink_get_status(ctx->inputs[1 + s->selir]) == AVERROR_EOF) + s->eof_coeffs[s->selir] = 1; - if (!s->eof_coeffs) { + if (!s->eof_coeffs[s->selir]) { if (ff_outlink_frame_wanted(ctx->outputs[0])) - ff_inlink_request_frame(ctx->inputs[1]); + 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]); + ff_inlink_request_frame(ctx->inputs[1 + s->selir]); return 0; } } - if (!s->have_coeffs && s->eof_coeffs) { + if (!s->have_coeffs && s->eof_coeffs[s->selir]) { ret = convert_coeffs(ctx); if (ret < 0) return ret; @@ -582,8 +670,12 @@ static int activate(AVFilterContext *ctx) 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; - return ff_filter_frame(ctx->outputs[1], av_frame_clone(s->video)); + clone = av_frame_clone(s->video); + if (!clone) + return AVERROR(ENOMEM); + return ff_filter_frame(ctx->outputs[1], clone); } } @@ -658,8 +750,10 @@ static int query_formats(AVFilterContext *ctx) return ret; if ((ret = ff_channel_layouts_ref(layouts, &ctx->outputs[0]->in_channel_layouts)) < 0) return ret; - if ((ret = ff_channel_layouts_ref(mono, &ctx->inputs[1]->out_channel_layouts)) < 0) - return ret; + for (int i = 1; i < ctx->nb_inputs; i++) { + if ((ret = ff_channel_layouts_ref(mono, &ctx->inputs[i]->out_channel_layouts)) < 0) + return ret; + } } formats = ff_make_format_list(sample_fmts); @@ -675,49 +769,19 @@ static int config_output(AVFilterLink *outlink) AVFilterContext *ctx = outlink->src; AudioFIRContext *s = ctx->priv; - s->one2many = ctx->inputs[1]->channels == 1; + s->one2many = ctx->inputs[1 + s->selir]->channels == 1; outlink->sample_rate = ctx->inputs[0]->sample_rate; outlink->time_base = ctx->inputs[0]->time_base; outlink->channel_layout = ctx->inputs[0]->channel_layout; outlink->channels = ctx->inputs[0]->channels; s->nb_channels = outlink->channels; - s->nb_coef_channels = ctx->inputs[1]->channels; + s->nb_coef_channels = ctx->inputs[1 + s->selir]->channels; s->pts = AV_NOPTS_VALUE; return 0; } -static void uninit_segment(AVFilterContext *ctx, AudioFIRSegment *seg) -{ - AudioFIRContext *s = ctx->priv; - - if (seg->rdft) { - for (int ch = 0; ch < s->nb_channels; ch++) { - av_rdft_end(seg->rdft[ch]); - } - } - av_freep(&seg->rdft); - - if (seg->irdft) { - for (int ch = 0; ch < s->nb_channels; ch++) { - av_rdft_end(seg->irdft[ch]); - } - } - av_freep(&seg->irdft); - - av_freep(&seg->output_offset); - av_freep(&seg->part_index); - - av_frame_free(&seg->block); - av_frame_free(&seg->sum); - av_frame_free(&seg->buffer); - av_frame_free(&seg->coeff); - av_frame_free(&seg->input); - av_frame_free(&seg->output); - seg->input_size = 0; -} - static av_cold void uninit(AVFilterContext *ctx) { AudioFIRContext *s = ctx->priv; @@ -727,7 +791,13 @@ static av_cold void uninit(AVFilterContext *ctx) } av_freep(&s->fdsp); - av_frame_free(&s->in[1]); + + for (int i = 0; i < s->nb_irs; i++) { + av_frame_free(&s->ir[i]); + } + + for (int i = 0; i < ctx->nb_inputs; i++) + av_freep(&ctx->input_pads[i].name); for (int i = 0; i < ctx->nb_outputs; i++) av_freep(&ctx->output_pads[i].name); @@ -767,7 +837,37 @@ static av_cold int init(AVFilterContext *ctx) AVFilterPad pad, vpad; int ret; - pad = (AVFilterPad){ + pad = (AVFilterPad) { + .name = av_strdup("main"), + .type = AVMEDIA_TYPE_AUDIO, + }; + + if (!pad.name) + return AVERROR(ENOMEM); + + ret = ff_insert_inpad(ctx, 0, &pad); + if (ret < 0) { + av_freep(&pad.name); + return ret; + } + + for (int n = 0; n < s->nb_irs; n++) { + pad = (AVFilterPad) { + .name = av_asprintf("ir%d", n), + .type = AVMEDIA_TYPE_AUDIO, + }; + + if (!pad.name) + return AVERROR(ENOMEM); + + ret = ff_insert_inpad(ctx, n + 1, &pad); + if (ret < 0) { + av_freep(&pad.name); + return ret; + } + } + + pad = (AVFilterPad) { .name = av_strdup("default"), .type = AVMEDIA_TYPE_AUDIO, .config_props = config_output, @@ -776,6 +876,12 @@ static av_cold int init(AVFilterContext *ctx) if (!pad.name) return AVERROR(ENOMEM); + ret = ff_insert_outpad(ctx, 0, &pad); + if (ret < 0) { + av_freep(&pad.name); + return ret; + } + if (s->response) { vpad = (AVFilterPad){ .name = av_strdup("filter_response"), @@ -784,15 +890,7 @@ static av_cold int init(AVFilterContext *ctx) }; if (!vpad.name) return AVERROR(ENOMEM); - } - - ret = ff_insert_outpad(ctx, 0, &pad); - if (ret < 0) { - av_freep(&pad.name); - return ret; - } - if (s->response) { ret = ff_insert_outpad(ctx, 1, &vpad); if (ret < 0) { av_freep(&vpad.name); @@ -809,18 +907,31 @@ static av_cold int init(AVFilterContext *ctx) return 0; } -static const AVFilterPad afir_inputs[] = { - { - .name = "main", - .type = AVMEDIA_TYPE_AUDIO, - },{ - .name = "ir", - .type = AVMEDIA_TYPE_AUDIO, - }, - { NULL } -}; +static int process_command(AVFilterContext *ctx, + const char *cmd, + const char *arg, + char *res, + int res_len, + int flags) +{ + AudioFIRContext *s = ctx->priv; + int prev_ir = s->selir; + int ret = ff_filter_process_command(ctx, cmd, arg, res, res_len, flags); + + if (ret < 0) + return ret; + + s->selir = FFMIN(s->nb_irs - 1, s->selir); + + if (prev_ir != s->selir) { + s->have_coeffs = 0; + } + + return 0; +} #define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define AFR AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM #define VF AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM #define OFFSET(x) offsetof(AudioFIRContext, x) @@ -842,8 +953,10 @@ static const AVOption afir_options[] = { { "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 }, - { "minp", "set min partition size", OFFSET(minp), AV_OPT_TYPE_INT, {.i64=8192}, 8, 32768, AF }, + { "minp", "set min partition size", OFFSET(minp), AV_OPT_TYPE_INT, {.i64=8192}, 1, 32768, AF }, { "maxp", "set max partition size", OFFSET(maxp), AV_OPT_TYPE_INT, {.i64=8192}, 8, 32768, 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 }, { NULL } }; @@ -851,14 +964,15 @@ AVFILTER_DEFINE_CLASS(afir); AVFilter ff_af_afir = { .name = "afir", - .description = NULL_IF_CONFIG_SMALL("Apply Finite Impulse Response filter with supplied coefficients in 2nd stream."), + .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, .query_formats = query_formats, .init = init, .activate = activate, .uninit = uninit, - .inputs = afir_inputs, - .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS | + .process_command = process_command, + .flags = AVFILTER_FLAG_DYNAMIC_INPUTS | + AVFILTER_FLAG_DYNAMIC_OUTPUTS | AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/af_afir.h b/libavfilter/af_afir.h index f665c0ef808..4f44675848a 100644 --- a/libavfilter/af_afir.h +++ b/libavfilter/af_afir.h @@ -74,10 +74,12 @@ typedef struct AudioFIRContext { int ir_channel; int minp; int maxp; + int nb_irs; + int selir; float gain; - int eof_coeffs; + int eof_coeffs[32]; int have_coeffs; int nb_taps; int nb_channels; @@ -87,7 +89,8 @@ typedef struct AudioFIRContext { AudioFIRSegment seg[1024]; int nb_segments; - AVFrame *in[2]; + AVFrame *in; + AVFrame *ir[32]; AVFrame *video; int min_part_size; int64_t pts; diff --git a/libavfilter/af_aformat.c b/libavfilter/af_aformat.c index e43149561a3..1a702778c35 100644 --- a/libavfilter/af_aformat.c +++ b/libavfilter/af_aformat.c @@ -50,8 +50,11 @@ typedef struct AFormatContext { #define F AV_OPT_FLAG_FILTERING_PARAM static const AVOption aformat_options[] = { { "sample_fmts", "A '|'-separated list of sample formats.", OFFSET(formats_str), AV_OPT_TYPE_STRING, .flags = A|F }, + { "f", "A '|'-separated list of sample formats.", OFFSET(formats_str), AV_OPT_TYPE_STRING, .flags = A|F }, { "sample_rates", "A '|'-separated list of sample rates.", OFFSET(sample_rates_str), AV_OPT_TYPE_STRING, .flags = A|F }, + { "r", "A '|'-separated list of sample rates.", OFFSET(sample_rates_str), AV_OPT_TYPE_STRING, .flags = A|F }, { "channel_layouts", "A '|'-separated list of channel layouts.", OFFSET(channel_layouts_str), AV_OPT_TYPE_STRING, .flags = A|F }, + { "cl", "A '|'-separated list of channel layouts.", OFFSET(channel_layouts_str), AV_OPT_TYPE_STRING, .flags = A|F }, { NULL } }; diff --git a/libavfilter/af_agate.c b/libavfilter/af_agate.c index 0609dc222e0..936caa0a44d 100644 --- a/libavfilter/af_agate.c +++ b/libavfilter/af_agate.c @@ -318,7 +318,7 @@ static int activate(AVFilterContext *ctx) dst = (double *)out->data[0]; out->pts = s->pts; - s->pts += nb_samples; + s->pts += av_rescale_q(nb_samples, (AVRational){1, ctx->outputs[0]->sample_rate}, ctx->outputs[0]->time_base); gate(s, (double *)in[0]->data[0], dst, (double *)in[1]->data[0], nb_samples, diff --git a/libavfilter/af_aiir.c b/libavfilter/af_aiir.c index ffe2606da68..bc31e5141e4 100644 --- a/libavfilter/af_aiir.c +++ b/libavfilter/af_aiir.c @@ -38,8 +38,8 @@ typedef struct Pair { } Pair; typedef struct BiquadContext { - double a0, a1, a2; - double b0, b1, b2; + double a[3]; + double b[3]; double i1, i2; double o1, o2; } BiquadContext; @@ -58,6 +58,7 @@ typedef struct AudioIIRContext { char *a_str, *b_str, *g_str; double dry_gain, wet_gain; double mix; + int normalize; int format; int process; int precision; @@ -129,8 +130,8 @@ static int iir_ch_## name(AVFilterContext *ctx, void *arg, int ch, int nb_jobs) ThreadData *td = arg; \ AVFrame *in = td->in, *out = td->out; \ const type *src = (const type *)in->extended_data[ch]; \ - double *ic = (double *)s->iir[ch].cache[0]; \ - double *oc = (double *)s->iir[ch].cache[1]; \ + double *oc = (double *)s->iir[ch].cache[0]; \ + double *ic = (double *)s->iir[ch].cache[1]; \ const int nb_a = s->iir[ch].nb_ab[0]; \ const int nb_b = s->iir[ch].nb_ab[1]; \ const double *a = s->iir[ch].ab[0]; \ @@ -193,11 +194,11 @@ static int iir_ch_serial_## name(AVFilterContext *ctx, void *arg, int ch, int nb int n, i; \ \ for (i = 0; i < nb_biquads; i++) { \ - const double a1 = -iir->biquads[i].a1; \ - const double a2 = -iir->biquads[i].a2; \ - const double b0 = iir->biquads[i].b0; \ - const double b1 = iir->biquads[i].b1; \ - const double b2 = iir->biquads[i].b2; \ + const double a1 = -iir->biquads[i].a[1]; \ + const double a2 = -iir->biquads[i].a[2]; \ + const double b0 = iir->biquads[i].b[0]; \ + const double b1 = iir->biquads[i].b[1]; \ + const double b2 = iir->biquads[i].b[2]; \ double i1 = iir->biquads[i].i1; \ double i2 = iir->biquads[i].i2; \ double o1 = iir->biquads[i].o1; \ @@ -335,7 +336,7 @@ static int read_zp_coefficients(AVFilterContext *ctx, char *item_str, int nb_ite return 0; } -static const char *format[] = { "%lf", "%lf %lfi", "%lf %lfr", "%lf %lfd" }; +static const char *format[] = { "%lf", "%lf %lfi", "%lf %lfr", "%lf %lfd", "%lf %lfi" }; static int read_channels(AVFilterContext *ctx, int channels, uint8_t *item_str, int ab) { @@ -384,50 +385,65 @@ static int read_channels(AVFilterContext *ctx, int channels, uint8_t *item_str, return 0; } -static void multiply(double wre, double wim, int npz, double *coeffs) +static void cmul(double re, double im, double re2, double im2, double *RE, double *IM) { - double nwre = -wre, nwim = -wim; - double cre, cim; - int i; + *RE = re * re2 - im * im2; + *IM = re * im2 + re2 * im; +} + +static int expand(AVFilterContext *ctx, double *pz, int n, double *coefs) +{ + coefs[2 * n] = 1.0; - for (i = npz; i >= 1; i--) { - cre = coeffs[2 * i + 0]; - cim = coeffs[2 * i + 1]; + for (int i = 1; i <= n; i++) { + for (int j = n - i; j < n; j++) { + double re, im; - coeffs[2 * i + 0] = (nwre * cre - nwim * cim) + coeffs[2 * (i - 1) + 0]; - coeffs[2 * i + 1] = (nwre * cim + nwim * cre) + coeffs[2 * (i - 1) + 1]; + cmul(coefs[2 * (j + 1)], coefs[2 * (j + 1) + 1], + pz[2 * (i - 1)], pz[2 * (i - 1) + 1], &re, &im); + + coefs[2 * j] -= re; + coefs[2 * j + 1] -= im; + } } - cre = coeffs[0]; - cim = coeffs[1]; - coeffs[0] = nwre * cre - nwim * cim; - coeffs[1] = nwre * cim + nwim * cre; + for (int i = 0; i < n + 1; i++) { + if (fabs(coefs[2 * i + 1]) > FLT_EPSILON) { + av_log(ctx, AV_LOG_ERROR, "coefs: %f of z^%d is not real; poles/zeros are not complex conjugates.\n", + coefs[2 * i + 1], i); + return AVERROR(EINVAL); + } + } + + return 0; } -static int expand(AVFilterContext *ctx, double *pz, int nb, double *coeffs) +static void normalize_coeffs(AVFilterContext *ctx, int ch) { - int i; + AudioIIRContext *s = ctx->priv; + IIRChannel *iir = &s->iir[ch]; + double sum_den = 0.; - coeffs[0] = 1.0; - coeffs[1] = 0.0; + if (!s->normalize) + return; - for (i = 0; i < nb; i++) { - coeffs[2 * (i + 1) ] = 0.0; - coeffs[2 * (i + 1) + 1] = 0.0; + for (int i = 0; i < iir->nb_ab[1]; i++) { + sum_den += iir->ab[1][i]; } - for (i = 0; i < nb; i++) - multiply(pz[2 * i], pz[2 * i + 1], nb, coeffs); + if (sum_den > 1e-6) { + double factor, sum_num = 0.; - for (i = 0; i < nb + 1; i++) { - if (fabs(coeffs[2 * i + 1]) > FLT_EPSILON) { - av_log(ctx, AV_LOG_ERROR, "coeff: %f of z^%d is not real; poles/zeros are not complex conjugates.\n", - coeffs[2 * i + 1], i); - return AVERROR(EINVAL); + for (int i = 0; i < iir->nb_ab[0]; i++) { + sum_num += iir->ab[0][i]; } - } - return 0; + factor = sum_num / sum_den; + + for (int i = 0; i < iir->nb_ab[1]; i++) { + iir->ab[1][i] *= factor; + } + } } static int convert_zp2tf(AVFilterContext *ctx, int channels) @@ -439,8 +455,8 @@ static int convert_zp2tf(AVFilterContext *ctx, int channels) IIRChannel *iir = &s->iir[ch]; double *topc, *botc; - topc = av_calloc((iir->nb_ab[0] + 1) * 2, sizeof(*topc)); - botc = av_calloc((iir->nb_ab[1] + 1) * 2, sizeof(*botc)); + topc = av_calloc((iir->nb_ab[1] + 1) * 2, sizeof(*topc)); + botc = av_calloc((iir->nb_ab[0] + 1) * 2, sizeof(*botc)); if (!topc || !botc) { ret = AVERROR(ENOMEM); goto fail; @@ -466,6 +482,8 @@ static int convert_zp2tf(AVFilterContext *ctx, int channels) } iir->nb_ab[0]++; + normalize_coeffs(ctx, ch); + fail: av_free(topc); av_free(botc); @@ -499,6 +517,7 @@ static int decompose_zp2biquads(AVFilterContext *ctx, int channels) double a[6] = { 0 }; double min_distance = DBL_MAX; double max_mag = 0; + double factor; int i; for (i = 0; i < iir->nb_ab[0]; i++) { @@ -514,7 +533,7 @@ static int decompose_zp2biquads(AVFilterContext *ctx, int channels) } } - for (i = 0; i < iir->nb_ab[1]; i++) { + for (i = 0; i < iir->nb_ab[0]; i++) { if (isnan(iir->ab[0][2 * i]) || isnan(iir->ab[0][2 * i + 1])) continue; @@ -593,20 +612,42 @@ static int decompose_zp2biquads(AVFilterContext *ctx, int channels) iir->ab[1][2 * nearest_zero.a] = iir->ab[1][2 * nearest_zero.a + 1] = NAN; iir->ab[1][2 * nearest_zero.b] = iir->ab[1][2 * nearest_zero.b + 1] = NAN; - iir->biquads[current_biquad].a0 = 1.0; - iir->biquads[current_biquad].a1 = a[2] / a[4]; - iir->biquads[current_biquad].a2 = a[0] / a[4]; - iir->biquads[current_biquad].b0 = b[4] / a[4] * (current_biquad ? 1.0 : iir->g); - iir->biquads[current_biquad].b1 = b[2] / a[4] * (current_biquad ? 1.0 : iir->g); - iir->biquads[current_biquad].b2 = b[0] / a[4] * (current_biquad ? 1.0 : iir->g); + iir->biquads[current_biquad].a[0] = 1.; + iir->biquads[current_biquad].a[1] = a[2] / a[4]; + iir->biquads[current_biquad].a[2] = a[0] / a[4]; + iir->biquads[current_biquad].b[0] = b[4] / a[4]; + iir->biquads[current_biquad].b[1] = b[2] / a[4]; + iir->biquads[current_biquad].b[2] = b[0] / a[4]; + + if (s->normalize && + fabs(iir->biquads[current_biquad].b[0] + + iir->biquads[current_biquad].b[1] + + iir->biquads[current_biquad].b[2]) > 1e-6) { + factor = (iir->biquads[current_biquad].a[0] + + iir->biquads[current_biquad].a[1] + + iir->biquads[current_biquad].a[2]) / + (iir->biquads[current_biquad].b[0] + + iir->biquads[current_biquad].b[1] + + iir->biquads[current_biquad].b[2]); + + av_log(ctx, AV_LOG_VERBOSE, "factor=%f\n", factor); + + iir->biquads[current_biquad].b[0] *= factor; + iir->biquads[current_biquad].b[1] *= factor; + iir->biquads[current_biquad].b[2] *= factor; + } + + iir->biquads[current_biquad].b[0] *= (current_biquad ? 1.0 : iir->g); + iir->biquads[current_biquad].b[1] *= (current_biquad ? 1.0 : iir->g); + iir->biquads[current_biquad].b[2] *= (current_biquad ? 1.0 : iir->g); av_log(ctx, AV_LOG_VERBOSE, "a=%f %f %f:b=%f %f %f\n", - iir->biquads[current_biquad].a0, - iir->biquads[current_biquad].a1, - iir->biquads[current_biquad].a2, - iir->biquads[current_biquad].b0, - iir->biquads[current_biquad].b1, - iir->biquads[current_biquad].b2); + iir->biquads[current_biquad].a[0], + iir->biquads[current_biquad].a[1], + iir->biquads[current_biquad].a[2], + iir->biquads[current_biquad].b[0], + iir->biquads[current_biquad].b[1], + iir->biquads[current_biquad].b[2]); current_biquad++; } @@ -642,6 +683,39 @@ static void convert_pr2zp(AVFilterContext *ctx, int channels) } } +static void convert_sp2zp(AVFilterContext *ctx, int channels) +{ + AudioIIRContext *s = ctx->priv; + int ch; + + for (ch = 0; ch < channels; ch++) { + IIRChannel *iir = &s->iir[ch]; + int n; + + for (n = 0; n < iir->nb_ab[0]; n++) { + double sr = iir->ab[0][2*n]; + double si = iir->ab[0][2*n+1]; + double snr = 1. + sr; + double sdr = 1. - sr; + double div = sdr * sdr + si * si; + + iir->ab[0][2*n] = (snr * sdr - si * si) / div; + iir->ab[0][2*n+1] = (sdr * si + snr * si) / div; + } + + for (n = 0; n < iir->nb_ab[1]; n++) { + double sr = iir->ab[1][2*n]; + double si = iir->ab[1][2*n+1]; + double snr = 1. + sr; + double sdr = 1. - sr; + double div = sdr * sdr + si * si; + + iir->ab[1][2*n] = (snr * sdr - si * si) / div; + iir->ab[1][2*n+1] = (sdr * si + snr * si) / div; + } + } +} + static void convert_pd2zp(AVFilterContext *ctx, int channels) { AudioIIRContext *s = ctx->priv; @@ -669,6 +743,25 @@ static void convert_pd2zp(AVFilterContext *ctx, int channels) } } +static void check_stability(AVFilterContext *ctx, int channels) +{ + AudioIIRContext *s = ctx->priv; + int ch; + + for (ch = 0; ch < channels; ch++) { + IIRChannel *iir = &s->iir[ch]; + + for (int n = 0; n < iir->nb_ab[0]; n++) { + double pr = hypot(iir->ab[0][2*n], iir->ab[0][2*n+1]); + + if (pr >= 1.) { + av_log(ctx, AV_LOG_WARNING, "pole %d at channel %d is unstable\n", n, ch); + break; + } + } + } +} + static void drawtext(AVFrame *pic, int x, int y, const char *txt, uint32_t color) { const uint8_t *font; @@ -718,99 +811,121 @@ static void draw_line(AVFrame *out, int x0, int y0, int x1, int y1, uint32_t col } } -static void draw_response(AVFilterContext *ctx, AVFrame *out) +static double distance(double x0, double x1, double y0, double y1) +{ + return hypot(x0 - x1, y0 - y1); +} + +static void get_response(int channel, int format, double w, + const double *b, const double *a, + int nb_b, int nb_a, double *magnitude, double *phase) +{ + double realz, realp; + double imagz, imagp; + double real, imag; + double div; + + if (format == 0) { + realz = 0., realp = 0.; + imagz = 0., imagp = 0.; + for (int x = 0; x < nb_a; x++) { + realz += cos(-x * w) * a[x]; + imagz += sin(-x * w) * a[x]; + } + + for (int x = 0; x < nb_b; x++) { + realp += cos(-x * w) * b[x]; + imagp += sin(-x * w) * b[x]; + } + + div = realp * realp + imagp * imagp; + real = (realz * realp + imagz * imagp) / div; + imag = (imagz * realp - imagp * realz) / div; + + *magnitude = hypot(real, imag); + *phase = atan2(imag, real); + } else { + double p = 1., z = 1.; + double acc = 0.; + + for (int x = 0; x < nb_a; x++) { + z *= distance(cos(w), a[2 * x], sin(w), a[2 * x + 1]); + acc += atan2(sin(w) - a[2 * x + 1], cos(w) - a[2 * x]); + } + + for (int x = 0; x < nb_b; x++) { + p *= distance(cos(w), b[2 * x], sin(w), b[2 * x + 1]); + acc -= atan2(sin(w) - b[2 * x + 1], cos(w) - b[2 * x]); + } + + *magnitude = z / p; + *phase = acc; + } +} + +static void draw_response(AVFilterContext *ctx, AVFrame *out, int sample_rate) { AudioIIRContext *s = ctx->priv; - float *mag, *phase, *delay, min = FLT_MAX, max = FLT_MIN; - float min_delay = FLT_MAX, max_delay = FLT_MIN; + double *mag, *phase, *temp, *delay, min = DBL_MAX, max = -DBL_MAX; + double min_delay = DBL_MAX, max_delay = -DBL_MAX, min_phase, max_phase; int prev_ymag = -1, prev_yphase = -1, prev_ydelay = -1; char text[32]; - int ch, i, x; + int ch, i; memset(out->data[0], 0, s->h * out->linesize[0]); phase = av_malloc_array(s->w, sizeof(*phase)); + temp = av_malloc_array(s->w, sizeof(*temp)); mag = av_malloc_array(s->w, sizeof(*mag)); delay = av_malloc_array(s->w, sizeof(*delay)); - if (!mag || !phase || !delay) + if (!mag || !phase || !delay || !temp) goto end; ch = av_clip(s->ir_channel, 0, s->channels - 1); for (i = 0; i < s->w; i++) { const double *b = s->iir[ch].ab[0]; const double *a = s->iir[ch].ab[1]; + const int nb_b = s->iir[ch].nb_ab[0]; + const int nb_a = s->iir[ch].nb_ab[1]; double w = i * M_PI / (s->w - 1); - double realz, realp; - double imagz, imagp; - double real, imag, div; - - if (s->format == 0) { - realz = 0., realp = 0.; - imagz = 0., imagp = 0.; - for (x = 0; x < s->iir[ch].nb_ab[1]; x++) { - realz += cos(-x * w) * a[x]; - imagz += sin(-x * w) * a[x]; - } - - for (x = 0; x < s->iir[ch].nb_ab[0]; x++) { - realp += cos(-x * w) * b[x]; - imagp += sin(-x * w) * b[x]; - } - - div = realp * realp + imagp * imagp; - real = (realz * realp + imagz * imagp) / div; - imag = (imagz * realp - imagp * realz) / div; - } else { - real = 1; - imag = 0; - for (x = 0; x < s->iir[ch].nb_ab[1]; x++) { - double ore, oim, re, im; - - re = cos(w) - a[2 * x]; - im = sin(w) - a[2 * x + 1]; + double m, p; - ore = real; - oim = imag; + get_response(ch, s->format, w, b, a, nb_b, nb_a, &m, &p); - real = ore * re - oim * im; - imag = ore * im + oim * re; - } - - for (x = 0; x < s->iir[ch].nb_ab[0]; x++) { - double ore, oim, re, im; - - re = cos(w) - b[2 * x]; - im = sin(w) - b[2 * x + 1]; - - ore = real; - oim = imag; - div = re * re + im * im; + mag[i] = s->iir[ch].g * m; + phase[i] = p; + min = fmin(min, mag[i]); + max = fmax(max, mag[i]); + } - real = (ore * re + oim * im) / div; - imag = (oim * re - ore * im) / div; - } - } + temp[0] = 0.; + for (i = 0; i < s->w - 1; i++) { + double d = phase[i] - phase[i + 1]; + temp[i + 1] = ceil(fabs(d) / (2. * M_PI)) * 2. * M_PI * ((d > M_PI) - (d < -M_PI)); + } - mag[i] = s->iir[ch].g * hypot(real, imag); - phase[i] = atan2(imag, real); - min = fminf(min, mag[i]); - max = fmaxf(max, mag[i]); + min_phase = phase[0]; + max_phase = phase[0]; + for (i = 1; i < s->w; i++) { + temp[i] += temp[i - 1]; + phase[i] += temp[i]; + min_phase = fmin(min_phase, phase[i]); + max_phase = fmax(max_phase, phase[i]); } for (i = 0; i < s->w - 1; i++) { - float dw = M_PI / (s->w - 1); + double div = s->w / (double)sample_rate; - delay[i] = -(phase[i + 1] - phase[i]) / dw; - min_delay = fminf(min_delay, delay[i]); - max_delay = fmaxf(max_delay, delay[i]); + delay[i + 1] = -(phase[i] - phase[i + 1]) / div; + min_delay = fmin(min_delay, delay[i + 1]); + max_delay = fmax(max_delay, delay[i + 1]); } - - delay[i] = delay[i - 1]; + delay[0] = delay[1]; 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); + int yphase = (phase[i] - min_phase) / (max_phase - min_phase) * (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); @@ -841,17 +956,26 @@ static void draw_response(AVFilterContext *ctx, AVFrame *out) snprintf(text, sizeof(text), "%.2f", min); drawtext(out, 15 * 8 + 2, 12, text, 0xDDDDDDDD); - drawtext(out, 2, 22, "Max Delay:", 0xDDDDDDDD); + drawtext(out, 2, 22, "Max Phase:", 0xDDDDDDDD); + snprintf(text, sizeof(text), "%.2f", max_phase); + drawtext(out, 15 * 8 + 2, 22, text, 0xDDDDDDDD); + + drawtext(out, 2, 32, "Min Phase:", 0xDDDDDDDD); + snprintf(text, sizeof(text), "%.2f", min_phase); + drawtext(out, 15 * 8 + 2, 32, text, 0xDDDDDDDD); + + drawtext(out, 2, 42, "Max Delay:", 0xDDDDDDDD); snprintf(text, sizeof(text), "%.2f", max_delay); - drawtext(out, 11 * 8 + 2, 22, text, 0xDDDDDDDD); + drawtext(out, 11 * 8 + 2, 42, text, 0xDDDDDDDD); - drawtext(out, 2, 32, "Min Delay:", 0xDDDDDDDD); + drawtext(out, 2, 52, "Min Delay:", 0xDDDDDDDD); snprintf(text, sizeof(text), "%.2f", min_delay); - drawtext(out, 11 * 8 + 2, 32, text, 0xDDDDDDDD); + drawtext(out, 11 * 8 + 2, 52, text, 0xDDDDDDDD); } end: av_free(delay); + av_free(temp); av_free(phase); av_free(mag); } @@ -884,6 +1008,11 @@ static int config_output(AVFilterLink *outlink) convert_pr2zp(ctx, inlink->channels); } else if (s->format == 3) { convert_pd2zp(ctx, inlink->channels); + } else if (s->format == 4) { + convert_sp2zp(ctx, inlink->channels); + } + if (s->format > 0) { + check_stability(ctx, inlink->channels); } av_frame_free(&s->video); @@ -892,7 +1021,7 @@ static int config_output(AVFilterLink *outlink) if (!s->video) return AVERROR(ENOMEM); - draw_response(ctx, s->video); + draw_response(ctx, s->video, inlink->sample_rate); } if (s->format == 0) @@ -923,9 +1052,12 @@ static int config_output(AVFilterLink *outlink) iir->ab[0][i] /= iir->ab[0][0]; } + iir->ab[0][0] = 1.0; for (i = 0; i < iir->nb_ab[1]; i++) { - iir->ab[1][i] *= iir->g / iir->ab[0][0]; + iir->ab[1][i] *= iir->g; } + + normalize_coeffs(ctx, ch); } switch (inlink->format) { @@ -978,8 +1110,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) int64_t new_pts = av_rescale_q(out->pts, ctx->inputs[0]->time_base, outlink->time_base); if (new_pts > old_pts) { + AVFrame *clone; + s->video->pts = new_pts; - ret = ff_filter_frame(outlink, av_frame_clone(s->video)); + clone = av_frame_clone(s->video); + if (!clone) + return AVERROR(ENOMEM); + ret = ff_filter_frame(outlink, clone); if (ret < 0) return ret; } @@ -1030,6 +1167,10 @@ static av_cold int init(AVFilterContext *ctx) if (!pad.name) return AVERROR(ENOMEM); + ret = ff_insert_outpad(ctx, 0, &pad); + if (ret < 0) + return ret; + if (s->response) { vpad = (AVFilterPad){ .name = av_strdup("filter_response"), @@ -1038,13 +1179,7 @@ static av_cold int init(AVFilterContext *ctx) }; if (!vpad.name) return AVERROR(ENOMEM); - } - - ret = ff_insert_outpad(ctx, 0, &pad); - if (ret < 0) - return ret; - if (s->response) { ret = ff_insert_outpad(ctx, 1, &vpad); if (ret < 0) return ret; @@ -1090,24 +1225,33 @@ static const AVFilterPad inputs[] = { #define VF AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM static const AVOption aiir_options[] = { + { "zeros", "set B/numerator/zeros coefficients", OFFSET(b_str), AV_OPT_TYPE_STRING, {.str="1+0i 1-0i"}, 0, 0, AF }, { "z", "set B/numerator/zeros coefficients", OFFSET(b_str), AV_OPT_TYPE_STRING, {.str="1+0i 1-0i"}, 0, 0, AF }, + { "poles", "set A/denominator/poles coefficients", OFFSET(a_str),AV_OPT_TYPE_STRING, {.str="1+0i 1-0i"}, 0, 0, AF }, { "p", "set A/denominator/poles coefficients", OFFSET(a_str), AV_OPT_TYPE_STRING, {.str="1+0i 1-0i"}, 0, 0, AF }, + { "gains", "set channels gains", OFFSET(g_str), AV_OPT_TYPE_STRING, {.str="1|1"}, 0, 0, AF }, { "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 }, - { "f", "set coefficients format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=1}, 0, 3, AF, "format" }, - { "tf", "transfer function", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "format" }, + { "format", "set coefficients format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=1}, 0, 4, AF, "format" }, + { "f", "set coefficients format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=1}, 0, 4, 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, 1, AF, "process" }, { "r", "set kind of processing", OFFSET(process), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, AF, "process" }, { "d", "direct", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "process" }, { "s", "serial cascading", 0, AV_OPT_TYPE_CONST, {.i64=1}, 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" }, + { "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 }, { "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 }, diff --git a/libavfilter/af_amix.c b/libavfilter/af_amix.c index ec2556f9207..0826fc118ca 100644 --- a/libavfilter/af_amix.c +++ b/libavfilter/af_amix.c @@ -34,6 +34,7 @@ #include "libavutil/avstring.h" #include "libavutil/channel_layout.h" #include "libavutil/common.h" +#include "libavutil/eval.h" #include "libavutil/float_dsp.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" @@ -180,9 +181,10 @@ typedef struct MixContext { #define OFFSET(x) offsetof(MixContext, x) #define A AV_OPT_FLAG_AUDIO_PARAM #define F AV_OPT_FLAG_FILTERING_PARAM +#define T AV_OPT_FLAG_RUNTIME_PARAM static const AVOption amix_options[] = { { "inputs", "Number of inputs.", - OFFSET(nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, 1024, A|F }, + 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" }, @@ -192,7 +194,7 @@ static const AVOption amix_options[] = { "renormalization when an input stream ends.", OFFSET(dropout_transition), AV_OPT_TYPE_FLOAT, { .dbl = 2.0 }, 0, INT_MAX, A|F }, { "weights", "Set weight for each input.", - OFFSET(weights_str), AV_OPT_TYPE_STRING, {.str="1 1"}, 0, 0, A|F }, + OFFSET(weights_str), AV_OPT_TYPE_STRING, {.str="1 1"}, 0, 0, A|F|T }, { NULL } }; @@ -212,21 +214,21 @@ static void calculate_scales(MixContext *s, int nb_samples) for (i = 0; i < s->nb_inputs; i++) if (s->input_state[i] & INPUT_ON) - weight_sum += s->weights[i]; + weight_sum += FFABS(s->weights[i]); for (i = 0; i < s->nb_inputs; i++) { if (s->input_state[i] & INPUT_ON) { - if (s->scale_norm[i] > weight_sum / s->weights[i]) { - s->scale_norm[i] -= ((s->weight_sum / s->weights[i]) / s->nb_inputs) * + if (s->scale_norm[i] > weight_sum / FFABS(s->weights[i])) { + s->scale_norm[i] -= ((s->weight_sum / FFABS(s->weights[i])) / s->nb_inputs) * nb_samples / (s->dropout_transition * s->sample_rate); - s->scale_norm[i] = FFMAX(s->scale_norm[i], weight_sum / s->weights[i]); + s->scale_norm[i] = FFMAX(s->scale_norm[i], weight_sum / FFABS(s->weights[i])); } } } for (i = 0; i < s->nb_inputs; i++) { if (s->input_state[i] & INPUT_ON) - s->input_scale[i] = 1.0f / s->scale_norm[i]; + s->input_scale[i] = 1.0f / s->scale_norm[i] * FFSIGN(s->weights[i]); else s->input_scale[i] = 0.0f; } @@ -270,7 +272,7 @@ static int config_output(AVFilterLink *outlink) if (!s->input_scale || !s->scale_norm) return AVERROR(ENOMEM); for (i = 0; i < s->nb_inputs; i++) - s->scale_norm[i] = s->weight_sum / s->weights[i]; + s->scale_norm[i] = s->weight_sum / FFABS(s->weights[i]); calculate_scales(s, 0); av_get_channel_layout_string(buf, sizeof(buf), -1, outlink->channel_layout); @@ -503,11 +505,36 @@ static int activate(AVFilterContext *ctx) return 0; } -static av_cold int init(AVFilterContext *ctx) +static void parse_weights(AVFilterContext *ctx) { MixContext *s = ctx->priv; - char *p, *arg, *saveptr = NULL; float last_weight = 1.f; + char *p; + int i; + + s->weight_sum = 0.f; + p = s->weights_str; + for (i = 0; i < s->nb_inputs; i++) { + last_weight = av_strtod(p, &p); + s->weights[i] = last_weight; + s->weight_sum += FFABS(last_weight); + if (p && *p) { + p++; + } else { + i++; + break; + } + } + + for (; i < s->nb_inputs; i++) { + s->weights[i] = last_weight; + s->weight_sum += FFABS(last_weight); + } +} + +static av_cold int init(AVFilterContext *ctx) +{ + MixContext *s = ctx->priv; int i, ret; for (i = 0; i < s->nb_inputs; i++) { @@ -532,21 +559,7 @@ static av_cold int init(AVFilterContext *ctx) if (!s->weights) return AVERROR(ENOMEM); - p = s->weights_str; - for (i = 0; i < s->nb_inputs; i++) { - if (!(arg = av_strtok(p, " ", &saveptr))) - break; - - p = NULL; - sscanf(arg, "%f", &last_weight); - s->weights[i] = last_weight; - s->weight_sum += last_weight; - } - - for (; i < s->nb_inputs; i++) { - s->weights[i] = last_weight; - s->weight_sum += last_weight; - } + parse_weights(ctx); return 0; } @@ -601,6 +614,24 @@ static int query_formats(AVFilterContext *ctx) return ret; } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + MixContext *s = ctx->priv; + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + parse_weights(ctx); + for (int i = 0; i < s->nb_inputs; i++) + s->scale_norm[i] = s->weight_sum / FFABS(s->weights[i]); + calculate_scales(s, 0); + + return 0; +} + static const AVFilterPad avfilter_af_amix_outputs[] = { { .name = "default", @@ -621,5 +652,6 @@ AVFilter ff_af_amix = { .query_formats = query_formats, .inputs = NULL, .outputs = avfilter_af_amix_outputs, + .process_command = process_command, .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, }; diff --git a/libavfilter/af_anequalizer.c b/libavfilter/af_anequalizer.c index 03d939f170d..177e1c7b397 100644 --- a/libavfilter/af_anequalizer.c +++ b/libavfilter/af_anequalizer.c @@ -205,8 +205,10 @@ static av_cold int init(AVFilterContext *ctx) .type = AVMEDIA_TYPE_VIDEO, .config_props = config_video, }; - if (!vpad.name) + if (!vpad.name) { + av_freep(&pad.name); return AVERROR(ENOMEM); + } } ret = ff_insert_outpad(ctx, 0, &pad); @@ -564,7 +566,7 @@ static void equalizer(EqualizatorFilter *f, double sample_rate) static int add_filter(AudioNEqualizerContext *s, AVFilterLink *inlink) { equalizer(&s->filters[s->nb_filters], inlink->sample_rate); - if (s->nb_filters >= s->nb_allocated) { + if (s->nb_filters >= s->nb_allocated - 1) { EqualizatorFilter *filters; filters = av_calloc(s->nb_allocated, 2 * sizeof(*s->filters)); @@ -731,13 +733,18 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) } if (s->draw_curves) { + AVFrame *clone; + const int64_t pts = buf->pts + av_rescale_q(buf->nb_samples, (AVRational){ 1, inlink->sample_rate }, outlink->time_base); int ret; s->video->pts = pts; - ret = ff_filter_frame(ctx->outputs[1], av_frame_clone(s->video)); + clone = av_frame_clone(s->video); + if (!clone) + return AVERROR(ENOMEM); + ret = ff_filter_frame(ctx->outputs[1], clone); if (ret < 0) return ret; } diff --git a/libavfilter/af_anlmdn.c b/libavfilter/af_anlmdn.c index b5bc94b4eb4..b8aef31c359 100644 --- a/libavfilter/af_anlmdn.c +++ b/libavfilter/af_anlmdn.c @@ -73,15 +73,16 @@ enum OutModes { #define OFFSET(x) offsetof(AudioNLMeansContext, x) #define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define AFT AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption anlmdn_options[] = { - { "s", "set denoising strength", OFFSET(a), AV_OPT_TYPE_FLOAT, {.dbl=0.00001},0.00001, 10, AF }, + { "s", "set denoising strength", OFFSET(a), AV_OPT_TYPE_FLOAT, {.dbl=0.00001},0.00001, 10, AFT }, { "p", "set patch duration", OFFSET(pd), AV_OPT_TYPE_DURATION, {.i64=2000}, 1000, 100000, AF }, { "r", "set research duration", OFFSET(rd), AV_OPT_TYPE_DURATION, {.i64=6000}, 2000, 300000, AF }, - { "o", "set output mode", OFFSET(om), AV_OPT_TYPE_INT, {.i64=OUT_MODE}, 0, NB_MODES-1, AF, "mode" }, - { "i", "input", 0, AV_OPT_TYPE_CONST, {.i64=IN_MODE}, 0, 0, AF, "mode" }, - { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64=OUT_MODE}, 0, 0, AF, "mode" }, - { "n", "noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_MODE},0, 0, AF, "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" }, { "m", "set smooth factor", OFFSET(m), AV_OPT_TYPE_FLOAT, {.dbl=11.}, 1, 15, AF }, { NULL } }; @@ -297,7 +298,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) out->nb_samples = FFMIN(s->eof_left, s->offset); s->eof_left -= out->nb_samples; } - s->pts += s->offset; + s->pts += av_rescale_q(s->offset, (AVRational){1, outlink->sample_rate}, outlink->time_base); return ff_filter_frame(outlink, out); } @@ -339,29 +340,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&s->cache); } -static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, - char *res, int res_len, int flags) -{ - AudioNLMeansContext *s = ctx->priv; - - if (!strcmp(cmd, "s")) { - float a; - - if (av_sscanf(args, "%f", &a) == 1) - s->a = av_clipf(a, 0.00001, 10); - } else if (!strcmp(cmd, "o")) { - if (!strcmp(args, "i")) { - s->om = IN_MODE; - } else if (!strcmp(args, "o")) { - s->om = OUT_MODE; - } else if (!strcmp(args, "n")) { - s->om = NOISE_MODE; - } - } - - return 0; -} - static const AVFilterPad inputs[] = { { .name = "default", @@ -390,7 +368,7 @@ AVFilter ff_af_anlmdn = { .uninit = uninit, .inputs = inputs, .outputs = outputs, - .process_command = process_command, + .process_command = ff_filter_process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/af_anlms.c b/libavfilter/af_anlms.c new file mode 100644 index 00000000000..55e6946b682 --- /dev/null +++ b/libavfilter/af_anlms.c @@ -0,0 +1,330 @@ +/* + * 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/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, + NB_OMODES +}; + +typedef struct AudioNLMSContext { + const AVClass *class; + + int order; + float mu; + float eps; + float leakage; + int output_mode; + + int kernel_size; + AVFrame *offset; + AVFrame *delay; + AVFrame *coeffs; + AVFrame *tmp; + + AVFrame *frame[2]; + + AVFloatDSPContext *fdsp; +} AudioNLMSContext; + +#define OFFSET(x) offsetof(AudioNLMSContext, 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 anlms_options[] = { + { "order", "set the filter order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=256}, 1, INT16_MAX, A }, + { "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" }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(anlms); + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats; + AVFilterChannelLayouts *layouts; + static const enum AVSampleFormat sample_fmts[] = { + AV_SAMPLE_FMT_FLTP, + AV_SAMPLE_FMT_NONE + }; + int ret; + + layouts = ff_all_channel_counts(); + if (!layouts) + return AVERROR(ENOMEM); + ret = ff_set_common_channel_layouts(ctx, layouts); + if (ret < 0) + return ret; + + formats = ff_make_format_list(sample_fmts); + if (!formats) + return AVERROR(ENOMEM); + ret = ff_set_common_formats(ctx, formats); + if (ret < 0) + return ret; + + formats = ff_all_samplerates(); + if (!formats) + return AVERROR(ENOMEM); + return ff_set_common_samplerates(ctx, formats); +} + +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; + + 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->channels * jobnr) / nb_jobs; + const int end = (out->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); + } + + return 0; +} + +static int activate(AVFilterContext *ctx) +{ + AudioNLMSContext *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); + } + + ctx->internal->execute(ctx, process_channels, out, NULL, FFMIN(ctx->outputs[0]->channels, + ff_filter_get_nb_threads(ctx))); + + out->pts = s->frame[0]->pts; + + 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 (ff_inlink_queued_samples(ctx->inputs[i]) > 0) + continue; + ff_inlink_request_frame(ctx->inputs[i]); + return 0; + } + } + return 0; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AudioNLMSContext *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->tmp) + s->tmp = ff_get_audio_buffer(outlink, s->kernel_size); + if (!s->delay || !s->coeffs || !s->offset || !s->tmp) + return AVERROR(ENOMEM); + + return 0; +} + +static av_cold int init(AVFilterContext *ctx) +{ + AudioNLMSContext *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) +{ + AudioNLMSContext *s = ctx->priv; + + av_freep(&s->fdsp); + av_frame_free(&s->delay); + av_frame_free(&s->coeffs); + av_frame_free(&s->offset); + av_frame_free(&s->tmp); +} + +static const AVFilterPad inputs[] = { + { + .name = "input", + .type = AVMEDIA_TYPE_AUDIO, + }, + { + .name = "desired", + .type = AVMEDIA_TYPE_AUDIO, + }, + { NULL } +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, + }, + { NULL } +}; + +AVFilter ff_af_anlms = { + .name = "anlms", + .description = NULL_IF_CONFIG_SMALL("Apply Normalized Least-Mean-Squares algorithm to first audio stream."), + .priv_size = sizeof(AudioNLMSContext), + .priv_class = &anlms_class, + .init = init, + .uninit = uninit, + .activate = activate, + .query_formats = query_formats, + .inputs = inputs, + .outputs = outputs, + .flags = AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, +}; diff --git a/libavfilter/af_arnndn.c b/libavfilter/af_arnndn.c new file mode 100644 index 00000000000..7a26f89709e --- /dev/null +++ b/libavfilter/af_arnndn.c @@ -0,0 +1,1549 @@ +/* + * Copyright (c) 2018 Gregor Richards + * Copyright (c) 2017 Mozilla + * Copyright (c) 2005-2009 Xiph.Org Foundation + * Copyright (c) 2007-2008 CSIRO + * Copyright (c) 2008-2011 Octasic Inc. + * Copyright (c) Jean-Marc Valin + * Copyright (c) 2019 Paul B Mahol + * + * 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. + * + * 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 FOUNDATION 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/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/float_dsp.h" +#include "libavutil/opt.h" +#include "libavutil/tx.h" +#include "avfilter.h" +#include "audio.h" +#include "filters.h" +#include "formats.h" + +#define FRAME_SIZE_SHIFT 2 +#define FRAME_SIZE (120<name) { \ + av_free((void *) model->name->input_weights); \ + av_free((void *) model->name->bias); \ + av_free((void *) model->name); \ + } \ + } while (0) +#define FREE_GRU(name) do { \ + if (model->name) { \ + av_free((void *) model->name->input_weights); \ + av_free((void *) model->name->recurrent_weights); \ + av_free((void *) model->name->bias); \ + av_free((void *) model->name); \ + } \ + } while (0) + + if (!model) + return; + FREE_DENSE(input_dense); + FREE_GRU(vad_gru); + FREE_GRU(noise_gru); + FREE_GRU(denoise_gru); + FREE_DENSE(denoise_output); + FREE_DENSE(vad_output); + av_free(model); +} + +static RNNModel *rnnoise_model_from_file(FILE *f) +{ + RNNModel *ret; + DenseLayer *input_dense; + GRULayer *vad_gru; + GRULayer *noise_gru; + GRULayer *denoise_gru; + DenseLayer *denoise_output; + DenseLayer *vad_output; + int in; + + if (fscanf(f, "rnnoise-nu model file version %d\n", &in) != 1 || in != 1) + return NULL; + + ret = av_calloc(1, sizeof(RNNModel)); + if (!ret) + return NULL; + +#define ALLOC_LAYER(type, name) \ + name = av_calloc(1, sizeof(type)); \ + if (!name) { \ + rnnoise_model_free(ret); \ + return NULL; \ + } \ + ret->name = name + + ALLOC_LAYER(DenseLayer, input_dense); + ALLOC_LAYER(GRULayer, vad_gru); + ALLOC_LAYER(GRULayer, noise_gru); + ALLOC_LAYER(GRULayer, denoise_gru); + ALLOC_LAYER(DenseLayer, denoise_output); + ALLOC_LAYER(DenseLayer, vad_output); + +#define INPUT_VAL(name) do { \ + if (fscanf(f, "%d", &in) != 1 || in < 0 || in > 128) { \ + rnnoise_model_free(ret); \ + return NULL; \ + } \ + name = in; \ + } while (0) + +#define INPUT_ACTIVATION(name) do { \ + int activation; \ + INPUT_VAL(activation); \ + switch (activation) { \ + case F_ACTIVATION_SIGMOID: \ + name = ACTIVATION_SIGMOID; \ + break; \ + case F_ACTIVATION_RELU: \ + name = ACTIVATION_RELU; \ + break; \ + default: \ + name = ACTIVATION_TANH; \ + } \ + } while (0) + +#define INPUT_ARRAY(name, len) do { \ + float *values = av_calloc((len), sizeof(float)); \ + if (!values) { \ + rnnoise_model_free(ret); \ + return NULL; \ + } \ + name = values; \ + for (int i = 0; i < (len); i++) { \ + if (fscanf(f, "%d", &in) != 1) { \ + rnnoise_model_free(ret); \ + return NULL; \ + } \ + values[i] = in; \ + } \ + } while (0) + +#define INPUT_ARRAY3(name, len0, len1, len2) do { \ + float *values = av_calloc(FFALIGN((len0), 4) * FFALIGN((len1), 4) * (len2), sizeof(float)); \ + if (!values) { \ + rnnoise_model_free(ret); \ + return NULL; \ + } \ + name = values; \ + for (int k = 0; k < (len0); k++) { \ + for (int i = 0; i < (len2); i++) { \ + for (int j = 0; j < (len1); j++) { \ + if (fscanf(f, "%d", &in) != 1) { \ + rnnoise_model_free(ret); \ + return NULL; \ + } \ + values[j * (len2) * FFALIGN((len0), 4) + i * FFALIGN((len0), 4) + k] = in; \ + } \ + } \ + } \ + } while (0) + +#define INPUT_DENSE(name) do { \ + INPUT_VAL(name->nb_inputs); \ + INPUT_VAL(name->nb_neurons); \ + ret->name ## _size = name->nb_neurons; \ + INPUT_ACTIVATION(name->activation); \ + INPUT_ARRAY(name->input_weights, name->nb_inputs * name->nb_neurons); \ + INPUT_ARRAY(name->bias, name->nb_neurons); \ + } while (0) + +#define INPUT_GRU(name) do { \ + INPUT_VAL(name->nb_inputs); \ + INPUT_VAL(name->nb_neurons); \ + ret->name ## _size = name->nb_neurons; \ + INPUT_ACTIVATION(name->activation); \ + INPUT_ARRAY3(name->input_weights, name->nb_inputs, name->nb_neurons, 3); \ + INPUT_ARRAY3(name->recurrent_weights, name->nb_neurons, name->nb_neurons, 3); \ + INPUT_ARRAY(name->bias, name->nb_neurons * 3); \ + } while (0) + + INPUT_DENSE(input_dense); + INPUT_GRU(vad_gru); + INPUT_GRU(noise_gru); + INPUT_GRU(denoise_gru); + INPUT_DENSE(denoise_output); + INPUT_DENSE(vad_output); + + if (vad_output->nb_neurons != 1) { + rnnoise_model_free(ret); + return NULL; + } + + return ret; +} + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats = NULL; + AVFilterChannelLayouts *layouts = NULL; + static const enum AVSampleFormat sample_fmts[] = { + AV_SAMPLE_FMT_FLTP, + AV_SAMPLE_FMT_NONE + }; + int ret, sample_rates[] = { 48000, -1 }; + + formats = ff_make_format_list(sample_fmts); + if (!formats) + return AVERROR(ENOMEM); + ret = ff_set_common_formats(ctx, formats); + if (ret < 0) + return ret; + + layouts = ff_all_channel_counts(); + if (!layouts) + return AVERROR(ENOMEM); + + ret = ff_set_common_channel_layouts(ctx, layouts); + if (ret < 0) + return ret; + + formats = ff_make_format_list(sample_rates); + if (!formats) + return AVERROR(ENOMEM); + return ff_set_common_samplerates(ctx, formats); +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + AudioRNNContext *s = ctx->priv; + int ret; + + s->channels = inlink->channels; + + s->st = av_calloc(s->channels, sizeof(DenoiseState)); + if (!s->st) + return AVERROR(ENOMEM); + + for (int i = 0; i < s->channels; i++) { + DenoiseState *st = &s->st[i]; + + st->rnn.model = s->model; + st->rnn.vad_gru_state = av_calloc(sizeof(float), FFALIGN(s->model->vad_gru_size, 16)); + st->rnn.noise_gru_state = av_calloc(sizeof(float), FFALIGN(s->model->noise_gru_size, 16)); + st->rnn.denoise_gru_state = av_calloc(sizeof(float), FFALIGN(s->model->denoise_gru_size, 16)); + if (!st->rnn.vad_gru_state || + !st->rnn.noise_gru_state || + !st->rnn.denoise_gru_state) + return AVERROR(ENOMEM); + + ret = av_tx_init(&st->tx, &st->tx_fn, AV_TX_FLOAT_FFT, 0, WINDOW_SIZE, NULL, 0); + if (ret < 0) + return ret; + + ret = av_tx_init(&st->txi, &st->txi_fn, AV_TX_FLOAT_FFT, 1, WINDOW_SIZE, NULL, 0); + if (ret < 0) + return ret; + } + + return 0; +} + +static void biquad(float *y, float mem[2], const float *x, + const float *b, const float *a, int N) +{ + for (int i = 0; i < N; i++) { + float xi, yi; + + xi = x[i]; + yi = x[i] + mem[0]; + mem[0] = mem[1] + (b[0]*xi - a[0]*yi); + mem[1] = (b[1]*xi - a[1]*yi); + y[i] = yi; + } +} + +#define RNN_MOVE(dst, src, n) (memmove((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) )) +#define RNN_CLEAR(dst, n) (memset((dst), 0, (n)*sizeof(*(dst)))) +#define RNN_COPY(dst, src, n) (memcpy((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) )) + +static void forward_transform(DenoiseState *st, AVComplexFloat *out, const float *in) +{ + AVComplexFloat x[WINDOW_SIZE]; + AVComplexFloat y[WINDOW_SIZE]; + + for (int i = 0; i < WINDOW_SIZE; i++) { + x[i].re = in[i]; + x[i].im = 0; + } + + st->tx_fn(st->tx, y, x, sizeof(float)); + + RNN_COPY(out, y, FREQ_SIZE); +} + +static void inverse_transform(DenoiseState *st, float *out, const AVComplexFloat *in) +{ + AVComplexFloat x[WINDOW_SIZE]; + AVComplexFloat y[WINDOW_SIZE]; + + for (int i = 0; i < FREQ_SIZE; i++) + x[i] = in[i]; + + for (int i = FREQ_SIZE; i < WINDOW_SIZE; i++) { + x[i].re = x[WINDOW_SIZE - i].re; + x[i].im = -x[WINDOW_SIZE - i].im; + } + + st->txi_fn(st->txi, y, x, sizeof(float)); + + for (int i = 0; i < WINDOW_SIZE; i++) + out[i] = y[i].re / WINDOW_SIZE; +} + +static const uint8_t eband5ms[] = { +/*0 200 400 600 800 1k 1.2 1.4 1.6 2k 2.4 2.8 3.2 4k 4.8 5.6 6.8 8k 9.6 12k 15.6 20k*/ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 34, 40, 48, 60, 78, 100 +}; + +static void compute_band_energy(float *bandE, const AVComplexFloat *X) +{ + float sum[NB_BANDS] = {0}; + + for (int i = 0; i < NB_BANDS - 1; i++) { + int band_size; + + band_size = (eband5ms[i + 1] - eband5ms[i]) << FRAME_SIZE_SHIFT; + for (int j = 0; j < band_size; j++) { + float tmp, frac = (float)j / band_size; + + tmp = SQUARE(X[(eband5ms[i] << FRAME_SIZE_SHIFT) + j].re); + tmp += SQUARE(X[(eband5ms[i] << FRAME_SIZE_SHIFT) + j].im); + sum[i] += (1.f - frac) * tmp; + sum[i + 1] += frac * tmp; + } + } + + sum[0] *= 2; + sum[NB_BANDS - 1] *= 2; + + for (int i = 0; i < NB_BANDS; i++) + bandE[i] = sum[i]; +} + +static void compute_band_corr(float *bandE, const AVComplexFloat *X, const AVComplexFloat *P) +{ + float sum[NB_BANDS] = { 0 }; + + for (int i = 0; i < NB_BANDS - 1; i++) { + int band_size; + + band_size = (eband5ms[i + 1] - eband5ms[i]) << FRAME_SIZE_SHIFT; + for (int j = 0; j < band_size; j++) { + float tmp, frac = (float)j / band_size; + + tmp = X[(eband5ms[i]<analysis_mem, FRAME_SIZE); + RNN_COPY(x + FRAME_SIZE, in, FRAME_SIZE); + RNN_COPY(st->analysis_mem, in, FRAME_SIZE); + s->fdsp->vector_fmul(x, x, s->window, WINDOW_SIZE); + forward_transform(st, X, x); + compute_band_energy(Ex, X); +} + +static void frame_synthesis(AudioRNNContext *s, DenoiseState *st, float *out, const AVComplexFloat *y) +{ + LOCAL_ALIGNED_32(float, x, [WINDOW_SIZE]); + + inverse_transform(st, x, y); + s->fdsp->vector_fmul(x, x, s->window, WINDOW_SIZE); + s->fdsp->vector_fmac_scalar(x, st->synthesis_mem, 1.f, FRAME_SIZE); + RNN_COPY(out, x, FRAME_SIZE); + RNN_COPY(st->synthesis_mem, &x[FRAME_SIZE], FRAME_SIZE); +} + +static inline void xcorr_kernel(const float *x, const float *y, float sum[4], int len) +{ + float y_0, y_1, y_2, y_3 = 0; + int j; + + y_0 = *y++; + y_1 = *y++; + y_2 = *y++; + + for (j = 0; j < len - 3; j += 4) { + float tmp; + + tmp = *x++; + y_3 = *y++; + sum[0] += tmp * y_0; + sum[1] += tmp * y_1; + sum[2] += tmp * y_2; + sum[3] += tmp * y_3; + tmp = *x++; + y_0 = *y++; + sum[0] += tmp * y_1; + sum[1] += tmp * y_2; + sum[2] += tmp * y_3; + sum[3] += tmp * y_0; + tmp = *x++; + y_1 = *y++; + sum[0] += tmp * y_2; + sum[1] += tmp * y_3; + sum[2] += tmp * y_0; + sum[3] += tmp * y_1; + tmp = *x++; + y_2 = *y++; + sum[0] += tmp * y_3; + sum[1] += tmp * y_0; + sum[2] += tmp * y_1; + sum[3] += tmp * y_2; + } + + if (j++ < len) { + float tmp = *x++; + + y_3 = *y++; + sum[0] += tmp * y_0; + sum[1] += tmp * y_1; + sum[2] += tmp * y_2; + sum[3] += tmp * y_3; + } + + if (j++ < len) { + float tmp=*x++; + + y_0 = *y++; + sum[0] += tmp * y_1; + sum[1] += tmp * y_2; + sum[2] += tmp * y_3; + sum[3] += tmp * y_0; + } + + if (j < len) { + float tmp=*x++; + + y_1 = *y++; + sum[0] += tmp * y_2; + sum[1] += tmp * y_3; + sum[2] += tmp * y_0; + sum[3] += tmp * y_1; + } +} + +static inline float celt_inner_prod(const float *x, + const float *y, int N) +{ + float xy = 0.f; + + for (int i = 0; i < N; i++) + xy += x[i] * y[i]; + + return xy; +} + +static void celt_pitch_xcorr(const float *x, const float *y, + float *xcorr, int len, int max_pitch) +{ + int i; + + for (i = 0; i < max_pitch - 3; i += 4) { + float sum[4] = { 0, 0, 0, 0}; + + xcorr_kernel(x, y + i, sum, len); + + xcorr[i] = sum[0]; + xcorr[i + 1] = sum[1]; + xcorr[i + 2] = sum[2]; + xcorr[i + 3] = sum[3]; + } + /* In case max_pitch isn't a multiple of 4, do non-unrolled version. */ + for (; i < max_pitch; i++) { + xcorr[i] = celt_inner_prod(x, y + i, len); + } +} + +static int celt_autocorr(const float *x, /* in: [0...n-1] samples x */ + float *ac, /* out: [0...lag-1] ac values */ + const float *window, + int overlap, + int lag, + int n) +{ + int fastN = n - lag; + int shift; + const float *xptr; + float xx[PITCH_BUF_SIZE>>1]; + + if (overlap == 0) { + xptr = x; + } else { + for (int i = 0; i < n; i++) + xx[i] = x[i]; + for (int i = 0; i < overlap; i++) { + xx[i] = x[i] * window[i]; + xx[n-i-1] = x[n-i-1] * window[i]; + } + xptr = xx; + } + + shift = 0; + celt_pitch_xcorr(xptr, xptr, ac, fastN, lag+1); + + for (int k = 0; k <= lag; k++) { + float d = 0.f; + + for (int i = k + fastN; i < n; i++) + d += xptr[i] * xptr[i-k]; + ac[k] += d; + } + + return shift; +} + +static void celt_lpc(float *lpc, /* out: [0...p-1] LPC coefficients */ + const float *ac, /* in: [0...p] autocorrelation values */ + int p) +{ + float r, error = ac[0]; + + RNN_CLEAR(lpc, p); + if (ac[0] != 0) { + for (int i = 0; i < p; i++) { + /* Sum up this iteration's reflection coefficient */ + float rr = 0; + for (int j = 0; j < i; j++) + rr += (lpc[j] * ac[i - j]); + rr += ac[i + 1]; + r = -rr/error; + /* Update LPC coefficients and total error */ + lpc[i] = r; + for (int j = 0; j < (i + 1) >> 1; j++) { + float tmp1, tmp2; + tmp1 = lpc[j]; + tmp2 = lpc[i-1-j]; + lpc[j] = tmp1 + (r*tmp2); + lpc[i-1-j] = tmp2 + (r*tmp1); + } + + error = error - (r * r *error); + /* Bail out once we get 30 dB gain */ + if (error < .001f * ac[0]) + break; + } + } +} + +static void celt_fir5(const float *x, + const float *num, + float *y, + int N, + float *mem) +{ + float num0, num1, num2, num3, num4; + float mem0, mem1, mem2, mem3, mem4; + + num0 = num[0]; + num1 = num[1]; + num2 = num[2]; + num3 = num[3]; + num4 = num[4]; + mem0 = mem[0]; + mem1 = mem[1]; + mem2 = mem[2]; + mem3 = mem[3]; + mem4 = mem[4]; + + for (int i = 0; i < N; i++) { + float sum = x[i]; + + sum += (num0*mem0); + sum += (num1*mem1); + sum += (num2*mem2); + sum += (num3*mem3); + sum += (num4*mem4); + mem4 = mem3; + mem3 = mem2; + mem2 = mem1; + mem1 = mem0; + mem0 = x[i]; + y[i] = sum; + } + + mem[0] = mem0; + mem[1] = mem1; + mem[2] = mem2; + mem[3] = mem3; + mem[4] = mem4; +} + +static void pitch_downsample(float *x[], float *x_lp, + int len, int C) +{ + float ac[5]; + float tmp=Q15ONE; + float lpc[4], mem[5]={0,0,0,0,0}; + float lpc2[5]; + float c1 = .8f; + + for (int i = 1; i < len >> 1; i++) + x_lp[i] = .5f * (.5f * (x[0][(2*i-1)]+x[0][(2*i+1)])+x[0][2*i]); + x_lp[0] = .5f * (.5f * (x[0][1])+x[0][0]); + if (C==2) { + for (int i = 1; i < len >> 1; i++) + x_lp[i] += (.5f * (.5f * (x[1][(2*i-1)]+x[1][(2*i+1)])+x[1][2*i])); + x_lp[0] += .5f * (.5f * (x[1][1])+x[1][0]); + } + + celt_autocorr(x_lp, ac, NULL, 0, 4, len>>1); + + /* Noise floor -40 dB */ + ac[0] *= 1.0001f; + /* Lag windowing */ + for (int i = 1; i <= 4; i++) { + /*ac[i] *= exp(-.5*(2*M_PI*.002*i)*(2*M_PI*.002*i));*/ + ac[i] -= ac[i]*(.008f*i)*(.008f*i); + } + + celt_lpc(lpc, ac, 4); + for (int i = 0; i < 4; i++) { + tmp = .9f * tmp; + lpc[i] = (lpc[i] * tmp); + } + /* Add a zero */ + lpc2[0] = lpc[0] + .8f; + lpc2[1] = lpc[1] + (c1 * lpc[0]); + lpc2[2] = lpc[2] + (c1 * lpc[1]); + lpc2[3] = lpc[3] + (c1 * lpc[2]); + lpc2[4] = (c1 * lpc[3]); + celt_fir5(x_lp, lpc2, x_lp, len>>1, mem); +} + +static inline void dual_inner_prod(const float *x, const float *y01, const float *y02, + int N, float *xy1, float *xy2) +{ + float xy01 = 0, xy02 = 0; + + for (int i = 0; i < N; i++) { + xy01 += (x[i] * y01[i]); + xy02 += (x[i] * y02[i]); + } + + *xy1 = xy01; + *xy2 = xy02; +} + +static float compute_pitch_gain(float xy, float xx, float yy) +{ + return xy / sqrtf(1.f + xx * yy); +} + +static const int second_check[16] = {0, 0, 3, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 3, 2}; +static float remove_doubling(float *x, int maxperiod, int minperiod, int N, + int *T0_, int prev_period, float prev_gain) +{ + int k, i, T, T0; + float g, g0; + float pg; + float xy,xx,yy,xy2; + float xcorr[3]; + float best_xy, best_yy; + int offset; + int minperiod0; + float yy_lookup[PITCH_MAX_PERIOD+1]; + + minperiod0 = minperiod; + maxperiod /= 2; + minperiod /= 2; + *T0_ /= 2; + prev_period /= 2; + N /= 2; + x += maxperiod; + if (*T0_>=maxperiod) + *T0_=maxperiod-1; + + T = T0 = *T0_; + dual_inner_prod(x, x, x-T0, N, &xx, &xy); + yy_lookup[0] = xx; + yy=xx; + for (i = 1; i <= maxperiod; i++) { + yy = yy+(x[-i] * x[-i])-(x[N-i] * x[N-i]); + yy_lookup[i] = FFMAX(0, yy); + } + yy = yy_lookup[T0]; + best_xy = xy; + best_yy = yy; + g = g0 = compute_pitch_gain(xy, xx, yy); + /* Look for any pitch at T/k */ + for (k = 2; k <= 15; k++) { + int T1, T1b; + float g1; + float cont=0; + float thresh; + T1 = (2*T0+k)/(2*k); + if (T1 < minperiod) + break; + /* Look for another strong correlation at T1b */ + if (k==2) + { + if (T1+T0>maxperiod) + T1b = T0; + else + T1b = T0+T1; + } else + { + T1b = (2*second_check[k]*T0+k)/(2*k); + } + dual_inner_prod(x, &x[-T1], &x[-T1b], N, &xy, &xy2); + xy = .5f * (xy + xy2); + yy = .5f * (yy_lookup[T1] + yy_lookup[T1b]); + g1 = compute_pitch_gain(xy, xx, yy); + if (FFABS(T1-prev_period)<=1) + cont = prev_gain; + else if (FFABS(T1-prev_period)<=2 && 5 * k * k < T0) + cont = prev_gain * .5f; + else + cont = 0; + thresh = FFMAX(.3f, (.7f * g0) - cont); + /* Bias against very high pitch (very short period) to avoid false-positives + due to short-term correlation */ + if (T1<3*minperiod) + thresh = FFMAX(.4f, (.85f * g0) - cont); + else if (T1<2*minperiod) + thresh = FFMAX(.5f, (.9f * g0) - cont); + if (g1 > thresh) + { + best_xy = xy; + best_yy = yy; + T = T1; + g = g1; + } + } + best_xy = FFMAX(0, best_xy); + if (best_yy <= best_xy) + pg = Q15ONE; + else + pg = best_xy/(best_yy + 1); + + for (k = 0; k < 3; k++) + xcorr[k] = celt_inner_prod(x, x-(T+k-1), N); + if ((xcorr[2]-xcorr[0]) > .7f * (xcorr[1]-xcorr[0])) + offset = 1; + else if ((xcorr[0]-xcorr[2]) > (.7f * (xcorr[1] - xcorr[2]))) + offset = -1; + else + offset = 0; + if (pg > g) + pg = g; + *T0_ = 2*T+offset; + + if (*T0_0) { + float num; + float xcorr16; + + xcorr16 = xcorr[i]; + /* Considering the range of xcorr16, this should avoid both underflows + and overflows (inf) when squaring xcorr16 */ + xcorr16 *= 1e-12f; + num = xcorr16 * xcorr16; + if ((num * best_den[1]) > (best_num[1] * Syy)) { + if ((num * best_den[0]) > (best_num[0] * Syy)) { + best_num[1] = best_num[0]; + best_den[1] = best_den[0]; + best_pitch[1] = best_pitch[0]; + best_num[0] = num; + best_den[0] = Syy; + best_pitch[0] = i; + } else { + best_num[1] = num; + best_den[1] = Syy; + best_pitch[1] = i; + } + } + } + Syy += y[i+len]*y[i+len] - y[i] * y[i]; + Syy = FFMAX(1, Syy); + } +} + +static void pitch_search(const float *x_lp, float *y, + int len, int max_pitch, int *pitch) +{ + int lag; + int best_pitch[2]={0,0}; + int offset; + + float x_lp4[WINDOW_SIZE]; + float y_lp4[WINDOW_SIZE]; + float xcorr[WINDOW_SIZE]; + + lag = len+max_pitch; + + /* Downsample by 2 again */ + for (int j = 0; j < len >> 2; j++) + x_lp4[j] = x_lp[2*j]; + for (int j = 0; j < lag >> 2; j++) + y_lp4[j] = y[2*j]; + + /* Coarse search with 4x decimation */ + + celt_pitch_xcorr(x_lp4, y_lp4, xcorr, len>>2, max_pitch>>2); + + find_best_pitch(xcorr, y_lp4, len>>2, max_pitch>>2, best_pitch); + + /* Finer search with 2x decimation */ + for (int i = 0; i < max_pitch >> 1; i++) { + float sum; + xcorr[i] = 0; + if (FFABS(i-2*best_pitch[0])>2 && FFABS(i-2*best_pitch[1])>2) + continue; + sum = celt_inner_prod(x_lp, y+i, len>>1); + xcorr[i] = FFMAX(-1, sum); + } + + find_best_pitch(xcorr, y, len>>1, max_pitch>>1, best_pitch); + + /* Refine by pseudo-interpolation */ + if (best_pitch[0] > 0 && best_pitch[0] < (max_pitch >> 1) - 1) { + float a, b, c; + + a = xcorr[best_pitch[0] - 1]; + b = xcorr[best_pitch[0]]; + c = xcorr[best_pitch[0] + 1]; + if (c - a > .7f * (b - a)) + offset = 1; + else if (a - c > .7f * (b-c)) + offset = -1; + else + offset = 0; + } else { + offset = 0; + } + + *pitch = 2 * best_pitch[0] - offset; +} + +static void dct(AudioRNNContext *s, float *out, const float *in) +{ + for (int i = 0; i < NB_BANDS; i++) { + float sum = 0.f; + + for (int j = 0; j < NB_BANDS; j++) { + sum += in[j] * s->dct_table[j * NB_BANDS + i]; + } + out[i] = sum * sqrtf(2.f / 22); + } +} + +static int compute_frame_features(AudioRNNContext *s, DenoiseState *st, AVComplexFloat *X, AVComplexFloat *P, + float *Ex, float *Ep, float *Exp, float *features, const float *in) +{ + float E = 0; + float *ceps_0, *ceps_1, *ceps_2; + float spec_variability = 0; + float Ly[NB_BANDS]; + LOCAL_ALIGNED_32(float, p, [WINDOW_SIZE]); + float pitch_buf[PITCH_BUF_SIZE>>1]; + int pitch_index; + float gain; + float *(pre[1]); + float tmp[NB_BANDS]; + float follow, logMax; + + frame_analysis(s, st, X, Ex, in); + RNN_MOVE(st->pitch_buf, &st->pitch_buf[FRAME_SIZE], PITCH_BUF_SIZE-FRAME_SIZE); + RNN_COPY(&st->pitch_buf[PITCH_BUF_SIZE-FRAME_SIZE], in, FRAME_SIZE); + pre[0] = &st->pitch_buf[0]; + pitch_downsample(pre, pitch_buf, PITCH_BUF_SIZE, 1); + pitch_search(pitch_buf+(PITCH_MAX_PERIOD>>1), pitch_buf, PITCH_FRAME_SIZE, + PITCH_MAX_PERIOD-3*PITCH_MIN_PERIOD, &pitch_index); + pitch_index = PITCH_MAX_PERIOD-pitch_index; + + gain = remove_doubling(pitch_buf, PITCH_MAX_PERIOD, PITCH_MIN_PERIOD, + PITCH_FRAME_SIZE, &pitch_index, st->last_period, st->last_gain); + st->last_period = pitch_index; + st->last_gain = gain; + + for (int i = 0; i < WINDOW_SIZE; i++) + p[i] = st->pitch_buf[PITCH_BUF_SIZE-WINDOW_SIZE-pitch_index+i]; + + s->fdsp->vector_fmul(p, p, s->window, WINDOW_SIZE); + forward_transform(st, P, p); + compute_band_energy(Ep, P); + compute_band_corr(Exp, X, P); + + for (int i = 0; i < NB_BANDS; i++) + Exp[i] = Exp[i] / sqrtf(.001f+Ex[i]*Ep[i]); + + dct(s, tmp, Exp); + + for (int i = 0; i < NB_DELTA_CEPS; i++) + features[NB_BANDS+2*NB_DELTA_CEPS+i] = tmp[i]; + + features[NB_BANDS+2*NB_DELTA_CEPS] -= 1.3; + features[NB_BANDS+2*NB_DELTA_CEPS+1] -= 0.9; + features[NB_BANDS+3*NB_DELTA_CEPS] = .01*(pitch_index-300); + logMax = -2; + follow = -2; + + for (int i = 0; i < NB_BANDS; i++) { + Ly[i] = log10f(1e-2f + Ex[i]); + Ly[i] = FFMAX(logMax-7, FFMAX(follow-1.5, Ly[i])); + logMax = FFMAX(logMax, Ly[i]); + follow = FFMAX(follow-1.5, Ly[i]); + E += Ex[i]; + } + + if (E < 0.04f) { + /* If there's no audio, avoid messing up the state. */ + RNN_CLEAR(features, NB_FEATURES); + return 1; + } + + dct(s, features, Ly); + features[0] -= 12; + features[1] -= 4; + ceps_0 = st->cepstral_mem[st->memid]; + ceps_1 = (st->memid < 1) ? st->cepstral_mem[CEPS_MEM+st->memid-1] : st->cepstral_mem[st->memid-1]; + ceps_2 = (st->memid < 2) ? st->cepstral_mem[CEPS_MEM+st->memid-2] : st->cepstral_mem[st->memid-2]; + + for (int i = 0; i < NB_BANDS; i++) + ceps_0[i] = features[i]; + + st->memid++; + for (int i = 0; i < NB_DELTA_CEPS; i++) { + features[i] = ceps_0[i] + ceps_1[i] + ceps_2[i]; + features[NB_BANDS+i] = ceps_0[i] - ceps_2[i]; + features[NB_BANDS+NB_DELTA_CEPS+i] = ceps_0[i] - 2*ceps_1[i] + ceps_2[i]; + } + /* Spectral variability features. */ + if (st->memid == CEPS_MEM) + st->memid = 0; + + for (int i = 0; i < CEPS_MEM; i++) { + float mindist = 1e15f; + for (int j = 0; j < CEPS_MEM; j++) { + float dist = 0.f; + for (int k = 0; k < NB_BANDS; k++) { + float tmp; + + tmp = st->cepstral_mem[i][k] - st->cepstral_mem[j][k]; + dist += tmp*tmp; + } + + if (j != i) + mindist = FFMIN(mindist, dist); + } + + spec_variability += mindist; + } + + features[NB_BANDS+3*NB_DELTA_CEPS+1] = spec_variability/CEPS_MEM-2.1; + + return 0; +} + +static void interp_band_gain(float *g, const float *bandE) +{ + memset(g, 0, sizeof(*g) * FREQ_SIZE); + + for (int i = 0; i < NB_BANDS - 1; i++) { + const int band_size = (eband5ms[i + 1] - eband5ms[i]) << FRAME_SIZE_SHIFT; + + for (int j = 0; j < band_size; j++) { + float frac = (float)j / band_size; + + g[(eband5ms[i] << FRAME_SIZE_SHIFT) + j] = (1.f - frac) * bandE[i] + frac * bandE[i + 1]; + } + } +} + +static void pitch_filter(AVComplexFloat *X, const AVComplexFloat *P, const float *Ex, const float *Ep, + const float *Exp, const float *g) +{ + float newE[NB_BANDS]; + float r[NB_BANDS]; + float norm[NB_BANDS]; + float rf[FREQ_SIZE] = {0}; + float normf[FREQ_SIZE]={0}; + + for (int i = 0; i < NB_BANDS; i++) { + if (Exp[i]>g[i]) r[i] = 1; + else r[i] = SQUARE(Exp[i])*(1-SQUARE(g[i]))/(.001 + SQUARE(g[i])*(1-SQUARE(Exp[i]))); + r[i] = sqrtf(av_clipf(r[i], 0, 1)); + r[i] *= sqrtf(Ex[i]/(1e-8+Ep[i])); + } + interp_band_gain(rf, r); + for (int i = 0; i < FREQ_SIZE; i++) { + X[i].re += rf[i]*P[i].re; + X[i].im += rf[i]*P[i].im; + } + compute_band_energy(newE, X); + for (int i = 0; i < NB_BANDS; i++) { + norm[i] = sqrtf(Ex[i] / (1e-8+newE[i])); + } + interp_band_gain(normf, norm); + for (int i = 0; i < FREQ_SIZE; i++) { + X[i].re *= normf[i]; + X[i].im *= normf[i]; + } +} + +static const float tansig_table[201] = { + 0.000000f, 0.039979f, 0.079830f, 0.119427f, 0.158649f, + 0.197375f, 0.235496f, 0.272905f, 0.309507f, 0.345214f, + 0.379949f, 0.413644f, 0.446244f, 0.477700f, 0.507977f, + 0.537050f, 0.564900f, 0.591519f, 0.616909f, 0.641077f, + 0.664037f, 0.685809f, 0.706419f, 0.725897f, 0.744277f, + 0.761594f, 0.777888f, 0.793199f, 0.807569f, 0.821040f, + 0.833655f, 0.845456f, 0.856485f, 0.866784f, 0.876393f, + 0.885352f, 0.893698f, 0.901468f, 0.908698f, 0.915420f, + 0.921669f, 0.927473f, 0.932862f, 0.937863f, 0.942503f, + 0.946806f, 0.950795f, 0.954492f, 0.957917f, 0.961090f, + 0.964028f, 0.966747f, 0.969265f, 0.971594f, 0.973749f, + 0.975743f, 0.977587f, 0.979293f, 0.980869f, 0.982327f, + 0.983675f, 0.984921f, 0.986072f, 0.987136f, 0.988119f, + 0.989027f, 0.989867f, 0.990642f, 0.991359f, 0.992020f, + 0.992631f, 0.993196f, 0.993718f, 0.994199f, 0.994644f, + 0.995055f, 0.995434f, 0.995784f, 0.996108f, 0.996407f, + 0.996682f, 0.996937f, 0.997172f, 0.997389f, 0.997590f, + 0.997775f, 0.997946f, 0.998104f, 0.998249f, 0.998384f, + 0.998508f, 0.998623f, 0.998728f, 0.998826f, 0.998916f, + 0.999000f, 0.999076f, 0.999147f, 0.999213f, 0.999273f, + 0.999329f, 0.999381f, 0.999428f, 0.999472f, 0.999513f, + 0.999550f, 0.999585f, 0.999617f, 0.999646f, 0.999673f, + 0.999699f, 0.999722f, 0.999743f, 0.999763f, 0.999781f, + 0.999798f, 0.999813f, 0.999828f, 0.999841f, 0.999853f, + 0.999865f, 0.999875f, 0.999885f, 0.999893f, 0.999902f, + 0.999909f, 0.999916f, 0.999923f, 0.999929f, 0.999934f, + 0.999939f, 0.999944f, 0.999948f, 0.999952f, 0.999956f, + 0.999959f, 0.999962f, 0.999965f, 0.999968f, 0.999970f, + 0.999973f, 0.999975f, 0.999977f, 0.999978f, 0.999980f, + 0.999982f, 0.999983f, 0.999984f, 0.999986f, 0.999987f, + 0.999988f, 0.999989f, 0.999990f, 0.999990f, 0.999991f, + 0.999992f, 0.999992f, 0.999993f, 0.999994f, 0.999994f, + 0.999994f, 0.999995f, 0.999995f, 0.999996f, 0.999996f, + 0.999996f, 0.999997f, 0.999997f, 0.999997f, 0.999997f, + 0.999997f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, + 0.999998f, 0.999998f, 0.999999f, 0.999999f, 0.999999f, + 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, + 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, + 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, + 1.000000f, 1.000000f, 1.000000f, 1.000000f, 1.000000f, + 1.000000f, +}; + +static inline float tansig_approx(float x) +{ + float y, dy; + float sign=1; + int i; + + /* Tests are reversed to catch NaNs */ + if (!(x<8)) + return 1; + if (!(x>-8)) + return -1; + /* Another check in case of -ffast-math */ + + if (isnan(x)) + return 0; + + if (x < 0) { + x=-x; + sign=-1; + } + i = (int)floor(.5f+25*x); + x -= .04f*i; + y = tansig_table[i]; + dy = 1-y*y; + y = y + x*dy*(1 - y*x); + return sign*y; +} + +static inline float sigmoid_approx(float x) +{ + return .5f + .5f*tansig_approx(.5f*x); +} + +static void compute_dense(const DenseLayer *layer, float *output, const float *input) +{ + const int N = layer->nb_neurons, M = layer->nb_inputs, stride = N; + + for (int i = 0; i < N; i++) { + /* Compute update gate. */ + float sum = layer->bias[i]; + + for (int j = 0; j < M; j++) + sum += layer->input_weights[j * stride + i] * input[j]; + + output[i] = WEIGHTS_SCALE * sum; + } + + if (layer->activation == ACTIVATION_SIGMOID) { + for (int i = 0; i < N; i++) + output[i] = sigmoid_approx(output[i]); + } else if (layer->activation == ACTIVATION_TANH) { + for (int i = 0; i < N; i++) + output[i] = tansig_approx(output[i]); + } else if (layer->activation == ACTIVATION_RELU) { + for (int i = 0; i < N; i++) + output[i] = FFMAX(0, output[i]); + } else { + av_assert0(0); + } +} + +static void compute_gru(AudioRNNContext *s, const GRULayer *gru, float *state, const float *input) +{ + LOCAL_ALIGNED_32(float, z, [MAX_NEURONS]); + LOCAL_ALIGNED_32(float, r, [MAX_NEURONS]); + LOCAL_ALIGNED_32(float, h, [MAX_NEURONS]); + const int M = gru->nb_inputs; + const int N = gru->nb_neurons; + const int AN = FFALIGN(N, 4); + const int AM = FFALIGN(M, 4); + const int stride = 3 * AN, istride = 3 * AM; + + for (int i = 0; i < N; i++) { + /* Compute update gate. */ + float sum = gru->bias[i]; + + sum += s->fdsp->scalarproduct_float(gru->input_weights + i * istride, input, AM); + sum += s->fdsp->scalarproduct_float(gru->recurrent_weights + i * stride, state, AN); + z[i] = sigmoid_approx(WEIGHTS_SCALE * sum); + } + + for (int i = 0; i < N; i++) { + /* Compute reset gate. */ + float sum = gru->bias[N + i]; + + sum += s->fdsp->scalarproduct_float(gru->input_weights + AM + i * istride, input, AM); + sum += s->fdsp->scalarproduct_float(gru->recurrent_weights + AN + i * stride, state, AN); + r[i] = sigmoid_approx(WEIGHTS_SCALE * sum); + } + + for (int i = 0; i < N; i++) { + /* Compute output. */ + float sum = gru->bias[2 * N + i]; + + sum += s->fdsp->scalarproduct_float(gru->input_weights + 2 * AM + i * istride, input, AM); + for (int j = 0; j < N; j++) + sum += gru->recurrent_weights[2 * AN + i * stride + j] * state[j] * r[j]; + + if (gru->activation == ACTIVATION_SIGMOID) + sum = sigmoid_approx(WEIGHTS_SCALE * sum); + else if (gru->activation == ACTIVATION_TANH) + sum = tansig_approx(WEIGHTS_SCALE * sum); + else if (gru->activation == ACTIVATION_RELU) + sum = FFMAX(0, WEIGHTS_SCALE * sum); + else + av_assert0(0); + h[i] = z[i] * state[i] + (1.f - z[i]) * sum; + } + + RNN_COPY(state, h, N); +} + +#define INPUT_SIZE 42 + +static void compute_rnn(AudioRNNContext *s, RNNState *rnn, float *gains, float *vad, const float *input) +{ + LOCAL_ALIGNED_32(float, dense_out, [MAX_NEURONS]); + LOCAL_ALIGNED_32(float, noise_input, [MAX_NEURONS * 3]); + LOCAL_ALIGNED_32(float, denoise_input, [MAX_NEURONS * 3]); + + compute_dense(rnn->model->input_dense, dense_out, input); + compute_gru(s, rnn->model->vad_gru, rnn->vad_gru_state, dense_out); + compute_dense(rnn->model->vad_output, vad, rnn->vad_gru_state); + + for (int i = 0; i < rnn->model->input_dense_size; i++) + noise_input[i] = dense_out[i]; + for (int i = 0; i < rnn->model->vad_gru_size; i++) + noise_input[i + rnn->model->input_dense_size] = rnn->vad_gru_state[i]; + for (int i = 0; i < INPUT_SIZE; i++) + noise_input[i + rnn->model->input_dense_size + rnn->model->vad_gru_size] = input[i]; + + compute_gru(s, rnn->model->noise_gru, rnn->noise_gru_state, noise_input); + + for (int i = 0; i < rnn->model->vad_gru_size; i++) + denoise_input[i] = rnn->vad_gru_state[i]; + for (int i = 0; i < rnn->model->noise_gru_size; i++) + denoise_input[i + rnn->model->vad_gru_size] = rnn->noise_gru_state[i]; + for (int i = 0; i < INPUT_SIZE; i++) + denoise_input[i + rnn->model->vad_gru_size + rnn->model->noise_gru_size] = input[i]; + + compute_gru(s, rnn->model->denoise_gru, rnn->denoise_gru_state, denoise_input); + compute_dense(rnn->model->denoise_output, gains, rnn->denoise_gru_state); +} + +static float rnnoise_channel(AudioRNNContext *s, DenoiseState *st, float *out, const float *in) +{ + AVComplexFloat X[FREQ_SIZE]; + AVComplexFloat P[WINDOW_SIZE]; + float x[FRAME_SIZE]; + float Ex[NB_BANDS], Ep[NB_BANDS]; + float Exp[NB_BANDS]; + float features[NB_FEATURES]; + float g[NB_BANDS]; + float gf[FREQ_SIZE]; + float vad_prob = 0; + static const float a_hp[2] = {-1.99599, 0.99600}; + static const float b_hp[2] = {-2, 1}; + int silence; + + biquad(x, st->mem_hp_x, in, b_hp, a_hp, FRAME_SIZE); + silence = compute_frame_features(s, st, X, P, Ex, Ep, Exp, features, x); + + if (!silence) { + compute_rnn(s, &st->rnn, g, &vad_prob, features); + pitch_filter(X, P, Ex, Ep, Exp, g); + for (int i = 0; i < NB_BANDS; i++) { + float alpha = .6f; + + g[i] = FFMAX(g[i], alpha * st->lastg[i]); + st->lastg[i] = g[i]; + } + + interp_band_gain(gf, g); + + for (int i = 0; i < FREQ_SIZE; i++) { + X[i].re *= gf[i]; + X[i].im *= gf[i]; + } + } + + frame_synthesis(s, st, out, X); + + return vad_prob; +} + +typedef struct ThreadData { + AVFrame *in, *out; +} ThreadData; + +static int rnnoise_channels(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + AudioRNNContext *s = ctx->priv; + ThreadData *td = arg; + AVFrame *in = td->in; + AVFrame *out = td->out; + const int start = (out->channels * jobnr) / nb_jobs; + const int end = (out->channels * (jobnr+1)) / nb_jobs; + + for (int ch = start; ch < end; ch++) { + rnnoise_channel(s, &s->st[ch], + (float *)out->extended_data[ch], + (const float *)in->extended_data[ch]); + } + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *out = NULL; + ThreadData td; + + out = ff_get_audio_buffer(outlink, FRAME_SIZE); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + out->pts = in->pts; + + td.in = in; td.out = out; + ctx->internal->execute(ctx, rnnoise_channels, &td, NULL, FFMIN(outlink->channels, + ff_filter_get_nb_threads(ctx))); + + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *in = NULL; + int ret; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + ret = ff_inlink_consume_samples(inlink, FRAME_SIZE, FRAME_SIZE, &in); + if (ret < 0) + return ret; + + if (ret > 0) + return filter_frame(inlink, in); + + FF_FILTER_FORWARD_STATUS(inlink, outlink); + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; +} + +static av_cold int init(AVFilterContext *ctx) +{ + AudioRNNContext *s = ctx->priv; + FILE *f; + + s->fdsp = avpriv_float_dsp_alloc(0); + if (!s->fdsp) + return AVERROR(ENOMEM); + + if (!s->model_name) + return AVERROR(EINVAL); + f = av_fopen_utf8(s->model_name, "r"); + if (!f) + return AVERROR(EINVAL); + + s->model = rnnoise_model_from_file(f); + fclose(f); + if (!s->model) + return AVERROR(EINVAL); + + for (int i = 0; i < FRAME_SIZE; i++) { + s->window[i] = sin(.5*M_PI*sin(.5*M_PI*(i+.5)/FRAME_SIZE) * sin(.5*M_PI*(i+.5)/FRAME_SIZE)); + s->window[WINDOW_SIZE - 1 - i] = s->window[i]; + } + + for (int i = 0; i < NB_BANDS; i++) { + for (int j = 0; j < NB_BANDS; j++) { + s->dct_table[i*NB_BANDS + j] = cosf((i + .5f) * j * M_PI / NB_BANDS); + if (j == 0) + s->dct_table[i*NB_BANDS + j] *= sqrtf(.5); + } + } + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + AudioRNNContext *s = ctx->priv; + + av_freep(&s->fdsp); + rnnoise_model_free(s->model); + s->model = NULL; + + if (s->st) { + for (int ch = 0; ch < s->channels; ch++) { + av_freep(&s->st[ch].rnn.vad_gru_state); + av_freep(&s->st[ch].rnn.noise_gru_state); + av_freep(&s->st[ch].rnn.denoise_gru_state); + av_tx_uninit(&s->st[ch].tx); + av_tx_uninit(&s->st[ch].txi); + } + } + av_freep(&s->st); +} + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_input, + }, + { NULL } +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + }, + { NULL } +}; + +#define OFFSET(x) offsetof(AudioRNNContext, x) +#define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption arnndn_options[] = { + { "model", "set model name", OFFSET(model_name), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, AF }, + { "m", "set model name", OFFSET(model_name), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, AF }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(arnndn); + +AVFilter ff_af_arnndn = { + .name = "arnndn", + .description = NULL_IF_CONFIG_SMALL("Reduce noise from speech using Recurrent Neural Networks."), + .query_formats = query_formats, + .priv_size = sizeof(AudioRNNContext), + .priv_class = &arnndn_class, + .activate = activate, + .init = init, + .uninit = uninit, + .inputs = inputs, + .outputs = outputs, + .flags = AVFILTER_FLAG_SLICE_THREADS, +}; diff --git a/libavfilter/af_asoftclip.c b/libavfilter/af_asoftclip.c index a42b56dff4a..e9e3666ef6c 100644 --- a/libavfilter/af_asoftclip.c +++ b/libavfilter/af_asoftclip.c @@ -42,11 +42,11 @@ typedef struct ASoftClipContext { double param; void (*filter)(struct ASoftClipContext *s, void **dst, const void **src, - int nb_samples, int channels); + int nb_samples, int channels, int start, int end); } ASoftClipContext; #define OFFSET(x) offsetof(ASoftClipContext, x) -#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#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}, 0, NB_TYPES-1, A, "types" }, @@ -97,11 +97,12 @@ static int query_formats(AVFilterContext *ctx) static void filter_flt(ASoftClipContext *s, void **dptr, const void **sptr, - int nb_samples, int channels) + int nb_samples, int channels, + int start, int end) { float param = s->param; - for (int c = 0; c < channels; c++) { + for (int c = start; c < end; c++) { const float *src = sptr[c]; float *dst = dptr[c]; @@ -153,11 +154,12 @@ static void filter_flt(ASoftClipContext *s, static void filter_dbl(ASoftClipContext *s, void **dptr, const void **sptr, - int nb_samples, int channels) + int nb_samples, int channels, + int start, int end) { double param = s->param; - for (int c = 0; c < channels; c++) { + for (int c = start; c < end; c++) { const double *src = sptr[c]; double *dst = dptr[c]; @@ -222,12 +224,35 @@ static int config_input(AVFilterLink *inlink) return 0; } +typedef struct ThreadData { + AVFrame *in, *out; + int nb_samples; + int channels; +} ThreadData; + +static int filter_channels(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + ASoftClipContext *s = ctx->priv; + ThreadData *td = arg; + AVFrame *out = td->out; + AVFrame *in = td->in; + const int channels = td->channels; + const int nb_samples = td->nb_samples; + const int start = (channels * jobnr) / nb_jobs; + const int end = (channels * (jobnr+1)) / nb_jobs; + + s->filter(s, (void **)out->extended_data, (const void **)in->extended_data, + nb_samples, channels, start, end); + + return 0; +} + static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; - ASoftClipContext *s = ctx->priv; int nb_samples, channels; + ThreadData td; AVFrame *out; if (av_frame_is_writable(in)) { @@ -249,8 +274,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) channels = 1; } - s->filter(s, (void **)out->extended_data, (const void **)in->extended_data, - nb_samples, channels); + td.in = in; + td.out = out; + td.nb_samples = nb_samples; + td.channels = channels; + ctx->internal->execute(ctx, filter_channels, &td, NULL, FFMIN(channels, + ff_filter_get_nb_threads(ctx))); if (out != in) av_frame_free(&in); @@ -284,5 +313,7 @@ AVFilter ff_af_asoftclip = { .priv_class = &asoftclip_class, .inputs = inputs, .outputs = outputs, - .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, + .process_command = ff_filter_process_command, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | + AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/af_astats.c b/libavfilter/af_astats.c index 7707f3158d8..27036385cd3 100644 --- a/libavfilter/af_astats.c +++ b/libavfilter/af_astats.c @@ -27,6 +27,9 @@ #include "avfilter.h" #include "internal.h" +#define HISTOGRAM_SIZE 8192 +#define HISTOGRAM_MAX (HISTOGRAM_SIZE-1) + #define MEASURE_ALL UINT_MAX #define MEASURE_NONE 0 @@ -52,6 +55,8 @@ #define MEASURE_NUMBER_OF_NANS (1 << 19) #define MEASURE_NUMBER_OF_INFS (1 << 20) #define MEASURE_NUMBER_OF_DENORMALS (1 << 21) +#define MEASURE_NOISE_FLOOR (1 << 22) +#define MEASURE_NOISE_FLOOR_COUNT (1 << 23) #define MEASURE_MINMAXPEAK (MEASURE_MIN_LEVEL | MEASURE_MAX_LEVEL | MEASURE_PEAK_LEVEL) @@ -70,11 +75,17 @@ typedef struct ChannelStats { double diff1_sum_x2; uint64_t mask, imask; uint64_t min_count, max_count; + uint64_t noise_floor_count; uint64_t zero_runs; uint64_t nb_samples; uint64_t nb_nans; uint64_t nb_infs; uint64_t nb_denormals; + double *win_samples; + unsigned histogram[HISTOGRAM_SIZE]; + int win_pos; + int max_index; + double noise_floor; } ChannelStats; typedef struct AudioStatsContext { @@ -122,6 +133,8 @@ static const AVOption astats_options[] = { { "Dynamic_range" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_DYNAMIC_RANGE }, 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" }, + { "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_samples" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_SAMPLES }, 0, 0, FLAGS, "measure" }, { "Number_of_NaNs" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_NANS }, 0, 0, FLAGS, "measure" }, { "Number_of_Infs" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_INFS }, 0, 0, FLAGS, "measure" }, @@ -197,6 +210,11 @@ static void reset_stats(AudioStatsContext *s) p->nb_infs = 0; p->nb_denormals = 0; p->last = NAN; + p->noise_floor = NAN; + p->noise_floor_count = 0; + p->win_pos = 0; + memset(p->win_samples, 0, s->tc_samples * sizeof(*p->win_samples)); + memset(p->histogram, 0, sizeof(p->histogram)); } } @@ -207,9 +225,19 @@ static int config_output(AVFilterLink *outlink) s->chstats = av_calloc(sizeof(*s->chstats), outlink->channels); if (!s->chstats) return AVERROR(ENOMEM); + + s->tc_samples = 5 * s->time_constant * outlink->sample_rate + .5; s->nb_channels = outlink->channels; + + for (int i = 0; i < s->nb_channels; i++) { + ChannelStats *p = &s->chstats[i]; + + p->win_samples = av_calloc(s->tc_samples, sizeof(*p->win_samples)); + if (!p->win_samples) + return AVERROR(ENOMEM); + } + s->mult = exp((-1 / s->time_constant / outlink->sample_rate)); - s->tc_samples = 5 * s->time_constant * outlink->sample_rate + .5; s->nb_frames = 0; s->maxbitdepth = av_get_bytes_per_sample(outlink->format) * 8; s->is_double = outlink->format == AV_SAMPLE_FMT_DBL || @@ -249,6 +277,9 @@ 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; + int index; + if (d < p->min) { p->min = d; p->nmin = nd; @@ -296,6 +327,44 @@ static inline void update_stat(AudioStatsContext *s, ChannelStats *p, double d, p->mask |= i; p->imask &= i; + drop = p->win_samples[p->win_pos]; + p->win_samples[p->win_pos] = nd; + index = av_clip(FFABS(nd) * HISTOGRAM_MAX, 0, HISTOGRAM_MAX); + p->max_index = FFMAX(p->max_index, index); + p->histogram[index]++; + if (!isnan(p->noise_floor)) + p->histogram[av_clip(FFABS(drop) * 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.; + + for (int i = p->max_index; i >= 0; i--) { + if (p->histogram[i]) { + noise_floor = i / (double)HISTOGRAM_MAX; + break; + } + } + + if (isnan(p->noise_floor)) { + p->noise_floor = noise_floor; + p->noise_floor_count = 1; + } else { + if (noise_floor < p->noise_floor) { + p->noise_floor = noise_floor; + p->noise_floor_count = 1; + } else if (noise_floor == p->noise_floor) { + p->noise_floor_count++; + } + } + } + + 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); @@ -339,7 +408,7 @@ 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; + 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; double min_runs = 0, max_runs = 0, min = DBL_MAX, max =-DBL_MAX, min_diff = DBL_MAX, max_diff = 0, @@ -349,6 +418,7 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) diff1_sum_x2 = 0, sigma_x = 0, sigma_x2 = 0, + noise_floor = 0, min_sigma_x2 = DBL_MAX, max_sigma_x2 =-DBL_MAX; AVRational depth; @@ -372,6 +442,8 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) max_sigma_x2 = FFMAX(max_sigma_x2, p->max_sigma_x2); sigma_x += p->sigma_x; sigma_x2 += p->sigma_x2; + noise_floor = FFMAX(noise_floor, p->noise_floor); + noise_floor_count += p->noise_floor_count; min_count += p->min_count; max_count += p->max_count; min_runs += p->min_runs; @@ -413,6 +485,10 @@ 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_NOISE_FLOOR) + set_meta(metadata, c + 1, "Noise_floor", "%f", LINEAR_TO_DB(p->noise_floor)); + if (s->measure_perchannel & MEASURE_NOISE_FLOOR_COUNT) + set_meta(metadata, c + 1, "Noise_floor_count", "%f", p->noise_floor_count); 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); @@ -458,6 +534,10 @@ 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_NOISE_FLOOR) + set_meta(metadata, 0, "Overall.Noise_floor", "%f", LINEAR_TO_DB(noise_floor)); + if (s->measure_overall & MEASURE_NOISE_FLOOR_COUNT) + set_meta(metadata, 0, "Overall.Noise_floor_count", "%f", noise_floor_count / (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); @@ -474,7 +554,7 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) } #define UPDATE_STATS_P(type, update_func, update_float, channel_func) \ - for (int c = 0; c < channels; c++) { \ + for (int c = start; c < end; c++) { \ ChannelStats *p = &s->chstats[c]; \ const type *src = (const type *)data[c]; \ const type * const srcend = src + samples; \ @@ -486,7 +566,7 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) } #define UPDATE_STATS_I(type, update_func, update_float, channel_func) \ - for (int c = 0; c < channels; c++) { \ + for (int c = start; c < end; c++) { \ ChannelStats *p = &s->chstats[c]; \ const type *src = (const type *)data[0]; \ const type * const srcend = src + samples * channels; \ @@ -504,21 +584,16 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) UPDATE_STATS_##planar(type, update_minmax(s, p, sample), , p->nmin = p->min normalizer_suffix; p->nmax = p->max normalizer_suffix;); \ } -static int filter_frame(AVFilterLink *inlink, AVFrame *buf) +static int filter_channel(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { - AudioStatsContext *s = inlink->dst->priv; - AVDictionary **metadata = &buf->metadata; + AudioStatsContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + AVFrame *buf = arg; + const uint8_t * const * const data = (const uint8_t * const *)buf->extended_data; const int channels = s->nb_channels; const int samples = buf->nb_samples; - const uint8_t * const * const data = (const uint8_t * const *)buf->extended_data; - - if (s->reset_count > 0) { - if (s->nb_frames >= s->reset_count) { - reset_stats(s); - s->nb_frames = 0; - } - s->nb_frames++; - } + const int start = (buf->channels * jobnr) / nb_jobs; + const int end = (buf->channels * (jobnr+1)) / nb_jobs; switch (inlink->format) { case AV_SAMPLE_FMT_DBLP: @@ -553,6 +628,25 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) break; } + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *buf) +{ + AVFilterContext *ctx = inlink->dst; + AudioStatsContext *s = ctx->priv; + AVDictionary **metadata = &buf->metadata; + + if (s->reset_count > 0) { + if (s->nb_frames >= s->reset_count) { + reset_stats(s); + s->nb_frames = 0; + } + s->nb_frames++; + } + + ctx->internal->execute(ctx, filter_channel, buf, NULL, FFMIN(inlink->channels, ff_filter_get_nb_threads(ctx))); + if (s->metadata) set_metadata(s, metadata); @@ -562,7 +656,7 @@ 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; + 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; double min_runs = 0, max_runs = 0, min = DBL_MAX, max =-DBL_MAX, min_diff = DBL_MAX, max_diff = 0, @@ -572,6 +666,7 @@ static void print_stats(AVFilterContext *ctx) diff1_sum = 0, sigma_x = 0, sigma_x2 = 0, + noise_floor = 0, min_sigma_x2 = DBL_MAX, max_sigma_x2 =-DBL_MAX; AVRational depth; @@ -595,8 +690,10 @@ static void print_stats(AVFilterContext *ctx) max_sigma_x2 = FFMAX(max_sigma_x2, p->max_sigma_x2); sigma_x += p->sigma_x; sigma_x2 += p->sigma_x2; + noise_floor = FFMAX(noise_floor, p->noise_floor); min_count += p->min_count; max_count += p->max_count; + noise_floor_count += p->noise_floor_count; min_runs += p->min_runs; max_runs += p->max_runs; mask |= p->mask; @@ -638,6 +735,10 @@ 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_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) + av_log(ctx, AV_LOG_INFO, "Noise floor count: %"PRId64"\n", p->noise_floor_count); 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); @@ -684,6 +785,10 @@ 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_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) + av_log(ctx, AV_LOG_INFO, "Noise floor count: %f\n", noise_floor_count / (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); @@ -704,6 +809,13 @@ static av_cold void uninit(AVFilterContext *ctx) if (s->nb_channels) print_stats(ctx); + if (s->chstats) { + for (int i = 0; i < s->nb_channels; i++) { + ChannelStats *p = &s->chstats[i]; + + av_freep(&p->win_samples); + } + } av_freep(&s->chstats); } @@ -734,4 +846,5 @@ AVFilter ff_af_astats = { .uninit = uninit, .inputs = astats_inputs, .outputs = astats_outputs, + .flags = AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/af_asubboost.c b/libavfilter/af_asubboost.c new file mode 100644 index 00000000000..f8369fd8187 --- /dev/null +++ b/libavfilter/af_asubboost.c @@ -0,0 +1,232 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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/ffmath.h" +#include "libavutil/opt.h" +#include "avfilter.h" +#include "audio.h" +#include "formats.h" + +typedef struct ASubBoostContext { + const AVClass *class; + + double dry_gain; + double wet_gain; + double feedback; + double decay; + double delay; + double cutoff; + double slope; + + double a0, a1, a2; + double b0, b1, b2; + + int write_pos; + int buffer_samples; + + AVFrame *i, *o; + AVFrame *buffer; +} ASubBoostContext; + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats = NULL; + AVFilterChannelLayouts *layouts = NULL; + static const enum AVSampleFormat sample_fmts[] = { + AV_SAMPLE_FMT_DBLP, + AV_SAMPLE_FMT_NONE + }; + int ret; + + formats = ff_make_format_list(sample_fmts); + if (!formats) + return AVERROR(ENOMEM); + ret = ff_set_common_formats(ctx, formats); + if (ret < 0) + return ret; + + layouts = ff_all_channel_counts(); + if (!layouts) + return AVERROR(ENOMEM); + + ret = ff_set_common_channel_layouts(ctx, layouts); + if (ret < 0) + return ret; + + formats = ff_all_samplerates(); + return ff_set_common_samplerates(ctx, formats); +} + +static int get_coeffs(AVFilterContext *ctx) +{ + ASubBoostContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + double w0 = 2 * M_PI * s->cutoff / inlink->sample_rate; + double alpha = sin(w0) / 2 * sqrt(2. * (1. / s->slope - 1.) + 2.); + + 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->a1 /= s->a0; + s->a2 /= s->a0; + s->b0 /= s->a0; + s->b1 /= s->a0; + s->b2 /= s->a0; + + s->buffer_samples = inlink->sample_rate * s->delay / 1000; + + return 0; +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + ASubBoostContext *s = ctx->priv; + + s->buffer = ff_get_audio_buffer(inlink, inlink->sample_rate / 10); + s->i = ff_get_audio_buffer(inlink, 2); + s->o = ff_get_audio_buffer(inlink, 2); + if (!s->buffer || !s->i || !s->o) + return AVERROR(ENOMEM); + + return get_coeffs(ctx); +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + ASubBoostContext *s = ctx->priv; + const float wet = s->wet_gain, dry = s->dry_gain, feedback = s->feedback, decay = s->decay; + int write_pos; + AVFrame *out; + + if (av_frame_is_writable(in)) { + out = in; + } else { + out = ff_get_audio_buffer(outlink, in->nb_samples); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + } + + for (int ch = 0; ch < in->channels; ch++) { + const double *src = (const double *)in->extended_data[ch]; + double *dst = (double *)out->extended_data[ch]; + double *buffer = (double *)s->buffer->extended_data[ch]; + double *ix = (double *)s->i->extended_data[ch]; + double *ox = (double *)s->o->extended_data[ch]; + + write_pos = s->write_pos; + for (int n = 0; n < in->nb_samples; n++) { + double out_sample; + + out_sample = src[n] * s->b0 + ix[0] * s->b1 + ix[1] * s->b2 - ox[0] * s->a1 - ox[1] * s->a2; + ix[1] = ix[0]; + ix[0] = src[n]; + ox[1] = ox[0]; + ox[0] = out_sample; + + buffer[write_pos] = buffer[write_pos] * decay + out_sample * feedback; + dst[n] = src[n] * dry + buffer[write_pos] * wet; + + if (++write_pos >= s->buffer_samples) + write_pos = 0; + } + } + + s->write_pos = write_pos; + + if (out != in) + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + ASubBoostContext *s = ctx->priv; + + av_frame_free(&s->buffer); + av_frame_free(&s->i); + av_frame_free(&s->o); +} + +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + return get_coeffs(ctx); +} + +#define OFFSET(x) offsetof(ASubBoostContext, x) +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM + +static const AVOption asubboost_options[] = { + { "dry", "set dry gain", OFFSET(dry_gain), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 1, FLAGS }, + { "wet", "set wet gain", OFFSET(wet_gain), AV_OPT_TYPE_DOUBLE, {.dbl=0.8}, 0, 1, FLAGS }, + { "decay", "set decay", OFFSET(decay), AV_OPT_TYPE_DOUBLE, {.dbl=0.7}, 0, 1, FLAGS }, + { "feedback", "set feedback", OFFSET(feedback), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 1, FLAGS }, + { "cutoff", "set cutoff", OFFSET(cutoff), AV_OPT_TYPE_DOUBLE, {.dbl=100}, 50, 900, FLAGS }, + { "slope", "set slope", OFFSET(slope), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0.0001, 1, FLAGS }, + { "delay", "set delay", OFFSET(delay), AV_OPT_TYPE_DOUBLE, {.dbl=20}, 1, 100, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(asubboost); + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .filter_frame = filter_frame, + .config_props = config_input, + }, + { NULL } +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + }, + { NULL } +}; + +AVFilter ff_af_asubboost = { + .name = "asubboost", + .description = NULL_IF_CONFIG_SMALL("Boost subwoofer frequencies."), + .query_formats = query_formats, + .priv_size = sizeof(ASubBoostContext), + .priv_class = &asubboost_class, + .uninit = uninit, + .inputs = inputs, + .outputs = outputs, + .process_command = process_command, +}; diff --git a/libavfilter/af_atempo.c b/libavfilter/af_atempo.c index 688dac5464e..e4fc691abe6 100644 --- a/libavfilter/af_atempo.c +++ b/libavfilter/af_atempo.c @@ -162,7 +162,7 @@ static const AVOption atempo_options[] = { OFFSET(tempo), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, YAE_ATEMPO_MIN, YAE_ATEMPO_MAX, - AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM }, + AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM }, { NULL } }; @@ -328,28 +328,14 @@ static int yae_reset(ATempoContext *atempo, return 0; } -static int yae_set_tempo(AVFilterContext *ctx, const char *arg_tempo) +static int yae_update(AVFilterContext *ctx) { const AudioFragment *prev; ATempoContext *atempo = ctx->priv; - char *tail = NULL; - double tempo = av_strtod(arg_tempo, &tail); - - if (tail && *tail) { - av_log(ctx, AV_LOG_ERROR, "Invalid tempo value '%s'\n", arg_tempo); - return AVERROR(EINVAL); - } - - if (tempo < YAE_ATEMPO_MIN || tempo > YAE_ATEMPO_MAX) { - av_log(ctx, AV_LOG_ERROR, "Tempo value %f exceeds [%f, %f] range\n", - tempo, YAE_ATEMPO_MIN, YAE_ATEMPO_MAX); - return AVERROR(EINVAL); - } prev = yae_prev_frag(atempo); atempo->origin[0] = prev->position[0] + atempo->window / 2; atempo->origin[1] = prev->position[1] + atempo->window / 2; - atempo->tempo = tempo; return 0; } @@ -1189,7 +1175,12 @@ static int process_command(AVFilterContext *ctx, int res_len, int flags) { - return !strcmp(cmd, "tempo") ? yae_set_tempo(ctx, arg) : AVERROR(ENOSYS); + int ret = ff_filter_process_command(ctx, cmd, arg, res, res_len, flags); + + if (ret < 0) + return ret; + + return yae_update(ctx); } static const AVFilterPad atempo_inputs[] = { diff --git a/libavfilter/af_axcorrelate.c b/libavfilter/af_axcorrelate.c new file mode 100644 index 00000000000..6e9a0287162 --- /dev/null +++ b/libavfilter/af_axcorrelate.c @@ -0,0 +1,378 @@ +/* + * 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/audio_fifo.h" +#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 AudioXCorrelateContext { + const AVClass *class; + + int size; + int algo; + int64_t pts; + + AVAudioFifo *fifo[2]; + AVFrame *cache[2]; + AVFrame *mean_sum[2]; + AVFrame *num_sum; + AVFrame *den_sum[2]; + int used; + + int (*xcorrelate)(AVFilterContext *ctx, AVFrame *out); +} AudioXCorrelateContext; + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats; + AVFilterChannelLayouts *layouts; + static const enum AVSampleFormat sample_fmts[] = { + AV_SAMPLE_FMT_FLTP, + AV_SAMPLE_FMT_NONE + }; + int ret; + + layouts = ff_all_channel_counts(); + if (!layouts) + return AVERROR(ENOMEM); + ret = ff_set_common_channel_layouts(ctx, layouts); + if (ret < 0) + return ret; + + formats = ff_make_format_list(sample_fmts); + if (!formats) + return AVERROR(ENOMEM); + ret = ff_set_common_formats(ctx, formats); + if (ret < 0) + return ret; + + formats = ff_all_samplerates(); + if (!formats) + return AVERROR(ENOMEM); + return ff_set_common_samplerates(ctx, formats); +} + +static float mean_sum(const float *in, int size) +{ + float mean_sum = 0.f; + + for (int i = 0; i < size; i++) + mean_sum += in[i]; + + return mean_sum; +} + +static float square_sum(const float *x, const float *y, int size) +{ + float square_sum = 0.f; + + for (int i = 0; i < size; i++) + square_sum += x[i] * y[i]; + + return square_sum; +} + +static float xcorrelate(const float *x, const float *y, float sumx, float sumy, int size) +{ + const float xm = sumx / size, ym = sumy / size; + float num = 0.f, den, den0 = 0.f, den1 = 0.f; + + for (int i = 0; i < size; i++) { + float xd = x[i] - xm; + float yd = y[i] - ym; + + num += xd * yd; + den0 += xd * xd; + den1 += yd * yd; + } + + num /= size; + den = sqrtf((den0 * den1) / (size * size)); + + return den <= 1e-6f ? 0.f : num / den; +} + +static int xcorrelate_slow(AVFilterContext *ctx, AVFrame *out) +{ + AudioXCorrelateContext *s = ctx->priv; + const int size = s->size; + int used; + + for (int ch = 0; ch < out->channels; ch++) { + const float *x = (const float *)s->cache[0]->extended_data[ch]; + const float *y = (const float *)s->cache[1]->extended_data[ch]; + float *sumx = (float *)s->mean_sum[0]->extended_data[ch]; + float *sumy = (float *)s->mean_sum[1]->extended_data[ch]; + float *dst = (float *)out->extended_data[ch]; + + used = s->used; + if (!used) { + sumx[0] = mean_sum(x, size); + sumy[0] = mean_sum(y, size); + used = 1; + } + + for (int n = 0; n < out->nb_samples; n++) { + dst[n] = xcorrelate(x + n, y + n, sumx[0], sumy[0], size); + + sumx[0] -= x[n]; + sumx[0] += x[n + size]; + sumy[0] -= y[n]; + sumy[0] += y[n + size]; + } + } + + return used; +} + +static int xcorrelate_fast(AVFilterContext *ctx, AVFrame *out) +{ + AudioXCorrelateContext *s = ctx->priv; + const int size = s->size; + int used; + + for (int ch = 0; ch < out->channels; ch++) { + const float *x = (const float *)s->cache[0]->extended_data[ch]; + const float *y = (const float *)s->cache[1]->extended_data[ch]; + float *num_sum = (float *)s->num_sum->extended_data[ch]; + float *den_sumx = (float *)s->den_sum[0]->extended_data[ch]; + float *den_sumy = (float *)s->den_sum[1]->extended_data[ch]; + float *dst = (float *)out->extended_data[ch]; + + used = s->used; + if (!used) { + num_sum[0] = square_sum(x, y, size); + den_sumx[0] = square_sum(x, x, size); + den_sumy[0] = square_sum(y, y, size); + used = 1; + } + + for (int n = 0; n < out->nb_samples; n++) { + float num, den; + + num = num_sum[0] / size; + den = sqrtf((den_sumx[0] * den_sumy[0]) / (size * size)); + + dst[n] = den <= 1e-6f ? 0.f : num / den; + + num_sum[0] -= x[n] * y[n]; + num_sum[0] += x[n + size] * y[n + size]; + den_sumx[0] -= x[n] * x[n]; + den_sumx[0] = FFMAX(den_sumx[0], 0.f); + den_sumx[0] += x[n + size] * x[n + size]; + den_sumy[0] -= y[n] * y[n]; + den_sumy[0] = FFMAX(den_sumy[0], 0.f); + den_sumy[0] += y[n + size] * y[n + size]; + } + } + + return used; +} + +static int activate(AVFilterContext *ctx) +{ + AudioXCorrelateContext *s = ctx->priv; + AVFrame *frame = NULL; + int ret, status; + int available; + int64_t pts; + + FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); + + for (int i = 0; i < 2; i++) { + ret = ff_inlink_consume_frame(ctx->inputs[i], &frame); + if (ret > 0) { + if (s->pts == AV_NOPTS_VALUE) + s->pts = frame->pts; + ret = av_audio_fifo_write(s->fifo[i], (void **)frame->extended_data, + frame->nb_samples); + av_frame_free(&frame); + if (ret < 0) + return ret; + } + } + + available = FFMIN(av_audio_fifo_size(s->fifo[0]), av_audio_fifo_size(s->fifo[1])); + 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); + 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); + if (!s->cache[1]) + return AVERROR(ENOMEM); + } + + ret = av_audio_fifo_peek(s->fifo[0], (void **)s->cache[0]->extended_data, available); + if (ret < 0) + return ret; + + ret = av_audio_fifo_peek(s->fifo[1], (void **)s->cache[1]->extended_data, available); + if (ret < 0) + return ret; + + out = ff_get_audio_buffer(ctx->outputs[0], out_samples); + if (!out) + return AVERROR(ENOMEM); + + s->used = s->xcorrelate(ctx, out); + + out->pts = s->pts; + s->pts += out_samples; + + 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); + } + + if (av_audio_fifo_size(s->fifo[0]) > s->size && + av_audio_fifo_size(s->fifo[1]) > s->size) { + ff_filter_set_ready(ctx, 10); + return 0; + } + + 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, pts); + return 0; + } + } + + if (ff_outlink_frame_wanted(ctx->outputs[0])) { + for (int i = 0; i < 2; i++) { + if (av_audio_fifo_size(s->fifo[i]) > s->size) + continue; + ff_inlink_request_frame(ctx->inputs[i]); + return 0; + } + } + + return FFERROR_NOT_READY; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AVFilterLink *inlink = ctx->inputs[0]; + AudioXCorrelateContext *s = ctx->priv; + + s->pts = AV_NOPTS_VALUE; + + outlink->format = inlink->format; + outlink->channels = inlink->channels; + s->fifo[0] = av_audio_fifo_alloc(outlink->format, outlink->channels, s->size); + s->fifo[1] = av_audio_fifo_alloc(outlink->format, outlink->channels, s->size); + if (!s->fifo[0] || !s->fifo[1]) + return AVERROR(ENOMEM); + + s->mean_sum[0] = ff_get_audio_buffer(outlink, 1); + s->mean_sum[1] = ff_get_audio_buffer(outlink, 1); + s->num_sum = ff_get_audio_buffer(outlink, 1); + s->den_sum[0] = ff_get_audio_buffer(outlink, 1); + s->den_sum[1] = ff_get_audio_buffer(outlink, 1); + if (!s->mean_sum[0] || !s->mean_sum[1] || !s->num_sum || + !s->den_sum[0] || !s->den_sum[1]) + return AVERROR(ENOMEM); + + switch (s->algo) { + case 0: s->xcorrelate = xcorrelate_slow; break; + case 1: s->xcorrelate = xcorrelate_fast; break; + } + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + AudioXCorrelateContext *s = ctx->priv; + + av_audio_fifo_free(s->fifo[0]); + av_audio_fifo_free(s->fifo[1]); + av_frame_free(&s->cache[0]); + av_frame_free(&s->cache[1]); + av_frame_free(&s->mean_sum[0]); + av_frame_free(&s->mean_sum[1]); + av_frame_free(&s->num_sum); + av_frame_free(&s->den_sum[0]); + av_frame_free(&s->den_sum[1]); +} + +static const AVFilterPad inputs[] = { + { + .name = "axcorrelate0", + .type = AVMEDIA_TYPE_AUDIO, + }, + { + .name = "axcorrelate1", + .type = AVMEDIA_TYPE_AUDIO, + }, + { NULL } +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, + }, + { NULL } +}; + +#define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#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 alghorithm", 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" }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(axcorrelate); + +AVFilter ff_af_axcorrelate = { + .name = "axcorrelate", + .description = NULL_IF_CONFIG_SMALL("Cross-correlate two audio streams."), + .priv_size = sizeof(AudioXCorrelateContext), + .priv_class = &axcorrelate_class, + .query_formats = query_formats, + .activate = activate, + .uninit = uninit, + .inputs = inputs, + .outputs = outputs, +}; diff --git a/libavfilter/af_biquads.c b/libavfilter/af_biquads.c index 247a47256fc..81cdb0c10e9 100644 --- a/libavfilter/af_biquads.c +++ b/libavfilter/af_biquads.c @@ -112,6 +112,8 @@ typedef struct BiquadsContext { double width; double mix; uint64_t channels; + int normalize; + int order; double a0, a1, a2; double b0, b1, b2; @@ -263,6 +265,7 @@ static int config_filter(AVFilterLink *outlink, int reset) AVFilterLink *inlink = ctx->inputs[0]; double A = ff_exp10(s->gain / 40); double w0 = 2 * M_PI * s->frequency / inlink->sample_rate; + double K = tan(w0 / 2.); double alpha, beta; if (w0 > M_PI) { @@ -388,12 +391,24 @@ static int config_filter(AVFilterLink *outlink, int reset) } break; case allpass: - 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; + 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.; + 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; + break; + } break; default: av_assert0(0); @@ -408,6 +423,14 @@ static int config_filter(AVFilterLink *outlink, int reset) s->b2 /= s->a0; s->a0 /= s->a0; + 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); + + s->b0 *= factor; + s->b1 *= factor; + s->b2 *= factor; + } + s->cache = av_realloc_f(s->cache, sizeof(ChanCache), inlink->channels); if (!s->cache) return AVERROR(ENOMEM); @@ -503,127 +526,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, char *res, int res_len, int flags) { - BiquadsContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; + int ret; - if ((!strcmp(cmd, "frequency") || !strcmp(cmd, "f")) && - (s->filter_type == equalizer || - s->filter_type == lowshelf || - s->filter_type == highshelf || - s->filter_type == bass || - s->filter_type == treble || - s->filter_type == bandpass || - s->filter_type == bandreject|| - s->filter_type == lowpass || - s->filter_type == highpass || - s->filter_type == allpass)) { - double freq; - - if (sscanf(args, "%lf", &freq) != 1) { - av_log(ctx, AV_LOG_ERROR, "Invalid frequency value.\n"); - return AVERROR(EINVAL); - } - - s->frequency = freq; - } else if ((!strcmp(cmd, "gain") || !strcmp(cmd, "g")) && - (s->filter_type == equalizer || - s->filter_type == lowshelf || - s->filter_type == highshelf || - s->filter_type == bass || - s->filter_type == treble)) { - double gain; - - if (sscanf(args, "%lf", &gain) != 1) { - av_log(ctx, AV_LOG_ERROR, "Invalid gain value.\n"); - return AVERROR(EINVAL); - } - - s->gain = av_clipd(gain, -900, 900); - } else if (!strcmp(cmd, "mix") || !strcmp(cmd, "m")) { - double mix; - - if (sscanf(args, "%lf", &mix) != 1) { - av_log(ctx, AV_LOG_ERROR, "Invalid mix value.\n"); - return AVERROR(EINVAL); - } - - s->mix = av_clipd(mix, 0, 1); - } else if ((!strcmp(cmd, "width") || !strcmp(cmd, "w")) && - (s->filter_type == equalizer || - s->filter_type == lowshelf || - s->filter_type == highshelf || - s->filter_type == bass || - s->filter_type == treble || - s->filter_type == bandpass || - s->filter_type == bandreject|| - s->filter_type == lowpass || - s->filter_type == highpass || - s->filter_type == allpass)) { - double width; - - if (sscanf(args, "%lf", &width) != 1) { - av_log(ctx, AV_LOG_ERROR, "Invalid width value.\n"); - return AVERROR(EINVAL); - } - - s->width = width; - } else if ((!strcmp(cmd, "width_type") || !strcmp(cmd, "t")) && - (s->filter_type == equalizer || - s->filter_type == lowshelf || - s->filter_type == highshelf || - s->filter_type == bass || - s->filter_type == treble || - s->filter_type == bandpass || - s->filter_type == bandreject|| - s->filter_type == lowpass || - s->filter_type == highpass || - s->filter_type == allpass)) { - char width_type; - - if (sscanf(args, "%c", &width_type) != 1) { - av_log(ctx, AV_LOG_ERROR, "Invalid width_type value.\n"); - return AVERROR(EINVAL); - } - - switch (width_type) { - case 'h': width_type = HERTZ; break; - case 'q': width_type = QFACTOR; break; - case 'o': width_type = OCTAVE; break; - case 's': width_type = SLOPE; break; - case 'k': width_type = KHERTZ; break; - default: - av_log(ctx, AV_LOG_ERROR, "Invalid width_type value: %c\n", width_type); - return AVERROR(EINVAL); - } - - s->width_type = width_type; - } else if ((!strcmp(cmd, "a0") || - !strcmp(cmd, "a1") || - !strcmp(cmd, "a2") || - !strcmp(cmd, "b0") || - !strcmp(cmd, "b1") || - !strcmp(cmd, "b2")) && - s->filter_type == biquad) { - double value; - - if (sscanf(args, "%lf", &value) != 1) { - av_log(ctx, AV_LOG_ERROR, "Invalid biquad value.\n"); - return AVERROR(EINVAL); - } - - if (!strcmp(cmd, "a0")) - s->a0 = value; - else if (!strcmp(cmd, "a1")) - s->a1 = value; - else if (!strcmp(cmd, "a2")) - s->a2 = value; - else if (!strcmp(cmd, "b0")) - s->b0 = value; - else if (!strcmp(cmd, "b1")) - s->b1 = value; - else if (!strcmp(cmd, "b2")) - s->b2 = value; - } + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; return config_filter(outlink, 0); } @@ -654,7 +562,8 @@ static const AVFilterPad outputs[] = { }; #define OFFSET(x) offsetof(BiquadsContext, 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 +#define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM #define DEFINE_BIQUAD_FILTER(name_, description_) \ AVFILTER_DEFINE_CLASS(name_); \ @@ -699,6 +608,8 @@ static const AVOption equalizer_options[] = { {"m", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}, {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, + {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {NULL} }; @@ -723,6 +634,8 @@ static const AVOption bass_options[] = { {"m", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}, {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, + {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {NULL} }; @@ -747,6 +660,8 @@ static const AVOption treble_options[] = { {"m", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}, {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, + {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {NULL} }; @@ -770,6 +685,8 @@ static const AVOption bandpass_options[] = { {"m", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}, {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, + {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {NULL} }; @@ -792,6 +709,8 @@ static const AVOption bandreject_options[] = { {"m", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}, {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, + {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {NULL} }; @@ -810,12 +729,14 @@ static const AVOption lowpass_options[] = { {"k", "kHz", 0, AV_OPT_TYPE_CONST, {.i64=KHERTZ}, 0, 0, FLAGS, "width_type"}, {"width", "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS}, {"w", "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS}, - {"poles", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS}, - {"p", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS}, + {"poles", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, AF}, + {"p", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, AF}, {"mix", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}, {"m", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}, {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, + {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {NULL} }; @@ -834,12 +755,14 @@ static const AVOption highpass_options[] = { {"k", "kHz", 0, AV_OPT_TYPE_CONST, {.i64=KHERTZ}, 0, 0, FLAGS, "width_type"}, {"width", "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS}, {"w", "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS}, - {"poles", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS}, - {"p", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS}, + {"poles", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, AF}, + {"p", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, AF}, {"mix", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}, {"m", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}, {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, + {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {NULL} }; @@ -862,6 +785,10 @@ static const AVOption allpass_options[] = { {"m", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}, {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, + {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"order", "set filter order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS}, + {"o", "set filter order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS}, {NULL} }; @@ -886,6 +813,8 @@ static const AVOption lowshelf_options[] = { {"m", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}, {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, + {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {NULL} }; @@ -910,6 +839,8 @@ static const AVOption highshelf_options[] = { {"m", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}, {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, + {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {NULL} }; @@ -927,6 +858,8 @@ static const AVOption biquad_options[] = { {"m", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}, {"channels", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, {"c", "set channels to filter", OFFSET(channels), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64=-1}, INT64_MIN, INT64_MAX, FLAGS}, + {"normalize", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {NULL} }; diff --git a/libavfilter/af_compand.c b/libavfilter/af_compand.c index c138f0b1d8f..1e75be4f41e 100644 --- a/libavfilter/af_compand.c +++ b/libavfilter/af_compand.c @@ -349,9 +349,10 @@ static int config_output(AVFilterLink *outlink) } if (nb_attacks > channels || nb_decays > channels) { - av_log(ctx, AV_LOG_ERROR, - "Number of attacks/decays bigger than number of channels.\n"); - return AVERROR(EINVAL); + av_log(ctx, AV_LOG_WARNING, + "Number of attacks/decays bigger than number of channels. Ignoring rest of entries.\n"); + nb_attacks = FFMIN(nb_attacks, channels); + nb_decays = FFMIN(nb_decays, channels); } uninit(ctx); @@ -534,7 +535,7 @@ static int config_output(AVFilterLink *outlink) s->delay_frame->nb_samples = s->delay_samples; s->delay_frame->channel_layout = outlink->channel_layout; - err = av_frame_get_buffer(s->delay_frame, 32); + err = av_frame_get_buffer(s->delay_frame, 0); if (err) return err; diff --git a/libavfilter/af_compensationdelay.c b/libavfilter/af_compensationdelay.c index 05285cd2975..793332584bd 100644 --- a/libavfilter/af_compensationdelay.c +++ b/libavfilter/af_compensationdelay.c @@ -115,7 +115,7 @@ static int config_input(AVFilterLink *inlink) s->delay_frame->nb_samples = new_size; s->delay_frame->channel_layout = inlink->channel_layout; - return av_frame_get_buffer(s->delay_frame, 32); + return av_frame_get_buffer(s->delay_frame, 0); } static int filter_frame(AVFilterLink *inlink, AVFrame *in) diff --git a/libavfilter/af_crossfeed.c b/libavfilter/af_crossfeed.c index c819ca59a78..70dd26eb288 100644 --- a/libavfilter/af_crossfeed.c +++ b/libavfilter/af_crossfeed.c @@ -28,6 +28,7 @@ typedef struct CrossfeedContext { double range; double strength; + double slope; double level_in; double level_out; @@ -62,7 +63,7 @@ static int config_input(AVFilterLink *inlink) double w0 = 2 * M_PI * (1. - s->range) * 2100 / inlink->sample_rate; double alpha; - alpha = sin(w0) / 2 * sqrt(2 * (1 / 0.5 - 1) + 2); + alpha = sin(w0) / 2 * sqrt((A + 1 / A) * (1 / s->slope - 1) + 2); s->a0 = (A + 1) + (A - 1) * cos(w0) + 2 * sqrt(A) * alpha; s->a1 = -2 * ((A - 1) + (A + 1) * cos(w0)); @@ -133,12 +134,25 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) return ff_filter_frame(outlink, out); } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + return config_input(ctx->inputs[0]); +} + #define OFFSET(x) offsetof(CrossfeedContext, 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 crossfeed_options[] = { { "strength", "set crossfeed strength", OFFSET(strength), AV_OPT_TYPE_DOUBLE, {.dbl=.2}, 0, 1, FLAGS }, { "range", "set soundstage wideness", OFFSET(range), AV_OPT_TYPE_DOUBLE, {.dbl=.5}, 0, 1, FLAGS }, + { "slope", "set curve slope", OFFSET(slope), AV_OPT_TYPE_DOUBLE, {.dbl=.5}, .01, 1, FLAGS }, { "level_in", "set level in", OFFSET(level_in), AV_OPT_TYPE_DOUBLE, {.dbl=.9}, 0, 1, FLAGS }, { "level_out", "set level out", OFFSET(level_out), AV_OPT_TYPE_DOUBLE, {.dbl=1.}, 0, 1, FLAGS }, { NULL } @@ -173,4 +187,5 @@ AVFilter ff_af_crossfeed = { .inputs = inputs, .outputs = outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .process_command = process_command, }; diff --git a/libavfilter/af_crystalizer.c b/libavfilter/af_crystalizer.c index 5b27e1fb797..f3d8ae69918 100644 --- a/libavfilter/af_crystalizer.c +++ b/libavfilter/af_crystalizer.c @@ -29,12 +29,11 @@ typedef struct CrystalizerContext { float mult; int clip; AVFrame *prev; - void (*filter)(void **dst, void **prv, const void **src, - int nb_samples, int channels, float mult, int clip); + int (*filter)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); } CrystalizerContext; #define OFFSET(x) offsetof(CrystalizerContext, x) -#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption crystalizer_options[] = { { "i", "set intensity", OFFSET(mult), AV_OPT_TYPE_FLOAT, {.dbl=2.0}, 0, 10, A }, @@ -74,42 +73,71 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_samplerates(ctx, formats); } -static void filter_flt(void **d, void **p, const void **s, - int nb_samples, int channels, - float mult, int clip) +typedef struct ThreadData { + void **d; + void **p; + const void **s; + int nb_samples; + int channels; + float mult; + int clip; +} ThreadData; + +static int filter_flt(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { - const float *src = s[0]; - float *dst = d[0]; + ThreadData *td = arg; + void **d = td->d; + void **p = td->p; + const void **s = td->s; + const int nb_samples = td->nb_samples; + const int channels = td->channels; + float mult = td->mult; + const int clip = td->clip; + const int start = (channels * jobnr) / nb_jobs; + const int end = (channels * (jobnr+1)) / nb_jobs; float *prv = p[0]; int n, c; - for (n = 0; n < nb_samples; n++) { - for (c = 0; c < channels; c++) { - float current = src[c]; + for (c = start; c < end; c++) { + const float *src = s[0]; + float *dst = d[0]; + for (n = 0; n < nb_samples; n++) { + float current = src[c]; dst[c] = current + (current - prv[c]) * mult; prv[c] = current; if (clip) { dst[c] = av_clipf(dst[c], -1, 1); } - } - dst += c; - src += c; + dst += channels; + src += channels; + } } + + return 0; } -static void filter_dbl(void **d, void **p, const void **s, - int nb_samples, int channels, - float mult, int clip) +static int filter_dbl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { - const double *src = s[0]; - double *dst = d[0]; + ThreadData *td = arg; + void **d = td->d; + void **p = td->p; + const void **s = td->s; + const int nb_samples = td->nb_samples; + const int channels = td->channels; + float mult = td->mult; + const int clip = td->clip; + const int start = (channels * jobnr) / nb_jobs; + const int end = (channels * (jobnr+1)) / nb_jobs; double *prv = p[0]; int n, c; - for (n = 0; n < nb_samples; n++) { - for (c = 0; c < channels; c++) { + for (c = start; c < end; c++) { + const double *src = s[0]; + double *dst = d[0]; + + for (n = 0; n < nb_samples; n++) { double current = src[c]; dst[c] = current + (current - prv[c]) * mult; @@ -117,20 +145,30 @@ static void filter_dbl(void **d, void **p, const void **s, if (clip) { dst[c] = av_clipd(dst[c], -1, 1); } - } - dst += c; - src += c; + dst += channels; + src += channels; + } } + + return 0; } -static void filter_fltp(void **d, void **p, const void **s, - int nb_samples, int channels, - float mult, int clip) +static int filter_fltp(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { + ThreadData *td = arg; + void **d = td->d; + void **p = td->p; + const void **s = td->s; + const int nb_samples = td->nb_samples; + const int channels = td->channels; + float mult = td->mult; + const int clip = td->clip; + const int start = (channels * jobnr) / nb_jobs; + const int end = (channels * (jobnr+1)) / nb_jobs; int n, c; - for (c = 0; c < channels; c++) { + for (c = start; c < end; c++) { const float *src = s[c]; float *dst = d[c]; float *prv = p[c]; @@ -145,15 +183,25 @@ static void filter_fltp(void **d, void **p, const void **s, } } } + + return 0; } -static void filter_dblp(void **d, void **p, const void **s, - int nb_samples, int channels, - float mult, int clip) +static int filter_dblp(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { + ThreadData *td = arg; + void **d = td->d; + void **p = td->p; + const void **s = td->s; + const int nb_samples = td->nb_samples; + const int channels = td->channels; + float mult = td->mult; + const int clip = td->clip; + const int start = (channels * jobnr) / nb_jobs; + const int end = (channels * (jobnr+1)) / nb_jobs; int n, c; - for (c = 0; c < channels; c++) { + for (c = start; c < end; c++) { const double *src = s[c]; double *dst = d[c]; double *prv = p[c]; @@ -168,6 +216,8 @@ static void filter_dblp(void **d, void **p, const void **s, } } } + + return 0; } static int config_input(AVFilterLink *inlink) @@ -191,6 +241,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFilterLink *outlink = ctx->outputs[0]; CrystalizerContext *s = ctx->priv; AVFrame *out; + ThreadData td; if (!s->prev) { s->prev = ff_get_audio_buffer(inlink, 1); @@ -211,8 +262,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) av_frame_copy_props(out, in); } - s->filter((void **)out->extended_data, (void **)s->prev->extended_data, (const void **)in->extended_data, - in->nb_samples, in->channels, s->mult, s->clip); + td.d = (void **)out->extended_data; + td.s = (const void **)in->extended_data; + td.p = (void **)s->prev->extended_data; + td.nb_samples = in->nb_samples; + td.channels = in->channels; + td.mult = ctx->is_disabled ? 0.f : s->mult; + td.clip = s->clip; + ctx->internal->execute(ctx, s->filter, &td, NULL, FFMIN(inlink->channels, + ff_filter_get_nb_threads(ctx))); if (out != in) av_frame_free(&in); @@ -254,4 +312,7 @@ AVFilter ff_af_crystalizer = { .uninit = uninit, .inputs = inputs, .outputs = outputs, + .process_command = ff_filter_process_command, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | + AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/af_dynaudnorm.c b/libavfilter/af_dynaudnorm.c index fd430884d77..365453d60d9 100644 --- a/libavfilter/af_dynaudnorm.c +++ b/libavfilter/af_dynaudnorm.c @@ -29,7 +29,10 @@ #include "libavutil/avassert.h" #include "libavutil/opt.h" -#define FF_BUFQUEUE_SIZE 302 +#define MIN_FILTER_SIZE 3 +#define MAX_FILTER_SIZE 301 + +#define FF_BUFQUEUE_SIZE (MAX_FILTER_SIZE + 1) #include "libavfilter/bufferqueue.h" #include "audio.h" @@ -37,11 +40,16 @@ #include "filters.h" #include "internal.h" +typedef struct local_gain { + double max_gain; + double threshold; +} local_gain; + typedef struct cqueue { double *elements; int size; + int max_size; int nb_elements; - int first; } cqueue; typedef struct DynamicAudioNormalizerContext { @@ -60,37 +68,48 @@ typedef struct DynamicAudioNormalizerContext { double max_amplification; double target_rms; double compress_factor; + double threshold; double *prev_amplification_factor; double *dc_correction_value; double *compress_threshold; - double *fade_factors[2]; double *weights; int channels; - int delay; int eof; int64_t pts; cqueue **gain_history_original; cqueue **gain_history_minimum; cqueue **gain_history_smoothed; + cqueue **threshold_history; cqueue *is_enabled; } DynamicAudioNormalizerContext; #define OFFSET(x) offsetof(DynamicAudioNormalizerContext, 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 dynaudnorm_options[] = { - { "f", "set the frame length in msec", OFFSET(frame_len_msec), AV_OPT_TYPE_INT, {.i64 = 500}, 10, 8000, FLAGS }, - { "g", "set the filter size", OFFSET(filter_size), AV_OPT_TYPE_INT, {.i64 = 31}, 3, 301, FLAGS }, - { "p", "set the peak value", OFFSET(peak_value), AV_OPT_TYPE_DOUBLE, {.dbl = 0.95}, 0.0, 1.0, FLAGS }, - { "m", "set the max amplification", OFFSET(max_amplification), AV_OPT_TYPE_DOUBLE, {.dbl = 10.0}, 1.0, 100.0, FLAGS }, - { "r", "set the target RMS", OFFSET(target_rms), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 1.0, FLAGS }, - { "n", "set channel coupling", OFFSET(channels_coupled), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, - { "c", "set DC correction", OFFSET(dc_correction), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, - { "b", "set alternative boundary mode", OFFSET(alt_boundary_mode), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, - { "s", "set the compress factor", OFFSET(compress_factor), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 30.0, FLAGS }, + { "framelen", "set the frame length in msec", OFFSET(frame_len_msec), AV_OPT_TYPE_INT, {.i64 = 500}, 10, 8000, FLAGS }, + { "f", "set the frame length in msec", OFFSET(frame_len_msec), AV_OPT_TYPE_INT, {.i64 = 500}, 10, 8000, FLAGS }, + { "gausssize", "set the filter size", OFFSET(filter_size), AV_OPT_TYPE_INT, {.i64 = 31}, 3, 301, FLAGS }, + { "g", "set the filter size", OFFSET(filter_size), AV_OPT_TYPE_INT, {.i64 = 31}, 3, 301, FLAGS }, + { "peak", "set the peak value", OFFSET(peak_value), AV_OPT_TYPE_DOUBLE, {.dbl = 0.95}, 0.0, 1.0, FLAGS }, + { "p", "set the peak value", OFFSET(peak_value), AV_OPT_TYPE_DOUBLE, {.dbl = 0.95}, 0.0, 1.0, FLAGS }, + { "maxgain", "set the max amplification", OFFSET(max_amplification), AV_OPT_TYPE_DOUBLE, {.dbl = 10.0}, 1.0, 100.0, FLAGS }, + { "m", "set the max amplification", OFFSET(max_amplification), AV_OPT_TYPE_DOUBLE, {.dbl = 10.0}, 1.0, 100.0, FLAGS }, + { "targetrms", "set the target RMS", OFFSET(target_rms), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 1.0, FLAGS }, + { "r", "set the target RMS", OFFSET(target_rms), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 1.0, FLAGS }, + { "coupling", "set channel coupling", OFFSET(channels_coupled), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, + { "n", "set channel coupling", OFFSET(channels_coupled), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, + { "correctdc", "set DC correction", OFFSET(dc_correction), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, + { "c", "set DC correction", OFFSET(dc_correction), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, + { "altboundary", "set alternative boundary mode", OFFSET(alt_boundary_mode), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, + { "b", "set alternative boundary mode", OFFSET(alt_boundary_mode), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, + { "compress", "set the compress factor", OFFSET(compress_factor), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 30.0, FLAGS }, + { "s", "set the compress factor", OFFSET(compress_factor), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 30.0, FLAGS }, + { "threshold", "set the threshold value", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 1.0, FLAGS }, + { "t", "set the threshold value", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, 0.0, 1.0, FLAGS }, { NULL } }; @@ -101,8 +120,8 @@ static av_cold int init(AVFilterContext *ctx) DynamicAudioNormalizerContext *s = ctx->priv; if (!(s->filter_size & 1)) { - av_log(ctx, AV_LOG_ERROR, "filter size %d is invalid. Must be an odd value.\n", s->filter_size); - return AVERROR(EINVAL); + av_log(ctx, AV_LOG_WARNING, "filter size %d is invalid. Changing to an odd value.\n", s->filter_size); + s->filter_size |= 1; } return 0; @@ -144,30 +163,22 @@ static inline int frame_size(int sample_rate, int frame_len_msec) return frame_size + (frame_size % 2); } -static void precalculate_fade_factors(double *fade_factors[2], int frame_len) -{ - const double step_size = 1.0 / frame_len; - int pos; - - for (pos = 0; pos < frame_len; pos++) { - fade_factors[0][pos] = 1.0 - (step_size * (pos + 1.0)); - fade_factors[1][pos] = 1.0 - fade_factors[0][pos]; - } -} - -static cqueue *cqueue_create(int size) +static cqueue *cqueue_create(int size, int max_size) { cqueue *q; + if (max_size < size) + return NULL; + q = av_malloc(sizeof(cqueue)); if (!q) return NULL; + q->max_size = max_size; q->size = size; q->nb_elements = 0; - q->first = 0; - q->elements = av_malloc_array(size, sizeof(double)); + q->elements = av_malloc_array(max_size, sizeof(double)); if (!q->elements) { av_free(q); return NULL; @@ -190,17 +201,14 @@ static int cqueue_size(cqueue *q) static int cqueue_empty(cqueue *q) { - return !q->nb_elements; + return q->nb_elements <= 0; } static int cqueue_enqueue(cqueue *q, double element) { - int i; - - av_assert2(q->nb_elements != q->size); + av_assert2(q->nb_elements < q->max_size); - i = (q->first + q->nb_elements) % q->size; - q->elements[i] = element; + q->elements[q->nb_elements] = element; q->nb_elements++; return 0; @@ -209,15 +217,15 @@ static int cqueue_enqueue(cqueue *q, double element) static double cqueue_peek(cqueue *q, int index) { av_assert2(index < q->nb_elements); - return q->elements[(q->first + index) % q->size]; + return q->elements[index]; } static int cqueue_dequeue(cqueue *q, double *element) { av_assert2(!cqueue_empty(q)); - *element = q->elements[q->first]; - q->first = (q->first + 1) % q->size; + *element = q->elements[0]; + memmove(&q->elements[0], &q->elements[1], (q->nb_elements - 1) * sizeof(double)); q->nb_elements--; return 0; @@ -227,12 +235,34 @@ static int cqueue_pop(cqueue *q) { av_assert2(!cqueue_empty(q)); - q->first = (q->first + 1) % q->size; + memmove(&q->elements[0], &q->elements[1], (q->nb_elements - 1) * sizeof(double)); q->nb_elements--; return 0; } +static void cqueue_resize(cqueue *q, int new_size) +{ + av_assert2(q->max_size >= new_size); + av_assert2(MIN_FILTER_SIZE <= new_size); + + if (new_size > q->nb_elements) { + const int side = (new_size - q->nb_elements) / 2; + + memmove(q->elements + side, q->elements, sizeof(double) * q->nb_elements); + for (int i = 0; i < side; i++) + q->elements[i] = q->elements[side]; + q->nb_elements = new_size - 1 - side; + } else { + int count = (q->size - new_size + 1) / 2; + + while (count-- > 0) + cqueue_pop(q); + } + + q->size = new_size; +} + static void init_gaussian_filter(DynamicAudioNormalizerContext *s) { double total_weight = 0.0; @@ -268,8 +298,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&s->prev_amplification_factor); av_freep(&s->dc_correction_value); av_freep(&s->compress_threshold); - av_freep(&s->fade_factors[0]); - av_freep(&s->fade_factors[1]); for (c = 0; c < s->channels; c++) { if (s->gain_history_original) @@ -278,11 +306,14 @@ static av_cold void uninit(AVFilterContext *ctx) cqueue_free(s->gain_history_minimum[c]); if (s->gain_history_smoothed) cqueue_free(s->gain_history_smoothed[c]); + if (s->threshold_history) + cqueue_free(s->threshold_history[c]); } av_freep(&s->gain_history_original); av_freep(&s->gain_history_minimum); av_freep(&s->gain_history_smoothed); + av_freep(&s->threshold_history); cqueue_free(s->is_enabled); s->is_enabled = NULL; @@ -300,51 +331,50 @@ static int config_input(AVFilterLink *inlink) uninit(ctx); + s->channels = inlink->channels; s->frame_len = frame_size(inlink->sample_rate, s->frame_len_msec); av_log(ctx, AV_LOG_DEBUG, "frame len %d\n", s->frame_len); - s->fade_factors[0] = av_malloc_array(s->frame_len, sizeof(*s->fade_factors[0])); - s->fade_factors[1] = av_malloc_array(s->frame_len, sizeof(*s->fade_factors[1])); - s->prev_amplification_factor = av_malloc_array(inlink->channels, sizeof(*s->prev_amplification_factor)); s->dc_correction_value = av_calloc(inlink->channels, sizeof(*s->dc_correction_value)); s->compress_threshold = av_calloc(inlink->channels, sizeof(*s->compress_threshold)); s->gain_history_original = av_calloc(inlink->channels, sizeof(*s->gain_history_original)); s->gain_history_minimum = av_calloc(inlink->channels, sizeof(*s->gain_history_minimum)); s->gain_history_smoothed = av_calloc(inlink->channels, sizeof(*s->gain_history_smoothed)); - s->weights = av_malloc_array(s->filter_size, sizeof(*s->weights)); - s->is_enabled = cqueue_create(s->filter_size); + s->threshold_history = av_calloc(inlink->channels, sizeof(*s->threshold_history)); + s->weights = av_malloc_array(MAX_FILTER_SIZE, sizeof(*s->weights)); + s->is_enabled = cqueue_create(s->filter_size, MAX_FILTER_SIZE); if (!s->prev_amplification_factor || !s->dc_correction_value || - !s->compress_threshold || !s->fade_factors[0] || !s->fade_factors[1] || + !s->compress_threshold || !s->gain_history_original || !s->gain_history_minimum || - !s->gain_history_smoothed || !s->is_enabled || !s->weights) + !s->gain_history_smoothed || !s->threshold_history || + !s->is_enabled || !s->weights) return AVERROR(ENOMEM); for (c = 0; c < inlink->channels; c++) { s->prev_amplification_factor[c] = 1.0; - s->gain_history_original[c] = cqueue_create(s->filter_size); - s->gain_history_minimum[c] = cqueue_create(s->filter_size); - s->gain_history_smoothed[c] = cqueue_create(s->filter_size); + s->gain_history_original[c] = cqueue_create(s->filter_size, MAX_FILTER_SIZE); + s->gain_history_minimum[c] = cqueue_create(s->filter_size, MAX_FILTER_SIZE); + s->gain_history_smoothed[c] = cqueue_create(s->filter_size, MAX_FILTER_SIZE); + s->threshold_history[c] = cqueue_create(s->filter_size, MAX_FILTER_SIZE); if (!s->gain_history_original[c] || !s->gain_history_minimum[c] || - !s->gain_history_smoothed[c]) + !s->gain_history_smoothed[c] || !s->threshold_history[c]) return AVERROR(ENOMEM); } - precalculate_fade_factors(s->fade_factors, s->frame_len); init_gaussian_filter(s); - s->channels = inlink->channels; - s->delay = s->filter_size; - return 0; } -static inline double fade(double prev, double next, int pos, - double *fade_factors[2]) +static inline double fade(double prev, double next, int pos, int length) { - return fade_factors[0][pos] * prev + fade_factors[1][pos] * next; + const double step_size = 1.0 / length; + const double f0 = 1.0 - (step_size * (pos + 1.0)); + const double f1 = 1.0 - f0; + return f0 * prev + f1 * next; } static inline double pow_2(const double value) @@ -407,12 +437,18 @@ static double compute_frame_rms(AVFrame *frame, int channel) return FFMAX(sqrt(rms_value), DBL_EPSILON); } -static double get_max_local_gain(DynamicAudioNormalizerContext *s, AVFrame *frame, - int channel) +static local_gain get_max_local_gain(DynamicAudioNormalizerContext *s, AVFrame *frame, + int channel) { - const double maximum_gain = s->peak_value / find_peak_magnitude(frame, channel); + const double peak_magnitude = find_peak_magnitude(frame, channel); + const double maximum_gain = s->peak_value / peak_magnitude; const double rms_gain = s->target_rms > DBL_EPSILON ? (s->target_rms / compute_frame_rms(frame, channel)) : DBL_MAX; - return bound(s->max_amplification, FFMIN(maximum_gain, rms_gain)); + local_gain gain; + + gain.threshold = peak_magnitude > s->threshold; + gain.max_gain = bound(s->max_amplification, FFMIN(maximum_gain, rms_gain)); + + return gain; } static double minimum_filter(cqueue *q) @@ -427,38 +463,41 @@ static double minimum_filter(cqueue *q) return min; } -static double gaussian_filter(DynamicAudioNormalizerContext *s, cqueue *q) +static double gaussian_filter(DynamicAudioNormalizerContext *s, cqueue *q, cqueue *tq) { - double result = 0.0; + double result = 0.0, tsum = 0.0; int i; for (i = 0; i < cqueue_size(q); i++) { - result += cqueue_peek(q, i) * s->weights[i]; + tsum += cqueue_peek(tq, i) * s->weights[i]; + result += cqueue_peek(q, i) * s->weights[i] * cqueue_peek(tq, i); } + if (tsum == 0.0) + result = 1.0; + return result; } static void update_gain_history(DynamicAudioNormalizerContext *s, int channel, - double current_gain_factor) + local_gain gain) { - if (cqueue_empty(s->gain_history_original[channel]) || - cqueue_empty(s->gain_history_minimum[channel])) { + if (cqueue_empty(s->gain_history_original[channel])) { const int pre_fill_size = s->filter_size / 2; - const double initial_value = s->alt_boundary_mode ? current_gain_factor : 1.0; + const double initial_value = s->alt_boundary_mode ? gain.max_gain : s->peak_value; s->prev_amplification_factor[channel] = initial_value; while (cqueue_size(s->gain_history_original[channel]) < pre_fill_size) { cqueue_enqueue(s->gain_history_original[channel], initial_value); + cqueue_enqueue(s->threshold_history[channel], gain.threshold); } } - cqueue_enqueue(s->gain_history_original[channel], current_gain_factor); + cqueue_enqueue(s->gain_history_original[channel], gain.max_gain); while (cqueue_size(s->gain_history_original[channel]) >= s->filter_size) { double minimum; - av_assert0(cqueue_size(s->gain_history_original[channel]) == s->filter_size); if (cqueue_empty(s->gain_history_minimum[channel])) { const int pre_fill_size = s->filter_size / 2; @@ -476,17 +515,22 @@ static void update_gain_history(DynamicAudioNormalizerContext *s, int channel, cqueue_enqueue(s->gain_history_minimum[channel], minimum); + cqueue_enqueue(s->threshold_history[channel], gain.threshold); + cqueue_pop(s->gain_history_original[channel]); } while (cqueue_size(s->gain_history_minimum[channel]) >= s->filter_size) { - double smoothed; - av_assert0(cqueue_size(s->gain_history_minimum[channel]) == s->filter_size); - smoothed = gaussian_filter(s, s->gain_history_minimum[channel]); + double smoothed, limit; + + smoothed = gaussian_filter(s, s->gain_history_minimum[channel], s->threshold_history[channel]); + limit = cqueue_peek(s->gain_history_original[channel], 0); + smoothed = FFMIN(smoothed, limit); cqueue_enqueue(s->gain_history_smoothed[channel], smoothed); cqueue_pop(s->gain_history_minimum[channel]); + cqueue_pop(s->threshold_history[channel]); } } @@ -514,7 +558,7 @@ static void perform_dc_correction(DynamicAudioNormalizerContext *s, AVFrame *fra s->dc_correction_value[c] = is_first_frame ? current_average_value : update_value(current_average_value, s->dc_correction_value[c], 0.1); for (i = 0; i < frame->nb_samples; i++) { - dst_ptr[i] -= fade(prev_value, s->dc_correction_value[c], i, s->fade_factors); + dst_ptr[i] -= fade(prev_value, s->dc_correction_value[c], i, frame->nb_samples); } } } @@ -587,7 +631,7 @@ static void perform_compression(DynamicAudioNormalizerContext *s, AVFrame *frame for (c = 0; c < s->channels; c++) { double *const dst_ptr = (double *)frame->extended_data[c]; for (i = 0; i < frame->nb_samples; i++) { - const double localThresh = fade(prev_actual_thresh, curr_actual_thresh, i, s->fade_factors); + const double localThresh = fade(prev_actual_thresh, curr_actual_thresh, i, frame->nb_samples); dst_ptr[i] = copysign(bound(localThresh, fabs(dst_ptr[i])), dst_ptr[i]); } } @@ -606,7 +650,7 @@ static void perform_compression(DynamicAudioNormalizerContext *s, AVFrame *frame dst_ptr = (double *)frame->extended_data[c]; for (i = 0; i < frame->nb_samples; i++) { - const double localThresh = fade(prev_actual_thresh, curr_actual_thresh, i, s->fade_factors); + const double localThresh = fade(prev_actual_thresh, curr_actual_thresh, i, frame->nb_samples); dst_ptr[i] = copysign(bound(localThresh, fabs(dst_ptr[i])), dst_ptr[i]); } } @@ -624,11 +668,11 @@ static void analyze_frame(DynamicAudioNormalizerContext *s, AVFrame *frame) } if (s->channels_coupled) { - const double current_gain_factor = get_max_local_gain(s, frame, -1); + const local_gain gain = get_max_local_gain(s, frame, -1); int c; for (c = 0; c < s->channels; c++) - update_gain_history(s, c, current_gain_factor); + update_gain_history(s, c, gain); } else { int c; @@ -650,12 +694,9 @@ static void amplify_frame(DynamicAudioNormalizerContext *s, AVFrame *frame, int for (i = 0; i < frame->nb_samples && enabled; i++) { const double amplification_factor = fade(s->prev_amplification_factor[c], current_amplification_factor, i, - s->fade_factors); + frame->nb_samples); dst_ptr[i] *= amplification_factor; - - if (fabs(dst_ptr[i]) > s->peak_value) - dst_ptr[i] = copysign(s->peak_value, dst_ptr[i]); } s->prev_amplification_factor[c] = current_amplification_factor; @@ -666,12 +707,14 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; DynamicAudioNormalizerContext *s = ctx->priv; - AVFilterLink *outlink = inlink->dst->outputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; int ret = 1; - if (!cqueue_empty(s->gain_history_smoothed[0])) { - double is_enabled; + while (((s->queue.available >= s->filter_size) || + (s->eof && s->queue.available)) && + !cqueue_empty(s->gain_history_smoothed[0])) { AVFrame *out = ff_bufqueue_get(&s->queue); + double is_enabled; cqueue_dequeue(s->is_enabled, &is_enabled); @@ -680,9 +723,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) } av_frame_make_writable(in); - cqueue_enqueue(s->is_enabled, !ctx->is_disabled); analyze_frame(s, in); - ff_bufqueue_add(ctx, &s->queue, in); + if (!s->eof) { + ff_bufqueue_add(ctx, &s->queue, in); + cqueue_enqueue(s->is_enabled, !ctx->is_disabled); + } else { + av_frame_free(&in); + } return ret; } @@ -708,7 +755,6 @@ static int flush_buffer(DynamicAudioNormalizerContext *s, AVFilterLink *inlink, } } - s->delay--; return filter_frame(inlink, out); } @@ -725,7 +771,6 @@ static int flush(AVFilterLink *outlink) s->pts = out->pts; ret = ff_filter_frame(outlink, out); - s->delay = s->queue.available; } return ret; @@ -763,10 +808,10 @@ static int activate(AVFilterContext *ctx) s->eof = 1; } - if (s->eof && s->delay > 0) + if (s->eof && s->queue.available) return flush(outlink); - if (s->eof && s->delay <= 0) { + if (s->eof && !s->queue.available) { ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); return 0; } @@ -777,6 +822,34 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + DynamicAudioNormalizerContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + int prev_filter_size = s->filter_size; + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + s->filter_size |= 1; + if (prev_filter_size != s->filter_size) { + init_gaussian_filter(s); + + for (int c = 0; c < s->channels; c++) { + cqueue_resize(s->gain_history_original[c], s->filter_size); + cqueue_resize(s->gain_history_minimum[c], s->filter_size); + cqueue_resize(s->threshold_history[c], s->filter_size); + } + } + + s->frame_len = frame_size(inlink->sample_rate, s->frame_len_msec); + + return 0; +} + static const AVFilterPad avfilter_af_dynaudnorm_inputs[] = { { .name = "default", @@ -806,4 +879,5 @@ AVFilter ff_af_dynaudnorm = { .outputs = avfilter_af_dynaudnorm_outputs, .priv_class = &dynaudnorm_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .process_command = process_command, }; diff --git a/libavfilter/af_extrastereo.c b/libavfilter/af_extrastereo.c index 83eba47410a..d8e4da9a936 100644 --- a/libavfilter/af_extrastereo.c +++ b/libavfilter/af_extrastereo.c @@ -31,7 +31,7 @@ typedef struct ExtraStereoContext { } ExtraStereoContext; #define OFFSET(x) offsetof(ExtraStereoContext, x) -#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption extrastereo_options[] = { { "m", "set the difference coefficient", OFFSET(mult), AV_OPT_TYPE_FLOAT, {.dbl=2.5}, -10, 10, A }, @@ -129,4 +129,5 @@ AVFilter ff_af_extrastereo = { .inputs = inputs, .outputs = outputs, .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 00ddc873412..f4513a1c46c 100644 --- a/libavfilter/af_firequalizer.c +++ b/libavfilter/af_firequalizer.c @@ -112,10 +112,11 @@ typedef struct FIREqualizerContext { #define OFFSET(x) offsetof(FIREqualizerContext, x) #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define TFLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption firequalizer_options[] = { - { "gain", "set gain curve", OFFSET(gain), AV_OPT_TYPE_STRING, { .str = "gain_interpolate(f)" }, 0, 0, FLAGS }, - { "gain_entry", "set gain entry", OFFSET(gain_entry), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, + { "gain", "set gain curve", OFFSET(gain), AV_OPT_TYPE_STRING, { .str = "gain_interpolate(f)" }, 0, 0, TFLAGS }, + { "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" }, diff --git a/libavfilter/af_flanger.c b/libavfilter/af_flanger.c index b7497a12eda..33c8245ea55 100644 --- a/libavfilter/af_flanger.c +++ b/libavfilter/af_flanger.c @@ -72,7 +72,7 @@ static const AVOption flanger_options[] = { AVFILTER_DEFINE_CLASS(flanger); -static int init(AVFilterContext *ctx) +static av_cold int init(AVFilterContext *ctx) { FlangerContext *s = ctx->priv; diff --git a/libavfilter/af_headphone.c b/libavfilter/af_headphone.c index 10638f9e7b5..552ad84837e 100644 --- a/libavfilter/af_headphone.c +++ b/libavfilter/af_headphone.c @@ -709,7 +709,7 @@ static int query_formats(AVFilterContext *ctx) if (s->hrir_fmt == HRIR_MULTI) { hrir_layouts = ff_all_channel_counts(); if (!hrir_layouts) - ret = AVERROR(ENOMEM); + return AVERROR(ENOMEM); ret = ff_channel_layouts_ref(hrir_layouts, &ctx->inputs[1]->out_channel_layouts); if (ret) return ret; diff --git a/libavfilter/af_join.c b/libavfilter/af_join.c index 930c9e48e73..ea03b60d67f 100644 --- a/libavfilter/af_join.c +++ b/libavfilter/af_join.c @@ -25,6 +25,7 @@ */ #include "libavutil/avassert.h" +#include "libavutil/avstring.h" #include "libavutil/channel_layout.h" #include "libavutil/common.h" #include "libavutil/opt.h" @@ -185,12 +186,10 @@ static av_cold int join_init(AVFilterContext *ctx) return ret; for (i = 0; i < s->inputs; i++) { - char name[32]; AVFilterPad pad = { 0 }; - snprintf(name, sizeof(name), "input%d", i); - pad.type = AVMEDIA_TYPE_AUDIO; - pad.name = av_strdup(name); + pad.type = AVMEDIA_TYPE_AUDIO; + pad.name = av_asprintf("input%d", i); if (!pad.name) return AVERROR(ENOMEM); @@ -208,9 +207,12 @@ static av_cold void join_uninit(AVFilterContext *ctx) JoinContext *s = ctx->priv; int i; + for (i = 0; i < s->inputs && s->input_frames; i++) { + av_frame_free(&s->input_frames[i]); + } + for (i = 0; i < ctx->nb_inputs; i++) { av_freep(&ctx->input_pads[i].name); - av_frame_free(&s->input_frames[i]); } av_freep(&s->channels); diff --git a/libavfilter/af_loudnorm.c b/libavfilter/af_loudnorm.c index 314b25fa39f..8e3cdc36db5 100644 --- a/libavfilter/af_loudnorm.c +++ b/libavfilter/af_loudnorm.c @@ -453,10 +453,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) true_peak = tmp; } - offset = s->target_i - global; - offset_tp = true_peak + offset; + offset = pow(10., (s->target_i - global) / 20.); + offset_tp = true_peak * offset; s->offset = offset_tp < s->target_tp ? offset : s->target_tp - true_peak; - s->offset = pow(10., s->offset / 20.); s->frame_type = LINEAR_MODE; } diff --git a/libavfilter/af_pan.c b/libavfilter/af_pan.c index 34e522c9d4e..6924d1c721e 100644 --- a/libavfilter/af_pan.c +++ b/libavfilter/af_pan.c @@ -424,7 +424,7 @@ static av_cold void uninit(AVFilterContext *ctx) #define OFFSET(x) offsetof(PanContext, x) static const AVOption pan_options[] = { - { "args", NULL, OFFSET(args), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM }, + { "args", NULL, OFFSET(args), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM }, { NULL } }; diff --git a/libavfilter/af_replaygain.c b/libavfilter/af_replaygain.c index 97617346edf..53fe49d7697 100644 --- a/libavfilter/af_replaygain.c +++ b/libavfilter/af_replaygain.c @@ -551,7 +551,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; ReplayGainContext *s = ctx->priv; - uint32_t level; + int64_t level; AVFrame *out; out = ff_get_audio_buffer(outlink, in->nb_samples); @@ -567,9 +567,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) out->nb_samples); butter_filter_stereo_samples(s, (float *)out->data[0], out->nb_samples); - level = (uint32_t)floor(100 * calc_stereo_rms((float *)out->data[0], - out->nb_samples)); - level = av_clip(level, 0, HISTOGRAM_SLOTS - 1); + level = lrint(floor(100 * calc_stereo_rms((float *)out->data[0], + out->nb_samples))); + level = av_clip64(level, 0, HISTOGRAM_SLOTS - 1); s->histogram[level]++; diff --git a/libavfilter/af_rubberband.c b/libavfilter/af_rubberband.c index ef937f44157..1dfd74422c3 100644 --- a/libavfilter/af_rubberband.c +++ b/libavfilter/af_rubberband.c @@ -43,10 +43,11 @@ typedef struct RubberBandContext { #define OFFSET(x) offsetof(RubberBandContext, 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 rubberband_options[] = { - { "tempo", "set tempo scale factor", OFFSET(tempo), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.01, 100, A }, - { "pitch", "set pitch scale factor", OFFSET(pitch), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.01, 100, A }, + { "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" }, @@ -120,8 +121,9 @@ static int query_formats(AVFilterContext *ctx) static int filter_frame(AVFilterLink *inlink, AVFrame *in) { - RubberBandContext *s = inlink->dst->priv; - AVFilterLink *outlink = inlink->dst->outputs[0]; + AVFilterContext *ctx = inlink->dst; + RubberBandContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; AVFrame *out; int ret = 0, nb_samples; @@ -148,7 +150,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) } av_frame_free(&in); - return ret < 0 ? ret : nb_samples; + if (ff_inlink_queued_samples(inlink) >= s->nb_samples) + ff_filter_set_ready(ctx, 100); + return ret < 0 ? ret : nb_samples; } static int config_input(AVFilterLink *inlink) @@ -200,30 +204,14 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar char *res, int res_len, int flags) { RubberBandContext *s = ctx->priv; + int ret; - if (!strcmp(cmd, "tempo")) { - double arg; - - sscanf(args, "%lf", &arg); - if (arg < 0.01 || arg > 100) { - av_log(ctx, AV_LOG_ERROR, - "Tempo scale factor '%f' out of range\n", arg); - return AVERROR(EINVAL); - } - rubberband_set_time_ratio(s->rbs, 1. / arg); - } - - if (!strcmp(cmd, "pitch")) { - double arg; + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; - sscanf(args, "%lf", &arg); - if (arg < 0.01 || arg > 100) { - av_log(ctx, AV_LOG_ERROR, - "Pitch scale factor '%f' out of range\n", arg); - return AVERROR(EINVAL); - } - rubberband_set_pitch_scale(s->rbs, arg); - } + rubberband_set_time_ratio(s->rbs, 1. / s->tempo); + rubberband_set_pitch_scale(s->rbs, s->pitch); return 0; } diff --git a/libavfilter/af_sidechaincompress.c b/libavfilter/af_sidechaincompress.c index 219643acb6a..e79c04d40ec 100644 --- a/libavfilter/af_sidechaincompress.c +++ b/libavfilter/af_sidechaincompress.c @@ -70,26 +70,27 @@ typedef struct SidechainCompressContext { #define OFFSET(x) offsetof(SidechainCompressContext, x) #define A AV_OPT_FLAG_AUDIO_PARAM #define F AV_OPT_FLAG_FILTERING_PARAM +#define R AV_OPT_FLAG_RUNTIME_PARAM static const AVOption options[] = { - { "level_in", "set input gain", OFFSET(level_in), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A|F }, - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, A|F, "mode" }, - { "downward",0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A|F, "mode" }, - { "upward", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A|F, "mode" }, - { "threshold", "set threshold", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0.125}, 0.000976563, 1, A|F }, - { "ratio", "set ratio", OFFSET(ratio), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 1, 20, A|F }, - { "attack", "set attack", OFFSET(attack), AV_OPT_TYPE_DOUBLE, {.dbl=20}, 0.01, 2000, A|F }, - { "release", "set release", OFFSET(release), AV_OPT_TYPE_DOUBLE, {.dbl=250}, 0.01, 9000, A|F }, - { "makeup", "set make up gain", OFFSET(makeup), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 1, 64, A|F }, - { "knee", "set knee", OFFSET(knee), AV_OPT_TYPE_DOUBLE, {.dbl=2.82843}, 1, 8, A|F }, - { "link", "set link type", OFFSET(link), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, A|F, "link" }, - { "average", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A|F, "link" }, - { "maximum", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A|F, "link" }, - { "detection", "set detection", OFFSET(detection), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, A|F, "detection" }, - { "peak", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A|F, "detection" }, - { "rms", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A|F, "detection" }, - { "level_sc", "set sidechain gain", OFFSET(level_sc), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A|F }, - { "mix", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, A|F }, + { "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" }, + { "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" }, + { "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 } }; @@ -214,6 +215,20 @@ static void compressor(SidechainCompressContext *s, } } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + compressor_config_output(ctx->outputs[0]); + + return 0; +} + #if CONFIG_SIDECHAINCOMPRESS_FILTER static int activate(AVFilterContext *ctx) { @@ -256,7 +271,7 @@ static int activate(AVFilterContext *ctx) dst = (double *)out->data[0]; out->pts = s->pts; - s->pts += nb_samples; + s->pts += av_rescale_q(nb_samples, (AVRational){1, ctx->outputs[0]->sample_rate}, ctx->outputs[0]->time_base); compressor(s, (double *)in[0]->data[0], dst, (double *)in[1]->data[0], nb_samples, @@ -382,6 +397,7 @@ AVFilter ff_af_sidechaincompress = { .uninit = uninit, .inputs = sidechaincompress_inputs, .outputs = sidechaincompress_outputs, + .process_command = process_command, }; #endif /* CONFIG_SIDECHAINCOMPRESS_FILTER */ @@ -475,5 +491,6 @@ AVFilter ff_af_acompressor = { .query_formats = acompressor_query_formats, .inputs = acompressor_inputs, .outputs = acompressor_outputs, + .process_command = process_command, }; #endif /* CONFIG_ACOMPRESSOR_FILTER */ diff --git a/libavfilter/af_silencedetect.c b/libavfilter/af_silencedetect.c index 3a71f3902a9..ff3b219e77a 100644 --- a/libavfilter/af_silencedetect.c +++ b/libavfilter/af_silencedetect.c @@ -35,7 +35,7 @@ typedef struct SilenceDetectContext { const AVClass *class; double noise; ///< noise amplitude ratio - double duration; ///< minimum duration of silence until notification + int64_t duration; ///< minimum duration of silence until notification int mono; ///< mono mode : check each channel separately (default = check when ALL channels are silent) int channels; ///< number of channels int independent_channels; ///< number of entries in following arrays (always 1 in mono mode) @@ -50,15 +50,16 @@ typedef struct SilenceDetectContext { AVRational time_base); } SilenceDetectContext; +#define MAX_DURATION (24*3600*1000000LL) #define OFFSET(x) offsetof(SilenceDetectContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM static const AVOption silencedetect_options[] = { { "n", "set noise tolerance", OFFSET(noise), AV_OPT_TYPE_DOUBLE, {.dbl=0.001}, 0, DBL_MAX, FLAGS }, { "noise", "set noise tolerance", OFFSET(noise), AV_OPT_TYPE_DOUBLE, {.dbl=0.001}, 0, DBL_MAX, FLAGS }, - { "d", "set minimum duration in seconds", OFFSET(duration), AV_OPT_TYPE_DOUBLE, {.dbl=2.}, 0, 24*60*60, FLAGS }, - { "duration", "set minimum duration in seconds", OFFSET(duration), AV_OPT_TYPE_DOUBLE, {.dbl=2.}, 0, 24*60*60, FLAGS }, - { "mono", "check each channel separately", OFFSET(mono), AV_OPT_TYPE_BOOL, {.i64=0.}, 0, 1, FLAGS }, - { "m", "check each channel separately", OFFSET(mono), AV_OPT_TYPE_BOOL, {.i64=0.}, 0, 1, FLAGS }, + { "d", "set minimum duration in seconds", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=2000000}, 0, MAX_DURATION,FLAGS }, + { "duration", "set minimum duration in seconds", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=2000000}, 0, MAX_DURATION,FLAGS }, + { "mono", "check each channel separately", OFFSET(mono), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, + { "m", "check each channel separately", OFFSET(mono), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, { NULL } }; @@ -142,6 +143,7 @@ static int config_input(AVFilterLink *inlink) int c; s->channels = inlink->channels; + s->duration = av_rescale(s->duration, inlink->sample_rate, AV_TIME_BASE); s->independent_channels = s->mono ? s->channels : 1; s->nb_null_samples = av_mallocz_array(sizeof(*s->nb_null_samples), s->independent_channels); if (!s->nb_null_samples) @@ -174,7 +176,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) const int nb_channels = inlink->channels; const int srate = inlink->sample_rate; const int nb_samples = insamples->nb_samples * nb_channels; - const int64_t nb_samples_notify = srate * s->duration * (s->mono ? 1 : nb_channels); + const int64_t nb_samples_notify = s->duration * (s->mono ? 1 : nb_channels); int c; // scale number of null samples to the new sample rate @@ -187,7 +189,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) s->frame_end = insamples->pts + av_rescale_q(insamples->nb_samples, (AVRational){ 1, s->last_sample_rate }, inlink->time_base); - // TODO: document metadata s->silencedetect(s, insamples, nb_samples, nb_samples_notify, inlink->time_base); diff --git a/libavfilter/af_stereowiden.c b/libavfilter/af_stereowiden.c index d23c8dba754..251f08438e9 100644 --- a/libavfilter/af_stereowiden.c +++ b/libavfilter/af_stereowiden.c @@ -39,13 +39,14 @@ typedef struct StereoWidenContext { } StereoWidenContext; #define OFFSET(x) offsetof(StereoWidenContext, x) -#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#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 stereowiden_options[] = { { "delay", "set delay time", OFFSET(delay), AV_OPT_TYPE_FLOAT, {.dbl=20}, 1, 100, A }, - { "feedback", "set feedback gain", OFFSET(feedback), AV_OPT_TYPE_FLOAT, {.dbl=.3}, 0, 0.9, A }, - { "crossfeed", "set cross feed", OFFSET(crossfeed), AV_OPT_TYPE_FLOAT, {.dbl=.3}, 0, 0.8, A }, - { "drymix", "set dry-mix", OFFSET(drymix), AV_OPT_TYPE_FLOAT, {.dbl=.8}, 0, 1.0, A }, + { "feedback", "set feedback gain", OFFSET(feedback), AV_OPT_TYPE_FLOAT, {.dbl=.3}, 0, 0.9, AT }, + { "crossfeed", "set cross feed", OFFSET(crossfeed), AV_OPT_TYPE_FLOAT, {.dbl=.3}, 0, 0.8, AT }, + { "drymix", "set dry-mix", OFFSET(drymix), AV_OPT_TYPE_FLOAT, {.dbl=.8}, 0, 1.0, AT }, { NULL } }; @@ -165,4 +166,5 @@ AVFilter ff_af_stereowiden = { .inputs = inputs, .outputs = outputs, .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 5a4eca77557..11406786ee8 100644 --- a/libavfilter/af_surround.c +++ b/libavfilter/af_surround.c @@ -1372,7 +1372,7 @@ static void filter_5_1_back(AVFilterContext *ctx) } } -static int init(AVFilterContext *ctx) +static av_cold int init(AVFilterContext *ctx) { AudioSurroundContext *s = ctx->priv; float overlap; diff --git a/libavfilter/af_tremolo.c b/libavfilter/af_tremolo.c index 8cbc79892d1..f55e8e2b09b 100644 --- a/libavfilter/af_tremolo.c +++ b/libavfilter/af_tremolo.c @@ -28,6 +28,7 @@ typedef struct TremoloContext { double freq; double depth; double *table; + int table_size; int index; } TremoloContext; @@ -72,7 +73,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) dst += channels; src += channels; s->index++; - if (s->index >= inlink->sample_rate / s->freq) + if (s->index >= s->table_size) s->index = 0; } @@ -125,11 +126,12 @@ static int config_input(AVFilterLink *inlink) const double offset = 1. - s->depth / 2.; int i; - s->table = av_malloc_array(inlink->sample_rate / s->freq, sizeof(*s->table)); + s->table_size = inlink->sample_rate / s->freq; + s->table = av_malloc_array(s->table_size, sizeof(*s->table)); if (!s->table) return AVERROR(ENOMEM); - for (i = 0; i < inlink->sample_rate / s->freq; i++) { + for (i = 0; i < s->table_size; i++) { double env = s->freq * i / inlink->sample_rate; env = sin(2 * M_PI * fmod(env + 0.25, 1.0)); s->table[i] = env * (1 - fabs(offset)) + offset; diff --git a/libavfilter/af_volume.c b/libavfilter/af_volume.c index b106ed8cf4a..213c57195a4 100644 --- a/libavfilter/af_volume.c +++ b/libavfilter/af_volume.c @@ -62,10 +62,11 @@ static const char *const var_names[] = { #define OFFSET(x) offsetof(VolumeContext, x) #define A AV_OPT_FLAG_AUDIO_PARAM #define F AV_OPT_FLAG_FILTERING_PARAM +#define T AV_OPT_FLAG_RUNTIME_PARAM static const AVOption volume_options[] = { { "volume", "set volume adjustment expression", - OFFSET(volume_expr), AV_OPT_TYPE_STRING, { .str = "1.0" }, .flags = A|F }, + 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" }, diff --git a/libavfilter/all_channel_layouts.inc b/libavfilter/all_channel_layouts.inc deleted file mode 100644 index 878e1f5f8ed..00000000000 --- a/libavfilter/all_channel_layouts.inc +++ /dev/null @@ -1,68 +0,0 @@ -AV_CH_FRONT_CENTER, -AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY, -AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER, -AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_CENTER, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER, -AV_CH_FRONT_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, -AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 04a3df7d56d..1183e402675 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -56,6 +56,7 @@ extern AVFilter ff_af_amix; extern AVFilter ff_af_amultiply; extern AVFilter ff_af_anequalizer; extern AVFilter ff_af_anlmdn; +extern AVFilter ff_af_anlms; extern AVFilter ff_af_anull; extern AVFilter ff_af_apad; extern AVFilter ff_af_aperms; @@ -64,6 +65,7 @@ extern AVFilter ff_af_apulsator; extern AVFilter ff_af_arealtime; extern AVFilter ff_af_aresample; extern AVFilter ff_af_areverse; +extern AVFilter ff_af_arnndn; extern AVFilter ff_af_aselect; extern AVFilter ff_af_asendcmd; extern AVFilter ff_af_asetnsamples; @@ -77,14 +79,17 @@ extern AVFilter ff_af_asplit; extern AVFilter ff_af_asr; extern AVFilter ff_af_astats; extern AVFilter ff_af_astreamselect; +extern AVFilter ff_af_asubboost; extern AVFilter ff_af_atempo; extern AVFilter ff_af_atrim; +extern AVFilter ff_af_axcorrelate; extern AVFilter ff_af_azmq; extern AVFilter ff_af_bandpass; extern AVFilter ff_af_bandreject; extern AVFilter ff_af_bass; extern AVFilter ff_af_biquad; extern AVFilter ff_af_bs2b; +extern AVFilter ff_vf_chromaber_vulkan; extern AVFilter ff_af_channelmap; extern AVFilter ff_af_channelsplit; extern AVFilter ff_af_chorus; @@ -134,6 +139,7 @@ extern AVFilter ff_af_volume; extern AVFilter ff_af_volumedetect; extern AVFilter ff_asrc_aevalsrc; +extern AVFilter ff_asrc_afirsrc; extern AVFilter ff_asrc_anoisesrc; extern AVFilter ff_asrc_anullsrc; extern AVFilter ff_asrc_flite; @@ -143,6 +149,7 @@ extern AVFilter ff_asrc_sine; extern AVFilter ff_asink_anullsink; +extern AVFilter ff_vf_addroi; extern AVFilter ff_vf_alphaextract; extern AVFilter ff_vf_alphamerge; extern AVFilter ff_vf_amplify; @@ -150,8 +157,10 @@ extern AVFilter ff_vf_ass; extern AVFilter ff_vf_atadenoise; extern AVFilter ff_vf_avgblur; extern AVFilter ff_vf_avgblur_opencl; +extern AVFilter ff_vf_avgblur_vulkan; extern AVFilter ff_vf_bbox; extern AVFilter ff_vf_bench; +extern AVFilter ff_vf_bilateral; extern AVFilter ff_vf_bitplanenoise; extern AVFilter ff_vf_blackdetect; extern AVFilter ff_vf_blackframe; @@ -160,6 +169,7 @@ extern AVFilter ff_vf_bm3d; extern AVFilter ff_vf_boxblur; extern AVFilter ff_vf_boxblur_opencl; extern AVFilter ff_vf_bwdif; +extern AVFilter ff_vf_cas; extern AVFilter ff_vf_chromahold; extern AVFilter ff_vf_chromakey; extern AVFilter ff_vf_chromashift; @@ -184,6 +194,7 @@ extern AVFilter ff_vf_cropdetect; extern AVFilter ff_vf_cue; extern AVFilter ff_vf_curves; extern AVFilter ff_vf_datascope; +extern AVFilter ff_vf_dblur; extern AVFilter ff_vf_dctdnoiz; extern AVFilter ff_vf_deband; extern AVFilter ff_vf_deblock; @@ -199,11 +210,13 @@ extern AVFilter ff_vf_delogo; extern AVFilter ff_vf_denoise_vaapi; extern AVFilter ff_vf_derain; extern AVFilter ff_vf_deshake; +extern AVFilter ff_vf_deshake_opencl; extern AVFilter ff_vf_despill; extern AVFilter ff_vf_detelecine; extern AVFilter ff_vf_dilation; extern AVFilter ff_vf_dilation_opencl; extern AVFilter ff_vf_displace; +extern AVFilter ff_vf_dnn_processing; extern AVFilter ff_vf_doubleweave; extern AVFilter ff_vf_drawbox; extern AVFilter ff_vf_drawgraph; @@ -232,6 +245,7 @@ extern AVFilter ff_vf_framepack; extern AVFilter ff_vf_framerate; extern AVFilter ff_vf_framestep; extern AVFilter ff_vf_freezedetect; +extern AVFilter ff_vf_freezeframes; extern AVFilter ff_vf_frei0r; extern AVFilter ff_vf_fspp; extern AVFilter ff_vf_gblur; @@ -272,9 +286,13 @@ extern AVFilter ff_vf_lut3d; extern AVFilter ff_vf_lutrgb; extern AVFilter ff_vf_lutyuv; extern AVFilter ff_vf_maskedclamp; +extern AVFilter ff_vf_maskedmax; extern AVFilter ff_vf_maskedmerge; +extern AVFilter ff_vf_maskedmin; +extern AVFilter ff_vf_maskedthreshold; extern AVFilter ff_vf_maskfun; extern AVFilter ff_vf_mcdeint; +extern AVFilter ff_vf_median; extern AVFilter ff_vf_mergeplanes; extern AVFilter ff_vf_mestimate; extern AVFilter ff_vf_metadata; @@ -296,13 +314,17 @@ extern AVFilter ff_vf_oscilloscope; extern AVFilter ff_vf_overlay; extern AVFilter ff_vf_overlay_opencl; extern AVFilter ff_vf_overlay_qsv; +extern AVFilter ff_vf_overlay_vulkan; +extern AVFilter ff_vf_overlay_cuda; extern AVFilter ff_vf_owdenoise; extern AVFilter ff_vf_pad; +extern AVFilter ff_vf_pad_opencl; extern AVFilter ff_vf_palettegen; extern AVFilter ff_vf_paletteuse; extern AVFilter ff_vf_perms; extern AVFilter ff_vf_perspective; extern AVFilter ff_vf_phase; +extern AVFilter ff_vf_photosensitivity; extern AVFilter ff_vf_pixdesctest; extern AVFilter ff_vf_pixscope; extern AVFilter ff_vf_pp; @@ -335,7 +357,10 @@ extern AVFilter ff_vf_scale_cuda; extern AVFilter ff_vf_scale_npp; extern AVFilter ff_vf_scale_qsv; extern AVFilter ff_vf_scale_vaapi; +extern AVFilter ff_vf_scale_vulkan; extern AVFilter ff_vf_scale2ref; +extern AVFilter ff_vf_scdet; +extern AVFilter ff_vf_scroll; extern AVFilter ff_vf_select; extern AVFilter ff_vf_selectivecolor; extern AVFilter ff_vf_sendcmd; @@ -370,15 +395,18 @@ extern AVFilter ff_vf_swaprect; extern AVFilter ff_vf_swapuv; extern AVFilter ff_vf_tblend; extern AVFilter ff_vf_telecine; +extern AVFilter ff_vf_thistogram; extern AVFilter ff_vf_threshold; extern AVFilter ff_vf_thumbnail; extern AVFilter ff_vf_thumbnail_cuda; extern AVFilter ff_vf_tile; extern AVFilter ff_vf_tinterlace; extern AVFilter ff_vf_tlut2; +extern AVFilter ff_vf_tmedian; extern AVFilter ff_vf_tmix; extern AVFilter ff_vf_tonemap; extern AVFilter ff_vf_tonemap_opencl; +extern AVFilter ff_vf_tonemap_vaapi; extern AVFilter ff_vf_tpad; extern AVFilter ff_vf_transpose; extern AVFilter ff_vf_transpose_npp; @@ -388,7 +416,9 @@ extern AVFilter ff_vf_trim; extern AVFilter ff_vf_unpremultiply; extern AVFilter ff_vf_unsharp; extern AVFilter ff_vf_unsharp_opencl; +extern AVFilter ff_vf_untile; extern AVFilter ff_vf_uspp; +extern AVFilter ff_vf_v360; extern AVFilter ff_vf_vaguedenoiser; extern AVFilter ff_vf_vectorscope; extern AVFilter ff_vf_vflip; @@ -404,10 +434,13 @@ extern AVFilter ff_vf_w3fdif; extern AVFilter ff_vf_waveform; extern AVFilter ff_vf_weave; extern AVFilter ff_vf_xbr; +extern AVFilter ff_vf_xfade; +extern AVFilter ff_vf_xfade_opencl; extern AVFilter ff_vf_xmedian; extern AVFilter ff_vf_xstack; extern AVFilter ff_vf_yadif; extern AVFilter ff_vf_yadif_cuda; +extern AVFilter ff_vf_yaepblur; extern AVFilter ff_vf_zmq; extern AVFilter ff_vf_zoompan; extern AVFilter ff_vf_zscale; @@ -418,6 +451,7 @@ extern AVFilter ff_vsrc_cellauto; extern AVFilter ff_vsrc_color; extern AVFilter ff_vsrc_coreimagesrc; extern AVFilter ff_vsrc_frei0r_src; +extern AVFilter ff_vsrc_gradients; extern AVFilter ff_vsrc_haldclutsrc; extern AVFilter ff_vsrc_life; extern AVFilter ff_vsrc_mandelbrot; @@ -427,6 +461,7 @@ extern AVFilter ff_vsrc_openclsrc; extern AVFilter ff_vsrc_pal75bars; extern AVFilter ff_vsrc_pal100bars; extern AVFilter ff_vsrc_rgbtestsrc; +extern AVFilter ff_vsrc_sierpinski; extern AVFilter ff_vsrc_smptebars; extern AVFilter ff_vsrc_smptehdbars; extern AVFilter ff_vsrc_testsrc; diff --git a/libavfilter/asink_anullsink.c b/libavfilter/asink_anullsink.c index 9b53d3fbc2c..c10a348475a 100644 --- a/libavfilter/asink_anullsink.c +++ b/libavfilter/asink_anullsink.c @@ -40,9 +40,7 @@ static const AVFilterPad avfilter_asink_anullsink_inputs[] = { AVFilter ff_asink_anullsink = { .name = "anullsink", .description = NULL_IF_CONFIG_SMALL("Do absolutely nothing with the input audio."), - - .priv_size = 0, - - .inputs = avfilter_asink_anullsink_inputs, - .outputs = NULL, + .priv_size = 0, + .inputs = avfilter_asink_anullsink_inputs, + .outputs = NULL, }; diff --git a/libavfilter/asrc_afirsrc.c b/libavfilter/asrc_afirsrc.c new file mode 100644 index 00000000000..b90ffad57f3 --- /dev/null +++ b/libavfilter/asrc_afirsrc.c @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2020 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/eval.h" +#include "libavutil/opt.h" +#include "libavutil/tx.h" +#include "audio.h" +#include "avfilter.h" +#include "internal.h" +#include "window_func.h" + +typedef struct AudioFIRSourceContext { + const AVClass *class; + + char *freq_points_str; + char *magnitude_str; + char *phase_str; + int nb_taps; + int sample_rate; + int nb_samples; + int win_func; + + AVComplexFloat *complexf; + float *freq; + float *magnitude; + float *phase; + int freq_size; + int magnitude_size; + int phase_size; + int nb_freq; + int nb_magnitude; + int nb_phase; + + float *taps; + float *win; + int64_t pts; + + AVTXContext *tx_ctx; + av_tx_fn tx_fn; +} AudioFIRSourceContext; + +#define OFFSET(x) offsetof(AudioFIRSourceContext, x) +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption afirsrc_options[] = { + { "taps", "set number of taps", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=1025}, 9, UINT16_MAX, FLAGS }, + { "t", "set number of taps", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=1025}, 9, UINT16_MAX, FLAGS }, + { "frequency", "set frequency points", OFFSET(freq_points_str), AV_OPT_TYPE_STRING, {.str="0 1"}, 0, 0, FLAGS }, + { "f", "set frequency points", OFFSET(freq_points_str), AV_OPT_TYPE_STRING, {.str="0 1"}, 0, 0, FLAGS }, + { "magnitude", "set magnitude values", OFFSET(magnitude_str), AV_OPT_TYPE_STRING, {.str="1 1"}, 0, 0, FLAGS }, + { "m", "set magnitude values", OFFSET(magnitude_str), AV_OPT_TYPE_STRING, {.str="1 1"}, 0, 0, FLAGS }, + { "phase", "set phase values", OFFSET(phase_str), AV_OPT_TYPE_STRING, {.str="0 0"}, 0, 0, FLAGS }, + { "p", "set phase values", OFFSET(phase_str), AV_OPT_TYPE_STRING, {.str="0 0"}, 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, 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 }, + { "win_func", "set window function", OFFSET(win_func), AV_OPT_TYPE_INT, {.i64=WFUNC_BLACKMAN}, 0, NB_WFUNC-1, FLAGS, "win_func" }, + { "w", "set window function", OFFSET(win_func), AV_OPT_TYPE_INT, {.i64=WFUNC_BLACKMAN}, 0, NB_WFUNC-1, FLAGS, "win_func" }, + { "rect", "Rectangular", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_RECT}, 0, 0, FLAGS, "win_func" }, + { "bartlett", "Bartlett", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BARTLETT}, 0, 0, FLAGS, "win_func" }, + { "hanning", "Hanning", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HANNING}, 0, 0, FLAGS, "win_func" }, + { "hamming", "Hamming", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HAMMING}, 0, 0, FLAGS, "win_func" }, + { "blackman", "Blackman", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BLACKMAN}, 0, 0, FLAGS, "win_func" }, + { "welch", "Welch", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_WELCH}, 0, 0, FLAGS, "win_func" }, + { "flattop", "Flat-top", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_FLATTOP}, 0, 0, FLAGS, "win_func" }, + { "bharris", "Blackman-Harris", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHARRIS}, 0, 0, FLAGS, "win_func" }, + { "bnuttall", "Blackman-Nuttall", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BNUTTALL}, 0, 0, FLAGS, "win_func" }, + { "bhann", "Bartlett-Hann", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHANN}, 0, 0, FLAGS, "win_func" }, + { "sine", "Sine", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_SINE}, 0, 0, FLAGS, "win_func" }, + { "nuttall", "Nuttall", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_NUTTALL}, 0, 0, FLAGS, "win_func" }, + { "lanczos", "Lanczos", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_LANCZOS}, 0, 0, FLAGS, "win_func" }, + { "gauss", "Gauss", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_GAUSS}, 0, 0, FLAGS, "win_func" }, + { "tukey", "Tukey", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_TUKEY}, 0, 0, FLAGS, "win_func" }, + { "dolph", "Dolph-Chebyshev", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_DOLPH}, 0, 0, FLAGS, "win_func" }, + { "cauchy", "Cauchy", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_CAUCHY}, 0, 0, FLAGS, "win_func" }, + { "parzen", "Parzen", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_PARZEN}, 0, 0, FLAGS, "win_func" }, + { "poisson", "Poisson", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_POISSON}, 0, 0, FLAGS, "win_func" }, + { "bohman" , "Bohman", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BOHMAN}, 0, 0, FLAGS, "win_func" }, + {NULL} +}; + +AVFILTER_DEFINE_CLASS(afirsrc); + +static av_cold int init(AVFilterContext *ctx) +{ + AudioFIRSourceContext *s = ctx->priv; + + if (!(s->nb_taps & 1)) { + av_log(s, AV_LOG_WARNING, "Number of taps %d must be odd length.\n", s->nb_taps); + s->nb_taps |= 1; + } + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + AudioFIRSourceContext *s = ctx->priv; + + av_freep(&s->win); + av_freep(&s->taps); + av_freep(&s->freq); + av_freep(&s->magnitude); + av_freep(&s->phase); + av_freep(&s->complexf); + av_tx_uninit(&s->tx_ctx); +} + +static av_cold int query_formats(AVFilterContext *ctx) +{ + AudioFIRSourceContext *s = ctx->priv; + static const int64_t chlayouts[] = { AV_CH_LAYOUT_MONO, -1 }; + int sample_rates[] = { s->sample_rate, -1 }; + static const enum AVSampleFormat sample_fmts[] = { + AV_SAMPLE_FMT_FLT, + AV_SAMPLE_FMT_NONE + }; + + AVFilterFormats *formats; + AVFilterChannelLayouts *layouts; + int ret; + + formats = ff_make_format_list(sample_fmts); + if (!formats) + return AVERROR(ENOMEM); + ret = ff_set_common_formats (ctx, formats); + if (ret < 0) + return ret; + + layouts = avfilter_make_format64_list(chlayouts); + if (!layouts) + return AVERROR(ENOMEM); + ret = ff_set_common_channel_layouts(ctx, layouts); + if (ret < 0) + return ret; + + formats = ff_make_format_list(sample_rates); + if (!formats) + return AVERROR(ENOMEM); + return ff_set_common_samplerates(ctx, formats); +} + +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)); + if (!new_items) + return AVERROR(ENOMEM); + *items = new_items; + + tail = str; + if (!tail) + return AVERROR(EINVAL); + + do { + (*items)[(*nb_items)++] = av_strtod(tail, &tail); + new_items = av_fast_realloc(*items, items_size, (*nb_items + 1) * sizeof(float)); + if (!new_items) + return AVERROR(ENOMEM); + *items = new_items; + if (tail && *tail) + tail++; + } while (tail && *tail); + + return 0; +} + +static void lininterp(AVComplexFloat *complexf, + const float *freq, + const float *magnitude, + const float *phase, + int m, int minterp) +{ + for (int i = 0; i < minterp; i++) { + for (int j = 1; j < m; j++) { + const float x = i / (float)minterp; + + if (x <= freq[j]) { + const float mg = (x - freq[j-1]) / (freq[j] - freq[j-1]) * (magnitude[j] - magnitude[j-1]) + magnitude[j-1]; + const float ph = (x - freq[j-1]) / (freq[j] - freq[j-1]) * (phase[j] - phase[j-1]) + phase[j-1]; + + complexf[i].re = mg * cosf(ph); + complexf[i].im = mg * sinf(ph); + break; + } + } + } +} + +static av_cold int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AudioFIRSourceContext *s = ctx->priv; + float overlap, scale = 1.f, compensation; + int fft_size, middle, ret; + + s->nb_freq = s->nb_magnitude = s->nb_phase = 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; + + ret = parse_string(s->phase_str, &s->phase, &s->nb_phase, &s->phase_size); + if (ret < 0) + return ret; + + if (s->nb_freq != s->nb_magnitude && s->nb_freq != s->nb_phase && s->nb_freq >= 2) { + av_log(ctx, AV_LOG_ERROR, "Number of frequencies, magnitudes and phases must be same and >= 2.\n"); + return AVERROR(EINVAL); + } + + for (int i = 0; i < s->nb_freq; i++) { + if (i == 0 && s->freq[i] != 0.f) { + av_log(ctx, AV_LOG_ERROR, "First frequency must be 0.\n"); + return AVERROR(EINVAL); + } + + if (i == s->nb_freq - 1 && s->freq[i] != 1.f) { + av_log(ctx, AV_LOG_ERROR, "Last frequency must be 1.\n"); + return AVERROR(EINVAL); + } + + if (i && s->freq[i] < s->freq[i-1]) { + av_log(ctx, AV_LOG_ERROR, "Frequencies must be in increasing order.\n"); + return AVERROR(EINVAL); + } + } + + fft_size = 1 << (av_log2(s->nb_taps) + 1); + s->complexf = av_calloc(fft_size * 2, sizeof(*s->complexf)); + if (!s->complexf) + return AVERROR(ENOMEM); + + ret = av_tx_init(&s->tx_ctx, &s->tx_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); + + s->win = av_calloc(s->nb_taps, sizeof(*s->win)); + if (!s->win) + return AVERROR(ENOMEM); + + generate_window_func(s->win, s->nb_taps, s->win_func, &overlap); + + lininterp(s->complexf, s->freq, s->magnitude, s->phase, s->nb_freq, fft_size / 2); + + s->tx_fn(s->tx_ctx, s->complexf + fft_size, s->complexf, sizeof(float)); + + compensation = 2.f / fft_size; + middle = s->nb_taps / 2; + + for (int i = 0; i <= middle; i++) { + s->taps[ i] = s->complexf[fft_size + middle - i].re * compensation * s->win[i]; + s->taps[middle + i] = s->complexf[fft_size + i].re * compensation * s->win[middle + i]; + } + + s->pts = 0; + + return 0; +} + +static int request_frame(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AudioFIRSourceContext *s = ctx->priv; + AVFrame *frame; + int nb_samples; + + nb_samples = FFMIN(s->nb_samples, s->nb_taps - s->pts); + if (!nb_samples) + return AVERROR_EOF; + + if (!(frame = ff_get_audio_buffer(outlink, nb_samples))) + return AVERROR(ENOMEM); + + memcpy(frame->data[0], s->taps + s->pts, nb_samples * sizeof(float)); + + frame->pts = s->pts; + s->pts += nb_samples; + return ff_filter_frame(outlink, frame); +} + +static const AVFilterPad afirsrc_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .request_frame = request_frame, + .config_props = config_output, + }, + { NULL } +}; + +AVFilter ff_asrc_afirsrc = { + .name = "afirsrc", + .description = NULL_IF_CONFIG_SMALL("Generate a FIR coefficients audio stream."), + .query_formats = query_formats, + .init = init, + .uninit = uninit, + .priv_size = sizeof(AudioFIRSourceContext), + .inputs = NULL, + .outputs = afirsrc_outputs, + .priv_class = &afirsrc_class, +}; diff --git a/libavfilter/asrc_anoisesrc.c b/libavfilter/asrc_anoisesrc.c index 78f0af4fdee..7aa878707db 100644 --- a/libavfilter/asrc_anoisesrc.c +++ b/libavfilter/asrc_anoisesrc.c @@ -21,6 +21,7 @@ #include "libavutil/opt.h" #include "audio.h" #include "avfilter.h" +#include "filters.h" #include "internal.h" #include "libavutil/lfg.h" #include "libavutil/random_seed.h" @@ -30,13 +31,13 @@ typedef struct ANoiseSrcContext { int sample_rate; double amplitude; int64_t duration; - int64_t color; + int color; int64_t seed; int nb_samples; int64_t pts; int infinite; - double (*filter)(double white, double *buf); + double (*filter)(double white, double *buf, double half_amplitude); double buf[7]; AVLFG c; } ANoiseSrcContext; @@ -47,6 +48,7 @@ enum NoiseMode { NM_BROWN, NM_BLUE, NM_VIOLET, + NM_VELVET, NM_NB }; @@ -68,6 +70,7 @@ static const AVOption anoisesrc_options[] = { { "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" }, { "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 }, @@ -111,12 +114,12 @@ static av_cold int query_formats(AVFilterContext *ctx) return ff_set_common_samplerates(ctx, formats); } -static double white_filter(double white, double *buf) +static double white_filter(double white, double *buf, double ha) { return white; } -static double pink_filter(double white, double *buf) +static double pink_filter(double white, double *buf, double ha) { double pink; @@ -132,7 +135,7 @@ static double pink_filter(double white, double *buf) return pink * 0.11; } -static double blue_filter(double white, double *buf) +static double blue_filter(double white, double *buf, double ha) { double blue; @@ -148,7 +151,7 @@ static double blue_filter(double white, double *buf) return blue * 0.11; } -static double brown_filter(double white, double *buf) +static double brown_filter(double white, double *buf, double ha) { double brown; @@ -157,7 +160,7 @@ static double brown_filter(double white, double *buf) return brown * 3.5; } -static double violet_filter(double white, double *buf) +static double violet_filter(double white, double *buf, double ha) { double violet; @@ -166,6 +169,11 @@ static double violet_filter(double white, double *buf) return violet * 3.5; } +static double velvet_filter(double white, double *buf, double ha) +{ + return 2. * ha * ((white > ha) - (white < -ha)); +} + static av_cold int config_props(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; @@ -185,21 +193,26 @@ 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; } return 0; } -static int request_frame(AVFilterLink *outlink) +static int activate(AVFilterContext *ctx) { - AVFilterContext *ctx = outlink->src; + AVFilterLink *outlink = ctx->outputs[0]; ANoiseSrcContext *s = ctx->priv; AVFrame *frame; int nb_samples, i; double *dst; + if (!ff_outlink_frame_wanted(outlink)) + return FFERROR_NOT_READY; + if (!s->infinite && s->duration <= 0) { - return AVERROR_EOF; + ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); + return 0; } else if (!s->infinite && s->duration < s->nb_samples) { nb_samples = s->duration; } else { @@ -213,7 +226,7 @@ static int request_frame(AVFilterLink *outlink) 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); + dst[i] = s->filter(white, s->buf, s->amplitude * 0.5); } if (!s->infinite) @@ -228,7 +241,6 @@ static const AVFilterPad anoisesrc_outputs[] = { { .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .request_frame = request_frame, .config_props = config_props, }, { NULL } @@ -240,6 +252,7 @@ AVFilter ff_asrc_anoisesrc = { .query_formats = query_formats, .priv_size = sizeof(ANoiseSrcContext), .inputs = NULL, + .activate = activate, .outputs = anoisesrc_outputs, .priv_class = &anoisesrc_class, }; diff --git a/libavfilter/asrc_anullsrc.c b/libavfilter/asrc_anullsrc.c index cb676947d8f..52db61685d4 100644 --- a/libavfilter/asrc_anullsrc.c +++ b/libavfilter/asrc_anullsrc.c @@ -114,11 +114,8 @@ static int request_frame(AVFilterLink *outlink) return AVERROR(ENOMEM); samplesref->pts = null->pts; - samplesref->channel_layout = null->channel_layout; - samplesref->sample_rate = outlink->sample_rate; - ret = ff_filter_frame(outlink, av_frame_clone(samplesref)); - av_frame_free(&samplesref); + ret = ff_filter_frame(outlink, samplesref); if (ret < 0) return ret; diff --git a/libavfilter/asrc_flite.c b/libavfilter/asrc_flite.c index c9619ebbae9..3e543a3ab65 100644 --- a/libavfilter/asrc_flite.c +++ b/libavfilter/asrc_flite.c @@ -54,10 +54,10 @@ static const AVOption flite_options[] = { { "list_voices", "list voices and exit", OFFSET(list_voices), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, { "nb_samples", "set number of samples per frame", OFFSET(frame_nb_samples), AV_OPT_TYPE_INT, {.i64=512}, 0, INT_MAX, FLAGS }, { "n", "set number of samples per frame", OFFSET(frame_nb_samples), AV_OPT_TYPE_INT, {.i64=512}, 0, INT_MAX, FLAGS }, - { "text", "set text to speak", OFFSET(text), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "textfile", "set filename of the text to speak", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "v", "set voice", OFFSET(voice_str), AV_OPT_TYPE_STRING, {.str="kal"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "voice", "set voice", OFFSET(voice_str), AV_OPT_TYPE_STRING, {.str="kal"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "text", "set text to speak", OFFSET(text), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + { "textfile", "set filename of the text to speak", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + { "v", "set voice", OFFSET(voice_str), AV_OPT_TYPE_STRING, {.str="kal"}, 0, 0, FLAGS }, + { "voice", "set voice", OFFSET(voice_str), AV_OPT_TYPE_STRING, {.str="kal"}, 0, 0, FLAGS }, { NULL } }; diff --git a/libavfilter/asrc_sinc.c b/libavfilter/asrc_sinc.c index 0135eb9023b..24aeab6e7bd 100644 --- a/libavfilter/asrc_sinc.c +++ b/libavfilter/asrc_sinc.c @@ -227,10 +227,11 @@ static int fir_to_phase(SincContext *s, float **h, int *len, int *post_len, floa for (i = *len, work_len = 2 * 2 * 8; i > 1; work_len <<= 1, i >>= 1); - work = av_calloc(work_len + 2, sizeof(*work)); /* +2: (UN)PACK */ - pi_wraps = av_calloc(((work_len + 2) / 2), sizeof(*pi_wraps)); - if (!work || !pi_wraps) + /* The first part is for work (+2 for (UN)PACK), the latter for pi_wraps. */ + work = av_calloc((work_len + 2) + (work_len / 2 + 1), sizeof(float)); + if (!work) return AVERROR(ENOMEM); + pi_wraps = &work[work_len + 2]; memcpy(work, *h, *len * sizeof(*work)); @@ -239,8 +240,10 @@ static int fir_to_phase(SincContext *s, float **h, int *len, int *post_len, floa s->rdft = s->irdft = NULL; s->rdft = av_rdft_init(av_log2(work_len), DFT_R2C); s->irdft = av_rdft_init(av_log2(work_len), IDFT_C2R); - if (!s->rdft || !s->irdft) + if (!s->rdft || !s->irdft) { + av_free(work); return AVERROR(ENOMEM); + } av_rdft_calc(s->rdft, work); /* Cepstral: */ UNPACK(work, work_len); @@ -320,7 +323,6 @@ static int fir_to_phase(SincContext *s, float **h, int *len, int *post_len, floa *len = end - begin; *h = av_realloc_f(*h, *len, sizeof(**h)); if (!*h) { - av_free(pi_wraps); av_free(work); return AVERROR(ENOMEM); } @@ -335,7 +337,6 @@ 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)); - av_free(pi_wraps); av_free(work); return 0; diff --git a/libavfilter/atadenoise.h b/libavfilter/atadenoise.h new file mode 100644 index 00000000000..26cb20b9c84 --- /dev/null +++ b/libavfilter/atadenoise.h @@ -0,0 +1,42 @@ + /* + * 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 + */ + +#ifndef AVFILTER_ATADENOISE_H +#define AVFILTER_ATADENOISE_H + +#include +#include + +enum ATAAlgorithm { + PARALLEL, + SERIAL, + NB_ATAA +}; + +typedef struct ATADenoiseDSPContext { + void (*filter_row)(const uint8_t *src, uint8_t *dst, + const uint8_t **srcf, + int w, int mid, int size, + int thra, int thrb); +} ATADenoiseDSPContext; + +void ff_atadenoise_init_x86(ATADenoiseDSPContext *dsp, int depth, int algorithm); + +#endif /* AVFILTER_ATADENOISE_H */ diff --git a/libavfilter/avf_abitscope.c b/libavfilter/avf_abitscope.c index 6a38521b6ca..759f821e740 100644 --- a/libavfilter/avf_abitscope.c +++ b/libavfilter/avf_abitscope.c @@ -142,7 +142,7 @@ static void count_bits(AudioBitScopeContext *s, uint32_t sample, int max) int i; for (i = 0; i < max; i++) { - if (sample & (1 << i)) + if (sample & (1U << i)) s->counter[i]++; } } diff --git a/libavfilter/avf_ahistogram.c b/libavfilter/avf_ahistogram.c index a0931bfa58a..92cda467561 100644 --- a/libavfilter/avf_ahistogram.c +++ b/libavfilter/avf_ahistogram.c @@ -163,6 +163,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) const int w = s->w; int c, y, n, p, bin; uint64_t acmax = 1; + AVFrame *clone; if (!s->out || s->out->width != outlink->w || s->out->height != outlink->h) { @@ -363,7 +364,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->ypos = H; } - return ff_filter_frame(outlink, av_frame_clone(s->out)); + clone = av_frame_clone(s->out); + if (!clone) + return AVERROR(ENOMEM); + + return ff_filter_frame(outlink, clone); } static int activate(AVFilterContext *ctx) diff --git a/libavfilter/avf_aphasemeter.c b/libavfilter/avf_aphasemeter.c index f497bc99697..be0b2fb70f5 100644 --- a/libavfilter/avf_aphasemeter.c +++ b/libavfilter/avf_aphasemeter.c @@ -213,8 +213,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) } if (s->do_video) { + AVFrame *clone; + s->out->pts = in->pts; - ff_filter_frame(outlink, av_frame_clone(s->out)); + clone = av_frame_clone(s->out); + if (!clone) + return AVERROR(ENOMEM); + ff_filter_frame(outlink, clone); } return ff_filter_frame(aoutlink, in); } diff --git a/libavfilter/avf_avectorscope.c b/libavfilter/avf_avectorscope.c index 0f53b36dfba..b288ff63ffb 100644 --- a/libavfilter/avf_avectorscope.c +++ b/libavfilter/avf_avectorscope.c @@ -238,6 +238,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) AudioVectorScopeContext *s = ctx->priv; const int hw = s->hw; const int hh = s->hh; + AVFrame *clone; unsigned x, y; unsigned prev_x = s->prev_x, prev_y = s->prev_y; double zoom = s->zoom; @@ -360,7 +361,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) s->prev_x = x, s->prev_y = y; av_frame_free(&insamples); - return ff_filter_frame(outlink, av_frame_clone(s->outpicref)); + clone = av_frame_clone(s->outpicref); + if (!clone) + return AVERROR(ENOMEM); + + return ff_filter_frame(outlink, clone); } static int activate(AVFilterContext *ctx) diff --git a/libavfilter/avf_concat.c b/libavfilter/avf_concat.c index 1d0c2de2905..28bd5407add 100644 --- a/libavfilter/avf_concat.c +++ b/libavfilter/avf_concat.c @@ -131,8 +131,21 @@ static int config_output(AVFilterLink *outlink) outlink->h = inlink->h; outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; outlink->format = inlink->format; + outlink->frame_rate = inlink->frame_rate; + + for (seg = 1; seg < cat->nb_segments; seg++) { + inlink = ctx->inputs[in_no + seg * ctx->nb_outputs]; + if (outlink->frame_rate.num != inlink->frame_rate.num || + outlink->frame_rate.den != inlink->frame_rate.den) { + av_log(ctx, AV_LOG_VERBOSE, + "Video inputs have different frame rates, output will be VFR\n"); + outlink->frame_rate = av_make_q(1, 0); + break; + } + } + for (seg = 1; seg < cat->nb_segments; seg++) { - inlink = ctx->inputs[in_no += ctx->nb_outputs]; + inlink = ctx->inputs[in_no + seg * ctx->nb_outputs]; if (!outlink->sample_aspect_ratio.num) outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; /* possible enhancement: unsafe mode, do not check */ diff --git a/libavfilter/avf_showcqt.c b/libavfilter/avf_showcqt.c index 875ba48cee2..cb0dca505f8 100644 --- a/libavfilter/avf_showcqt.c +++ b/libavfilter/avf_showcqt.c @@ -67,10 +67,10 @@ static const AVOption showcqt_options[] = { { "axis_h", "set axis height", OFFSET(axis_h), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS }, { "sono_h", "set sonogram height", OFFSET(sono_h), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS }, { "fullhd", "set fullhd size", OFFSET(fullhd), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS }, - { "sono_v", "set sonogram volume", OFFSET(sono_v), AV_OPT_TYPE_STRING, { .str = "16" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "volume", "set sonogram volume", OFFSET(sono_v), AV_OPT_TYPE_STRING, { .str = "16" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "bar_v", "set bargraph volume", OFFSET(bar_v), AV_OPT_TYPE_STRING, { .str = "sono_v" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "volume2", "set bargraph volume", OFFSET(bar_v), AV_OPT_TYPE_STRING, { .str = "sono_v" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "sono_v", "set sonogram volume", OFFSET(sono_v), AV_OPT_TYPE_STRING, { .str = "16" }, 0, 0, FLAGS }, + { "volume", "set sonogram volume", OFFSET(sono_v), AV_OPT_TYPE_STRING, { .str = "16" }, 0, 0, FLAGS }, + { "bar_v", "set bargraph volume", OFFSET(bar_v), AV_OPT_TYPE_STRING, { .str = "sono_v" }, 0, 0, FLAGS }, + { "volume2", "set bargraph volume", OFFSET(bar_v), AV_OPT_TYPE_STRING, { .str = "sono_v" }, 0, 0, FLAGS }, { "sono_g", "set sonogram gamma", OFFSET(sono_g), AV_OPT_TYPE_FLOAT, { .dbl = 3.0 }, 1.0, 7.0, FLAGS }, { "gamma", "set sonogram gamma", OFFSET(sono_g), AV_OPT_TYPE_FLOAT, { .dbl = 3.0 }, 1.0, 7.0, FLAGS }, { "bar_g", "set bargraph gamma", OFFSET(bar_g), AV_OPT_TYPE_FLOAT, { .dbl = 1.0 }, 1.0, 7.0, FLAGS }, @@ -82,13 +82,13 @@ static const AVOption showcqt_options[] = { { "basefreq", "set base frequency", OFFSET(basefreq), AV_OPT_TYPE_DOUBLE, { .dbl = BASEFREQ }, 10.0, 100000.0, FLAGS }, { "endfreq", "set end frequency", OFFSET(endfreq), AV_OPT_TYPE_DOUBLE, { .dbl = ENDFREQ }, 10.0, 100000.0, FLAGS }, { "coeffclamp", "set coeffclamp", OFFSET(coeffclamp), AV_OPT_TYPE_FLOAT, { .dbl = 1.0 }, 0.1, 10.0, FLAGS }, - { "tlength", "set tlength", OFFSET(tlength), AV_OPT_TYPE_STRING, { .str = TLENGTH }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "tlength", "set tlength", OFFSET(tlength), AV_OPT_TYPE_STRING, { .str = TLENGTH }, 0, 0, FLAGS }, { "count", "set transform count", OFFSET(count), AV_OPT_TYPE_INT, { .i64 = 6 }, 1, 30, FLAGS }, { "fcount", "set frequency count", OFFSET(fcount), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 10, FLAGS }, - { "fontfile", "set axis font file", OFFSET(fontfile), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "font", "set axis font", OFFSET(font), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "fontcolor", "set font color", OFFSET(fontcolor), AV_OPT_TYPE_STRING, { .str = FONTCOLOR }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "axisfile", "set axis image", OFFSET(axisfile), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "fontfile", "set axis font file", OFFSET(fontfile), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, + { "font", "set axis font", OFFSET(font), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, + { "fontcolor", "set font color", OFFSET(fontcolor), AV_OPT_TYPE_STRING, { .str = FONTCOLOR }, 0, 0, FLAGS }, + { "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" }, @@ -99,7 +99,7 @@ static const AVOption showcqt_options[] = { { "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" }, - { "cscheme", "set color scheme", OFFSET(cscheme), AV_OPT_TYPE_STRING, { .str = CSCHEME }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "cscheme", "set color scheme", OFFSET(cscheme), AV_OPT_TYPE_STRING, { .str = CSCHEME }, 0, 0, FLAGS }, { NULL } }; @@ -365,7 +365,7 @@ static AVFrame *alloc_frame_empty(enum AVPixelFormat format, int w, int h) out->format = format; out->width = w; out->height = h; - if (av_frame_get_buffer(out, 32) < 0) { + if (av_frame_get_buffer(out, 0) < 0) { av_frame_free(&out); return NULL; } diff --git a/libavfilter/avf_showfreqs.c b/libavfilter/avf_showfreqs.c index 8755ac3a9ee..645754ded36 100644 --- a/libavfilter/avf_showfreqs.c +++ b/libavfilter/avf_showfreqs.c @@ -195,7 +195,7 @@ static int config_output(AVFilterLink *outlink) if (!s->fft_data) return AVERROR(ENOMEM); s->avg_data = av_calloc(s->nb_channels, sizeof(*s->avg_data)); - if (!s->fft_data) + if (!s->avg_data) return AVERROR(ENOMEM); for (i = 0; i < s->nb_channels; i++) { s->fft_data[i] = av_calloc(s->win_size, sizeof(**s->fft_data)); @@ -475,6 +475,7 @@ static int activate(AVFilterContext *ctx) av_audio_fifo_write(s->fifo, (void **)in->extended_data, in->nb_samples); if (s->pts == AV_NOPTS_VALUE) s->pts = in->pts; + av_frame_free(&in); } if (av_audio_fifo_size(s->fifo) >= s->win_size) { diff --git a/libavfilter/avf_showspectrum.c b/libavfilter/avf_showspectrum.c index f175bf1cb54..e99f377fb01 100644 --- a/libavfilter/avf_showspectrum.c +++ b/libavfilter/avf_showspectrum.c @@ -695,17 +695,20 @@ static int draw_legend(AVFilterContext *ctx, int samples) inlink->channel_layout); text = av_asprintf("%d Hz | %s", inlink->sample_rate, chlayout_str); + if (!text) + return AVERROR(ENOMEM); drawtext(s->outpicref, 2, outlink->h - 10, "CREATED BY LIBAVFILTER", 0); drawtext(s->outpicref, outlink->w - 2 - strlen(text) * 10, outlink->h - 10, text, 0); + av_freep(&text); if (s->stop) { - char *text = av_asprintf("Zoom: %d Hz - %d Hz", s->start, s->stop); + text = av_asprintf("Zoom: %d Hz - %d Hz", s->start, s->stop); + if (!text) + return AVERROR(ENOMEM); drawtext(s->outpicref, outlink->w - 2 - strlen(text) * 10, 3, text, 0); av_freep(&text); } - av_freep(&text); - dst = s->outpicref->data[0] + (s->start_y - 1) * s->outpicref->linesize[0] + s->start_x - 1; for (x = 0; x < s->w + 1; x++) dst[x] = 200; @@ -766,6 +769,8 @@ static int draw_legend(AVFilterContext *ctx, int samples) for (x = 0; x < s->w && s->single_pic; x+=80) { float seconds = x * spp / inlink->sample_rate; char *units = get_time(ctx, seconds, x); + if (!units) + return AVERROR(ENOMEM); drawtext(s->outpicref, s->start_x + x - 4 * strlen(units), s->h + s->start_y + 6, units, 0); drawtext(s->outpicref, s->start_x + x - 4 * strlen(units), s->start_y - 12, units, 0); @@ -822,6 +827,8 @@ static int draw_legend(AVFilterContext *ctx, int samples) for (y = 0; y < s->h && s->single_pic; y+=40) { float seconds = y * spp / inlink->sample_rate; char *units = get_time(ctx, seconds, x); + if (!units) + return AVERROR(ENOMEM); drawtext(s->outpicref, s->start_x - 8 * strlen(units) - 4, s->start_y + y - 4, units, 0); av_free(units); @@ -1358,8 +1365,12 @@ static int plot_spectrum_column(AVFilterLink *inlink, AVFrame *insamples) s->xpos = 0; if (!s->single_pic && (s->sliding != FULLFRAME || s->xpos == 0)) { if (s->old_pts < outpicref->pts) { + AVFrame *clone; + if (s->legend) { char *units = get_time(ctx, insamples->pts /(float)inlink->sample_rate, x); + if (!units) + return AVERROR(ENOMEM); if (s->orientation == VERTICAL) { for (y = 0; y < 10; y++) { @@ -1384,7 +1395,10 @@ static int plot_spectrum_column(AVFilterLink *inlink, AVFrame *insamples) av_free(units); } s->old_pts = outpicref->pts; - ret = ff_filter_frame(outlink, av_frame_clone(s->outpicref)); + clone = av_frame_clone(s->outpicref); + if (!clone) + return AVERROR(ENOMEM); + ret = ff_filter_frame(outlink, clone); if (ret < 0) return ret; return 0; @@ -1420,7 +1434,8 @@ static int activate(AVFilterContext *ctx) } } - if (s->outpicref && av_audio_fifo_size(s->fifo) >= s->win_size) { + if (s->outpicref && (av_audio_fifo_size(s->fifo) >= s->win_size || + ff_outlink_get_status(inlink))) { AVFrame *fin = ff_get_audio_buffer(inlink, s->win_size); if (!fin) return AVERROR(ENOMEM); @@ -1448,7 +1463,7 @@ static int activate(AVFilterContext *ctx) av_frame_free(&fin); av_audio_fifo_drain(s->fifo, s->hop_size); - if (ret <= 0) + if (ret <= 0 && !ff_outlink_get_status(inlink)) return ret; } @@ -1479,15 +1494,18 @@ static int activate(AVFilterContext *ctx) } FF_FILTER_FORWARD_STATUS(inlink, outlink); - if (ff_outlink_frame_wanted(outlink) && av_audio_fifo_size(s->fifo) < s->win_size) { - ff_inlink_request_frame(inlink); + if (av_audio_fifo_size(s->fifo) >= s->win_size || + ff_outlink_get_status(inlink) == AVERROR_EOF) { + ff_filter_set_ready(ctx, 10); return 0; } - if (av_audio_fifo_size(s->fifo) >= s->win_size) { - ff_filter_set_ready(ctx, 10); + if (ff_outlink_frame_wanted(outlink) && av_audio_fifo_size(s->fifo) < s->win_size && + ff_outlink_get_status(inlink) != AVERROR_EOF) { + ff_inlink_request_frame(inlink); return 0; } + return FFERROR_NOT_READY; } diff --git a/libavfilter/avf_showvolume.c b/libavfilter/avf_showvolume.c index 548d73c14f0..7ab2fcfc4cb 100644 --- a/libavfilter/avf_showvolume.c +++ b/libavfilter/avf_showvolume.c @@ -82,7 +82,7 @@ static const AVOption showvolume_options[] = { { "t", "display channel names", OFFSET(draw_text), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, { "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"}, CHAR_MIN, CHAR_MAX, 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" }, diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index 93e866b79c8..394811916df 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -88,7 +88,7 @@ const char *avfilter_configuration(void) const char *avfilter_license(void) { #define LICENSE_PREFIX "libavfilter license: " - return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1; + return &LICENSE_PREFIX FFMPEG_LICENSE[sizeof(LICENSE_PREFIX) - 1]; } void ff_command_queue_pop(AVFilterContext *filter) @@ -467,24 +467,6 @@ static int ff_request_frame_to_filter(AVFilterLink *link) return ret; } -int ff_poll_frame(AVFilterLink *link) -{ - int i, min = INT_MAX; - - if (link->srcpad->poll_frame) - return link->srcpad->poll_frame(link); - - for (i = 0; i < link->src->nb_inputs; i++) { - int val; - if (!link->src->inputs[i]) - return AVERROR(EINVAL); - val = ff_poll_frame(link->src->inputs[i]); - min = FFMIN(min, val); - } - - return min; -} - static const char *const var_names[] = { "t", "n", @@ -801,9 +783,9 @@ void avfilter_free(AVFilterContext *filter) int ff_filter_get_nb_threads(AVFilterContext *ctx) { - if (ctx->nb_threads > 0) - return FFMIN(ctx->nb_threads, ctx->graph->nb_threads); - return ctx->graph->nb_threads; + if (ctx->nb_threads > 0) + return FFMIN(ctx->nb_threads, ctx->graph->nb_threads); + return ctx->graph->nb_threads; } static int process_options(AVFilterContext *ctx, AVDictionary **options, @@ -884,6 +866,19 @@ static int process_options(AVFilterContext *ctx, AVDictionary **options, return count; } +int ff_filter_process_command(AVFilterContext *ctx, const char *cmd, + const char *arg, char *res, int res_len, int flags) +{ + const AVOption *o; + + if (!ctx->filter->priv_class) + return 0; + o = av_opt_find2(ctx->priv, cmd, NULL, AV_OPT_FLAG_RUNTIME_PARAM | AV_OPT_FLAG_FILTERING_PARAM, AV_OPT_SEARCH_CHILDREN, NULL); + if (!o) + return AVERROR(ENOSYS); + return av_opt_set(ctx->priv, cmd, arg, 0); +} + int avfilter_init_dict(AVFilterContext *ctx, AVDictionary **options) { int ret = 0; diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index 9d70e7118be..49b4f7a939e 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -79,7 +79,7 @@ int avfilter_pad_count(const AVFilterPad *pads); * Get the name of an AVFilterPad. * * @param pads an array of AVFilterPads - * @param pad_idx index of the pad in the array it; is the caller's + * @param pad_idx index of the pad in the array; it is the caller's * responsibility to ensure the index is valid * * @return name of the pad_idx'th pad in pads @@ -947,7 +947,7 @@ AVFilterContext *avfilter_graph_get_filter(AVFilterGraph *graph, const char *nam /** * Create and add a filter instance into an existing graph. * The filter instance is created from the filter filt and inited - * with the parameters args and opaque. + * with the parameter args. opaque is currently ignored. * * In case of success put in *filt_ctx the pointer to the created * filter instance, otherwise set *filt_ctx to NULL. diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index a149f8fb6d6..2fe4f0b0f9b 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -419,8 +419,10 @@ static int can_merge_formats(AVFilterFormats *a_arg, av_freep(&ret); return 1; } else { - av_freep(&a->formats); - av_freep(&b->formats); + if (a) + av_freep(&a->formats); + if (b) + av_freep(&b->formats); av_freep(&a); av_freep(&b); return 0; diff --git a/libavfilter/boxblur.c b/libavfilter/boxblur.c index 4534b456d97..2287396f2e5 100644 --- a/libavfilter/boxblur.c +++ b/libavfilter/boxblur.c @@ -91,7 +91,7 @@ int ff_boxblur_eval_filter_params(AVFilterLink *inlink, NULL, NULL, NULL, NULL, NULL, 0, ctx); \ comp->radius = res; \ if (ret < 0) { \ - av_log(NULL, AV_LOG_ERROR, \ + av_log(ctx, AV_LOG_ERROR, \ "Error when evaluating " #comp " radius expression '%s'\n", expr); \ return ret; \ } diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c index f9b0b5e7d86..76a46f6678b 100644 --- a/libavfilter/buffersink.c +++ b/libavfilter/buffersink.c @@ -61,8 +61,6 @@ typedef struct BufferSinkContext { } BufferSinkContext; #define NB_ITEMS(list) (list ## _size / sizeof(*list)) -#define FIFO_INIT_SIZE 8 -#define FIFO_INIT_ELEMENT_SIZE sizeof(void *) int attribute_align_arg av_buffersink_get_frame(AVFilterContext *ctx, AVFrame *frame) { @@ -127,6 +125,7 @@ int attribute_align_arg av_buffersink_get_samples(AVFilterContext *ctx, return get_frame_internal(ctx, frame, 0, nb_samples); } +#if FF_API_NEXT AVBufferSinkParams *av_buffersink_params_alloc(void) { static const int pixel_fmts[] = { AV_PIX_FMT_NONE }; @@ -146,6 +145,7 @@ AVABufferSinkParams *av_abuffersink_params_alloc(void) return NULL; return params; } +#endif static av_cold int common_init(AVFilterContext *ctx) { @@ -201,20 +201,6 @@ MAKE_AVFILTERLINK_ACCESSOR(int , sample_rate ) MAKE_AVFILTERLINK_ACCESSOR(AVBufferRef * , hw_frames_ctx ) -static av_cold int vsink_init(AVFilterContext *ctx, void *opaque) -{ - BufferSinkContext *buf = ctx->priv; - AVBufferSinkParams *params = opaque; - int ret; - - if (params) { - if ((ret = av_opt_set_int_list(buf, "pix_fmts", params->pixel_fmts, AV_PIX_FMT_NONE, 0)) < 0) - return ret; - } - - return common_init(ctx); -} - #define CHECK_LIST_SIZE(field) \ if (buf->field ## _size % sizeof(*buf->field)) { \ av_log(ctx, AV_LOG_ERROR, "Invalid size for " #field ": %d, " \ @@ -244,23 +230,6 @@ static int vsink_query_formats(AVFilterContext *ctx) return 0; } -static av_cold int asink_init(AVFilterContext *ctx, void *opaque) -{ - BufferSinkContext *buf = ctx->priv; - AVABufferSinkParams *params = opaque; - int ret; - - if (params) { - if ((ret = av_opt_set_int_list(buf, "sample_fmts", params->sample_fmts, AV_SAMPLE_FMT_NONE, 0)) < 0 || - (ret = av_opt_set_int_list(buf, "sample_rates", params->sample_rates, -1, 0)) < 0 || - (ret = av_opt_set_int_list(buf, "channel_layouts", params->channel_layouts, -1, 0)) < 0 || - (ret = av_opt_set_int_list(buf, "channel_counts", params->channel_counts, -1, 0)) < 0 || - (ret = av_opt_set_int(buf, "all_channel_counts", params->all_channel_counts, 0)) < 0) - return ret; - } - return common_init(ctx); -} - static int asink_query_formats(AVFilterContext *ctx) { BufferSinkContext *buf = ctx->priv; @@ -336,42 +305,40 @@ AVFILTER_DEFINE_CLASS(abuffersink); static const AVFilterPad avfilter_vsink_buffer_inputs[] = { { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, }, { NULL } }; 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_opaque = vsink_init, - + .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, .query_formats = vsink_query_formats, - .activate = activate, - .inputs = avfilter_vsink_buffer_inputs, - .outputs = NULL, + .activate = activate, + .inputs = avfilter_vsink_buffer_inputs, + .outputs = NULL, }; static const AVFilterPad avfilter_asink_abuffer_inputs[] = { { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, }, { NULL } }; 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_opaque = asink_init, - + .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, .query_formats = asink_query_formats, - .activate = activate, - .inputs = avfilter_asink_abuffer_inputs, - .outputs = NULL, + .activate = activate, + .inputs = avfilter_asink_abuffer_inputs, + .outputs = NULL, }; diff --git a/libavfilter/buffersink.h b/libavfilter/buffersink.h index 3c846bb527f..2ec821c6857 100644 --- a/libavfilter/buffersink.h +++ b/libavfilter/buffersink.h @@ -59,6 +59,7 @@ int av_buffersink_get_frame_flags(AVFilterContext *ctx, AVFrame *frame, int flag */ #define AV_BUFFERSINK_FLAG_NO_REQUEST 2 +#if FF_API_NEXT /** * Struct to use for initializing a buffersink context. */ @@ -71,6 +72,7 @@ typedef struct AVBufferSinkParams { * * Must be freed with av_free(). */ +attribute_deprecated AVBufferSinkParams *av_buffersink_params_alloc(void); /** @@ -89,7 +91,9 @@ typedef struct AVABufferSinkParams { * * Must be freed with av_free(). */ +attribute_deprecated AVABufferSinkParams *av_abuffersink_params_alloc(void); +#endif /** * Set the frame size for an audio buffer sink. diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c index e0ff7e4dd84..bf30f54177a 100644 --- a/libavfilter/buffersrc.c +++ b/libavfilter/buffersrc.c @@ -25,9 +25,9 @@ #include +#include "libavutil/avassert.h" #include "libavutil/channel_layout.h" #include "libavutil/common.h" -#include "libavutil/fifo.h" #include "libavutil/frame.h" #include "libavutil/imgutils.h" #include "libavutil/internal.h" @@ -43,17 +43,17 @@ typedef struct BufferSourceContext { const AVClass *class; - AVFifoBuffer *fifo; AVRational time_base; ///< time_base to set in the output link AVRational frame_rate; ///< frame_rate to set in the output link unsigned nb_failed_requests; - unsigned warning_limit; /* video only */ int w, h; enum AVPixelFormat pix_fmt; AVRational pixel_aspect; +#if FF_API_SWS_PARAM_OPTION char *sws_param; +#endif AVBufferRef *hw_frames_ctx; @@ -64,7 +64,6 @@ typedef struct BufferSourceContext { uint64_t channel_layout; char *channel_layout_str; - int got_format_from_params; int eof; } BufferSourceContext; @@ -106,7 +105,6 @@ 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->got_format_from_params = 1; s->pix_fmt = param->format; } if (param->width > 0) @@ -126,7 +124,6 @@ int av_buffersrc_parameters_set(AVFilterContext *ctx, AVBufferSrcParameters *par break; case AVMEDIA_TYPE_AUDIO: if (param->format != AV_SAMPLE_FMT_NONE) { - s->got_format_from_params = 1; s->sample_fmt = param->format; } if (param->sample_rate > 0) @@ -229,11 +226,6 @@ static int av_buffersrc_add_frame_internal(AVFilterContext *ctx, } - if (!av_fifo_space(s->fifo) && - (ret = av_fifo_realloc2(s->fifo, av_fifo_size(s->fifo) + - sizeof(copy))) < 0) - return ret; - if (!(copy = av_frame_alloc())) return AVERROR(ENOMEM); @@ -247,14 +239,8 @@ static int av_buffersrc_add_frame_internal(AVFilterContext *ctx, } } - if ((ret = av_fifo_generic_write(s->fifo, ©, sizeof(copy), NULL)) < 0) { - if (refcounted) - av_frame_move_ref(frame, copy); - av_frame_free(©); - return ret; - } - - if ((ret = ctx->output_pads[0].request_frame(ctx->outputs[0])) < 0) + ret = ff_filter_frame(ctx->outputs[0], copy); + if (ret < 0) return ret; if ((flags & AV_BUFFERSRC_FLAG_PUSH)) { @@ -279,20 +265,22 @@ static av_cold int init_video(AVFilterContext *ctx) { BufferSourceContext *c = ctx->priv; - if (!(c->pix_fmt != AV_PIX_FMT_NONE || c->got_format_from_params) || !c->w || !c->h || + 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"); return AVERROR(EINVAL); } - if (!(c->fifo = av_fifo_alloc(sizeof(AVFrame*)))) - return AVERROR(ENOMEM); - - av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d pixfmt:%s tb:%d/%d fr:%d/%d sar:%d/%d sws_param:%s\n", + av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d pixfmt:%s tb:%d/%d fr:%d/%d sar:%d/%d\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, (char *)av_x_if_null(c->sws_param, "")); - c->warning_limit = 100; + c->pixel_aspect.num, c->pixel_aspect.den); + +#if FF_API_SWS_PARAM_OPTION + if (c->sws_param) + av_log(ctx, AV_LOG_WARNING, "sws_param option is deprecated and ignored\n"); +#endif + return 0; } @@ -314,7 +302,9 @@ 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 }, +#if FF_API_SWS_PARAM_OPTION { "sws_param", NULL, OFFSET(sws_param), AV_OPT_TYPE_STRING, .flags = V }, +#endif { NULL }, }; @@ -336,7 +326,7 @@ static av_cold int init_audio(AVFilterContext *ctx) BufferSourceContext *s = ctx->priv; int ret = 0; - if (!(s->sample_fmt != AV_SAMPLE_FMT_NONE || s->got_format_from_params)) { + if (s->sample_fmt == AV_SAMPLE_FMT_NONE) { av_log(ctx, AV_LOG_ERROR, "Sample format was not set or was invalid\n"); return AVERROR(EINVAL); } @@ -369,9 +359,6 @@ static av_cold int init_audio(AVFilterContext *ctx) return AVERROR(EINVAL); } - if (!(s->fifo = av_fifo_alloc(sizeof(AVFrame*)))) - return AVERROR(ENOMEM); - if (!s->time_base.num) s->time_base = (AVRational){1, s->sample_rate}; @@ -379,7 +366,6 @@ static av_cold int init_audio(AVFilterContext *ctx) "tb:%d/%d samplefmt:%s samplerate:%d chlayout:%s\n", s->time_base.num, s->time_base.den, av_get_sample_fmt_name(s->sample_fmt), s->sample_rate, s->channel_layout_str); - s->warning_limit = 100; return ret; } @@ -387,13 +373,7 @@ static av_cold int init_audio(AVFilterContext *ctx) static av_cold void uninit(AVFilterContext *ctx) { BufferSourceContext *s = ctx->priv; - while (s->fifo && av_fifo_size(s->fifo)) { - AVFrame *frame; - av_fifo_generic_read(s->fifo, &frame, sizeof(frame), NULL); - av_frame_free(&frame); - } av_buffer_unref(&s->hw_frames_ctx); - av_fifo_freep(&s->fifo); } static int query_formats(AVFilterContext *ctx) @@ -463,29 +443,11 @@ static int config_props(AVFilterLink *link) static int request_frame(AVFilterLink *link) { BufferSourceContext *c = link->src->priv; - AVFrame *frame; - int ret; - - if (!av_fifo_size(c->fifo)) { - if (c->eof) - return AVERROR_EOF; - c->nb_failed_requests++; - return AVERROR(EAGAIN); - } - av_fifo_generic_read(c->fifo, &frame, sizeof(frame), NULL); - - ret = ff_filter_frame(link, frame); - return ret; -} - -static int poll_frame(AVFilterLink *link) -{ - BufferSourceContext *c = link->src->priv; - int size = av_fifo_size(c->fifo); - if (!size && c->eof) + if (c->eof) return AVERROR_EOF; - return size/sizeof(AVFrame*); + c->nb_failed_requests++; + return AVERROR(EAGAIN); } static const AVFilterPad avfilter_vsrc_buffer_outputs[] = { @@ -493,7 +455,6 @@ static const AVFilterPad avfilter_vsrc_buffer_outputs[] = { .name = "default", .type = AVMEDIA_TYPE_VIDEO, .request_frame = request_frame, - .poll_frame = poll_frame, .config_props = config_props, }, { NULL } @@ -518,7 +479,6 @@ static const AVFilterPad avfilter_asrc_abuffer_outputs[] = { .name = "default", .type = AVMEDIA_TYPE_AUDIO, .request_frame = request_frame, - .poll_frame = poll_frame, .config_props = config_props, }, { NULL } diff --git a/libavfilter/convolution.h b/libavfilter/convolution.h new file mode 100644 index 00000000000..fc6aad58fd6 --- /dev/null +++ b/libavfilter/convolution.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2012-2013 Oka Motofumi (chikuzen.mo at gmail dot com) + * Copyright (c) 2015 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 + */ +#ifndef AVFILTER_CONVOLUTION_H +#define AVFILTER_CONVOLUTION_H +#include "avfilter.h" + +enum MatrixMode { + MATRIX_SQUARE, + MATRIX_ROW, + MATRIX_COLUMN, + MATRIX_NBMODES, +}; + +typedef struct ConvolutionContext { + const AVClass *class; + + char *matrix_str[4]; + float rdiv[4]; + float bias[4]; + int mode[4]; + float scale; + float delta; + int planes; + + int size[4]; + int depth; + int max; + int bpc; + int nb_planes; + int nb_threads; + int planewidth[4]; + int planeheight[4]; + int matrix[4][49]; + int matrix_length[4]; + int copy[4]; + + void (*setup[4])(int radius, const uint8_t *c[], const uint8_t *src, int stride, + int x, int width, int y, int height, int bpc); + void (*filter[4])(uint8_t *dst, int width, + float rdiv, float bias, const int *const matrix, + const uint8_t *c[], int peak, int radius, + int dstride, int stride); +} ConvolutionContext; + +void ff_convolution_init_x86(ConvolutionContext *s); +#endif diff --git a/libavfilter/dnn/Makefile b/libavfilter/dnn/Makefile new file mode 100644 index 00000000000..bb37298b588 --- /dev/null +++ b/libavfilter/dnn/Makefile @@ -0,0 +1,13 @@ +OBJS-$(CONFIG_DNN) += dnn/dnn_interface.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_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 + +OBJS-$(CONFIG_DNN) += $(DNN-OBJS-yes) diff --git a/libavfilter/dnn/dnn_backend_native.c b/libavfilter/dnn/dnn_backend_native.c new file mode 100644 index 00000000000..a685efb0923 --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native.c @@ -0,0 +1,342 @@ +/* + * 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" + +static DNNReturnType get_input_native(void *model, DNNData *input, const char *input_name) +{ + ConvolutionalNetwork *network = (ConvolutionalNetwork *)model; + + for (int i = 0; i < network->operands_num; ++i) { + DnnOperand *oprd = &network->operands[i]; + if (strcmp(oprd->name, input_name) == 0) { + if (oprd->type != DOT_INPUT) + return DNN_ERROR; + 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 DNN_SUCCESS; + } + } + + // do not find the input operand + return DNN_ERROR; +} + +static DNNReturnType set_input_output_native(void *model, DNNData *input, const char *input_name, const char **output_names, uint32_t nb_output) +{ + ConvolutionalNetwork *network = (ConvolutionalNetwork *)model; + DnnOperand *oprd = NULL; + + if (network->layers_num <= 0 || network->operands_num <= 0) + return DNN_ERROR; + + /* inputs */ + for (int i = 0; i < network->operands_num; ++i) { + oprd = &network->operands[i]; + if (strcmp(oprd->name, input_name) == 0) { + if (oprd->type != DOT_INPUT) + return DNN_ERROR; + break; + } + oprd = NULL; + } + + if (!oprd) + return DNN_ERROR; + + oprd->dims[0] = 1; + oprd->dims[1] = input->height; + oprd->dims[2] = input->width; + oprd->dims[3] = input->channels; + + av_freep(&oprd->data); + oprd->length = calculate_operand_data_length(oprd); + if (oprd->length <= 0) + return DNN_ERROR; + oprd->data = av_malloc(oprd->length); + if (!oprd->data) + return DNN_ERROR; + + input->data = oprd->data; + + /* outputs */ + network->nb_output = 0; + av_freep(&network->output_indexes); + network->output_indexes = av_mallocz_array(nb_output, sizeof(*network->output_indexes)); + if (!network->output_indexes) + return DNN_ERROR; + + for (uint32_t i = 0; i < nb_output; ++i) { + const char *output_name = output_names[i]; + for (int j = 0; j < network->operands_num; ++j) { + oprd = &network->operands[j]; + if (strcmp(oprd->name, output_name) == 0) { + network->output_indexes[network->nb_output++] = j; + break; + } + } + } + + if (network->nb_output != nb_output) + return DNN_ERROR; + + return DNN_SUCCESS; +} + +// 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) +{ + DNNModel *model = NULL; + char header_expected[] = "FFMPEGDNNNATIVE"; + char *buf; + size_t size; + int version, header_size, major_version_expected = 1; + ConvolutionalNetwork *network = 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 + */ + size = sizeof(header_expected); + buf = av_malloc(size); + if (!buf) { + goto fail; + } + + // size - 1 to skip the ending '\0' which is not saved in file + avio_get_str(model_file_context, size - 1, buf, size); + dnn_size = size - 1; + if (strncmp(buf, header_expected, size) != 0) { + av_freep(&buf); + goto fail; + } + av_freep(&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; + + network = av_mallocz(sizeof(ConvolutionalNetwork)); + if (!network){ + goto fail; + } + model->model = (void *)network; + + avio_seek(model_file_context, file_size - 8, SEEK_SET); + network->layers_num = (int32_t)avio_rl32(model_file_context); + network->operands_num = (int32_t)avio_rl32(model_file_context); + dnn_size += 8; + avio_seek(model_file_context, header_size, SEEK_SET); + + network->layers = av_mallocz(network->layers_num * sizeof(Layer)); + if (!network->layers){ + goto fail; + } + + network->operands = av_mallocz(network->operands_num * sizeof(DnnOperand)); + if (!network->operands){ + goto fail; + } + + for (layer = 0; layer < network->layers_num; ++layer){ + layer_type = (int32_t)avio_rl32(model_file_context); + dnn_size += 4; + + if (layer_type >= DLT_COUNT) { + goto fail; + } + + network->layers[layer].type = layer_type; + parsed_size = layer_funcs[layer_type].pf_load(&network->layers[layer], model_file_context, file_size, network->operands_num); + if (!parsed_size) { + goto fail; + } + dnn_size += parsed_size; + } + + for (int32_t i = 0; i < network->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 >= network->operands_num) { + goto fail; + } + + oprd = &network->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; + } + + oprd->isNHWC = 1; + } + + avio_closep(&model_file_context); + + if (dnn_size != file_size){ + ff_dnn_free_model_native(&model); + return NULL; + } + + model->set_input_output = &set_input_output_native; + model->get_input = &get_input_native; + + return model; + +fail: + ff_dnn_free_model_native(&model); + avio_closep(&model_file_context); + return NULL; +} + +DNNReturnType ff_dnn_execute_model_native(const DNNModel *model, DNNData *outputs, uint32_t nb_output) +{ + ConvolutionalNetwork *network = (ConvolutionalNetwork *)model->model; + int32_t layer; + uint32_t nb = FFMIN(nb_output, network->nb_output); + + if (network->layers_num <= 0 || network->operands_num <= 0) + return DNN_ERROR; + if (!network->operands[0].data) + return DNN_ERROR; + + for (layer = 0; layer < network->layers_num; ++layer){ + DNNLayerType layer_type = network->layers[layer].type; + layer_funcs[layer_type].pf_exec(network->operands, + network->layers[layer].input_operand_indexes, + network->layers[layer].output_operand_index, + network->layers[layer].params); + } + + for (uint32_t i = 0; i < nb; ++i) { + DnnOperand *oprd = &network->operands[network->output_indexes[i]]; + outputs[i].data = oprd->data; + outputs[i].height = oprd->dims[1]; + outputs[i].width = oprd->dims[2]; + outputs[i].channels = oprd->dims[3]; + outputs[i].dt = oprd->data_type; + } + + return DNN_SUCCESS; +} + +int32_t 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 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) +{ + ConvolutionalNetwork *network; + ConvolutionalParams *conv_params; + int32_t layer; + + if (*model) + { + if ((*model)->model) { + network = (ConvolutionalNetwork *)(*model)->model; + if (network->layers) { + for (layer = 0; layer < network->layers_num; ++layer){ + if (network->layers[layer].type == DLT_CONV2D){ + conv_params = (ConvolutionalParams *)network->layers[layer].params; + av_freep(&conv_params->kernel); + av_freep(&conv_params->biases); + } + av_freep(&network->layers[layer].params); + } + av_freep(&network->layers); + } + + if (network->operands) { + for (uint32_t operand = 0; operand < network->operands_num; ++operand) + av_freep(&network->operands[operand].data); + av_freep(&network->operands); + } + + av_freep(&network->output_indexes); + av_freep(&network); + } + av_freep(model); + } +} diff --git a/libavfilter/dnn/dnn_backend_native.h b/libavfilter/dnn/dnn_backend_native.h new file mode 100644 index 00000000000..62191ffe883 --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native.h @@ -0,0 +1,127 @@ +/* + * 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" + +/** + * 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_COUNT +} DNNLayerType; + +typedef enum {DOT_INPUT = 1, DOT_OUTPUT = 2, DOT_INTERMEDIATE = DOT_INPUT | DOT_OUTPUT} DNNOperandType; + +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; + +// Represents simple feed-forward convolutional network. +typedef struct ConvolutionalNetwork{ + Layer *layers; + int32_t layers_num; + DnnOperand *operands; + int32_t operands_num; + int32_t *output_indexes; + uint32_t nb_output; +} ConvolutionalNetwork; + +DNNModel *ff_dnn_load_model_native(const char *model_filename); + +DNNReturnType ff_dnn_execute_model_native(const DNNModel *model, DNNData *outputs, uint32_t nb_output); + +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 calculate_operand_data_length(const DnnOperand *oprd); +int32_t calculate_operand_dims_count(const DnnOperand *oprd); +#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_conv2d.c b/libavfilter/dnn/dnn_backend_native_layer_conv2d.c new file mode 100644 index 00000000000..a2202e40738 --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native_layer_conv2d.c @@ -0,0 +1,174 @@ +/* + * 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 "dnn_backend_native_layer_conv2d.h" + +#define CLAMP_TO_EDGE(x, w) ((x) < 0 ? 0 : ((x) >= (w) ? (w - 1) : (x))) + +int 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(kernel_size * sizeof(float)); + 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(conv_params->output_num * sizeof(float)); + 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; +} + +int dnn_execute_layer_conv2d(DnnOperand *operands, const int32_t *input_operand_indexes, + int32_t output_operand_index, const void *parameters) +{ + 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 ConvolutionalParams *conv_params = (const ConvolutionalParams *)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; + + DnnOperand *output_operand = &operands[output_operand_index]; + output_operand->dims[0] = number; + 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_index].data_type; + output_operand->length = calculate_operand_data_length(output_operand); + if (output_operand->length <= 0) + return -1; + output_operand->data = av_realloc(output_operand->data, output_operand->length); + if (!output_operand->data) + return -1; + output = output_operand->data; + + av_assert0(channel == conv_params->input_num); + + for (int y = pad_size; y < height - pad_size; ++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 0; +} diff --git a/libavfilter/dnn_backend_native.h b/libavfilter/dnn/dnn_backend_native_layer_conv2d.h similarity index 57% rename from libavfilter/dnn_backend_native.h rename to libavfilter/dnn/dnn_backend_native_layer_conv2d.h index 59179557336..eeb15fdf018 100644 --- a/libavfilter/dnn_backend_native.h +++ b/libavfilter/dnn/dnn_backend_native_layer_conv2d.h @@ -18,57 +18,25 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -/** - * @file - * DNN inference functions interface for native backend. - */ - - -#ifndef AVFILTER_DNN_BACKEND_NATIVE_H -#define AVFILTER_DNN_BACKEND_NATIVE_H +#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_CONV2D_H +#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_CONV2D_H -#include "dnn_interface.h" -#include "libavformat/avio.h" - -typedef enum {INPUT, CONV, DEPTH_TO_SPACE} DNNLayerType; +#include "dnn_backend_native.h" typedef enum {RELU, TANH, SIGMOID, NONE, LEAKY_RELU} DNNActivationFunc; - typedef enum {VALID, SAME, SAME_CLAMP_TO_EDGE} DNNConvPaddingParam; -typedef struct Layer{ - DNNLayerType type; - float *output; - void *params; -} Layer; - typedef struct ConvolutionalParams{ int32_t input_num, output_num, kernel_size; DNNActivationFunc activation; DNNConvPaddingParam padding_method; int32_t dilation; + int32_t has_bias; float *kernel; float *biases; } ConvolutionalParams; -typedef struct InputParams{ - int height, width, channels; -} InputParams; - -typedef struct DepthToSpaceParams{ - int block_size; -} DepthToSpaceParams; - -// Represents simple feed-forward convolutional network. -typedef struct ConvolutionalNetwork{ - Layer *layers; - int32_t layers_num; -} ConvolutionalNetwork; - -DNNModel *ff_dnn_load_model_native(const char *model_filename); - -DNNReturnType ff_dnn_execute_model_native(const DNNModel *model, DNNData *outputs, uint32_t nb_output); - -void ff_dnn_free_model_native(DNNModel **model); - +int dnn_load_layer_conv2d(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); +int dnn_execute_layer_conv2d(DnnOperand *operands, const int32_t *input_operand_indexes, + int32_t output_operand_index, const void *parameters); #endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_depth2space.c b/libavfilter/dnn/dnn_backend_native_layer_depth2space.c new file mode 100644 index 00000000000..2c8bddf23da --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native_layer_depth2space.c @@ -0,0 +1,99 @@ +/* + * 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_depth2space.h" + +int 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 dnn_execute_layer_depth2space(DnnOperand *operands, const int32_t *input_operand_indexes, + int32_t output_operand_index, const void *parameters) +{ + float *output; + const DepthToSpaceParams *params = (const DepthToSpaceParams *)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 = calculate_operand_data_length(output_operand); + if (output_operand->length <= 0) + return -1; + output_operand->data = av_realloc(output_operand->data, output_operand->length); + if (!output_operand->data) + return -1; + 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 new file mode 100644 index 00000000000..b2901e0141a --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native_layer_depth2space.h @@ -0,0 +1,41 @@ +/* + * 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; + +int dnn_load_layer_depth2space(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); +int dnn_execute_layer_depth2space(DnnOperand *operands, const int32_t *input_operand_indexes, + int32_t output_operand_index, const void *parameters); + +#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_mathbinary.c b/libavfilter/dnn/dnn_backend_native_layer_mathbinary.c new file mode 100644 index 00000000000..dd42c329a9d --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native_layer_mathbinary.c @@ -0,0 +1,181 @@ +/* + * 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 "libavutil/avassert.h" +#include "dnn_backend_native_layer_mathbinary.h" + +int dnn_load_layer_math_binary(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) +{ + DnnLayerMathBinaryParams *params; + int dnn_size = 0; + int input_index = 0; + params = av_malloc(sizeof(*params)); + if (!params) + return 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; + layer->params = params; + + if (layer->output_operand_index >= operands_num) { + return 0; + } + + return dnn_size; +} + +int dnn_execute_layer_math_binary(DnnOperand *operands, const int32_t *input_operand_indexes, + int32_t output_operand_index, const void *parameters) +{ + const DnnOperand *input = &operands[input_operand_indexes[0]]; + DnnOperand *output = &operands[output_operand_index]; + const DnnLayerMathBinaryParams *params = (const DnnLayerMathBinaryParams *)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 = calculate_operand_data_length(output); + if (output->length <= 0) + return DNN_ERROR; + output->data = av_realloc(output->data, output->length); + if (!output->data) + return DNN_ERROR; + + dims_count = calculate_operand_dims_count(output); + src = input->data; + dst = output->data; + + switch (params->bin_op) { + case DMBO_SUB: + if (params->input0_broadcast) { + for (int i = 0; i < dims_count; ++i) { + dst[i] = params->v - src[i]; + } + } else if (params->input1_broadcast) { + for (int i = 0; i < dims_count; ++i) { + dst[i] = 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] = src[i] - src1[i]; + } + } + return 0; + case DMBO_ADD: + if (params->input0_broadcast || params->input1_broadcast) { + for (int i = 0; i < dims_count; ++i) { + dst[i] = 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] = src[i] + src1[i]; + } + } + return 0; + case DMBO_MUL: + if (params->input0_broadcast || params->input1_broadcast) { + for (int i = 0; i < dims_count; ++i) { + dst[i] = 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] = src[i] * src1[i]; + } + } + return 0; + case DMBO_REALDIV: + if (params->input0_broadcast) { + for (int i = 0; i < dims_count; ++i) { + dst[i] = params->v / src[i]; + } + } else if (params->input1_broadcast) { + for (int i = 0; i < dims_count; ++i) { + dst[i] = 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] = src[i] / src1[i]; + } + } + return 0; + case DMBO_MINIMUM: + if (params->input0_broadcast || params->input1_broadcast) { + for (int i = 0; i < dims_count; ++i) { + dst[i] = FFMIN(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] = FFMIN(src[i], src1[i]); + } + } + return 0; + default: + return -1; + } +} diff --git a/libavfilter/dnn/dnn_backend_native_layer_mathbinary.h b/libavfilter/dnn/dnn_backend_native_layer_mathbinary.h new file mode 100644 index 00000000000..0acf3b0ea0b --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native_layer_mathbinary.h @@ -0,0 +1,53 @@ +/* + * 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_COUNT +} DNNMathBinaryOperation; + +typedef struct DnnLayerMathBinaryParams{ + DNNMathBinaryOperation bin_op; + int input0_broadcast; + int input1_broadcast; + float v; +} DnnLayerMathBinaryParams; + +int dnn_load_layer_math_binary(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); +int dnn_execute_layer_math_binary(DnnOperand *operands, const int32_t *input_operand_indexes, + int32_t output_operand_index, const void *parameters); + +#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_mathunary.c b/libavfilter/dnn/dnn_backend_native_layer_mathunary.c new file mode 100644 index 00000000000..6f02faef787 --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native_layer_mathunary.c @@ -0,0 +1,86 @@ +/* + * 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 "libavutil/avassert.h" +#include "dnn_backend_native_layer_mathunary.h" + +int 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 dnn_execute_layer_math_unary(DnnOperand *operands, const int32_t *input_operand_indexes, + int32_t output_operand_index, const void *parameters) +{ + const DnnOperand *input = &operands[input_operand_indexes[0]]; + DnnOperand *output = &operands[output_operand_index]; + const DnnLayerMathUnaryParams *params = (const DnnLayerMathUnaryParams *)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 = calculate_operand_data_length(output); + if (output->length <= 0) + return DNN_ERROR; + output->data = av_realloc(output->data, output->length); + if (!output->data) + return DNN_ERROR; + + dims_count = 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; + default: + return -1; + } +} diff --git a/libavfilter/dnn/dnn_backend_native_layer_mathunary.h b/libavfilter/dnn/dnn_backend_native_layer_mathunary.h new file mode 100644 index 00000000000..a9a8a0d3060 --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native_layer_mathunary.h @@ -0,0 +1,45 @@ +/* + * 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_COUNT +} DNNMathUnaryOperation; + +typedef struct DnnLayerMathUnaryParams{ + DNNMathUnaryOperation un_op; +} DnnLayerMathUnaryParams; + +int dnn_load_layer_math_unary(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); +int dnn_execute_layer_math_unary(DnnOperand *operands, const int32_t *input_operand_indexes, + int32_t output_operand_index, const void *parameters); + +#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_maximum.c b/libavfilter/dnn/dnn_backend_native_layer_maximum.c new file mode 100644 index 00000000000..cdddfdd87b2 --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native_layer_maximum.c @@ -0,0 +1,80 @@ +/* + * 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 "libavutil/avassert.h" +#include "dnn_backend_native_layer_maximum.h" + +int 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 dnn_execute_layer_maximum(DnnOperand *operands, const int32_t *input_operand_indexes, + int32_t output_operand_index, const void *parameters) +{ + const DnnOperand *input = &operands[input_operand_indexes[0]]; + DnnOperand *output = &operands[output_operand_index]; + const DnnLayerMaximumParams *params = (const DnnLayerMaximumParams *)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 = calculate_operand_data_length(output); + if (output->length <= 0) + return DNN_ERROR; + output->data = av_realloc(output->data, output->length); + if (!output->data) + return DNN_ERROR; + + dims_count = 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_maximum.h b/libavfilter/dnn/dnn_backend_native_layer_maximum.h new file mode 100644 index 00000000000..c049c63fd88 --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native_layer_maximum.h @@ -0,0 +1,44 @@ +/* + * 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 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 "libavformat/avio.h" +#include "dnn_backend_native.h" + +typedef struct DnnLayerMaximumParams{ + union { + uint32_t u32; + float y; + }val; +} DnnLayerMaximumParams; + +int dnn_load_layer_maximum(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); +int dnn_execute_layer_maximum(DnnOperand *operands, const int32_t *input_operand_indexes, + int32_t output_operand_index, const void *parameters); + +#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_pad.c b/libavfilter/dnn/dnn_backend_native_layer_pad.c new file mode 100644 index 00000000000..feaab001e89 --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native_layer_pad.c @@ -0,0 +1,264 @@ +/* + * 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 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 dnn_execute_layer_pad(DnnOperand *operands, const int32_t *input_operand_indexes, + int32_t output_operand_index, const void *parameters) +{ + int32_t before_paddings; + int32_t after_paddings; + float* output; + const LayerPadParams *params = (const LayerPadParams *)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 = calculate_operand_data_length(output_operand); + if (output_operand->length <= 0) + return -1; + output_operand->data = av_realloc(output_operand->data, output_operand->length); + if (!output_operand->data) + return -1; + 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 new file mode 100644 index 00000000000..18e05bdd5c1 --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native_layer_pad.h @@ -0,0 +1,43 @@ +/* + * 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 dnn_load_layer_pad(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); +int dnn_execute_layer_pad(DnnOperand *operands, const int32_t *input_operand_indexes, + int32_t output_operand_index, const void *parameters); + +#endif diff --git a/libavfilter/dnn/dnn_backend_native_layers.c b/libavfilter/dnn/dnn_backend_native_layers.c new file mode 100644 index 00000000000..70f9a5f958c --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native_layers.c @@ -0,0 +1,38 @@ +/* + * 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" + +LayerFunc layer_funcs[DLT_COUNT] = { + {NULL, NULL}, + {dnn_execute_layer_conv2d, dnn_load_layer_conv2d}, + {dnn_execute_layer_depth2space, dnn_load_layer_depth2space}, + {dnn_execute_layer_pad, dnn_load_layer_pad}, + {dnn_execute_layer_maximum, dnn_load_layer_maximum}, + {dnn_execute_layer_math_binary, dnn_load_layer_math_binary}, + {dnn_execute_layer_math_unary, dnn_load_layer_math_unary}, +}; diff --git a/libavfilter/dnn/dnn_backend_native_layers.h b/libavfilter/dnn/dnn_backend_native_layers.h new file mode 100644 index 00000000000..b696e9c6fa7 --- /dev/null +++ b/libavfilter/dnn/dnn_backend_native_layers.h @@ -0,0 +1,38 @@ +/* + * 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 + */ + +#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYERS_H +#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYERS_H + +#include +#include "dnn_backend_native.h" + +typedef int (*LAYER_EXEC_FUNC)(DnnOperand *operands, const int32_t *input_operand_indexes, + int32_t output_operand_index, const void *parameters); +typedef int (*LAYER_LOAD_FUNC)(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); + +typedef struct LayerFunc { + LAYER_EXEC_FUNC pf_exec; + LAYER_LOAD_FUNC pf_load; +}LayerFunc; + +extern LayerFunc layer_funcs[DLT_COUNT]; + +#endif diff --git a/libavfilter/dnn_backend_tf.c b/libavfilter/dnn/dnn_backend_tf.c similarity index 82% rename from libavfilter/dnn_backend_tf.c rename to libavfilter/dnn/dnn_backend_tf.c index ba959ae3a26..9ceca5cea09 100644 --- a/libavfilter/dnn_backend_tf.c +++ b/libavfilter/dnn/dnn_backend_tf.c @@ -25,8 +25,12 @@ #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 "dnn_backend_native_layer_pad.h" +#include "dnn_backend_native_layer_maximum.h" #include @@ -79,7 +83,7 @@ static TF_Buffer *read_graph(const char *model_filename) return graph_buf; } -static TF_Tensor *allocate_input_tensor(const DNNInputData *input) +static TF_Tensor *allocate_input_tensor(const DNNData *input) { TF_DataType dt; size_t size; @@ -91,7 +95,7 @@ static TF_Tensor *allocate_input_tensor(const DNNInputData *input) break; case DNN_UINT8: dt = TF_UINT8; - size = sizeof(char); + size = 1; break; default: av_assert0(!"should not reach here"); @@ -101,7 +105,38 @@ static TF_Tensor *allocate_input_tensor(const DNNInputData *input) input_dims[1] * input_dims[2] * input_dims[3] * size); } -static DNNReturnType set_input_output_tf(void *model, DNNInputData *input, const char *input_name, const char **output_names, uint32_t nb_output) +static DNNReturnType get_input_tf(void *model, DNNData *input, const char *input_name) +{ + TFModel *tf_model = (TFModel *)model; + TF_Status *status; + int64_t dims[4]; + + TF_Output tf_output; + tf_output.oper = TF_GraphOperationByName(tf_model->graph, input_name); + if (!tf_output.oper) + return DNN_ERROR; + + tf_output.index = 0; + input->dt = TF_OperationOutputType(tf_output); + + status = TF_NewStatus(); + TF_GraphGetTensorShape(tf_model->graph, tf_output, dims, 4, status); + if (TF_GetCode(status) != TF_OK){ + TF_DeleteStatus(status); + return DNN_ERROR; + } + TF_DeleteStatus(status); + + // currently only NHWC is supported + av_assert0(dims[0] == 1); + input->height = dims[1]; + input->width = dims[2]; + input->channels = dims[3]; + + return DNN_SUCCESS; +} + +static DNNReturnType set_input_output_tf(void *model, DNNData *input, const char *input_name, const char **output_names, uint32_t nb_output) { TFModel *tf_model = (TFModel *)model; TF_SessionOptions *sess_opts; @@ -347,23 +382,8 @@ static DNNReturnType add_depth_to_space_layer(TFModel *tf_model, TF_Operation ** return DNN_SUCCESS; } -static int calculate_pad(const ConvolutionalNetwork *conv_network) -{ - ConvolutionalParams *params; - int32_t layer; - int pad = 0; - - for (layer = 0; layer < conv_network->layers_num; ++layer){ - if (conv_network->layers[layer].type == CONV){ - params = (ConvolutionalParams *)conv_network->layers[layer].params; - pad += params->kernel_size >> 1; - } - } - - return pad; -} - -static DNNReturnType add_pad_op(TFModel *tf_model, TF_Operation **cur_op, const int32_t pad) +static DNNReturnType add_pad_layer(TFModel *tf_model, TF_Operation **cur_op, + LayerPadParams *params, const int layer) { TF_Operation *op; TF_Tensor *tensor; @@ -372,16 +392,21 @@ static DNNReturnType add_pad_op(TFModel *tf_model, TF_Operation **cur_op, const int32_t *pads; int64_t pads_shape[] = {4, 2}; - input.index = 0; + char name_buffer[NAME_BUFFER_SIZE]; + snprintf(name_buffer, NAME_BUFFER_SIZE, "pad%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "Const", "pads"); + 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] = 0; pads[1] = 0; - pads[2] = pad; pads[3] = pad; - pads[4] = pad; pads[5] = pad; - pads[6] = 0; pads[7] = 0; + 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){ return DNN_ERROR; @@ -393,6 +418,7 @@ static DNNReturnType add_pad_op(TFModel *tf_model, TF_Operation **cur_op, const 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); @@ -407,6 +433,48 @@ static DNNReturnType add_pad_op(TFModel *tf_model, TF_Operation **cur_op, const return DNN_SUCCESS; } +static DNNReturnType add_maximum_layer(TFModel *tf_model, TF_Operation **cur_op, + DnnLayerMaximumParams *params, const int layer) +{ + 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){ + return DNN_ERROR; + } + op = TF_FinishOperation(op_desc, tf_model->status); + if (TF_GetCode(tf_model->status) != TF_OK){ + return DNN_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){ + return DNN_ERROR; + } + + return DNN_SUCCESS; +} + static DNNReturnType load_native_model(TFModel *tf_model, const char *model_filename) { int32_t layer; @@ -418,7 +486,6 @@ static DNNReturnType load_native_model(TFModel *tf_model, const char *model_file int32_t *transpose_perm; int64_t transpose_perm_shape[] = {4}; int64_t input_shape[] = {1, -1, -1, -1}; - int32_t pad; DNNReturnType layer_add_res; DNNModel *native_model = NULL; ConvolutionalNetwork *conv_network; @@ -429,7 +496,6 @@ static DNNReturnType load_native_model(TFModel *tf_model, const char *model_file } conv_network = (ConvolutionalNetwork *)native_model->model; - pad = calculate_pad(conv_network); tf_model->graph = TF_NewGraph(); tf_model->status = TF_NewStatus(); @@ -448,10 +514,6 @@ static DNNReturnType load_native_model(TFModel *tf_model, const char *model_file CLEANUP_ON_ERROR(tf_model); } - if (add_pad_op(tf_model, &op, pad) != DNN_SUCCESS){ - CLEANUP_ON_ERROR(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)); @@ -468,17 +530,25 @@ static DNNReturnType load_native_model(TFModel *tf_model, const char *model_file for (layer = 0; layer < conv_network->layers_num; ++layer){ switch (conv_network->layers[layer].type){ - case INPUT: + case DLT_INPUT: layer_add_res = DNN_SUCCESS; break; - case CONV: + case DLT_CONV2D: layer_add_res = add_conv_layer(tf_model, transpose_op, &op, (ConvolutionalParams *)conv_network->layers[layer].params, layer); break; - case DEPTH_TO_SPACE: + case DLT_DEPTH_TO_SPACE: layer_add_res = add_depth_to_space_layer(tf_model, &op, (DepthToSpaceParams *)conv_network->layers[layer].params, layer); break; + case DLT_MIRROR_PAD: + layer_add_res = add_pad_layer(tf_model, &op, + (LayerPadParams *)conv_network->layers[layer].params, layer); + break; + case DLT_MAXIMUM: + layer_add_res = add_maximum_layer(tf_model, &op, + (DnnLayerMaximumParams *)conv_network->layers[layer].params, layer); + break; default: CLEANUP_ON_ERROR(tf_model); } @@ -490,6 +560,7 @@ static DNNReturnType load_native_model(TFModel *tf_model, const char *model_file 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){ @@ -528,6 +599,7 @@ DNNModel *ff_dnn_load_model_tf(const char *model_filename) model->model = (void *)tf_model; model->set_input_output = &set_input_output_tf; + model->get_input = &get_input_tf; return model; } @@ -563,6 +635,7 @@ DNNReturnType ff_dnn_execute_model_tf(const DNNModel *model, DNNData *outputs, u outputs[i].width = TF_Dim(tf_model->output_tensors[i], 2); outputs[i].channels = TF_Dim(tf_model->output_tensors[i], 3); outputs[i].data = TF_TensorData(tf_model->output_tensors[i]); + outputs[i].dt = TF_TensorType(tf_model->output_tensors[i]); } return DNN_SUCCESS; diff --git a/libavfilter/dnn_backend_tf.h b/libavfilter/dnn/dnn_backend_tf.h similarity index 91% rename from libavfilter/dnn_backend_tf.h rename to libavfilter/dnn/dnn_backend_tf.h index 07877b1209d..3e4508912e6 100644 --- a/libavfilter/dnn_backend_tf.h +++ b/libavfilter/dnn/dnn_backend_tf.h @@ -24,10 +24,10 @@ */ -#ifndef AVFILTER_DNN_BACKEND_TF_H -#define AVFILTER_DNN_BACKEND_TF_H +#ifndef AVFILTER_DNN_DNN_BACKEND_TF_H +#define AVFILTER_DNN_DNN_BACKEND_TF_H -#include "dnn_interface.h" +#include "../dnn_interface.h" DNNModel *ff_dnn_load_model_tf(const char *model_filename); diff --git a/libavfilter/dnn_interface.c b/libavfilter/dnn/dnn_interface.c similarity index 98% rename from libavfilter/dnn_interface.c rename to libavfilter/dnn/dnn_interface.c index 86fc2830246..62da55f43e8 100644 --- a/libavfilter/dnn_interface.c +++ b/libavfilter/dnn/dnn_interface.c @@ -23,7 +23,7 @@ * Implements DNN module initialization with specified backend. */ -#include "dnn_interface.h" +#include "../dnn_interface.h" #include "dnn_backend_native.h" #include "dnn_backend_tf.h" #include "libavutil/mem.h" diff --git a/libavfilter/dnn_backend_native.c b/libavfilter/dnn_backend_native.c deleted file mode 100644 index 82e900bd8c7..00000000000 --- a/libavfilter/dnn_backend_native.c +++ /dev/null @@ -1,389 +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" - -static DNNReturnType set_input_output_native(void *model, DNNInputData *input, const char *input_name, const char **output_names, uint32_t nb_output) -{ - ConvolutionalNetwork *network = (ConvolutionalNetwork *)model; - InputParams *input_params; - ConvolutionalParams *conv_params; - DepthToSpaceParams *depth_to_space_params; - int cur_width, cur_height, cur_channels; - int32_t layer; - - if (network->layers_num <= 0 || network->layers[0].type != INPUT){ - return DNN_ERROR; - } - else{ - input_params = (InputParams *)network->layers[0].params; - input_params->width = cur_width = input->width; - input_params->height = cur_height = input->height; - input_params->channels = cur_channels = input->channels; - if (input->data){ - av_freep(&input->data); - } - av_assert0(input->dt == DNN_FLOAT); - network->layers[0].output = input->data = av_malloc(cur_height * cur_width * cur_channels * sizeof(float)); - if (!network->layers[0].output){ - return DNN_ERROR; - } - } - - for (layer = 1; layer < network->layers_num; ++layer){ - switch (network->layers[layer].type){ - case CONV: - conv_params = (ConvolutionalParams *)network->layers[layer].params; - if (conv_params->input_num != cur_channels){ - return DNN_ERROR; - } - cur_channels = conv_params->output_num; - - if (conv_params->padding_method == VALID) { - int pad_size = (conv_params->kernel_size - 1) * conv_params->dilation; - cur_height -= pad_size; - cur_width -= pad_size; - } - break; - case DEPTH_TO_SPACE: - depth_to_space_params = (DepthToSpaceParams *)network->layers[layer].params; - if (cur_channels % (depth_to_space_params->block_size * depth_to_space_params->block_size) != 0){ - return DNN_ERROR; - } - cur_channels = cur_channels / (depth_to_space_params->block_size * depth_to_space_params->block_size); - cur_height *= depth_to_space_params->block_size; - cur_width *= depth_to_space_params->block_size; - break; - default: - return DNN_ERROR; - } - if (network->layers[layer].output){ - av_freep(&network->layers[layer].output); - } - - if (cur_height <= 0 || cur_width <= 0) - return DNN_ERROR; - - network->layers[layer].output = av_malloc(cur_height * cur_width * cur_channels * sizeof(float)); - if (!network->layers[layer].output){ - return DNN_ERROR; - } - } - - return DNN_SUCCESS; -} - -// 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) -{ - DNNModel *model = NULL; - ConvolutionalNetwork *network = NULL; - AVIOContext *model_file_context; - int file_size, dnn_size, kernel_size, i; - int32_t layer; - DNNLayerType layer_type; - ConvolutionalParams *conv_params; - DepthToSpaceParams *depth_to_space_params; - - model = av_malloc(sizeof(DNNModel)); - if (!model){ - return NULL; - } - - if (avio_open(&model_file_context, model_filename, AVIO_FLAG_READ) < 0){ - av_freep(&model); - return NULL; - } - file_size = avio_size(model_file_context); - - network = av_malloc(sizeof(ConvolutionalNetwork)); - if (!network){ - avio_closep(&model_file_context); - av_freep(&model); - return NULL; - } - model->model = (void *)network; - - network->layers_num = 1 + (int32_t)avio_rl32(model_file_context); - dnn_size = 4; - - network->layers = av_malloc(network->layers_num * sizeof(Layer)); - if (!network->layers){ - av_freep(&network); - avio_closep(&model_file_context); - av_freep(&model); - return NULL; - } - - for (layer = 0; layer < network->layers_num; ++layer){ - network->layers[layer].output = NULL; - network->layers[layer].params = NULL; - } - network->layers[0].type = INPUT; - network->layers[0].params = av_malloc(sizeof(InputParams)); - if (!network->layers[0].params){ - avio_closep(&model_file_context); - ff_dnn_free_model_native(&model); - return NULL; - } - - for (layer = 1; layer < network->layers_num; ++layer){ - layer_type = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - switch (layer_type){ - case CONV: - conv_params = av_malloc(sizeof(ConvolutionalParams)); - if (!conv_params){ - avio_closep(&model_file_context); - ff_dnn_free_model_native(&model); - return NULL; - } - 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); - kernel_size = conv_params->input_num * conv_params->output_num * - conv_params->kernel_size * conv_params->kernel_size; - dnn_size += 24 + (kernel_size + conv_params->output_num << 2); - if (dnn_size > file_size || conv_params->input_num <= 0 || - conv_params->output_num <= 0 || conv_params->kernel_size <= 0){ - avio_closep(&model_file_context); - ff_dnn_free_model_native(&model); - return NULL; - } - conv_params->kernel = av_malloc(kernel_size * sizeof(float)); - conv_params->biases = av_malloc(conv_params->output_num * sizeof(float)); - if (!conv_params->kernel || !conv_params->biases){ - avio_closep(&model_file_context); - ff_dnn_free_model_native(&model); - return NULL; - } - for (i = 0; i < kernel_size; ++i){ - conv_params->kernel[i] = av_int2float(avio_rl32(model_file_context)); - } - for (i = 0; i < conv_params->output_num; ++i){ - conv_params->biases[i] = av_int2float(avio_rl32(model_file_context)); - } - network->layers[layer].type = CONV; - network->layers[layer].params = conv_params; - break; - case DEPTH_TO_SPACE: - depth_to_space_params = av_malloc(sizeof(DepthToSpaceParams)); - if (!depth_to_space_params){ - avio_closep(&model_file_context); - ff_dnn_free_model_native(&model); - return NULL; - } - depth_to_space_params->block_size = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - network->layers[layer].type = DEPTH_TO_SPACE; - network->layers[layer].params = depth_to_space_params; - break; - default: - avio_closep(&model_file_context); - ff_dnn_free_model_native(&model); - return NULL; - } - } - - avio_closep(&model_file_context); - - if (dnn_size != file_size){ - ff_dnn_free_model_native(&model); - return NULL; - } - - model->set_input_output = &set_input_output_native; - - return model; -} - -#define CLAMP_TO_EDGE(x, w) ((x) < 0 ? 0 : ((x) >= (w) ? (w - 1) : (x))) - -static void convolve(const float *input, float *output, const ConvolutionalParams *conv_params, int width, int height) -{ - 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; - - for (int y = pad_size; y < height - pad_size; ++y) { - for (int x = pad_size; x < width - pad_size; ++x) { - for (int n_filter = 0; n_filter < conv_params->output_num; ++n_filter) { - output[n_filter] = conv_params->biases[n_filter]; - - 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; - } - } -} - -static void depth_to_space(const float *input, float *output, int block_size, int width, int height, int channels) -{ - 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; - - 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; - } -} - -DNNReturnType ff_dnn_execute_model_native(const DNNModel *model, DNNData *outputs, uint32_t nb_output) -{ - ConvolutionalNetwork *network = (ConvolutionalNetwork *)model->model; - int cur_width, cur_height, cur_channels; - int32_t layer; - InputParams *input_params; - ConvolutionalParams *conv_params; - DepthToSpaceParams *depth_to_space_params; - - if (network->layers_num <= 0 || network->layers[0].type != INPUT || !network->layers[0].output){ - return DNN_ERROR; - } - else{ - input_params = (InputParams *)network->layers[0].params; - cur_width = input_params->width; - cur_height = input_params->height; - cur_channels = input_params->channels; - } - - for (layer = 1; layer < network->layers_num; ++layer){ - if (!network->layers[layer].output){ - return DNN_ERROR; - } - switch (network->layers[layer].type){ - case CONV: - conv_params = (ConvolutionalParams *)network->layers[layer].params; - convolve(network->layers[layer - 1].output, network->layers[layer].output, conv_params, cur_width, cur_height); - cur_channels = conv_params->output_num; - if (conv_params->padding_method == VALID) { - int pad_size = (conv_params->kernel_size - 1) * conv_params->dilation; - cur_height -= pad_size; - cur_width -= pad_size; - } - break; - case DEPTH_TO_SPACE: - depth_to_space_params = (DepthToSpaceParams *)network->layers[layer].params; - depth_to_space(network->layers[layer - 1].output, network->layers[layer].output, - depth_to_space_params->block_size, cur_width, cur_height, cur_channels); - cur_height *= depth_to_space_params->block_size; - cur_width *= depth_to_space_params->block_size; - cur_channels /= depth_to_space_params->block_size * depth_to_space_params->block_size; - break; - case INPUT: - return DNN_ERROR; - } - } - - // native mode does not support multiple outputs yet - if (nb_output > 1) - return DNN_ERROR; - outputs[0].data = network->layers[network->layers_num - 1].output; - outputs[0].height = cur_height; - outputs[0].width = cur_width; - outputs[0].channels = cur_channels; - - return DNN_SUCCESS; -} - -void ff_dnn_free_model_native(DNNModel **model) -{ - ConvolutionalNetwork *network; - ConvolutionalParams *conv_params; - int32_t layer; - - if (*model) - { - network = (ConvolutionalNetwork *)(*model)->model; - for (layer = 0; layer < network->layers_num; ++layer){ - av_freep(&network->layers[layer].output); - if (network->layers[layer].type == CONV){ - conv_params = (ConvolutionalParams *)network->layers[layer].params; - av_freep(&conv_params->kernel); - av_freep(&conv_params->biases); - } - av_freep(&network->layers[layer].params); - } - av_freep(&network->layers); - av_freep(&network); - av_freep(model); - } -} diff --git a/libavfilter/dnn_interface.h b/libavfilter/dnn_interface.h index c24df0e9617..b20e5c8fabe 100644 --- a/libavfilter/dnn_interface.h +++ b/libavfilter/dnn_interface.h @@ -32,25 +32,23 @@ typedef enum {DNN_SUCCESS, DNN_ERROR} DNNReturnType; typedef enum {DNN_NATIVE, DNN_TF} DNNBackendType; -typedef enum {DNN_FLOAT, DNN_UINT8} DNNDataType; +typedef enum {DNN_FLOAT = 1, DNN_UINT8 = 4} DNNDataType; -typedef struct DNNInputData{ +typedef struct DNNData{ void *data; DNNDataType dt; int width, height, channels; -} DNNInputData; - -typedef struct DNNData{ - float *data; - int width, height, channels; } DNNData; typedef struct DNNModel{ // Stores model that can be different for different backends. void *model; + // Gets model input information + // Just reuse struct DNNData here, actually the DNNData.data field is not needed. + DNNReturnType (*get_input)(void *model, DNNData *input, const char *input_name); // Sets model input and output. // Should be called at least once before model execution. - DNNReturnType (*set_input_output)(void *model, DNNInputData *input, const char *input_name, const char **output_names, uint32_t nb_output); + DNNReturnType (*set_input_output)(void *model, DNNData *input, const char *input_name, const char **output_names, uint32_t nb_output); } DNNModel; // Stores pointers to functions for loading, executing, freeing DNN models for one of the backends. diff --git a/libavfilter/ebur128.c b/libavfilter/ebur128.c index c8986fb5e1a..ca2fca0066c 100644 --- a/libavfilter/ebur128.c +++ b/libavfilter/ebur128.c @@ -155,7 +155,7 @@ static int ebur128_init_channel_map(FFEBUR128State * st) { size_t i; st->d->channel_map = - (int *) av_malloc_array(st->channels, sizeof(int)); + (int *) av_malloc_array(st->channels, sizeof(*st->d->channel_map)); if (!st->d->channel_map) return AVERROR(ENOMEM); if (st->channels == 4) { @@ -221,17 +221,17 @@ FFEBUR128State *ff_ebur128_init(unsigned int channels, int errcode; FFEBUR128State *st; - st = (FFEBUR128State *) av_malloc(sizeof(FFEBUR128State)); + st = (FFEBUR128State *) av_malloc(sizeof(*st)); CHECK_ERROR(!st, 0, exit) st->d = (struct FFEBUR128StateInternal *) - av_malloc(sizeof(struct FFEBUR128StateInternal)); + av_malloc(sizeof(*st->d)); CHECK_ERROR(!st->d, 0, free_state) st->channels = channels; errcode = ebur128_init_channel_map(st); CHECK_ERROR(errcode, 0, free_internal) st->d->sample_peak = - (double *) av_mallocz_array(channels, sizeof(double)); + (double *) av_mallocz_array(channels, sizeof(*st->d->sample_peak)); CHECK_ERROR(!st->d->sample_peak, 0, free_channel_map) st->samplerate = samplerate; @@ -253,16 +253,16 @@ FFEBUR128State *ff_ebur128_init(unsigned int channels, } st->d->audio_data = (double *) av_mallocz_array(st->d->audio_data_frames, - st->channels * sizeof(double)); + st->channels * sizeof(*st->d->audio_data)); CHECK_ERROR(!st->d->audio_data, 0, free_sample_peak) ebur128_init_filter(st); st->d->block_energy_histogram = - av_mallocz(1000 * sizeof(unsigned long)); + av_mallocz(1000 * sizeof(*st->d->block_energy_histogram)); CHECK_ERROR(!st->d->block_energy_histogram, 0, free_audio_data) st->d->short_term_block_energy_histogram = - av_mallocz(1000 * sizeof(unsigned long)); + av_mallocz(1000 * sizeof(*st->d->short_term_block_energy_histogram)); CHECK_ERROR(!st->d->short_term_block_energy_histogram, 0, free_block_energy_histogram) st->d->short_term_frame_counter = 0; @@ -275,7 +275,7 @@ FFEBUR128State *ff_ebur128_init(unsigned int channels, if (ff_thread_once(&histogram_init, &init_histogram) != 0) goto free_short_term_block_energy_histogram; - st->d->data_ptrs = av_malloc_array(channels, sizeof(void *)); + st->d->data_ptrs = av_malloc_array(channels, sizeof(*st->d->data_ptrs)); CHECK_ERROR(!st->d->data_ptrs, 0, free_short_term_block_energy_histogram); diff --git a/libavfilter/f_drawgraph.c b/libavfilter/f_drawgraph.c index 955047368dd..030afb9b4ee 100644 --- a/libavfilter/f_drawgraph.c +++ b/libavfilter/f_drawgraph.c @@ -40,6 +40,7 @@ typedef struct DrawGraphContext { int mode; int slide; int w, h; + AVRational frame_rate; AVFrame *out; int x; @@ -48,21 +49,22 @@ typedef struct DrawGraphContext { float *values[4]; int values_size[4]; int nb_values; + int64_t prev_pts; } DrawGraphContext; #define OFFSET(x) offsetof(DrawGraphContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM static const AVOption drawgraph_options[] = { - { "m1", "set 1st metadata key", OFFSET(key[0]), AV_OPT_TYPE_STRING, {.str=""}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "fg1", "set 1st foreground color expression", OFFSET(fg_str[0]), AV_OPT_TYPE_STRING, {.str="0xffff0000"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "m2", "set 2nd metadata key", OFFSET(key[1]), AV_OPT_TYPE_STRING, {.str=""}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "fg2", "set 2nd foreground color expression", OFFSET(fg_str[1]), AV_OPT_TYPE_STRING, {.str="0xff00ff00"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "m3", "set 3rd metadata key", OFFSET(key[2]), AV_OPT_TYPE_STRING, {.str=""}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "fg3", "set 3rd foreground color expression", OFFSET(fg_str[2]), AV_OPT_TYPE_STRING, {.str="0xffff00ff"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "m4", "set 4th metadata key", OFFSET(key[3]), AV_OPT_TYPE_STRING, {.str=""}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "fg4", "set 4th foreground color expression", OFFSET(fg_str[3]), AV_OPT_TYPE_STRING, {.str="0xffffff00"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "bg", "set background color", OFFSET(bg), AV_OPT_TYPE_COLOR, {.str="white"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "m1", "set 1st metadata key", OFFSET(key[0]), AV_OPT_TYPE_STRING, {.str=""}, 0, 0, FLAGS }, + { "fg1", "set 1st foreground color expression", OFFSET(fg_str[0]), AV_OPT_TYPE_STRING, {.str="0xffff0000"}, 0, 0, FLAGS }, + { "m2", "set 2nd metadata key", OFFSET(key[1]), AV_OPT_TYPE_STRING, {.str=""}, 0, 0, FLAGS }, + { "fg2", "set 2nd foreground color expression", OFFSET(fg_str[1]), AV_OPT_TYPE_STRING, {.str="0xff00ff00"}, 0, 0, FLAGS }, + { "m3", "set 3rd metadata key", OFFSET(key[2]), AV_OPT_TYPE_STRING, {.str=""}, 0, 0, FLAGS }, + { "fg3", "set 3rd foreground color expression", OFFSET(fg_str[2]), AV_OPT_TYPE_STRING, {.str="0xffff00ff"}, 0, 0, FLAGS }, + { "m4", "set 4th metadata key", OFFSET(key[3]), AV_OPT_TYPE_STRING, {.str=""}, 0, 0, FLAGS }, + { "fg4", "set 4th foreground color expression", OFFSET(fg_str[3]), AV_OPT_TYPE_STRING, {.str="0xffffff00"}, 0, 0, FLAGS }, + { "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" }, @@ -77,6 +79,8 @@ static const AVOption drawgraph_options[] = { {"picture", "display graph in single frame", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, "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 }, + { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, { NULL } }; @@ -159,6 +163,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVDictionary *metadata; AVDictionaryEntry *e; AVFrame *out = s->out; + AVFrame *clone = NULL; + int64_t in_pts, out_pts; int i; if (s->slide == 4 && s->nb_values >= s->values_size[0] / sizeof(float)) { @@ -309,12 +315,24 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->nb_values++; s->x++; + in_pts = in->pts; + av_frame_free(&in); if (s->slide == 4) return 0; - return ff_filter_frame(outlink, av_frame_clone(s->out)); + out_pts = av_rescale_q(in_pts, inlink->time_base, outlink->time_base); + + if (out_pts == s->prev_pts) + return 0; + + clone = av_frame_clone(s->out); + if (!clone) + return AVERROR(ENOMEM); + + clone->pts = s->prev_pts = out_pts; + return ff_filter_frame(outlink, clone); } static int request_frame(AVFilterLink *outlink) @@ -406,6 +424,9 @@ static int config_output(AVFilterLink *outlink) 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); + s->prev_pts = AV_NOPTS_VALUE; return 0; } diff --git a/libavfilter/f_ebur128.c b/libavfilter/f_ebur128.c index f25d5f096e5..31b75ab097e 100644 --- a/libavfilter/f_ebur128.c +++ b/libavfilter/f_ebur128.c @@ -774,6 +774,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) /* push one video frame */ if (ebur128->do_video) { + AVFrame *clone; int x, y, ret; uint8_t *p; double gauge_value; @@ -823,7 +824,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) /* set pts and push frame */ pic->pts = pts; - ret = ff_filter_frame(outlink, av_frame_clone(pic)); + clone = av_frame_clone(pic); + if (!clone) + return AVERROR(ENOMEM); + ret = ff_filter_frame(outlink, clone); if (ret < 0) return ret; } diff --git a/libavfilter/f_graphmonitor.c b/libavfilter/f_graphmonitor.c index c001835364b..a9c4ba42f5c 100644 --- a/libavfilter/f_graphmonitor.c +++ b/libavfilter/f_graphmonitor.c @@ -42,6 +42,7 @@ typedef struct GraphMonitorContext { AVRational frame_rate; int64_t pts; + int64_t next_pts; uint8_t white[4]; uint8_t yellow[4]; uint8_t red[4]; @@ -300,7 +301,7 @@ static int create_frame(AVFilterContext *ctx, int64_t pts) } out->pts = pts; - s->pts = pts; + s->pts = pts + 1; return ff_filter_frame(outlink, out); } @@ -328,10 +329,14 @@ static int activate(AVFilterContext *ctx) if (pts != AV_NOPTS_VALUE) { pts = av_rescale_q(pts, inlink->time_base, outlink->time_base); - if (s->pts < pts && ff_outlink_frame_wanted(outlink)) - return create_frame(ctx, pts); + if (s->pts == AV_NOPTS_VALUE) + s->pts = pts; + s->next_pts = pts; } + if (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); @@ -347,6 +352,8 @@ static int config_output(AVFilterLink *outlink) s->yellow[0] = s->yellow[1] = 255; s->red[0] = 255; s->green[1] = 255; + s->pts = AV_NOPTS_VALUE; + s->next_pts = AV_NOPTS_VALUE; outlink->w = s->w; outlink->h = s->h; outlink->sample_aspect_ratio = (AVRational){1,1}; diff --git a/libavfilter/f_interleave.c b/libavfilter/f_interleave.c index 06f4cda7985..a18bbe79b3e 100644 --- a/libavfilter/f_interleave.c +++ b/libavfilter/f_interleave.c @@ -37,15 +37,25 @@ typedef struct InterleaveContext { const AVClass *class; int nb_inputs; + int duration_mode; int64_t pts; } InterleaveContext; +#define DURATION_LONGEST 0 +#define DURATION_SHORTEST 1 +#define DURATION_FIRST 2 + #define OFFSET(x) offsetof(InterleaveContext, x) #define DEFINE_OPTIONS(filt_name, flags_) \ 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" }, \ { NULL } \ } @@ -55,20 +65,21 @@ static int activate(AVFilterContext *ctx) InterleaveContext *s = ctx->priv; int64_t q_pts, pts = INT64_MAX; int i, nb_eofs = 0, input_idx = -1; + int nb_inputs_with_frames = 0; FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); for (i = 0; i < ctx->nb_inputs; i++) { - if (!ff_outlink_get_status(ctx->inputs[i]) && - !ff_inlink_queued_frames(ctx->inputs[i])) - break; + if (!ff_inlink_queued_frames(ctx->inputs[i])) + continue; + nb_inputs_with_frames++; } - if (i == ctx->nb_inputs) { + if (nb_inputs_with_frames > 0) { for (i = 0; i < ctx->nb_inputs; i++) { AVFrame *frame; - if (ff_outlink_get_status(ctx->inputs[i])) + if (ff_inlink_queued_frames(ctx->inputs[i]) == 0) continue; frame = ff_inlink_peek_frame(ctx->inputs[i], 0); @@ -104,6 +115,16 @@ static int activate(AVFilterContext *ctx) } } + for (i = 0; i < ctx->nb_inputs; i++) + nb_eofs += !!ff_outlink_get_status(ctx->inputs[i]); + + if ((nb_eofs > 0 && s->duration_mode == DURATION_SHORTEST) || + (nb_eofs == ctx->nb_inputs && s->duration_mode == DURATION_LONGEST) || + (ff_outlink_get_status(ctx->inputs[0]) && s->duration_mode == DURATION_FIRST)) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); + return 0; + } + for (i = 0; i < ctx->nb_inputs; i++) { if (ff_inlink_queued_frames(ctx->inputs[i])) continue; @@ -112,11 +133,10 @@ static int activate(AVFilterContext *ctx) ff_inlink_request_frame(ctx->inputs[i]); return 0; } - nb_eofs++; } - if (nb_eofs == ctx->nb_inputs) { - ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); + if (i) { + ff_filter_set_ready(ctx, 100); return 0; } diff --git a/libavfilter/f_loop.c b/libavfilter/f_loop.c index 5ec44d9da2e..0a029f0104c 100644 --- a/libavfilter/f_loop.c +++ b/libavfilter/f_loop.c @@ -107,7 +107,7 @@ static int push_samples(AVFilterContext *ctx, int nb_samples) } out->pts = s->pts; out->nb_samples = ret; - s->pts += out->nb_samples; + s->pts += av_rescale_q(out->nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base); i += out->nb_samples; s->current_sample += out->nb_samples; @@ -116,6 +116,7 @@ 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) @@ -145,7 +146,7 @@ static int afilter_frame(AVFilterLink *inlink, AVFrame *frame) drain = FFMAX(0, s->start - s->ignored_samples); s->pts = frame->pts; av_audio_fifo_drain(s->fifo, drain); - s->pts += s->start - s->ignored_samples; + s->pts += av_rescale_q(s->start - s->ignored_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base); } s->nb_samples += ret - drain; drain = frame->nb_samples - written; @@ -158,7 +159,7 @@ static int afilter_frame(AVFilterLink *inlink, AVFrame *frame) av_audio_fifo_drain(s->left, drain); } frame->nb_samples = ret; - s->pts += ret; + s->pts += av_rescale_q(ret, (AVRational){1, outlink->sample_rate}, outlink->time_base); ret = ff_filter_frame(outlink, frame); } else { int nb_samples = frame->nb_samples; @@ -169,7 +170,7 @@ static int afilter_frame(AVFilterLink *inlink, AVFrame *frame) } else { s->ignored_samples += frame->nb_samples; frame->pts = s->pts; - s->pts += frame->nb_samples; + s->pts += av_rescale_q(frame->nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base); ret = ff_filter_frame(outlink, frame); } @@ -195,7 +196,7 @@ static int arequest_frame(AVFilterLink *outlink) return AVERROR(ENOMEM); av_audio_fifo_read(s->left, (void **)out->extended_data, nb_samples); out->pts = s->pts; - s->pts += nb_samples; + s->pts += av_rescale_q(nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base); ret = ff_filter_frame(outlink, out); if (ret < 0) return ret; @@ -205,13 +206,55 @@ static int arequest_frame(AVFilterLink *outlink) ret = push_samples(ctx, 1024); } - if (ret == AVERROR_EOF && s->nb_samples > 0 && s->loop != 0) { - ret = push_samples(ctx, outlink->sample_rate); + if (s->eof && s->nb_samples > 0 && s->loop != 0) { + ret = push_samples(ctx, 1024); } return ret; } +static int aactivate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + LoopContext *s = ctx->priv; + AVFrame *frame = NULL; + int ret, status; + int64_t pts; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + if (!s->eof && (s->nb_samples < s->size || !s->loop || !s->size)) { + ret = ff_inlink_consume_frame(inlink, &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 (status == AVERROR_EOF) { + s->size = s->nb_samples; + s->eof = 1; + } + } + + if (s->eof && (!s->loop || !s->size)) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->duration); + return 0; + } + + if (!s->eof && (!s->size || + (s->nb_samples < s->size) || + (s->nb_samples >= s->size && s->loop == 0))) { + FF_FILTER_FORWARD_WANTED(outlink, inlink); + } else if (s->loop && s->nb_samples == s->size) { + return arequest_frame(outlink); + } + + return FFERROR_NOT_READY; +} + 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 }, @@ -225,7 +268,6 @@ static const AVFilterPad ainputs[] = { { .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = afilter_frame, .config_props = aconfig_input, }, { NULL } @@ -235,7 +277,6 @@ static const AVFilterPad aoutputs[] = { { .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .request_frame = arequest_frame, }, { NULL } }; @@ -245,6 +286,7 @@ AVFilter ff_af_aloop = { .description = NULL_IF_CONFIG_SMALL("Loop audio samples."), .priv_size = sizeof(LoopContext), .priv_class = &aloop_class, + .activate = aactivate, .uninit = auninit, .inputs = ainputs, .outputs = aoutputs, diff --git a/libavfilter/f_metadata.c b/libavfilter/f_metadata.c index 523a94d38cb..598257b15b8 100644 --- a/libavfilter/f_metadata.c +++ b/libavfilter/f_metadata.c @@ -54,6 +54,7 @@ enum MetadataFunction { METADATAF_EQUAL, METADATAF_GREATER, METADATAF_EXPR, + METADATAF_ENDS_WITH, METADATAF_NB }; @@ -87,6 +88,8 @@ typedef struct MetadataContext { int (*compare)(struct MetadataContext *s, const char *value1, const char *value2); void (*print)(AVFilterContext *ctx, const char *msg, ...) av_printf_format(2, 3); + + int direct; // reduces buffering when printing to user-supplied URL } MetadataContext; #define OFFSET(x) offsetof(MetadataContext, x) @@ -107,8 +110,10 @@ static const AVOption filt_name##_options[] = { \ { "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" }, \ { "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 }, \ { NULL } \ } @@ -122,6 +127,14 @@ static int starts_with(MetadataContext *s, const char *value1, const char *value return !strncmp(value1, value2, strlen(value2)); } +static int ends_with(MetadataContext *s, const char *value1, const char *value2) +{ + const int len1 = strlen(value1); + const int len2 = strlen(value2); + + return !strncmp(value1 + FFMAX(len1 - len2, 0), value2, len2); +} + static int equal(MetadataContext *s, const char *value1, const char *value2) { float f1, f2; @@ -212,6 +225,9 @@ static av_cold int init(AVFilterContext *ctx) case METADATAF_STARTS_WITH: s->compare = starts_with; break; + case METADATAF_ENDS_WITH: + s->compare = ends_with; + break; case METADATAF_LESS: s->compare = less; break; @@ -261,6 +277,9 @@ static av_cold int init(AVFilterContext *ctx) s->file_str, buf); return ret; } + + if (s->direct) + s->avio_context->direct = AVIO_FLAG_DIRECT; } return 0; @@ -270,6 +289,8 @@ static av_cold void uninit(AVFilterContext *ctx) { MetadataContext *s = ctx->priv; + av_expr_free(s->expr); + s->expr = NULL; if (s->avio_context) { avio_closep(&s->avio_context); } @@ -283,7 +304,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) AVDictionary **metadata = &frame->metadata; AVDictionaryEntry *e; - if (!*metadata) + if (!*metadata && s->mode != METADATA_ADD) return ff_filter_frame(outlink, frame); e = av_dict_get(*metadata, !s->key ? "" : s->key, NULL, @@ -305,13 +326,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) av_dict_set(metadata, s->key, s->value, 0); } return ff_filter_frame(outlink, frame); - break; case METADATA_MODIFY: if (e && e->value) { av_dict_set(metadata, s->key, s->value, 0); } return ff_filter_frame(outlink, frame); - break; case METADATA_PRINT: if (!s->key && e) { s->print(ctx, "frame:%-4"PRId64" pts:%-7s pts_time:%s\n", @@ -326,7 +345,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) s->print(ctx, "%s=%s\n", s->key, e->value); } return ff_filter_frame(outlink, frame); - break; case METADATA_DELETE: if (!s->key) { av_dict_free(metadata); @@ -334,7 +352,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) av_dict_set(metadata, s->key, NULL, 0); } return ff_filter_frame(outlink, frame); - break; default: av_assert0(0); }; diff --git a/libavfilter/f_reverse.c b/libavfilter/f_reverse.c index 5f279270755..1e27264dd62 100644 --- a/libavfilter/f_reverse.c +++ b/libavfilter/f_reverse.c @@ -58,6 +58,11 @@ static av_cold void uninit(AVFilterContext *ctx) { ReverseContext *s = ctx->priv; + while (s->nb_frames > 0) { + av_frame_free(&s->frames[s->nb_frames - 1]); + s->nb_frames--; + } + av_freep(&s->pts); av_freep(&s->frames); } @@ -103,6 +108,7 @@ static int request_frame(AVFilterLink *outlink) AVFrame *out = s->frames[s->nb_frames - 1]; out->pts = s->pts[s->flush_idx++]; ret = ff_filter_frame(outlink, out); + s->frames[s->nb_frames - 1] = NULL; s->nb_frames--; } @@ -262,6 +268,7 @@ static int areverse_request_frame(AVFilterLink *outlink) else reverse_samples_packed(out); ret = ff_filter_frame(outlink, out); + s->frames[s->nb_frames - 1] = NULL; s->nb_frames--; } diff --git a/libavfilter/f_select.c b/libavfilter/f_select.c index 11323757588..755e10a3992 100644 --- a/libavfilter/f_select.c +++ b/libavfilter/f_select.c @@ -26,8 +26,10 @@ #include "libavutil/avstring.h" #include "libavutil/eval.h" #include "libavutil/fifo.h" +#include "libavutil/imgutils.h" #include "libavutil/internal.h" #include "libavutil/opt.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" #include "audio.h" #include "formats.h" @@ -144,6 +146,10 @@ typedef struct SelectContext { char *expr_str; AVExpr *expr; double var_values[VAR_VARS_NB]; + int bitdepth; + int nb_planes; + ptrdiff_t width[4]; + ptrdiff_t height[4]; int do_scene_detect; ///< 1 if the expression requires scene detection variables, 0 otherwise ff_scene_sad_fn sad; ///< Sum of the absolute difference function (scene detect only) double prev_mafd; ///< previous MAFD (scene detect only) @@ -202,6 +208,21 @@ static av_cold int init(AVFilterContext *ctx) static int config_input(AVFilterLink *inlink) { SelectContext *select = inlink->dst->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + int is_yuv = !(desc->flags & AV_PIX_FMT_FLAG_RGB) && + (desc->flags & AV_PIX_FMT_FLAG_PLANAR) && + desc->nb_components >= 3; + + select->bitdepth = desc->comp[0].depth; + select->nb_planes = is_yuv ? 1 : av_pix_fmt_count_planes(inlink->format); + + for (int plane = 0; plane < select->nb_planes; plane++) { + ptrdiff_t line_size = av_image_get_linesize(inlink->format, inlink->w, plane); + int vsub = desc->log2_chroma_h; + + select->width[plane] = line_size >> (select->bitdepth > 8); + select->height[plane] = plane == 1 || plane == 2 ? AV_CEIL_RSHIFT(inlink->h, vsub) : inlink->h; + } select->var_values[VAR_N] = 0.0; select->var_values[VAR_SELECTED_N] = 0.0; @@ -242,7 +263,7 @@ static int config_input(AVFilterLink *inlink) inlink->type == AVMEDIA_TYPE_AUDIO ? inlink->sample_rate : NAN; if (CONFIG_SELECT_FILTER && select->do_scene_detect) { - select->sad = ff_scene_sad_get_fn(8); + select->sad = ff_scene_sad_get_fn(select->bitdepth == 8 ? 8 : 16); if (!select->sad) return AVERROR(EINVAL); } @@ -258,12 +279,21 @@ static double get_scene_score(AVFilterContext *ctx, AVFrame *frame) if (prev_picref && frame->height == prev_picref->height && frame->width == prev_picref->width) { - uint64_t sad; + uint64_t sad = 0; double mafd, diff; + uint64_t count = 0; + + for (int plane = 0; plane < select->nb_planes; plane++) { + uint64_t plane_sad; + select->sad(prev_picref->data[plane], prev_picref->linesize[plane], + frame->data[plane], frame->linesize[plane], + select->width[plane], select->height[plane], &plane_sad); + sad += plane_sad; + count += select->width[plane] * select->height[plane]; + } - select->sad(prev_picref->data[0], prev_picref->linesize[0], frame->data[0], frame->linesize[0], frame->width * 3, frame->height, &sad); emms_c(); - mafd = (double)sad / (frame->width * 3 * frame->height); + mafd = (double)sad / count / (1ULL << (select->bitdepth - 8)); diff = fabs(mafd - select->prev_mafd); ret = av_clipf(FFMIN(mafd, diff) / 100., 0, 1); select->prev_mafd = mafd; @@ -472,7 +502,11 @@ static int query_formats(AVFilterContext *ctx) } else { int ret; static const enum AVPixelFormat pix_fmts[] = { - AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, + AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, AV_PIX_FMT_RGBA, + AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA, AV_PIX_FMT_GRAY8, + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P, + AV_PIX_FMT_YUV420P10, AV_PIX_FMT_NONE }; AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); diff --git a/libavfilter/f_sendcmd.c b/libavfilter/f_sendcmd.c index b8740e88830..0ac87e07ef4 100644 --- a/libavfilter/f_sendcmd.c +++ b/libavfilter/f_sendcmd.c @@ -25,6 +25,7 @@ #include "libavutil/avstring.h" #include "libavutil/bprint.h" +#include "libavutil/eval.h" #include "libavutil/file.h" #include "libavutil/opt.h" #include "libavutil/parseutils.h" @@ -35,10 +36,33 @@ #define COMMAND_FLAG_ENTER 1 #define COMMAND_FLAG_LEAVE 2 +#define COMMAND_FLAG_EXPR 4 + +static const char *const var_names[] = { + "N", /* frame number */ + "T", /* frame time in seconds */ + "POS", /* original position in the file of the frame */ + "PTS", /* frame pts */ + "TS", /* interval start time in seconds */ + "TE", /* interval end time in seconds */ + "TI", /* interval interpolated value: TI = (T - TS) / (TE - TS) */ + NULL +}; + +enum var_name { + VAR_N, + VAR_T, + VAR_POS, + VAR_PTS, + VAR_TS, + VAR_TE, + VAR_TI, + VAR_VARS_NB +}; static inline char *make_command_flags_str(AVBPrint *pbuf, int flags) { - static const char * const flag_strings[] = { "enter", "leave" }; + static const char * const flag_strings[] = { "enter", "leave", "expr" }; int i, is_first = 1; av_bprint_init(pbuf, 0, AV_BPRINT_SIZE_AUTOMATIC); @@ -129,6 +153,7 @@ static int parse_command(Command *cmd, int cmd_count, int interval_count, if (!strncmp(*buf, "enter", strlen("enter"))) cmd->flags |= COMMAND_FLAG_ENTER; else if (!strncmp(*buf, "leave", strlen("leave"))) cmd->flags |= COMMAND_FLAG_LEAVE; + else if (!strncmp(*buf, "expr", strlen("expr"))) cmd->flags |= COMMAND_FLAG_EXPR; else { char flag_buf[64]; av_strlcpy(flag_buf, *buf, sizeof(flag_buf)); @@ -450,6 +475,9 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&s->intervals); } +#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts)) +#define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts)*av_q2d(tb)) + static int filter_frame(AVFilterLink *inlink, AVFrame *ref) { AVFilterContext *ctx = inlink->dst; @@ -476,6 +504,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *ref) flags += COMMAND_FLAG_LEAVE; interval->enabled = 0; } + if (interval->enabled) + flags += COMMAND_FLAG_EXPR; if (flags) { AVBPrint pbuf; @@ -487,19 +517,49 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *ref) for (j = 0; flags && j < interval->nb_commands; j++) { Command *cmd = &interval->commands[j]; + char *cmd_arg = cmd->arg; char buf[1024]; if (cmd->flags & flags) { + if (cmd->flags & COMMAND_FLAG_EXPR) { + double var_values[VAR_VARS_NB], res; + double start = TS2T(interval->start_ts, AV_TIME_BASE_Q); + double end = TS2T(interval->end_ts, AV_TIME_BASE_Q); + double current = TS2T(ref->pts, inlink->time_base); + + var_values[VAR_N] = inlink->frame_count_in; + var_values[VAR_POS] = ref->pkt_pos == -1 ? NAN : ref->pkt_pos; + var_values[VAR_PTS] = TS2D(ref->pts); + var_values[VAR_T] = current; + var_values[VAR_TS] = start; + var_values[VAR_TE] = end; + var_values[VAR_TI] = (current - start) / (end - start); + + if ((ret = av_expr_parse_and_eval(&res, cmd->arg, var_names, var_values, + NULL, NULL, NULL, NULL, NULL, 0, NULL)) < 0) { + av_log(ctx, AV_LOG_ERROR, "Invalid expression '%s' for command argument.\n", cmd->arg); + av_frame_free(&ref); + return AVERROR(EINVAL); + } + + cmd_arg = av_asprintf("%g", res); + if (!cmd_arg) { + av_frame_free(&ref); + return AVERROR(ENOMEM); + } + } av_log(ctx, AV_LOG_VERBOSE, "Processing command #%d target:%s command:%s arg:%s\n", - cmd->index, cmd->target, cmd->command, cmd->arg); + cmd->index, cmd->target, cmd->command, cmd_arg); ret = avfilter_graph_send_command(inlink->graph, - cmd->target, cmd->command, cmd->arg, + cmd->target, cmd->command, cmd_arg, buf, sizeof(buf), AVFILTER_CMD_FLAG_ONE); av_log(ctx, AV_LOG_VERBOSE, "Command reply for command #%d: ret:%s res:%s\n", cmd->index, av_err2str(ret), buf); + if (cmd->flags & COMMAND_FLAG_EXPR) + av_freep(&cmd_arg); } } } diff --git a/libavfilter/f_sidedata.c b/libavfilter/f_sidedata.c index 381da5a0528..4210dcac4c2 100644 --- a/libavfilter/f_sidedata.c +++ b/libavfilter/f_sidedata.c @@ -39,7 +39,7 @@ typedef struct SideDataContext { const AVClass *class; int mode; - enum AVFrameSideDataType type; + int type; // enum AVFrameSideDataType or -1 for delete side data mode } SideDataContext; #define OFFSET(x) offsetof(SideDataContext, x) diff --git a/libavfilter/f_streamselect.c b/libavfilter/f_streamselect.c index 923deb1a854..b3ae4bed62e 100644 --- a/libavfilter/f_streamselect.c +++ b/libavfilter/f_streamselect.c @@ -21,6 +21,7 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" +#include "filters.h" #include "formats.h" #include "framesync.h" #include "internal.h" @@ -40,9 +41,10 @@ typedef struct StreamSelectContext { #define OFFSET(x) offsetof(StreamSelectContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM +#define TFLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM static const AVOption streamselect_options[] = { { "inputs", "number of input streams", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64=2}, 2, INT_MAX, .flags=FLAGS }, - { "map", "input indexes to remap to outputs", OFFSET(map_str), AV_OPT_TYPE_STRING, {.str=NULL}, .flags=FLAGS }, + { "map", "input indexes to remap to outputs", OFFSET(map_str), AV_OPT_TYPE_STRING, {.str=NULL}, .flags=TFLAGS }, { NULL } }; @@ -53,7 +55,7 @@ static int process_frame(FFFrameSync *fs) AVFilterContext *ctx = fs->parent; StreamSelectContext *s = fs->opaque; AVFrame **in = s->frames; - int i, j, ret = 0; + int i, j, ret = 0, have_out = 0; for (i = 0; i < ctx->nb_inputs; i++) { if ((ret = ff_framesync_get_frame(&s->fs, i, &in[i], 0)) < 0) @@ -75,12 +77,15 @@ static int process_frame(FFFrameSync *fs) out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, ctx->outputs[i]->time_base); s->last_pts[j] = in[j]->pts; ret = ff_filter_frame(ctx->outputs[i], out); + have_out = 1; if (ret < 0) return ret; } } } + if (!have_out) + ff_filter_set_ready(ctx, 100); return ret; } @@ -290,6 +295,12 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&s->map); av_freep(&s->frames); ff_framesync_uninit(&s->fs); + + for (int i = 0; i < ctx->nb_inputs; i++) + av_freep(&ctx->input_pads[i].name); + + for (int i = 0; i < ctx->nb_outputs; i++) + av_freep(&ctx->output_pads[i].name); } static int query_formats(AVFilterContext *ctx) diff --git a/libavfilter/f_zmq.c b/libavfilter/f_zmq.c index 89da5bef064..744c7213057 100644 --- a/libavfilter/f_zmq.c +++ b/libavfilter/f_zmq.c @@ -139,7 +139,7 @@ static int recv_msg(AVFilterContext *ctx, char **buf, int *buf_size) ret = AVERROR(ENOMEM); goto end; } - memcpy(*buf, zmq_msg_data(&msg), *buf_size); + memcpy(*buf, zmq_msg_data(&msg), *buf_size - 1); (*buf)[*buf_size-1] = 0; end: diff --git a/libavfilter/fifo.c b/libavfilter/fifo.c index 0fa0f86cb3f..f5587df62a7 100644 --- a/libavfilter/fifo.c +++ b/libavfilter/fifo.c @@ -36,7 +36,7 @@ typedef struct Buf { AVFrame *frame; - struct Buf *next; + struct Buf *next; } Buf; typedef struct FifoContext { @@ -53,38 +53,38 @@ typedef struct FifoContext { static av_cold int init(AVFilterContext *ctx) { - FifoContext *fifo = ctx->priv; - fifo->last = &fifo->root; + FifoContext *s = ctx->priv; + s->last = &s->root; return 0; } static av_cold void uninit(AVFilterContext *ctx) { - FifoContext *fifo = ctx->priv; + FifoContext *s = ctx->priv; Buf *buf, *tmp; - for (buf = fifo->root.next; 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(&fifo->out); + av_frame_free(&s->out); } static int add_to_queue(AVFilterLink *inlink, AVFrame *frame) { - FifoContext *fifo = inlink->dst->priv; + FifoContext *s = inlink->dst->priv; - fifo->last->next = av_mallocz(sizeof(Buf)); - if (!fifo->last->next) { + s->last->next = av_mallocz(sizeof(Buf)); + if (!s->last->next) { av_frame_free(&frame); return AVERROR(ENOMEM); } - fifo->last = fifo->last->next; - fifo->last->frame = frame; + s->last = s->last->next; + s->last->frame = frame; return 0; } @@ -229,24 +229,24 @@ static int return_audio_frame(AVFilterContext *ctx) static int request_frame(AVFilterLink *outlink) { - FifoContext *fifo = outlink->src->priv; + FifoContext *s = outlink->src->priv; int ret = 0; - if (!fifo->root.next) { + if (!s->root.next) { if ((ret = ff_request_frame(outlink->src->inputs[0])) < 0) { if (ret == AVERROR_EOF && outlink->request_samples) return return_audio_frame(outlink->src); return ret; } - if (!fifo->root.next) + if (!s->root.next) return 0; } if (outlink->request_samples) { return return_audio_frame(outlink->src); } else { - ret = ff_filter_frame(outlink, fifo->root.next->frame); - queue_pop(fifo); + ret = ff_filter_frame(outlink, s->root.next->frame); + queue_pop(s); } return ret; @@ -254,9 +254,9 @@ static int request_frame(AVFilterLink *outlink) static const AVFilterPad avfilter_vf_fifo_inputs[] = { { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = add_to_queue, + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = add_to_queue, }, { NULL } }; @@ -271,23 +271,20 @@ static const AVFilterPad avfilter_vf_fifo_outputs[] = { }; AVFilter ff_vf_fifo = { - .name = "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), - - .inputs = avfilter_vf_fifo_inputs, - .outputs = avfilter_vf_fifo_outputs, + .init = init, + .uninit = uninit, + .priv_size = sizeof(FifoContext), + .inputs = avfilter_vf_fifo_inputs, + .outputs = avfilter_vf_fifo_outputs, }; static const AVFilterPad avfilter_af_afifo_inputs[] = { { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = add_to_queue, + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .filter_frame = add_to_queue, }, { NULL } }; @@ -304,12 +301,9 @@ static const AVFilterPad avfilter_af_afifo_outputs[] = { 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), - - .inputs = avfilter_af_afifo_inputs, - .outputs = avfilter_af_afifo_outputs, + .init = init, + .uninit = uninit, + .priv_size = sizeof(FifoContext), + .inputs = avfilter_af_afifo_inputs, + .outputs = avfilter_af_afifo_outputs, }; diff --git a/libavfilter/formats.c b/libavfilter/formats.c index 31ee445c494..de4315369d5 100644 --- a/libavfilter/formats.c +++ b/libavfilter/formats.c @@ -317,7 +317,6 @@ do { \ void *oldf = *f; \ \ if (!(*f) && !(*f = av_mallocz(sizeof(**f)))) { \ - unref_fn(f); \ return AVERROR(ENOMEM); \ } \ \ @@ -369,15 +368,46 @@ AVFilterFormats *ff_all_formats(enum AVMediaType type) return ret; } -const int64_t avfilter_all_channel_layouts[] = { -#include "all_channel_layouts.inc" - -1 -}; - -// AVFilterFormats *avfilter_make_all_channel_layouts(void) -// { -// return avfilter_make_format64_list(avfilter_all_channel_layouts); -// } +int ff_formats_pixdesc_filter(AVFilterFormats **rfmts, unsigned want, unsigned rej) +{ + unsigned nb_formats, fmt, flags; + AVFilterFormats *formats = NULL; + + while (1) { + nb_formats = 0; + for (fmt = 0;; fmt++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); + if (!desc) + break; + flags = desc->flags; + if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL) && + !(desc->flags & AV_PIX_FMT_FLAG_PLANAR) && + (desc->log2_chroma_w || desc->log2_chroma_h)) + flags |= FF_PIX_FMT_FLAG_SW_FLAT_SUB; + if ((flags & (want | rej)) != want) + continue; + if (formats) + formats->formats[nb_formats] = fmt; + nb_formats++; + } + if (formats) { + av_assert0(formats->nb_formats == nb_formats); + *rfmts = formats; + return 0; + } + formats = av_mallocz(sizeof(*formats)); + if (!formats) + return AVERROR(ENOMEM); + formats->nb_formats = nb_formats; + if (nb_formats) { + formats->formats = av_malloc_array(nb_formats, sizeof(*formats->formats)); + if (!formats->formats) { + av_freep(&formats); + return AVERROR(ENOMEM); + } + } + } +} AVFilterFormats *ff_planar_sample_fmts(void) { @@ -456,7 +486,7 @@ do { \ do { \ int idx = -1; \ \ - if (!*ref || !(*ref)->refs) \ + if (!ref || !*ref || !(*ref)->refs) \ return; \ \ FIND_REF_INDEX(ref, idx); \ @@ -518,7 +548,8 @@ void ff_formats_changeref(AVFilterFormats **oldref, AVFilterFormats **newref) int ret = ref_fn(fmts, &ctx->inputs[i]->out_fmts); \ if (ret < 0) { \ unref_fn(&fmts); \ - av_freep(&fmts->list); \ + if (fmts) \ + av_freep(&fmts->list); \ av_freep(&fmts); \ return ret; \ } \ @@ -530,7 +561,8 @@ void ff_formats_changeref(AVFilterFormats **oldref, AVFilterFormats **newref) int ret = ref_fn(fmts, &ctx->outputs[i]->in_fmts); \ if (ret < 0) { \ unref_fn(&fmts); \ - av_freep(&fmts->list); \ + if (fmts) \ + av_freep(&fmts->list); \ av_freep(&fmts); \ return ret; \ } \ diff --git a/libavfilter/formats.h b/libavfilter/formats.h index 870809b5a03..cc588f30d86 100644 --- a/libavfilter/formats.h +++ b/libavfilter/formats.h @@ -221,6 +221,16 @@ int ff_add_format(AVFilterFormats **avff, int64_t fmt); av_warn_unused_result AVFilterFormats *ff_all_formats(enum AVMediaType type); +/** + * Construct a formats list containing all pixel formats with certain + * properties + */ +av_warn_unused_result +int ff_formats_pixdesc_filter(AVFilterFormats **rfmts, unsigned want, unsigned rej); + +//* format is software, non-planar with sub-sampling +#define FF_PIX_FMT_FLAG_SW_FLAT_SUB (1 << 24) + /** * Construct a formats list containing all planar sample formats. */ diff --git a/libavfilter/framerate.h b/libavfilter/framerate.h index 8048dfa36a6..d2556356867 100644 --- a/libavfilter/framerate.h +++ b/libavfilter/framerate.h @@ -28,8 +28,7 @@ ptrdiff_t width, ptrdiff_t height, \ int factor1, int factor2, int half -#define BLEND_FACTOR_DEPTH8 7 -#define BLEND_FACTOR_DEPTH16 15 +#define BLEND_FACTOR_DEPTH(n) (n-1) typedef void (*blend_func)(BLEND_FUNC_PARAMS); @@ -43,6 +42,7 @@ typedef struct FrameRateContext { int interp_end; ///< end of range to apply linear interpolation int line_size[4]; ///< bytes of pixel data per line for each plane + int height[4]; ///< height of each plane int vsub; AVRational srce_time_base; ///< timebase of source diff --git a/libavfilter/framesync.c b/libavfilter/framesync.c index bc95f7d9048..b32a5cba6c9 100644 --- a/libavfilter/framesync.c +++ b/libavfilter/framesync.c @@ -117,7 +117,6 @@ static void framesync_sync_level_update(FFFrameSync *fs) int ff_framesync_configure(FFFrameSync *fs) { unsigned i; - int64_t gcd, lcm; if (!fs->opt_repeatlast || fs->opt_eof_action == EOF_ACTION_PASS) { fs->opt_repeatlast = 0; @@ -142,17 +141,8 @@ int ff_framesync_configure(FFFrameSync *fs) for (i = 0; i < fs->nb_in; i++) { if (fs->in[i].sync) { if (fs->time_base.num) { - gcd = av_gcd(fs->time_base.den, fs->in[i].time_base.den); - lcm = (fs->time_base.den / gcd) * fs->in[i].time_base.den; - if (lcm < AV_TIME_BASE / 2) { - fs->time_base.den = lcm; - fs->time_base.num = av_gcd(fs->time_base.num, - fs->in[i].time_base.num); - } else { - fs->time_base.num = 1; - fs->time_base.den = AV_TIME_BASE; - break; - } + fs->time_base = av_gcd_q(fs->time_base, fs->in[i].time_base, + AV_TIME_BASE / 2, AV_TIME_BASE_Q); } else { fs->time_base = fs->in[i].time_base; } diff --git a/libavfilter/glslang.cpp b/libavfilter/glslang.cpp new file mode 100644 index 00000000000..497df6e245c --- /dev/null +++ b/libavfilter/glslang.cpp @@ -0,0 +1,243 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 + +extern "C" { +#include "libavutil/mem.h" +#include "libavutil/avassert.h" +} + +#include +#include +#include +#include + +#include "glslang.h" + +using namespace glslang; + +static pthread_mutex_t glslang_mutex = PTHREAD_MUTEX_INITIALIZER; +static int glslang_refcount = 0; + +/* We require Vulkan 1.1 */ +#define GLSL_VERSION EShTargetVulkan_1_1 + +/* Vulkan 1.1 implementations require SPIR-V 1.3 to be implemented */ +#define SPIRV_VERSION EShTargetSpv_1_3 + +// Taken from glslang's examples, which apparently generally bases the choices +// on OpenGL specification limits +static const TBuiltInResource DefaultTBuiltInResource = { + /* .MaxLights = */ 32, + /* .MaxClipPlanes = */ 6, + /* .MaxTextureUnits = */ 32, + /* .MaxTextureCoords = */ 32, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 4096, + /* .MaxVaryingFloats = */ 64, + /* .MaxVertexTextureImageUnits = */ 32, + /* .MaxCombinedTextureImageUnits = */ 80, + /* .MaxTextureImageUnits = */ 32, + /* .MaxFragmentUniformComponents = */ 4096, + /* .MaxDrawBuffers = */ 32, + /* .MaxVertexUniformVectors = */ 128, + /* .MaxVaryingVectors = */ 8, + /* .MaxFragmentUniformVectors = */ 16, + /* .MaxVertexOutputVectors = */ 16, + /* .MaxFragmentInputVectors = */ 15, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 16, + /* .MaxComputeImageUniforms = */ 8, + /* .MaxComputeAtomicCounters = */ 8, + /* .MaxComputeAtomicCounterBuffers = */ 1, + /* .MaxVaryingComponents = */ 60, + /* .MaxVertexOutputComponents = */ 64, + /* .MaxGeometryInputComponents = */ 64, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 128, + /* .MaxImageUnits = */ 8, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8, + /* .MaxCombinedShaderOutputResources = */ 8, + /* .MaxImageSamples = */ 0, + /* .MaxVertexImageUniforms = */ 0, + /* .MaxTessControlImageUniforms = */ 0, + /* .MaxTessEvaluationImageUniforms = */ 0, + /* .MaxGeometryImageUniforms = */ 0, + /* .MaxFragmentImageUniforms = */ 8, + /* .MaxCombinedImageUniforms = */ 8, + /* .MaxGeometryTextureImageUnits = */ 16, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 16, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 0, + /* .MaxTessControlAtomicCounters = */ 0, + /* .MaxTessEvaluationAtomicCounters = */ 0, + /* .MaxGeometryAtomicCounters = */ 0, + /* .MaxFragmentAtomicCounters = */ 8, + /* .MaxCombinedAtomicCounters = */ 8, + /* .MaxAtomicCounterBindings = */ 1, + /* .MaxVertexAtomicCounterBuffers = */ 0, + /* .MaxTessControlAtomicCounterBuffers = */ 0, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, + /* .MaxGeometryAtomicCounterBuffers = */ 0, + /* .MaxFragmentAtomicCounterBuffers = */ 1, + /* .MaxCombinedAtomicCounterBuffers = */ 1, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 4, + /* .maxMeshOutputVerticesNV = */ 256, + /* .maxMeshOutputPrimitivesNV = */ 512, + /* .maxMeshWorkGroupSizeX_NV = */ 32, + /* .maxMeshWorkGroupSizeY_NV = */ 1, + /* .maxMeshWorkGroupSizeZ_NV = */ 1, + /* .maxTaskWorkGroupSizeX_NV = */ 32, + /* .maxTaskWorkGroupSizeY_NV = */ 1, + /* .maxTaskWorkGroupSizeZ_NV = */ 1, + /* .maxMeshViewCountNV = */ 4, + + .limits = { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + } +}; + +GLSlangResult *glslang_compile(const char *glsl, enum GLSlangStage stage) +{ + GLSlangResult *res = (GLSlangResult *)av_mallocz(sizeof(*res)); + if (!res) + return NULL; + + static const EShLanguage lang[] = { + [GLSLANG_VERTEX] = EShLangVertex, + [GLSLANG_FRAGMENT] = EShLangFragment, + [GLSLANG_COMPUTE] = EShLangCompute, + }; + + assert(glslang_refcount); + TShader *shader = new TShader(lang[stage]); + if (!shader) { + res->rval = AVERROR(ENOMEM); + return res; + } + + shader->setEnvClient(EShClientVulkan, GLSL_VERSION); + shader->setEnvTarget(EShTargetSpv, SPIRV_VERSION); + shader->setStrings(&glsl, 1); + if (!shader->parse(&DefaultTBuiltInResource, GLSL_VERSION, true, EShMsgDefault)) { + res->error_msg = av_strdup(shader->getInfoLog()); + res->rval = AVERROR_EXTERNAL; + delete shader; + return res; + } + + TProgram *prog = new TProgram(); + if (!prog) { + res->rval = AVERROR(ENOMEM); + delete shader; + return res; + } + + prog->addShader(shader); + if (!prog->link(EShMsgDefault)) { + res->error_msg = av_strdup(prog->getInfoLog()); + res->rval = AVERROR_EXTERNAL; + delete shader; + delete prog; + return res; + } + + std::vector spirv; /* Result */ + + SpvOptions options; /* Options - by default all optimizations are off */ + options.generateDebugInfo = false; /* Makes sense for files but not here */ + options.disassemble = false; /* Will print disassembly on compilation */ + options.validate = false; /* Validates the generated SPIRV, unneeded */ + options.disableOptimizer = false; /* For debugging */ + options.optimizeSize = true; /* Its faster */ + + GlslangToSpv(*prog->getIntermediate(lang[stage]), spirv, NULL, &options); + + res->size = spirv.size()*sizeof(unsigned int); + res->data = av_memdup(spirv.data(), res->size); + if (!res->data) { + res->rval = AVERROR(ENOMEM); + delete shader; + delete prog; + return res; + } + + delete shader; + delete prog; + + return res; +} + +int glslang_init(void) +{ + int ret = 0; + + pthread_mutex_lock(&glslang_mutex); + if (glslang_refcount++ == 0) + ret = !InitializeProcess(); + pthread_mutex_unlock(&glslang_mutex); + + return ret; +} + +void glslang_uninit(void) +{ + pthread_mutex_lock(&glslang_mutex); + av_assert0(glslang_refcount > 0); + if (--glslang_refcount == 0) + FinalizeProcess(); + pthread_mutex_unlock(&glslang_mutex); +} diff --git a/libavfilter/glslang.h b/libavfilter/glslang.h new file mode 100644 index 00000000000..d33808b9457 --- /dev/null +++ b/libavfilter/glslang.h @@ -0,0 +1,52 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_GLSLANG_H +#define AVFILTER_GLSLANG_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int glslang_init(void); +void glslang_uninit(void); + +typedef struct GLSlangResult { + int rval; + char *error_msg; + + void *data; /* Shader data or NULL */ + size_t size; +} GLSlangResult; + +enum GLSlangStage { + GLSLANG_VERTEX, + GLSLANG_FRAGMENT, + GLSLANG_COMPUTE, +}; + +/* Compile GLSL into a SPIRV stream, if possible */ +GLSlangResult *glslang_compile(const char *glsl, enum GLSlangStage stage); + +#ifdef __cplusplus +} +#endif + +#endif /* AVFILTER_GLSLANG_H */ diff --git a/libavfilter/graphdump.c b/libavfilter/graphdump.c index 8bc7b162d77..79ef1a733fe 100644 --- a/libavfilter/graphdump.c +++ b/libavfilter/graphdump.c @@ -154,7 +154,7 @@ static void avfilter_graph_dump_to_buf(AVBPrint *buf, AVFilterGraph *graph) char *avfilter_graph_dump(AVFilterGraph *graph, const char *options) { AVBPrint buf; - char *dump; + char *dump = NULL; av_bprint_init(&buf, 0, AV_BPRINT_SIZE_COUNT_ONLY); avfilter_graph_dump_to_buf(&buf, graph); diff --git a/libavfilter/interlace.h b/libavfilter/interlace.h deleted file mode 100644 index b41f0d5706f..00000000000 --- a/libavfilter/interlace.h +++ /dev/null @@ -1,68 +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 General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with FFmpeg; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/** - * @file - * progressive to interlaced content filter, inspired by heavy debugging of - * tinterlace filter. - */ - -#ifndef AVFILTER_INTERLACE_H -#define AVFILTER_INTERLACE_H - -#include "libavutil/bswap.h" -#include "libavutil/common.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" - -enum ScanMode { - MODE_TFF = 0, - MODE_BFF = 1, -}; - -enum FieldType { - FIELD_UPPER = 0, - FIELD_LOWER = 1, -}; - -enum VLPFilter { - VLPF_OFF = 0, - VLPF_LIN = 1, - VLPF_CMP = 2, -}; - -typedef struct InterlaceContext { - const AVClass *class; - enum ScanMode scan; // top or bottom field first scanning - int lowpass; // enable or disable low pass filtering - AVFrame *cur, *next; // the two frames from which the new one is obtained - const AVPixFmtDescriptor *csp; - void (*lowpass_line)(uint8_t *dstp, ptrdiff_t linesize, const uint8_t *srcp, - ptrdiff_t mref, ptrdiff_t pref, int clip_max); -} InterlaceContext; - -void ff_interlace_init(InterlaceContext *interlace, int depth); -void ff_interlace_init_x86(InterlaceContext *interlace, int depth); - -#endif /* AVFILTER_INTERLACE_H */ diff --git a/libavfilter/internal.h b/libavfilter/internal.h index 498bd3328d0..abe7537b5da 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -92,17 +92,6 @@ struct AVFilterPad { */ int (*filter_frame)(AVFilterLink *link, AVFrame *frame); - /** - * Frame poll callback. This returns the number of immediately available - * samples. It should return a positive value if the next request_frame() - * is guaranteed to return one frame (with no delay). - * - * Defaults to just calling the source poll_frame() method. - * - * Output pads only. - */ - int (*poll_frame)(AVFilterLink *link); - /** * Frame request callback. A call to this should result in some progress * towards producing output over the given link. This should return zero @@ -289,15 +278,6 @@ static inline int ff_insert_outpad(AVFilterContext *f, unsigned index, &f->output_pads, &f->outputs, p); } -/** - * Poll a frame from the filter chain. - * - * @param link the input link - * @return the number of immediately available frames, a negative - * number in case of error - */ -int ff_poll_frame(AVFilterLink *link); - /** * Request an input frame from the filter at the other end of the link. * @@ -411,6 +391,13 @@ static inline int ff_norm_qscale(int qscale, int type) */ int ff_filter_get_nb_threads(AVFilterContext *ctx); +/** + * Generic processing of user supplied commands that are set + * in the same way as the filter options. + */ +int ff_filter_process_command(AVFilterContext *ctx, const char *cmd, + const char *arg, char *res, int res_len, int flags); + /** * Perform any additional setup required for hardware frames. * diff --git a/libavfilter/maskedclamp.h b/libavfilter/maskedclamp.h new file mode 100644 index 00000000000..6a1fd9c04b7 --- /dev/null +++ b/libavfilter/maskedclamp.h @@ -0,0 +1,35 @@ + /* + * 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 + */ + +#ifndef AVFILTER_MASKEDCLAMP_H +#define AVFILTER_MASKEDCLAMP_H + +#include +#include + +typedef struct MaskedClampDSPContext { + void (*maskedclamp)(const uint8_t *bsrc, uint8_t *dst, + const uint8_t *darksrc, const uint8_t *brightsrc, + int w, int undershoot, int overshoot); +} MaskedClampDSPContext; + +void ff_maskedclamp_init_x86(MaskedClampDSPContext *dsp, int depth); + +#endif /* AVFILTER_MASKEDCLAMP_H */ diff --git a/libavfilter/median.h b/libavfilter/median.h new file mode 100644 index 00000000000..672607caa83 --- /dev/null +++ b/libavfilter/median.h @@ -0,0 +1,56 @@ +/* + * 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 + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + */ + +#ifndef AVFILTER_MEDIAN_H +#define AVFILTER_MEDIAN_H + +#include "avfilter.h" + +typedef struct MedianContext { + const AVClass *class; + + int planes; + int radius; + int radiusV; + float percentile; + + int planewidth[4]; + int planeheight[4]; + int depth; + int nb_planes; + int nb_threads; + + uint16_t **coarse, **fine; + int coarse_size, fine_size; + int bins; + int t; + + void (*hadd)(uint16_t *dst, const uint16_t *src, int bins); + void (*hsub)(uint16_t *dst, const uint16_t *src, int bins); + void (*hmuladd)(uint16_t *dst, const uint16_t *src, int f, int bins); + + void (*filter_plane)(AVFilterContext *ctx, const uint8_t *ssrc, int src_linesize, + uint8_t *ddst, int dst_linesize, int width, int height, + int slice_h_start, int slice_h_end, int jobnr); +} MedianContext; + +#endif diff --git a/libavfilter/median_template.c b/libavfilter/median_template.c new file mode 100644 index 00000000000..bb7646e0eb1 --- /dev/null +++ b/libavfilter/median_template.c @@ -0,0 +1,160 @@ +/* + * 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 + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + */ + +#include "libavutil/avassert.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +#undef pixel +#if DEPTH == 8 +#define pixel uint8_t +#else +#define pixel uint16_t +#endif + +#undef htype +#define htype uint16_t + +#undef fn +#undef fn2 +#undef fn3 +#define SHIFT ((DEPTH + 1) / 2) +#define BINS (1 << SHIFT) +#define MASK (BINS - 1) +#define fn3(a,b) a##_##b +#define fn2(a,b) fn3(a,b) +#define fn(a) fn2(a, DEPTH) + +#define PICK_COARSE_BIN(x, y) (BINS * (x) + ((y) >> SHIFT)) +#define PICK_FINE_BIN(x, y, z) (BINS * ((x) * ((y) >> SHIFT) + (z)) + ((y) & MASK)) + +static void fn(filter_plane)(AVFilterContext *ctx, const uint8_t *ssrc, int src_linesize, + uint8_t *ddst, int dst_linesize, int width, int height, + int slice_h_start, int slice_h_end, int jobnr) +{ + MedianContext *s = ctx->priv; + htype *ccoarse = s->coarse[jobnr]; + htype *cfine = s->fine[jobnr]; + const int radius = s->radius; + const int radiusV = s->radiusV; + const int t = s->t; + const pixel *src = (const pixel *)ssrc; + pixel *dst = (pixel *)ddst; + const pixel *srcp; + const pixel *p; + + src_linesize /= sizeof(pixel); + dst_linesize /= sizeof(pixel); + + memset(cfine, 0, s->fine_size * sizeof(*cfine)); + memset(ccoarse, 0, s->coarse_size * sizeof(*ccoarse)); + + srcp = src + FFMAX(0, slice_h_start - radiusV) * src_linesize; + if (jobnr == 0) { + for (int i = 0; i < width; i++) { + cfine[PICK_FINE_BIN(width, srcp[i], i)] += radiusV + 1; + ccoarse[PICK_COARSE_BIN(i, srcp[i])] += radiusV + 1; + } + } + + srcp = src + FFMAX(0, slice_h_start - radiusV - (jobnr != 0)) * src_linesize; + for (int i = 0; i < radiusV + (jobnr != 0) * (1 + radiusV); i++) { + for (int j = 0; j < width; j++) { + cfine[PICK_FINE_BIN(width, srcp[j], j)]++; + ccoarse[PICK_COARSE_BIN(j, srcp[j])]++; + } + srcp += src_linesize; + } + + srcp = src; + + for (int i = slice_h_start; i < slice_h_end; i++) { + htype coarse[BINS] = { 0 }; + htype fine[BINS][BINS] = { { 0 } }; + htype luc[BINS] = { 0 }; + + p = srcp + src_linesize * FFMAX(0, i - radiusV - 1); + for (int j = 0; j < width; j++) { + cfine[PICK_FINE_BIN(width, p[j], j)]--; + ccoarse[PICK_COARSE_BIN(j, p[j])]--; + } + + p = srcp + src_linesize * FFMIN(height - 1, i + radiusV); + for (int j = 0; j < width; j++) { + cfine[PICK_FINE_BIN(width, p[j], j)]++; + ccoarse[PICK_COARSE_BIN(j, p[j])]++; + } + + s->hmuladd(coarse, &ccoarse[0], radius, BINS); + for (int j = 0; j < radius; j++) + s->hadd(coarse, &ccoarse[BINS * j], BINS); + for (int k = 0; k < BINS; k++) + s->hmuladd(&fine[k][0], &cfine[BINS * width * k], 2 * radius + 1, BINS); + + for (int j = 0; j < width; j++) { + int sum = 0, k, b; + htype *segment; + + s->hadd(coarse, &ccoarse[BINS * FFMIN(j + radius, width - 1)], BINS); + + for (k = 0; k < BINS; k++) { + sum += coarse[k]; + if (sum > t) { + sum -= coarse[k]; + break; + } + } + av_assert0(k < BINS); + + if (luc[k] <= j - radius) { + memset(&fine[k], 0, BINS * sizeof(htype)); + for (luc[k] = j - radius; luc[k] < FFMIN(j + radius + 1, width); luc[k]++) + s->hadd(fine[k], &cfine[BINS * (width * k + luc[k])], BINS); + if (luc[k] < j + radius + 1) { + s->hmuladd(&fine[k][0], &cfine[BINS * (width * k + width - 1)], j + radius + 1 - width, BINS); + luc[k] = j + radius + 1; + } + } else { + for (; luc[k] < j + radius + 1; luc[k]++) { + s->hsub(fine[k], &cfine[BINS * (width * k + FFMAX(luc[k] - 2 * radius - 1, 0))], BINS); + s->hadd(fine[k], &cfine[BINS * (width * k + FFMIN(luc[k], width - 1))], BINS); + } + } + + s->hsub(coarse, &ccoarse[BINS * FFMAX(j - radius, 0)], BINS); + + segment = fine[k]; + for (b = 0; b < BINS; b++) { + sum += segment[b]; + if (sum > t) { + dst[j] = BINS * k + b; + break; + } + } + av_assert0(b < BINS); + } + + dst += dst_linesize; + } +} diff --git a/libavfilter/opencl.c b/libavfilter/opencl.c index 95f0bfc604c..9c46cfdc092 100644 --- a/libavfilter/opencl.c +++ b/libavfilter/opencl.c @@ -225,7 +225,7 @@ int ff_opencl_filter_load_program_from_file(AVFilterContext *avctx, const char *src_const; int err; - file = fopen(filename, "r"); + file = av_fopen_utf8(filename, "r"); if (!file) { av_log(avctx, AV_LOG_ERROR, "Unable to open program " "source file \"%s\".\n", filename); @@ -350,3 +350,13 @@ void ff_opencl_print_const_matrix_3x3(AVBPrint *buf, const char *name_str, } av_bprintf(buf, "};\n"); } + +cl_ulong ff_opencl_get_event_time(cl_event event) { + cl_ulong time_start; + cl_ulong time_end; + + clGetEventProfilingInfo(event, CL_PROFILING_COMMAND_START, sizeof(time_start), &time_start, NULL); + clGetEventProfilingInfo(event, CL_PROFILING_COMMAND_END, sizeof(time_end), &time_end, NULL); + + return time_end - time_start; +} diff --git a/libavfilter/opencl.h b/libavfilter/opencl.h index 973b6d82dd4..7487e602416 100644 --- a/libavfilter/opencl.h +++ b/libavfilter/opencl.h @@ -47,6 +47,11 @@ typedef struct OpenCLFilterContext { int output_height; } OpenCLFilterContext; +// Groups together information about a kernel argument +typedef struct OpenCLKernelArg { + size_t arg_size; + const void *arg_val; +} OpenCLKernelArg; /** * set argument to specific Kernel. @@ -73,9 +78,26 @@ typedef struct OpenCLFilterContext { goto fail; \ } \ } while(0) + +/** + * Create a kernel with the given name. + * + * The kernel variable in the context structure must have a name of the form + * kernel_. + * + * The OpenCLFilterContext variable in the context structure must be named ocf. + * + * Requires the presence of a local cl_int variable named cle and a fail label for error + * handling. + */ +#define CL_CREATE_KERNEL(ctx, kernel_name) do { \ + ctx->kernel_ ## kernel_name = clCreateKernel(ctx->ocf.program, #kernel_name, &cle); \ + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to create %s kernel: %d.\n", #kernel_name, cle); \ +} while(0) + /** - * release an OpenCL Kernel - */ + * release an OpenCL Kernel + */ #define CL_RELEASE_KERNEL(k) \ do { \ if (k) { \ @@ -87,8 +109,8 @@ do { \ } while(0) /** - * release an OpenCL Memory Object - */ + * release an OpenCL Memory Object + */ #define CL_RELEASE_MEMORY(m) \ do { \ if (m) { \ @@ -100,8 +122,8 @@ do { \ } while(0) /** - * release an OpenCL Command Queue - */ + * release an OpenCL Command Queue + */ #define CL_RELEASE_QUEUE(q) \ do { \ if (q) { \ @@ -112,6 +134,108 @@ do { \ } \ } while(0) +/** + * Enqueue a kernel with the given information. + * + * Kernel arguments are provided as KernelArg structures and are set in the order + * that they are passed. + * + * Requires the presence of a local cl_int variable named cle and a fail label for error + * handling. + */ +#define CL_ENQUEUE_KERNEL_WITH_ARGS(queue, kernel, global_work_size, local_work_size, event, ...) \ +do { \ + OpenCLKernelArg args[] = {__VA_ARGS__}; \ + for (int i = 0; i < FF_ARRAY_ELEMS(args); i++) { \ + cle = clSetKernelArg(kernel, i, args[i].arg_size, args[i].arg_val); \ + if (cle != CL_SUCCESS) { \ + av_log(avctx, AV_LOG_ERROR, "Failed to set kernel " \ + "argument %d: error %d.\n", i, cle); \ + err = AVERROR(EIO); \ + goto fail; \ + } \ + } \ + \ + cle = clEnqueueNDRangeKernel( \ + queue, \ + kernel, \ + FF_ARRAY_ELEMS(global_work_size), \ + NULL, \ + global_work_size, \ + local_work_size, \ + 0, \ + NULL, \ + event \ + ); \ + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to enqueue kernel: %d.\n", cle); \ +} while (0) + +/** + * Uses the above macro to enqueue the given kernel and then additionally runs it to + * completion via clFinish. + * + * Requires the presence of a local cl_int variable named cle and a fail label for error + * handling. + */ +#define CL_RUN_KERNEL_WITH_ARGS(queue, kernel, global_work_size, local_work_size, event, ...) do { \ + CL_ENQUEUE_KERNEL_WITH_ARGS( \ + queue, kernel, global_work_size, local_work_size, event, __VA_ARGS__ \ + ); \ + \ + cle = clFinish(queue); \ + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to finish command queue: %d.\n", cle); \ +} while (0) + +/** + * Create a buffer with the given information. + * + * The buffer variable in the context structure must be named . + * + * Requires the presence of a local cl_int variable named cle and a fail label for error + * handling. + */ +#define CL_CREATE_BUFFER_FLAGS(ctx, buffer_name, flags, size, host_ptr) do { \ + ctx->buffer_name = clCreateBuffer( \ + ctx->ocf.hwctx->context, \ + flags, \ + size, \ + host_ptr, \ + &cle \ + ); \ + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to create buffer %s: %d.\n", #buffer_name, cle); \ +} while(0) + +/** + * Perform a blocking write to a buffer. + * + * Requires the presence of a local cl_int variable named cle and a fail label for error + * handling. + */ +#define CL_BLOCKING_WRITE_BUFFER(queue, buffer, size, host_ptr, event) do { \ + cle = clEnqueueWriteBuffer( \ + queue, \ + buffer, \ + CL_TRUE, \ + 0, \ + size, \ + host_ptr, \ + 0, \ + NULL, \ + event \ + ); \ + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to write buffer to device: %d.\n", cle); \ +} while(0) + +/** + * Create a buffer with the given information. + * + * The buffer variable in the context structure must be named . + * + * Requires the presence of a local cl_int variable named cle and a fail label for error + * handling. + */ +#define CL_CREATE_BUFFER(ctx, buffer_name, size) CL_CREATE_BUFFER_FLAGS(ctx, buffer_name, 0, size, NULL) + /** * Return that all inputs and outputs support only AV_PIX_FMT_OPENCL. */ @@ -171,4 +295,10 @@ int ff_opencl_filter_work_size_from_image(AVFilterContext *avctx, void ff_opencl_print_const_matrix_3x3(AVBPrint *buf, const char *name_str, double mat[3][3]); +/** + * Gets the command start and end times for the given event and returns the + * difference (the time that the event took). + */ +cl_ulong ff_opencl_get_event_time(cl_event event); + #endif /* AVFILTER_OPENCL_H */ diff --git a/libavfilter/opencl/.gitignore b/libavfilter/opencl/.gitignore new file mode 100644 index 00000000000..064a8d8ef55 --- /dev/null +++ b/libavfilter/opencl/.gitignore @@ -0,0 +1 @@ +*.c diff --git a/libavfilter/opencl/deshake.cl b/libavfilter/opencl/deshake.cl new file mode 100644 index 00000000000..fef2681dc69 --- /dev/null +++ b/libavfilter/opencl/deshake.cl @@ -0,0 +1,647 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 (C) 2000, Intel Corporation, all rights reserved. + * Copyright (C) 2013, OpenCV Foundation, all rights reserved. + * Third party copyrights are property of their respective owners. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistribution's of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistribution's 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. + * + * * The name of the copyright holders may not 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 Intel Corporation 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. + */ + +#define HARRIS_THRESHOLD 3.0f +// Block size over which to compute harris response +// +// Note that changing this will require fiddling with the local array sizes in +// harris_response +#define HARRIS_RADIUS 2 +#define DISTANCE_THRESHOLD 80 + +// Sub-pixel refinement window for feature points +#define REFINE_WIN_HALF_W 5 +#define REFINE_WIN_HALF_H 5 +#define REFINE_WIN_W 11 // REFINE_WIN_HALF_W * 2 + 1 +#define REFINE_WIN_H 11 + +// Non-maximum suppression window size +#define NONMAX_WIN 30 +#define NONMAX_WIN_HALF 15 // NONMAX_WIN / 2 + +typedef struct PointPair { + // Previous frame + float2 p1; + // Current frame + float2 p2; +} PointPair; + +typedef struct SmoothedPointPair { + // Non-smoothed point in current frame + int2 p1; + // Smoothed point in current frame + float2 p2; +} SmoothedPointPair; + +typedef struct MotionVector { + PointPair p; + // Used to mark vectors as potential outliers + int should_consider; +} MotionVector; + +const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | + CLK_ADDRESS_CLAMP_TO_EDGE | + CLK_FILTER_NEAREST; + +const sampler_t sampler_linear = CLK_NORMALIZED_COORDS_FALSE | + CLK_ADDRESS_CLAMP_TO_EDGE | + CLK_FILTER_LINEAR; + +const sampler_t sampler_linear_mirror = CLK_NORMALIZED_COORDS_TRUE | + CLK_ADDRESS_MIRRORED_REPEAT | + CLK_FILTER_LINEAR; + +// Writes to a 1D array at loc, treating it as a 2D array with the same +// dimensions as the global work size. +static void write_to_1d_arrf(__global float *buf, int2 loc, float val) { + buf[loc.x + loc.y * get_global_size(0)] = val; +} + +static void write_to_1d_arrul8(__global ulong8 *buf, int2 loc, ulong8 val) { + buf[loc.x + loc.y * get_global_size(0)] = val; +} + +static void write_to_1d_arrvec(__global MotionVector *buf, int2 loc, MotionVector val) { + buf[loc.x + loc.y * get_global_size(0)] = val; +} + +static void write_to_1d_arrf2(__global float2 *buf, int2 loc, float2 val) { + buf[loc.x + loc.y * get_global_size(0)] = val; +} + +static ulong8 read_from_1d_arrul8(__global const ulong8 *buf, int2 loc) { + return buf[loc.x + loc.y * get_global_size(0)]; +} + +static float2 read_from_1d_arrf2(__global const float2 *buf, int2 loc) { + return buf[loc.x + loc.y * get_global_size(0)]; +} + +// Returns the grayscale value at the given point. +static float pixel_grayscale(__read_only image2d_t src, int2 loc) { + float4 pixel = read_imagef(src, sampler, loc); + return (pixel.x + pixel.y + pixel.z) / 3.0f; +} + +static float convolve( + __local const float *grayscale, + int local_idx_x, + int local_idx_y, + float mask[3][3] +) { + float ret = 0; + + // These loops touch each pixel surrounding loc as well as loc itself + for (int i = 1, i2 = 0; i >= -1; --i, ++i2) { + for (int j = -1, j2 = 0; j <= 1; ++j, ++j2) { + ret += mask[i2][j2] * grayscale[(local_idx_x + 3 + j) + (local_idx_y + 3 + i) * 14]; + } + } + + return ret; +} + +// Sums dx * dy for all pixels within radius of loc +static float sum_deriv_prod( + __local const float *grayscale, + float mask_x[3][3], + float mask_y[3][3] +) { + float ret = 0; + + for (int i = HARRIS_RADIUS; i >= -HARRIS_RADIUS; --i) { + for (int j = -HARRIS_RADIUS; j <= HARRIS_RADIUS; ++j) { + ret += convolve(grayscale, get_local_id(0) + j, get_local_id(1) + i, mask_x) * + convolve(grayscale, get_local_id(0) + j, get_local_id(1) + i, mask_y); + } + } + + return ret; +} + +// Sums d<>^2 (determined by mask) for all pixels within radius of loc +static float sum_deriv_pow(__local const float *grayscale, float mask[3][3]) +{ + float ret = 0; + + for (int i = HARRIS_RADIUS; i >= -HARRIS_RADIUS; --i) { + for (int j = -HARRIS_RADIUS; j <= HARRIS_RADIUS; ++j) { + float deriv = convolve(grayscale, get_local_id(0) + j, get_local_id(1) + i, mask); + ret += deriv * deriv; + } + } + + return ret; +} + +// Fills a box with the given radius and pixel around loc +static void draw_box(__write_only image2d_t dst, int2 loc, float4 pixel, int radius) +{ + for (int i = -radius; i <= radius; ++i) { + for (int j = -radius; j <= radius; ++j) { + write_imagef( + dst, + (int2)( + // Clamp to avoid writing outside image bounds + clamp(loc.x + i, 0, get_image_dim(dst).x - 1), + clamp(loc.y + j, 0, get_image_dim(dst).y - 1) + ), + pixel + ); + } + } +} + +// Converts the src image to grayscale +__kernel void grayscale( + __read_only image2d_t src, + __write_only image2d_t grayscale +) { + int2 loc = (int2)(get_global_id(0), get_global_id(1)); + write_imagef(grayscale, loc, (float4)(pixel_grayscale(src, loc), 0.0f, 0.0f, 1.0f)); +} + +// This kernel computes the harris response for the given grayscale src image +// within the given radius and writes it to harris_buf +__kernel void harris_response( + __read_only image2d_t grayscale, + __global float *harris_buf +) { + int2 loc = (int2)(get_global_id(0), get_global_id(1)); + + if (loc.x > get_image_width(grayscale) - 1 || loc.y > get_image_height(grayscale) - 1) { + write_to_1d_arrf(harris_buf, loc, 0); + return; + } + + float scale = 1.0f / ((1 << 2) * HARRIS_RADIUS * 255.0f); + + float sobel_mask_x[3][3] = { + {-1, 0, 1}, + {-2, 0, 2}, + {-1, 0, 1} + }; + + float sobel_mask_y[3][3] = { + { 1, 2, 1}, + { 0, 0, 0}, + {-1, -2, -1} + }; + + // 8 x 8 local work + 3 pixels around each side (needed to accomodate for the + // block size radius of 2) + __local float grayscale_data[196]; + + int idx = get_group_id(0) * get_local_size(0); + int idy = get_group_id(1) * get_local_size(1); + + for (int i = idy - 3, it = 0; i < idy + (int)get_local_size(1) + 3; i++, it++) { + for (int j = idx - 3, jt = 0; j < idx + (int)get_local_size(0) + 3; j++, jt++) { + grayscale_data[jt + it * 14] = read_imagef(grayscale, sampler, (int2)(j, i)).x; + } + } + + barrier(CLK_LOCAL_MEM_FENCE); + + float sumdxdy = sum_deriv_prod(grayscale_data, sobel_mask_x, sobel_mask_y); + float sumdx2 = sum_deriv_pow(grayscale_data, sobel_mask_x); + float sumdy2 = sum_deriv_pow(grayscale_data, sobel_mask_y); + + float trace = sumdx2 + sumdy2; + // r = det(M) - k(trace(M))^2 + // k usually between 0.04 to 0.06 + float r = (sumdx2 * sumdy2 - sumdxdy * sumdxdy) - 0.04f * (trace * trace) * pown(scale, 4); + + // Threshold the r value + harris_buf[loc.x + loc.y * get_image_width(grayscale)] = r * step(HARRIS_THRESHOLD, r); +} + +// Gets a patch centered around a float coordinate from a grayscale image using +// bilinear interpolation +static void get_rect_sub_pix( + __read_only image2d_t grayscale, + float *buffer, + int size_x, + int size_y, + float2 center +) { + float2 offset = ((float2)(size_x, size_y) - 1.0f) * 0.5f; + + for (int i = 0; i < size_y; i++) { + for (int j = 0; j < size_x; j++) { + buffer[i * size_x + j] = read_imagef( + grayscale, + sampler_linear, + (float2)(j, i) + center - offset + ).x * 255.0f; + } + } +} + +// Refines detected features at a sub-pixel level +// +// This function is ported from OpenCV +static float2 corner_sub_pix( + __read_only image2d_t grayscale, + float2 feature, + float *mask +) { + float2 init = feature; + int src_width = get_global_size(0); + int src_height = get_global_size(1); + + const int max_iters = 40; + const float eps = 0.001f * 0.001f; + int i, j, k; + + int iter = 0; + float err = 0; + float subpix[(REFINE_WIN_W + 2) * (REFINE_WIN_H + 2)]; + const float flt_epsilon = 0x1.0p-23f; + + do { + float2 feature_tmp; + float a = 0, b = 0, c = 0, bb1 = 0, bb2 = 0; + + get_rect_sub_pix(grayscale, subpix, REFINE_WIN_W + 2, REFINE_WIN_H + 2, feature); + float *subpix_ptr = subpix; + subpix_ptr += REFINE_WIN_W + 2 + 1; + + // process gradient + for (i = 0, k = 0; i < REFINE_WIN_H; i++, subpix_ptr += REFINE_WIN_W + 2) { + float py = i - REFINE_WIN_HALF_H; + + for (j = 0; j < REFINE_WIN_W; j++, k++) { + float m = mask[k]; + float tgx = subpix_ptr[j + 1] - subpix_ptr[j - 1]; + float tgy = subpix_ptr[j + REFINE_WIN_W + 2] - subpix_ptr[j - REFINE_WIN_W - 2]; + float gxx = tgx * tgx * m; + float gxy = tgx * tgy * m; + float gyy = tgy * tgy * m; + float px = j - REFINE_WIN_HALF_W; + + a += gxx; + b += gxy; + c += gyy; + + bb1 += gxx * px + gxy * py; + bb2 += gxy * px + gyy * py; + } + } + + float det = a * c - b * b; + if (fabs(det) <= flt_epsilon * flt_epsilon) { + break; + } + + // 2x2 matrix inversion + float scale = 1.0f / det; + feature_tmp.x = (float)(feature.x + (c * scale * bb1) - (b * scale * bb2)); + feature_tmp.y = (float)(feature.y - (b * scale * bb1) + (a * scale * bb2)); + err = dot(feature_tmp - feature, feature_tmp - feature); + + feature = feature_tmp; + if (feature.x < 0 || feature.x >= src_width || feature.y < 0 || feature.y >= src_height) { + break; + } + } while (++iter < max_iters && err > eps); + + // Make sure new point isn't too far from the initial point (indicates poor convergence) + if (fabs(feature.x - init.x) > REFINE_WIN_HALF_W || fabs(feature.y - init.y) > REFINE_WIN_HALF_H) { + feature = init; + } + + return feature; +} + +// Performs non-maximum suppression on the harris response and writes the resulting +// feature locations to refined_features. +// +// Assumes that refined_features and the global work sizes are set up such that the image +// is split up into a grid of 32x32 blocks where each block has a single slot in the +// refined_features buffer. This kernel finds the best corner in each block (if the +// block has any) and writes it to the corresponding slot in the buffer. +// +// If subpixel_refine is true, the features are additionally refined at a sub-pixel +// level for increased precision. +__kernel void refine_features( + __read_only image2d_t grayscale, + __global const float *harris_buf, + __global float2 *refined_features, + int subpixel_refine +) { + int2 loc = (int2)(get_global_id(0), get_global_id(1)); + // The location in the grayscale buffer rather than the compacted grid + int2 loc_i = (int2)(loc.x * 32, loc.y * 32); + + float new_val; + float max_val = 0; + float2 loc_max = (float2)(-1, -1); + + int end_x = min(loc_i.x + 32, (int)get_image_dim(grayscale).x - 1); + int end_y = min(loc_i.y + 32, (int)get_image_dim(grayscale).y - 1); + + for (int i = loc_i.x; i < end_x; ++i) { + for (int j = loc_i.y; j < end_y; ++j) { + new_val = harris_buf[i + j * get_image_dim(grayscale).x]; + + if (new_val > max_val) { + max_val = new_val; + loc_max = (float2)(i, j); + } + } + } + + if (max_val == 0) { + // There are no features in this part of the frame + write_to_1d_arrf2(refined_features, loc, loc_max); + return; + } + + if (subpixel_refine) { + float mask[REFINE_WIN_H * REFINE_WIN_W]; + for (int i = 0; i < REFINE_WIN_H; i++) { + float y = (float)(i - REFINE_WIN_HALF_H) / REFINE_WIN_HALF_H; + float vy = exp(-y * y); + + for (int j = 0; j < REFINE_WIN_W; j++) { + float x = (float)(j - REFINE_WIN_HALF_W) / REFINE_WIN_HALF_W; + mask[i * REFINE_WIN_W + j] = (float)(vy * exp(-x * x)); + } + } + + loc_max = corner_sub_pix(grayscale, loc_max, mask); + } + + write_to_1d_arrf2(refined_features, loc, loc_max); +} + +// Extracts BRIEF descriptors from the grayscale src image for the given features +// using the provided sampler. +__kernel void brief_descriptors( + __read_only image2d_t grayscale, + __global const float2 *refined_features, + // for 512 bit descriptors + __global ulong8 *desc_buf, + __global const PointPair *brief_pattern +) { + int2 loc = (int2)(get_global_id(0), get_global_id(1)); + float2 feature = read_from_1d_arrf2(refined_features, loc); + + // There was no feature in this part of the frame + if (feature.x == -1) { + write_to_1d_arrul8(desc_buf, loc, (ulong8)(0)); + return; + } + + ulong8 desc = 0; + ulong *p = &desc; + + for (int i = 0; i < 8; ++i) { + for (int j = 0; j < 64; ++j) { + PointPair pair = brief_pattern[j * (i + 1)]; + float l1 = read_imagef(grayscale, sampler_linear, feature + pair.p1).x; + float l2 = read_imagef(grayscale, sampler_linear, feature + pair.p2).x; + + if (l1 < l2) { + p[i] |= 1UL << j; + } + } + } + + write_to_1d_arrul8(desc_buf, loc, desc); +} + +// Given buffers with descriptors for the current and previous frame, determines +// which ones match, writing correspondences to matches_buf. +// +// Feature and descriptor buffers are assumed to be compacted (each element sourced +// from a 32x32 block in the frame being processed). +__kernel void match_descriptors( + __global const float2 *prev_refined_features, + __global const float2 *refined_features, + __global const ulong8 *desc_buf, + __global const ulong8 *prev_desc_buf, + __global MotionVector *matches_buf +) { + int2 loc = (int2)(get_global_id(0), get_global_id(1)); + ulong8 desc = read_from_1d_arrul8(desc_buf, loc); + const int search_radius = 3; + + MotionVector invalid_vector = (MotionVector) { + (PointPair) { + (float2)(-1, -1), + (float2)(-1, -1) + }, + 0 + }; + + if (desc.s0 == 0 && desc.s1 == 0) { + // There was no feature in this part of the frame + write_to_1d_arrvec( + matches_buf, + loc, + invalid_vector + ); + return; + } + + int2 start = max(loc - search_radius, 0); + int2 end = min(loc + search_radius, (int2)(get_global_size(0) - 1, get_global_size(1) - 1)); + + for (int i = start.x; i < end.x; ++i) { + for (int j = start.y; j < end.y; ++j) { + int2 prev_point = (int2)(i, j); + int total_dist = 0; + + ulong8 prev_desc = read_from_1d_arrul8(prev_desc_buf, prev_point); + + if (prev_desc.s0 == 0 && prev_desc.s1 == 0) { + continue; + } + + ulong *prev_desc_p = &prev_desc; + ulong *desc_p = &desc; + + for (int i = 0; i < 8; i++) { + total_dist += popcount(desc_p[i] ^ prev_desc_p[i]); + } + + if (total_dist < DISTANCE_THRESHOLD) { + write_to_1d_arrvec( + matches_buf, + loc, + (MotionVector) { + (PointPair) { + read_from_1d_arrf2(prev_refined_features, prev_point), + read_from_1d_arrf2(refined_features, loc) + }, + 1 + } + ); + + return; + } + } + } + + // There is no found match for this point + write_to_1d_arrvec( + matches_buf, + loc, + invalid_vector + ); +} + +// Returns the position of the given point after the transform is applied +static float2 transformed_point(float2 p, __global const float *transform) { + float2 ret; + + ret.x = p.x * transform[0] + p.y * transform[1] + transform[2]; + ret.y = p.x * transform[3] + p.y * transform[4] + transform[5]; + + return ret; +} + + +// Performs the given transform on the src image +__kernel void transform( + __read_only image2d_t src, + __write_only image2d_t dst, + __global const float *transform +) { + int2 loc = (int2)(get_global_id(0), get_global_id(1)); + float2 norm = convert_float2(get_image_dim(src)); + + write_imagef( + dst, + loc, + read_imagef( + src, + sampler_linear_mirror, + transformed_point((float2)(loc.x, loc.y), transform) / norm + ) + ); +} + +// Returns the new location of the given point using the given crop bounding box +// and the width and height of the original frame. +static float2 cropped_point( + float2 p, + float2 top_left, + float2 bottom_right, + int2 orig_dim +) { + float2 ret; + + float crop_width = bottom_right.x - top_left.x; + float crop_height = bottom_right.y - top_left.y; + + float width_norm = p.x / (float)orig_dim.x; + float height_norm = p.y / (float)orig_dim.y; + + ret.x = (width_norm * crop_width) + top_left.x; + ret.y = (height_norm * crop_height) + ((float)orig_dim.y - bottom_right.y); + + return ret; +} + +// Upscales the given cropped region to the size of the original frame +__kernel void crop_upscale( + __read_only image2d_t src, + __write_only image2d_t dst, + float2 top_left, + float2 bottom_right +) { + int2 loc = (int2)(get_global_id(0), get_global_id(1)); + + write_imagef( + dst, + loc, + read_imagef( + src, + sampler_linear, + cropped_point((float2)(loc.x, loc.y), top_left, bottom_right, get_image_dim(dst)) + ) + ); +} + +// Draws boxes to represent the given point matches and uses the given transform +// and crop info to make sure their positions are accurate on the transformed frame. +// +// model_matches is an array of three points that were used by the RANSAC process +// to generate the given transform +__kernel void draw_debug_info( + __write_only image2d_t dst, + __global const MotionVector *matches, + __global const MotionVector *model_matches, + int num_model_matches, + __global const float *transform +) { + int loc = get_global_id(0); + MotionVector vec = matches[loc]; + // Black box: matched point that RANSAC considered an outlier + float4 big_rect_color = (float4)(0.1f, 0.1f, 0.1f, 1.0f); + + if (vec.should_consider) { + // Green box: matched point that RANSAC considered an inlier + big_rect_color = (float4)(0.0f, 1.0f, 0.0f, 1.0f); + } + + for (int i = 0; i < num_model_matches; i++) { + if (vec.p.p2.x == model_matches[i].p.p2.x && vec.p.p2.y == model_matches[i].p.p2.y) { + // Orange box: point used to calculate model + big_rect_color = (float4)(1.0f, 0.5f, 0.0f, 1.0f); + } + } + + float2 transformed_p1 = transformed_point(vec.p.p1, transform); + float2 transformed_p2 = transformed_point(vec.p.p2, transform); + + draw_box(dst, (int2)(transformed_p2.x, transformed_p2.y), big_rect_color, 5); + // Small light blue box: the point in the previous frame + draw_box(dst, (int2)(transformed_p1.x, transformed_p1.y), (float4)(0.0f, 0.3f, 0.7f, 1.0f), 3); +} diff --git a/libavfilter/opencl/pad.cl b/libavfilter/opencl/pad.cl new file mode 100644 index 00000000000..e8dd6d6e42d --- /dev/null +++ b/libavfilter/opencl/pad.cl @@ -0,0 +1,36 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | + CLK_FILTER_NEAREST; + +__kernel void pad ( + __read_only image2d_t src, + __write_only image2d_t dst, + float4 color, + int2 xy) +{ + int2 size_src = get_image_dim(src); + int2 loc = (int2)(get_global_id(0), get_global_id(1)); + int2 src_pos = (int2)(get_global_id(0) - xy.x, get_global_id(1) - xy.y); + float4 pixel = loc.x >= size_src.x + xy.x || + loc.y >= size_src.y + xy.y || + loc.x < xy.x || + loc.y < xy.y ? color : read_imagef(src, sampler, src_pos); + write_imagef(dst, loc, pixel); +} diff --git a/libavfilter/opencl/xfade.cl b/libavfilter/opencl/xfade.cl new file mode 100644 index 00000000000..ae2f33c024d --- /dev/null +++ b/libavfilter/opencl/xfade.cl @@ -0,0 +1,145 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +const sampler_t sampler = (CLK_NORMALIZED_COORDS_FALSE | + CLK_FILTER_NEAREST); + +__kernel void fade(__write_only image2d_t dst, + __read_only image2d_t src1, + __read_only image2d_t src2, + float progress) +{ + int2 p = (int2)(get_global_id(0), get_global_id(1)); + + float4 val1 = read_imagef(src1, sampler, p); + float4 val2 = read_imagef(src2, sampler, p); + + write_imagef(dst, p, mix(val2, val1, progress)); +} + +__kernel void wipeleft(__write_only image2d_t dst, + __read_only image2d_t src1, + __read_only image2d_t src2, + float progress) +{ + int s = (int)(get_image_dim(src1).x * progress); + int2 p = (int2)(get_global_id(0), get_global_id(1)); + + float4 val1 = read_imagef(src1, sampler, p); + float4 val2 = read_imagef(src2, sampler, p); + + write_imagef(dst, p, p.x > s ? val2 : val1); +} + +__kernel void wiperight(__write_only image2d_t dst, + __read_only image2d_t src1, + __read_only image2d_t src2, + float progress) +{ + int s = (int)(get_image_dim(src1).x * (1.f - progress)); + int2 p = (int2)(get_global_id(0), get_global_id(1)); + + float4 val1 = read_imagef(src1, sampler, p); + float4 val2 = read_imagef(src2, sampler, p); + + write_imagef(dst, p, p.x > s ? val1 : val2); +} + +__kernel void wipeup(__write_only image2d_t dst, + __read_only image2d_t src1, + __read_only image2d_t src2, + float progress) +{ + int s = (int)(get_image_dim(src1).y * progress); + int2 p = (int2)(get_global_id(0), get_global_id(1)); + + float4 val1 = read_imagef(src1, sampler, p); + float4 val2 = read_imagef(src2, sampler, p); + + write_imagef(dst, p, p.y > s ? val2 : val1); +} + +__kernel void wipedown(__write_only image2d_t dst, + __read_only image2d_t src1, + __read_only image2d_t src2, + float progress) +{ + int s = (int)(get_image_dim(src1).y * (1.f - progress)); + int2 p = (int2)(get_global_id(0), get_global_id(1)); + + float4 val1 = read_imagef(src1, sampler, p); + float4 val2 = read_imagef(src2, sampler, p); + + write_imagef(dst, p, p.y > s ? val1 : val2); +} + +void slide(__write_only image2d_t dst, + __read_only image2d_t src1, + __read_only image2d_t src2, + float progress, + int2 direction) +{ + int w = get_image_dim(src1).x; + int h = get_image_dim(src1).y; + int2 wh = (int2)(w, h); + int2 uv = (int2)(get_global_id(0), get_global_id(1)); + int2 pi = (int2)(progress * w, progress * h); + int2 p = uv + pi * direction; + int2 f = p % wh; + + f = f + (int2)(w, h) * (int2)(f.x < 0, f.y < 0); + float4 val1 = read_imagef(src1, sampler, f); + float4 val2 = read_imagef(src2, sampler, f); + write_imagef(dst, uv, mix(val1, val2, (p.y >= 0) * (h > p.y) * (p.x >= 0) * (w > p.x))); +} + +__kernel void slidedown(__write_only image2d_t dst, + __read_only image2d_t src1, + __read_only image2d_t src2, + float progress) +{ + int2 direction = (int2)(0, 1); + slide(dst, src1, src2, progress, direction); +} + +__kernel void slideup(__write_only image2d_t dst, + __read_only image2d_t src1, + __read_only image2d_t src2, + float progress) +{ + int2 direction = (int2)(0, -1); + slide(dst, src1, src2, progress, direction); +} + +__kernel void slideleft(__write_only image2d_t dst, + __read_only image2d_t src1, + __read_only image2d_t src2, + float progress) +{ + int2 direction = (int2)(-1, 0); + slide(dst, src1, src2, progress, direction); +} + +__kernel void slideright(__write_only image2d_t dst, + __read_only image2d_t src1, + __read_only image2d_t src2, + float progress) +{ + int2 direction = (int2)(1, 0); + slide(dst, src1, src2, progress, direction); +} diff --git a/libavfilter/opencl_source.h b/libavfilter/opencl_source.h index 1a6cd7ca7a8..7e8133090ed 100644 --- a/libavfilter/opencl_source.h +++ b/libavfilter/opencl_source.h @@ -23,11 +23,14 @@ 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_tonemap; extern const char *ff_opencl_source_transpose; extern const char *ff_opencl_source_unsharp; +extern const char *ff_opencl_source_xfade; #endif /* AVFILTER_OPENCL_SOURCE_H */ diff --git a/libavfilter/phase_template.c b/libavfilter/phase_template.c new file mode 100644 index 00000000000..491612b8269 --- /dev/null +++ b/libavfilter/phase_template.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2004 Ville Saari + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +#undef pixel +#undef accumulator +#if DEPTH == 8 +#define pixel uint8_t +#define accumulator int +#else +#define pixel uint16_t +#define accumulator int64_t +#endif + +#define fn3(a,b) a##_##b +#define fn2(a,b) fn3(a,b) +#define fn(a) fn2(a, DEPTH) + +/* + * This macro interpolates the value of both fields at a point halfway + * between lines and takes the squared difference. In field resolution + * the point is a quarter pixel below a line in one field and a quarter + * pixel above a line in other. + * + * (The result is actually multiplied by 25) + */ +#define DIFF(a, as, b, bs) ((t) = ((*(a) - (b)[bs]) << 2) + (a)[(as) << 1] - (b)[-(bs)], (t) * (t)) + +/* + * Find which field combination has the smallest average squared difference + * between the fields. + */ +static enum PhaseMode fn(analyze_plane)(void *ctx, enum PhaseMode mode, AVFrame *old, AVFrame *new) +{ + double bdiff, tdiff, pdiff; + + if (mode == AUTO) { + mode = new->interlaced_frame ? new->top_field_first ? + TOP_FIRST : BOTTOM_FIRST : PROGRESSIVE; + } else if (mode == AUTO_ANALYZE) { + mode = new->interlaced_frame ? new->top_field_first ? + TOP_FIRST_ANALYZE : BOTTOM_FIRST_ANALYZE : FULL_ANALYZE; + } + + if (mode <= BOTTOM_FIRST) { + bdiff = pdiff = tdiff = 65536.0; + } else { + const double factor = 1. / (25. * (1 << (DEPTH - 8)) * (1 << (DEPTH - 8))); + const int ns = new->linesize[0] / sizeof(pixel); + const int os = old->linesize[0] / sizeof(pixel); + const pixel *nptr = (pixel *)new->data[0]; + const pixel *optr = (pixel *)old->data[0]; + const int h = new->height; + const int w = new->width; + accumulator bdif, tdif, pdif; + double scale; + + int top = 0, t; + const pixel *rend, *end = nptr + (h - 2) * ns; + + bdiff = pdiff = tdiff = 0.0; + + nptr += ns; + optr += os; + while (nptr < end) { + pdif = tdif = bdif = 0; + + switch (mode) { + case TOP_FIRST_ANALYZE: + if (top) { + for (rend = nptr + w; nptr < rend; nptr++, optr++) { + pdif += DIFF(nptr, ns, nptr, ns); + tdif += DIFF(nptr, ns, optr, os); + } + } else { + for (rend = nptr + w; nptr < rend; nptr++, optr++) { + pdif += DIFF(nptr, ns, nptr, ns); + tdif += DIFF(optr, os, nptr, ns); + } + } + break; + case BOTTOM_FIRST_ANALYZE: + if (top) { + for (rend = nptr + w; nptr < rend; nptr++, optr++) { + pdif += DIFF(nptr, ns, nptr, ns); + bdif += DIFF(optr, os, nptr, ns); + } + } else { + for (rend = nptr + w; nptr < rend; nptr++, optr++) { + pdif += DIFF(nptr, ns, nptr, ns); + bdif += DIFF(nptr, ns, optr, os); + } + } + break; + case ANALYZE: + if (top) { + for (rend = nptr + w; nptr < rend; nptr++, optr++) { + tdif += DIFF(nptr, ns, optr, os); + bdif += DIFF(optr, os, nptr, ns); + } + } else { + for (rend = nptr + w; nptr < rend; nptr++, optr++) { + bdif += DIFF(nptr, ns, optr, os); + tdif += DIFF(optr, os, nptr, ns); + } + } + break; + case FULL_ANALYZE: + if (top) { + for (rend = nptr + w; nptr < rend; nptr++, optr++) { + pdif += DIFF(nptr, ns, nptr, ns); + tdif += DIFF(nptr, ns, optr, os); + bdif += DIFF(optr, os, nptr, ns); + } + } else { + for (rend = nptr + w; nptr < rend; nptr++, optr++) { + pdif += DIFF(nptr, ns, nptr, ns); + bdif += DIFF(nptr, ns, optr, os); + tdif += DIFF(optr, os, nptr, ns); + } + } + break; + default: + av_assert0(0); + } + + pdiff += (double)pdif; + tdiff += (double)tdif; + bdiff += (double)bdif; + nptr += ns - w; + optr += os - w; + top ^= 1; + } + + scale = 1.0 / (w * (h - 3)) * factor; + pdiff *= scale; + tdiff *= scale; + bdiff *= scale; + + if (mode == TOP_FIRST_ANALYZE) { + bdiff = 65536.0; + } else if (mode == BOTTOM_FIRST_ANALYZE) { + tdiff = 65536.0; + } else if (mode == ANALYZE) { + pdiff = 65536.0; + } + + if (bdiff < pdiff && bdiff < tdiff) { + mode = BOTTOM_FIRST; + } else if (tdiff < pdiff && tdiff < bdiff) { + mode = TOP_FIRST; + } else { + mode = PROGRESSIVE; + } + } + + av_log(ctx, AV_LOG_DEBUG, "mode=%c tdiff=%f bdiff=%f pdiff=%f\n", + mode == BOTTOM_FIRST ? 'b' : mode == TOP_FIRST ? 't' : 'p', + tdiff, bdiff, pdiff); + return mode; +} diff --git a/libavfilter/scale.c b/libavfilter/scale_eval.c similarity index 62% rename from libavfilter/scale.c rename to libavfilter/scale_eval.c index eaee95fac6a..dfec081e15e 100644 --- a/libavfilter/scale.c +++ b/libavfilter/scale_eval.c @@ -19,15 +19,12 @@ */ #include -#include "scale.h" +#include "scale_eval.h" #include "libavutil/eval.h" #include "libavutil/mathematics.h" #include "libavutil/pixdesc.h" static const char *const var_names[] = { - "PI", - "PHI", - "E", "in_w", "iw", "in_h", "ih", "out_w", "ow", @@ -43,9 +40,6 @@ static const char *const var_names[] = { }; enum var_name { - VAR_PI, - VAR_PHI, - VAR_E, VAR_IN_W, VAR_IW, VAR_IN_H, VAR_IH, VAR_OUT_W, VAR_OW, @@ -60,49 +54,6 @@ enum var_name { VARS_NB }; -/** - * This must be kept in sync with var_names so that it is always a - * complete list of var_names with the scale2ref specific names - * appended. scale2ref values must appear in the order they appear - * in the var_name_scale2ref enum but also be below all of the - * non-scale2ref specific values. - */ -static const char *const var_names_scale2ref[] = { - "PI", - "PHI", - "E", - "in_w", "iw", - "in_h", "ih", - "out_w", "ow", - "out_h", "oh", - "a", - "sar", - "dar", - "hsub", - "vsub", - "ohsub", - "ovsub", - "main_w", - "main_h", - "main_a", - "main_sar", - "main_dar", "mdar", - "main_hsub", - "main_vsub", - NULL -}; - -enum var_name_scale2ref { - VAR_S2R_MAIN_W, - VAR_S2R_MAIN_H, - VAR_S2R_MAIN_A, - VAR_S2R_MAIN_SAR, - VAR_S2R_MAIN_DAR, VAR_S2R_MDAR, - VAR_S2R_MAIN_HSUB, - VAR_S2R_MAIN_VSUB, - VARS_S2R_NB -}; - int ff_scale_eval_dimensions(void *log_ctx, const char *w_expr, const char *h_expr, AVFilterLink *inlink, AVFilterLink *outlink, @@ -111,24 +62,10 @@ int ff_scale_eval_dimensions(void *log_ctx, const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); const AVPixFmtDescriptor *out_desc = av_pix_fmt_desc_get(outlink->format); const char *expr; - int w, h; - int factor_w, factor_h; int eval_w, eval_h; int ret; - const char scale2ref = outlink->src->nb_inputs == 2 && outlink->src->inputs[1] == inlink; - double var_values[VARS_NB + VARS_S2R_NB], res; - const AVPixFmtDescriptor *main_desc; - const AVFilterLink *main_link; - const char *const *names = scale2ref ? var_names_scale2ref : var_names; - - if (scale2ref) { - main_link = outlink->src->inputs[0]; - main_desc = av_pix_fmt_desc_get(main_link->format); - } + double var_values[VARS_NB], res; - var_values[VAR_PI] = M_PI; - var_values[VAR_PHI] = M_PHI; - var_values[VAR_E] = M_E; var_values[VAR_IN_W] = var_values[VAR_IW] = inlink->w; var_values[VAR_IN_H] = var_values[VAR_IH] = inlink->h; var_values[VAR_OUT_W] = var_values[VAR_OW] = NAN; @@ -142,40 +79,48 @@ int ff_scale_eval_dimensions(void *log_ctx, var_values[VAR_OHSUB] = 1 << out_desc->log2_chroma_w; var_values[VAR_OVSUB] = 1 << out_desc->log2_chroma_h; - if (scale2ref) { - var_values[VARS_NB + VAR_S2R_MAIN_W] = main_link->w; - var_values[VARS_NB + VAR_S2R_MAIN_H] = main_link->h; - var_values[VARS_NB + VAR_S2R_MAIN_A] = (double) main_link->w / main_link->h; - var_values[VARS_NB + VAR_S2R_MAIN_SAR] = main_link->sample_aspect_ratio.num ? - (double) main_link->sample_aspect_ratio.num / main_link->sample_aspect_ratio.den : 1; - var_values[VARS_NB + VAR_S2R_MAIN_DAR] = var_values[VARS_NB + VAR_S2R_MDAR] = - var_values[VARS_NB + VAR_S2R_MAIN_A] * var_values[VARS_NB + VAR_S2R_MAIN_SAR]; - var_values[VARS_NB + VAR_S2R_MAIN_HSUB] = 1 << main_desc->log2_chroma_w; - var_values[VARS_NB + VAR_S2R_MAIN_VSUB] = 1 << main_desc->log2_chroma_h; - } - /* evaluate width and height */ av_expr_parse_and_eval(&res, (expr = w_expr), - names, var_values, + var_names, var_values, NULL, NULL, NULL, NULL, NULL, 0, log_ctx); eval_w = var_values[VAR_OUT_W] = var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res; if ((ret = av_expr_parse_and_eval(&res, (expr = h_expr), - names, var_values, + var_names, var_values, NULL, NULL, NULL, NULL, NULL, 0, log_ctx)) < 0) goto fail; eval_h = var_values[VAR_OUT_H] = var_values[VAR_OH] = (int) res == 0 ? inlink->h : (int) res; /* evaluate again the width, as it may depend on the output height */ if ((ret = av_expr_parse_and_eval(&res, (expr = w_expr), - names, var_values, + var_names, var_values, NULL, NULL, NULL, NULL, NULL, 0, log_ctx)) < 0) goto fail; eval_w = (int) res == 0 ? inlink->w : (int) res; - w = eval_w; - h = eval_h; + *ret_w = eval_w; + *ret_h = eval_h; + + return 0; + +fail: + av_log(log_ctx, AV_LOG_ERROR, + "Error when evaluating the expression '%s'.\n" + "Maybe the expression for out_w:'%s' or for out_h:'%s' is self-referencing.\n", + expr, w_expr, h_expr); + return ret; +} - /* Check if it is requested that the result has to be divisible by a some +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; + int factor_w, factor_h; + + w = *ret_w; + h = *ret_h; + + /* Check if it is requested that the result has to be divisible by some * factor (w or h = -n with n being the factor). */ factor_w = 1; factor_h = 1; @@ -192,22 +137,41 @@ int ff_scale_eval_dimensions(void *log_ctx, } /* Make sure that the result is divisible by the factor we determined - * earlier. If no factor was set, it is nothing will happen as the default + * earlier. If no factor was set, nothing will happen as the default * factor is 1 */ if (w < 0) w = av_rescale(h, inlink->w, inlink->h * factor_w) * factor_w; if (h < 0) h = av_rescale(w, inlink->h, inlink->w * factor_h) * factor_h; + /* Note that force_original_aspect_ratio may overwrite the previous set + * dimensions so that it is not divisible by the set factors anymore + * unless force_divisible_by is defined as well */ + if (force_original_aspect_ratio) { + int tmp_w = av_rescale(h, inlink->w, inlink->h); + int tmp_h = av_rescale(w, inlink->h, inlink->w); + + if (force_original_aspect_ratio == 1) { + w = FFMIN(tmp_w, w); + h = FFMIN(tmp_h, h); + if (force_divisible_by > 1) { + // round down + w = w / force_divisible_by * force_divisible_by; + h = h / force_divisible_by * force_divisible_by; + } + } else { + w = FFMAX(tmp_w, w); + h = FFMAX(tmp_h, h); + if (force_divisible_by > 1) { + // round up + w = (w + force_divisible_by - 1) / force_divisible_by * force_divisible_by; + h = (h + force_divisible_by - 1) / force_divisible_by * force_divisible_by; + } + } + } + *ret_w = w; *ret_h = h; return 0; - -fail: - av_log(log_ctx, AV_LOG_ERROR, - "Error when evaluating the expression '%s'.\n" - "Maybe the expression for out_w:'%s' or for out_h:'%s' is self-referencing.\n", - expr, w_expr, h_expr); - return ret; } diff --git a/libavfilter/scale_eval.h b/libavfilter/scale_eval.h new file mode 100644 index 00000000000..fceb023fece --- /dev/null +++ b/libavfilter/scale_eval.h @@ -0,0 +1,48 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_SCALE_EVAL_H +#define AVFILTER_SCALE_EVAL_H + +#include "avfilter.h" + +/** + * Parse and evaluate string expressions for width and height. Upon success, + * ff_scale_adjust_dimensions must be called with evaluated width and height + * to obtain actual target dimensions. + * + * Returns 0 upon success, negative value if one of the expressions could + * not be parsed or if NaN was the result of their evaluation. + */ +int ff_scale_eval_dimensions(void *ctx, + const char *w_expr, const char *h_expr, + AVFilterLink *inlink, AVFilterLink *outlink, + int *ret_w, int *ret_h); + +/** + * Transform evaluated width and height obtained from ff_scale_eval_dimensions + * into actual target width and height for scaling. Adjustment can occur if one + * or both of the evaluated values are of the form '-n' or if + * force_original_aspect_ratio is set. + * + * Returns 0. + */ +int ff_scale_adjust_dimensions(AVFilterLink *inlink, + int *ret_w, int *ret_h, + int force_original_aspect_ratio, int force_divisible_by); +#endif diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c index 800ba6a83fc..c7c38362748 100644 --- a/libavfilter/setpts.c +++ b/libavfilter/setpts.c @@ -33,6 +33,7 @@ #include "libavutil/time.h" #include "audio.h" #include "avfilter.h" +#include "filters.h" #include "internal.h" #include "video.h" @@ -154,6 +155,28 @@ static inline char *double2int64str(char *buf, double v) return buf; } +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); + } + 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(); + + if (frame) { + if (inlink->type == AVMEDIA_TYPE_VIDEO) { + setpts->var_values[VAR_INTERLACED] = frame->interlaced_frame; + } else if (inlink->type == AVMEDIA_TYPE_AUDIO) { + setpts->var_values[VAR_S] = frame->nb_samples; + setpts->var_values[VAR_NB_SAMPLES] = frame->nb_samples; + } + } + + return av_expr_eval(setpts->expr, setpts->var_values, NULL); +} #define d2istr(v) double2int64str((char[BUF_SIZE]){0}, v) static int filter_frame(AVFilterLink *inlink, AVFrame *frame) @@ -162,23 +185,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) int64_t in_pts = frame->pts; double d; - if (isnan(setpts->var_values[VAR_STARTPTS])) { - setpts->var_values[VAR_STARTPTS] = TS2D(frame->pts); - setpts->var_values[VAR_STARTT ] = TS2T(frame->pts, inlink->time_base); - } - setpts->var_values[VAR_PTS ] = TS2D(frame->pts); - setpts->var_values[VAR_T ] = TS2T(frame->pts, inlink->time_base); - setpts->var_values[VAR_POS ] = frame->pkt_pos == -1 ? NAN : frame->pkt_pos; - setpts->var_values[VAR_RTCTIME ] = av_gettime(); - - if (inlink->type == AVMEDIA_TYPE_VIDEO) { - setpts->var_values[VAR_INTERLACED] = frame->interlaced_frame; - } else if (inlink->type == AVMEDIA_TYPE_AUDIO) { - setpts->var_values[VAR_S] = frame->nb_samples; - setpts->var_values[VAR_NB_SAMPLES] = frame->nb_samples; - } - - d = av_expr_eval(setpts->expr, setpts->var_values, NULL); + d = eval_pts(setpts, inlink, frame, frame->pts); frame->pts = D2TS(d); av_log(inlink->dst, AV_LOG_TRACE, @@ -216,6 +223,41 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) return ff_filter_frame(inlink->dst->outputs[0], frame); } +static int activate(AVFilterContext *ctx) +{ + SetPTSContext *setpts = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *in; + int status; + int64_t pts; + int ret; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + ret = ff_inlink_consume_frame(inlink, &in); + if (ret < 0) + return ret; + if (ret > 0) + return filter_frame(inlink, in); + + 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)); + ff_outlink_set_status(outlink, status, D2TS(d)); + return 0; + } + + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; +} + static av_cold void uninit(AVFilterContext *ctx) { SetPTSContext *setpts = ctx->priv; @@ -239,7 +281,6 @@ static const AVFilterPad avfilter_vf_setpts_inputs[] = { .name = "default", .type = AVMEDIA_TYPE_VIDEO, .config_props = config_input, - .filter_frame = filter_frame, }, { NULL } }; @@ -256,6 +297,7 @@ AVFilter ff_vf_setpts = { .name = "setpts", .description = NULL_IF_CONFIG_SMALL("Set PTS for the output video frame."), .init = init, + .activate = activate, .uninit = uninit, .priv_size = sizeof(SetPTSContext), @@ -276,7 +318,6 @@ static const AVFilterPad asetpts_inputs[] = { .name = "default", .type = AVMEDIA_TYPE_AUDIO, .config_props = config_input, - .filter_frame = filter_frame, }, { NULL } }; @@ -293,6 +334,7 @@ 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, diff --git a/libavfilter/settb.c b/libavfilter/settb.c index 83616c13610..dba52cff313 100644 --- a/libavfilter/settb.c +++ b/libavfilter/settb.c @@ -34,6 +34,7 @@ #include "libavutil/rational.h" #include "audio.h" #include "avfilter.h" +#include "filters.h" #include "internal.h" #include "video.h" @@ -104,22 +105,58 @@ static int config_output_props(AVFilterLink *outlink) return 0; } -static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +static int64_t rescale_pts(AVFilterLink *inlink, AVFilterLink *outlink, int64_t orig_pts) { AVFilterContext *ctx = inlink->dst; - AVFilterLink *outlink = ctx->outputs[0]; + int64_t new_pts = orig_pts; if (av_cmp_q(inlink->time_base, outlink->time_base)) { - int64_t orig_pts = frame->pts; - frame->pts = av_rescale_q(frame->pts, inlink->time_base, outlink->time_base); + new_pts = av_rescale_q(orig_pts, inlink->time_base, outlink->time_base); av_log(ctx, AV_LOG_DEBUG, "tb:%d/%d pts:%"PRId64" -> tb:%d/%d pts:%"PRId64"\n", inlink ->time_base.num, inlink ->time_base.den, orig_pts, - outlink->time_base.num, outlink->time_base.den, frame->pts); + outlink->time_base.num, outlink->time_base.den, new_pts); } + return new_pts; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + + frame->pts = rescale_pts(inlink, outlink, frame->pts); + return ff_filter_frame(outlink, frame); } +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *in; + int status; + int64_t pts; + int ret; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + ret = ff_inlink_consume_frame(inlink, &in); + if (ret < 0) + return ret; + if (ret > 0) + return filter_frame(inlink, in); + + if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { + ff_outlink_set_status(outlink, status, rescale_pts(inlink, outlink, pts)); + return 0; + } + + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; +} + #if CONFIG_SETTB_FILTER DEFINE_OPTIONS(settb, VIDEO); @@ -129,7 +166,6 @@ static const AVFilterPad avfilter_vf_settb_inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = filter_frame, }, { NULL } }; @@ -150,6 +186,7 @@ AVFilter ff_vf_settb = { .priv_class = &settb_class, .inputs = avfilter_vf_settb_inputs, .outputs = avfilter_vf_settb_outputs, + .activate = activate, }; #endif /* CONFIG_SETTB_FILTER */ @@ -162,7 +199,6 @@ static const AVFilterPad avfilter_af_asettb_inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = filter_frame, }, { NULL } }; @@ -183,5 +219,6 @@ AVFilter ff_af_asettb = { .inputs = avfilter_af_asettb_inputs, .outputs = avfilter_af_asettb_outputs, .priv_class = &asettb_class, + .activate = activate, }; #endif /* CONFIG_ASETTB_FILTER */ diff --git a/libavfilter/split.c b/libavfilter/split.c index 89af360cb09..622838d83d2 100644 --- a/libavfilter/split.c +++ b/libavfilter/split.c @@ -26,6 +26,7 @@ #include #include "libavutil/attributes.h" +#include "libavutil/avstring.h" #include "libavutil/internal.h" #include "libavutil/mem.h" #include "libavutil/opt.h" @@ -48,12 +49,10 @@ static av_cold int split_init(AVFilterContext *ctx) int i, ret; for (i = 0; i < s->nb_outputs; i++) { - char name[32]; AVFilterPad pad = { 0 }; - snprintf(name, sizeof(name), "output%d", i); pad.type = ctx->filter->inputs[0].type; - pad.name = av_strdup(name); + pad.name = av_asprintf("output%d", i); if (!pad.name) return AVERROR(ENOMEM); diff --git a/libavfilter/src_movie.c b/libavfilter/src_movie.c index bcabfcc4c29..476b8d3d967 100644 --- a/libavfilter/src_movie.c +++ b/libavfilter/src_movie.c @@ -89,8 +89,8 @@ static const AVOption movie_options[]= { { "si", "set stream index", OFFSET(stream_index), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS }, { "seek_point", "set seekpoint (seconds)", OFFSET(seek_point_d), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, 0, (INT64_MAX-1) / 1000000, FLAGS }, { "sp", "set seekpoint (seconds)", OFFSET(seek_point_d), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, 0, (INT64_MAX-1) / 1000000, FLAGS }, - { "streams", "set streams", OFFSET(stream_specs), AV_OPT_TYPE_STRING, {.str = 0}, CHAR_MAX, CHAR_MAX, FLAGS }, - { "s", "set streams", OFFSET(stream_specs), AV_OPT_TYPE_STRING, {.str = 0}, CHAR_MAX, CHAR_MAX, FLAGS }, + { "streams", "set streams", OFFSET(stream_specs), AV_OPT_TYPE_STRING, {.str = 0}, 0, 0, FLAGS }, + { "s", "set streams", OFFSET(stream_specs), AV_OPT_TYPE_STRING, {.str = 0}, 0, 0, FLAGS }, { "loop", "set loop count", OFFSET(loop_count), AV_OPT_TYPE_INT, {.i64 = 1}, 0, INT_MAX, FLAGS }, { "discontinuity", "set discontinuity threshold", OFFSET(discontinuity_threshold), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, INT64_MAX, FLAGS }, { NULL }, @@ -153,14 +153,14 @@ static AVStream *find_stream(void *log, AVFormatContext *avf, const char *spec) return found; } -static int open_stream(void *log, MovieStream *st) +static int open_stream(AVFilterContext *ctx, MovieStream *st) { AVCodec *codec; int ret; codec = avcodec_find_decoder(st->st->codecpar->codec_id); if (!codec) { - av_log(log, AV_LOG_ERROR, "Failed to find any codec\n"); + av_log(ctx, AV_LOG_ERROR, "Failed to find any codec\n"); return AVERROR(EINVAL); } @@ -173,9 +173,10 @@ static int open_stream(void *log, MovieStream *st) return ret; st->codec_ctx->refcounted_frames = 1; + st->codec_ctx->thread_count = ff_filter_get_nb_threads(ctx); if ((ret = avcodec_open2(st->codec_ctx, codec, NULL)) < 0) { - av_log(log, AV_LOG_ERROR, "Failed to open codec\n"); + av_log(ctx, AV_LOG_ERROR, "Failed to open codec\n"); return ret; } diff --git a/libavfilter/ssim.h b/libavfilter/ssim.h index ac0395a22a5..a6a41aabe65 100644 --- a/libavfilter/ssim.h +++ b/libavfilter/ssim.h @@ -28,7 +28,7 @@ typedef struct SSIMDSPContext { void (*ssim_4x4_line)(const uint8_t *buf, ptrdiff_t buf_stride, const uint8_t *ref, ptrdiff_t ref_stride, int (*sums)[4], int w); - float (*ssim_end_line)(const int (*sum0)[4], const int (*sum1)[4], int w); + double (*ssim_end_line)(const int (*sum0)[4], const int (*sum1)[4], int w); } SSIMDSPContext; void ff_ssim_init_x86(SSIMDSPContext *dsp); diff --git a/libavfilter/tests/.gitignore b/libavfilter/tests/.gitignore new file mode 100644 index 00000000000..65ef86f2e5b --- /dev/null +++ b/libavfilter/tests/.gitignore @@ -0,0 +1,4 @@ +/drawutils +/filtfmts +/formats +/integral diff --git a/libavfilter/tests/formats.c b/libavfilter/tests/formats.c index 5450742b687..ee497f3b907 100644 --- a/libavfilter/tests/formats.c +++ b/libavfilter/tests/formats.c @@ -22,6 +22,78 @@ #undef printf +const int64_t avfilter_all_channel_layouts[] = { + AV_CH_FRONT_CENTER, + AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY, + AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER, + AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_CENTER, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER, + AV_CH_FRONT_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT, + -1 +}; + int main(void) { const int64_t *cl; diff --git a/libavfilter/tinterlace.h b/libavfilter/tinterlace.h index 5bcb9a583a4..020887ff347 100644 --- a/libavfilter/tinterlace.h +++ b/libavfilter/tinterlace.h @@ -36,6 +36,13 @@ #define TINTERLACE_FLAG_VLPF 01 #define TINTERLACE_FLAG_CVLPF 2 #define TINTERLACE_FLAG_EXACT_TB 4 +#define TINTERLACE_FLAG_BYPASS_IL 8 + +enum VLPFilter { + VLPF_OFF = 0, + VLPF_LIN = 1, + VLPF_CMP = 2, +}; enum TInterlaceMode { MODE_MERGE = 0, @@ -59,6 +66,7 @@ typedef struct TInterlaceContext { int mode; ///< TInterlaceMode, interlace mode selected AVRational preout_time_base; int flags; ///< flags affecting interlacing algorithm + int lowpass; ///< legacy interlace filter lowpass mode int frame; ///< number of the output frame int vsub; ///< chroma vertical subsampling AVFrame *cur; diff --git a/libavfilter/transform.c b/libavfilter/transform.c index f92fc4d42fb..f4f9e0a47d9 100644 --- a/libavfilter/transform.c +++ b/libavfilter/transform.c @@ -103,12 +103,19 @@ INTERPOLATE_METHOD(interpolate_biquadratic) } } -void avfilter_get_matrix(float x_shift, float y_shift, float angle, float zoom, float *matrix) { - matrix[0] = zoom * cos(angle); +void ff_get_matrix( + float x_shift, + float y_shift, + float angle, + float scale_x, + float scale_y, + float *matrix +) { + matrix[0] = scale_x * cos(angle); matrix[1] = -sin(angle); matrix[2] = x_shift; matrix[3] = -matrix[1]; - matrix[4] = matrix[0]; + matrix[4] = scale_y * cos(angle); matrix[5] = y_shift; matrix[6] = 0; matrix[7] = 0; diff --git a/libavfilter/transform.h b/libavfilter/transform.h index 07436bfccb6..9b0c19ceca9 100644 --- a/libavfilter/transform.h +++ b/libavfilter/transform.h @@ -60,20 +60,28 @@ enum FillMethod { #define FILL_DEFAULT FILL_ORIGINAL /** - * Get an affine transformation matrix from a given translation, rotation, and - * zoom factor. The matrix will look like: + * Get an affine transformation matrix from given translation, rotation, and + * zoom factors. The matrix will look like: * - * [ zoom * cos(angle), -sin(angle), x_shift, - * sin(angle), zoom * cos(angle), y_shift, - * 0, 0, 1 ] + * [ scale_x * cos(angle), -sin(angle), x_shift, + * sin(angle), scale_y * cos(angle), y_shift, + * 0, 0, 1 ] * - * @param x_shift horizontal translation - * @param y_shift vertical translation - * @param angle rotation in radians - * @param zoom scale percent (1.0 = 100%) - * @param matrix 9-item affine transformation matrix + * @param x_shift horizontal translation + * @param y_shift vertical translation + * @param angle rotation in radians + * @param scale_x x scale percent (1.0 = 100%) + * @param scale_y y scale percent (1.0 = 100%) + * @param matrix 9-item affine transformation matrix */ -void avfilter_get_matrix(float x_shift, float y_shift, float angle, float zoom, float *matrix); +void ff_get_matrix( + float x_shift, + float y_shift, + float angle, + float scale_x, + float scale_y, + float *matrix +); /** * Add two matrices together. result = m1 + m2. diff --git a/libavfilter/transpose.h b/libavfilter/transpose.h index aa262b9487f..4e850ef067a 100644 --- a/libavfilter/transpose.h +++ b/libavfilter/transpose.h @@ -18,6 +18,9 @@ #ifndef AVFILTER_TRANSPOSE_H #define AVFILTER_TRANSPOSE_H +#include +#include + enum PassthroughType { TRANSPOSE_PT_TYPE_NONE, TRANSPOSE_PT_TYPE_LANDSCAPE, @@ -34,4 +37,14 @@ enum TransposeDir { TRANSPOSE_VFLIP, }; +typedef struct TransVtable { + void (*transpose_8x8)(uint8_t *src, ptrdiff_t src_linesize, + uint8_t *dst, ptrdiff_t dst_linesize); + void (*transpose_block)(uint8_t *src, ptrdiff_t src_linesize, + uint8_t *dst, ptrdiff_t dst_linesize, + int w, int h); +} TransVtable; + +void ff_transpose_init_x86(TransVtable *v, int pixstep); + #endif diff --git a/libavfilter/trim.c b/libavfilter/trim.c index 1dbbabbb931..889fe96b9bb 100644 --- a/libavfilter/trim.c +++ b/libavfilter/trim.c @@ -300,7 +300,8 @@ static int atrim_filter_frame(AVFilterLink *inlink, AVFrame *frame) s->nb_samples += frame->nb_samples; start_sample = FFMAX(0, start_sample); end_sample = FFMIN(frame->nb_samples, end_sample); - av_assert0(start_sample < end_sample || (start_sample == end_sample && !frame->nb_samples)); + if (start_sample >= end_sample || !frame->nb_samples) + goto drop; if (start_sample) { AVFrame *out = ff_get_audio_buffer(ctx->outputs[0], end_sample - start_sample); diff --git a/libavfilter/v360.h b/libavfilter/v360.h new file mode 100644 index 00000000000..e9a47593c9a --- /dev/null +++ b/libavfilter/v360.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2019 Eugene Lyapustin + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_V360_H +#define AVFILTER_V360_H +#include "avfilter.h" + +enum StereoFormats { + STEREO_2D, + STEREO_SBS, + STEREO_TB, + NB_STEREO_FMTS, +}; + +enum Projections { + EQUIRECTANGULAR, + CUBEMAP_3_2, + CUBEMAP_6_1, + EQUIANGULAR, + FLAT, + DUAL_FISHEYE, + BARREL, + CUBEMAP_1_6, + STEREOGRAPHIC, + MERCATOR, + BALL, + HAMMER, + SINUSOIDAL, + FISHEYE, + PANNINI, + CYLINDRICAL, + PERSPECTIVE, + TETRAHEDRON, + BARREL_SPLIT, + TSPYRAMID, + HEQUIRECTANGULAR, + NB_PROJECTIONS, +}; + +enum InterpMethod { + NEAREST, + BILINEAR, + LAGRANGE9, + BICUBIC, + LANCZOS, + SPLINE16, + GAUSSIAN, + NB_INTERP_METHODS, +}; + +enum Faces { + TOP_LEFT, + TOP_MIDDLE, + TOP_RIGHT, + BOTTOM_LEFT, + BOTTOM_MIDDLE, + BOTTOM_RIGHT, + NB_FACES, +}; + +enum Direction { + RIGHT, ///< Axis +X + LEFT, ///< Axis -X + UP, ///< Axis +Y + DOWN, ///< Axis -Y + FRONT, ///< Axis -Z + BACK, ///< Axis +Z + NB_DIRECTIONS, +}; + +enum Rotation { + ROT_0, + ROT_90, + ROT_180, + ROT_270, + NB_ROTATIONS, +}; + +enum RotationOrder { + YAW, + PITCH, + ROLL, + NB_RORDERS, +}; + +typedef struct XYRemap { + int16_t u[4][4]; + int16_t v[4][4]; + float ker[4][4]; +} XYRemap; + +typedef struct V360Context { + const AVClass *class; + int in, out; + int interp; + int alpha; + int width, height; + char *in_forder; + char *out_forder; + char *in_frot; + char *out_frot; + char *rorder; + + int in_cubemap_face_order[6]; + int out_cubemap_direction_order[6]; + int in_cubemap_face_rotation[6]; + int out_cubemap_face_rotation[6]; + int rotation_order[3]; + + int in_stereo, out_stereo; + + float in_pad, out_pad; + int fin_pad, fout_pad; + + float yaw, pitch, roll; + + int ih_flip, iv_flip; + int h_flip, v_flip, d_flip; + int in_transpose, out_transpose; + + float h_fov, v_fov, d_fov; + float ih_fov, iv_fov, id_fov; + float flat_range[2]; + float iflat_range[2]; + + float rot_mat[3][3]; + + float input_mirror_modifier[2]; + float output_mirror_modifier[3]; + + int in_width, in_height; + int out_width, out_height; + + int pr_width[4], pr_height[4]; + + int in_offset_w[4], in_offset_h[4]; + int out_offset_w[4], out_offset_h[4]; + + int planewidth[4], planeheight[4]; + int inplanewidth[4], inplaneheight[4]; + int uv_linesize[4]; + int nb_planes; + int nb_allocated; + int elements; + int mask_size; + int max_value; + + int16_t *u[2], *v[2]; + int16_t *ker[2]; + uint8_t *mask; + unsigned map[4]; + + int (*in_transform)(const struct V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv); + + int (*out_transform)(const struct V360Context *s, + int i, int j, int width, int height, + float *vec); + + void (*calculate_kernel)(float du, float dv, const XYRemap *rmap, + int16_t *u, int16_t *v, int16_t *ker); + + int (*remap_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); + + void (*remap_line)(uint8_t *dst, int width, const uint8_t *const src, ptrdiff_t in_linesize, + const int16_t *const u, const int16_t *const v, const int16_t *const ker); +} V360Context; + +void ff_v360_init(V360Context *s, int depth); +void ff_v360_init_x86(V360Context *s, int depth); + +#endif /* AVFILTER_V360_H */ diff --git a/libavfilter/version.h b/libavfilter/version.h index cc5b23b70bb..96b14d6794e 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -30,7 +30,7 @@ #include "libavutil/version.h" #define LIBAVFILTER_VERSION_MAJOR 7 -#define LIBAVFILTER_VERSION_MINOR 57 +#define LIBAVFILTER_VERSION_MINOR 85 #define LIBAVFILTER_VERSION_MICRO 100 @@ -59,6 +59,9 @@ #ifndef FF_API_FILTER_GET_SET #define FF_API_FILTER_GET_SET (LIBAVFILTER_VERSION_MAJOR < 8) #endif +#ifndef FF_API_SWS_PARAM_OPTION +#define FF_API_SWS_PARAM_OPTION (LIBAVFILTER_VERSION_MAJOR < 8) +#endif #ifndef FF_API_NEXT #define FF_API_NEXT (LIBAVFILTER_VERSION_MAJOR < 8) #endif diff --git a/libavfilter/vf_addroi.c b/libavfilter/vf_addroi.c new file mode 100644 index 00000000000..489998ce731 --- /dev/null +++ b/libavfilter/vf_addroi.c @@ -0,0 +1,269 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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/eval.h" +#include "libavutil/opt.h" +#include "avfilter.h" +#include "internal.h" + +enum { + X, Y, W, H, + NB_PARAMS, +}; +static const char *addroi_param_names[] = { + "x", "y", "w", "h", +}; + +enum { + VAR_IW, + VAR_IH, + NB_VARS, +}; +static const char *const addroi_var_names[] = { + "iw", + "ih", +}; + +typedef struct AddROIContext { + const AVClass *class; + + char *region_str[NB_PARAMS]; + AVExpr *region_expr[NB_PARAMS]; + + int region[NB_PARAMS]; + AVRational qoffset; + + int clear; +} AddROIContext; + +static int addroi_config_input(AVFilterLink *inlink) +{ + AVFilterContext *avctx = inlink->dst; + AddROIContext *ctx = avctx->priv; + int i; + double vars[NB_VARS]; + double val; + + vars[VAR_IW] = inlink->w; + vars[VAR_IH] = inlink->h; + + for (i = 0; i < NB_PARAMS; i++) { + int max_value; + switch (i) { + case X: max_value = inlink->w; break; + case Y: max_value = inlink->h; break; + case W: max_value = inlink->w - ctx->region[X]; break; + case H: max_value = inlink->h - ctx->region[Y]; break; + } + + val = av_expr_eval(ctx->region_expr[i], vars, NULL); + if (val < 0.0) { + av_log(avctx, AV_LOG_WARNING, "Calculated value %g for %s is " + "less than zero - using zero instead.\n", val, + addroi_param_names[i]); + val = 0.0; + } else if (val > max_value) { + av_log(avctx, AV_LOG_WARNING, "Calculated value %g for %s is " + "greater than maximum allowed value %d - " + "using %d instead.\n", val, addroi_param_names[i], + max_value, max_value); + val = max_value; + } + ctx->region[i] = val; + } + + return 0; +} + +static int addroi_filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterContext *avctx = inlink->dst; + AVFilterLink *outlink = avctx->outputs[0]; + AddROIContext *ctx = avctx->priv; + AVRegionOfInterest *roi; + AVFrameSideData *sd; + int err; + + if (ctx->clear) { + av_frame_remove_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST); + sd = NULL; + } else { + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST); + } + if (sd) { + const AVRegionOfInterest *old_roi; + uint32_t old_roi_size; + AVBufferRef *roi_ref; + int nb_roi, i; + + old_roi = (const AVRegionOfInterest*)sd->data; + old_roi_size = old_roi->self_size; + av_assert0(old_roi_size && sd->size % old_roi_size == 0); + nb_roi = sd->size / old_roi_size + 1; + + roi_ref = av_buffer_alloc(sizeof(*roi) * nb_roi); + if (!roi_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + roi = (AVRegionOfInterest*)roi_ref->data; + + for (i = 0; i < nb_roi - 1; i++) { + old_roi = (const AVRegionOfInterest*) + (sd->data + old_roi_size * i); + + roi[i] = (AVRegionOfInterest) { + .self_size = sizeof(*roi), + .top = old_roi->top, + .bottom = old_roi->bottom, + .left = old_roi->left, + .right = old_roi->right, + .qoffset = old_roi->qoffset, + }; + } + + roi[nb_roi - 1] = (AVRegionOfInterest) { + .self_size = sizeof(*roi), + .top = ctx->region[Y], + .bottom = ctx->region[Y] + ctx->region[H], + .left = ctx->region[X], + .right = ctx->region[X] + ctx->region[W], + .qoffset = ctx->qoffset, + }; + + av_frame_remove_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST); + + sd = av_frame_new_side_data_from_buf(frame, + AV_FRAME_DATA_REGIONS_OF_INTEREST, + roi_ref); + if (!sd) { + av_buffer_unref(&roi_ref); + err = AVERROR(ENOMEM); + goto fail; + } + + } else { + sd = av_frame_new_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST, + sizeof(AVRegionOfInterest)); + if (!sd) { + err = AVERROR(ENOMEM); + goto fail; + } + roi = (AVRegionOfInterest*)sd->data; + *roi = (AVRegionOfInterest) { + .self_size = sizeof(*roi), + .top = ctx->region[Y], + .bottom = ctx->region[Y] + ctx->region[H], + .left = ctx->region[X], + .right = ctx->region[X] + ctx->region[W], + .qoffset = ctx->qoffset, + }; + } + + return ff_filter_frame(outlink, frame); + +fail: + av_frame_free(&frame); + return err; +} + +static av_cold int addroi_init(AVFilterContext *avctx) +{ + AddROIContext *ctx = avctx->priv; + int i, err; + + for (i = 0; i < NB_PARAMS; i++) { + err = av_expr_parse(&ctx->region_expr[i], ctx->region_str[i], + addroi_var_names, NULL, NULL, NULL, NULL, + 0, avctx); + if (err < 0) { + av_log(ctx, AV_LOG_ERROR, + "Error parsing %s expression '%s'.\n", + addroi_param_names[i], ctx->region_str[i]); + return err; + } + } + + return 0; +} + +static av_cold void addroi_uninit(AVFilterContext *avctx) +{ + AddROIContext *ctx = avctx->priv; + int i; + + for (i = 0; i < NB_PARAMS; i++) { + av_expr_free(ctx->region_expr[i]); + ctx->region_expr[i] = NULL; + } +} + +#define OFFSET(x) offsetof(AddROIContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM +static const AVOption addroi_options[] = { + { "x", "Region distance from left edge of frame.", + OFFSET(region_str[X]), AV_OPT_TYPE_STRING, { .str = "0" }, .flags = FLAGS }, + { "y", "Region distance from top edge of frame.", + OFFSET(region_str[Y]), AV_OPT_TYPE_STRING, { .str = "0" }, .flags = FLAGS }, + { "w", "Region width.", + OFFSET(region_str[W]), AV_OPT_TYPE_STRING, { .str = "0" }, .flags = FLAGS }, + { "h", "Region height.", + OFFSET(region_str[H]), AV_OPT_TYPE_STRING, { .str = "0" }, .flags = FLAGS }, + + { "qoffset", "Quantisation offset to apply in the region.", + OFFSET(qoffset), AV_OPT_TYPE_RATIONAL, { .dbl = -0.1 }, -1, +1, FLAGS }, + + { "clear", "Remove any existing regions of interest before adding the new one.", + OFFSET(clear), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, + + { NULL } +}; + +AVFILTER_DEFINE_CLASS(addroi); + +static const AVFilterPad addroi_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = addroi_config_input, + .filter_frame = addroi_filter_frame, + }, + { NULL } +}; + +static const AVFilterPad addroi_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter ff_vf_addroi = { + .name = "addroi", + .description = NULL_IF_CONFIG_SMALL("Add region of interest to frame."), + .init = addroi_init, + .uninit = addroi_uninit, + + .priv_size = sizeof(AddROIContext), + .priv_class = &addroi_class, + + .inputs = addroi_inputs, + .outputs = addroi_outputs, +}; diff --git a/libavfilter/vf_alphamerge.c b/libavfilter/vf_alphamerge.c index c5602b62278..85b6d9b61a3 100644 --- a/libavfilter/vf_alphamerge.c +++ b/libavfilter/vf_alphamerge.c @@ -26,6 +26,7 @@ #include #include "libavutil/imgutils.h" +#include "libavutil/opt.h" #include "libavutil/pixfmt.h" #include "avfilter.h" #include "drawutils.h" @@ -37,6 +38,8 @@ enum { Y, U, V, A }; typedef struct AlphaMergeContext { + const AVClass *class; + int is_packed_rgb; uint8_t rgba_map[4]; AVFrame *main_frame; @@ -77,9 +80,9 @@ static int query_formats(AVFilterContext *ctx) static int config_input_main(AVFilterLink *inlink) { - AlphaMergeContext *merge = inlink->dst->priv; - merge->is_packed_rgb = - ff_fill_rgba_map(merge->rgba_map, inlink->format) >= 0 && + AlphaMergeContext *s = inlink->dst->priv; + s->is_packed_rgb = + ff_fill_rgba_map(s->rgba_map, inlink->format) >= 0 && inlink->format != AV_PIX_FMT_GBRAP; return 0; } @@ -109,15 +112,15 @@ static void draw_frame(AVFilterContext *ctx, AVFrame *main_buf, AVFrame *alpha_buf) { - AlphaMergeContext *merge = ctx->priv; + AlphaMergeContext *s = ctx->priv; int h = main_buf->height; - if (merge->is_packed_rgb) { + if (s->is_packed_rgb) { int x, y; uint8_t *pin, *pout; for (y = 0; y < h; y++) { pin = alpha_buf->data[0] + y * alpha_buf->linesize[0]; - pout = main_buf->data[0] + y * main_buf->linesize[0] + merge->rgba_map[A]; + pout = main_buf->data[0] + y * main_buf->linesize[0] + s->rgba_map[A]; for (x = 0; x < main_buf->width; x++) { *pout = *pin; pin += 1; @@ -154,7 +157,8 @@ static int activate(AVFilterContext *ctx) } if (s->main_frame && s->alpha_frame) { - draw_frame(ctx, s->main_frame, s->alpha_frame); + if (!ctx->is_disabled) + draw_frame(ctx, s->main_frame, s->alpha_frame); ret = ff_filter_frame(outlink, s->main_frame); av_frame_free(&s->alpha_frame); s->main_frame = NULL; @@ -203,13 +207,21 @@ static const AVFilterPad alphamerge_outputs[] = { { NULL } }; +static const AVOption alphamerge_options[] = { + { NULL } +}; + +AVFILTER_DEFINE_CLASS(alphamerge); + AVFilter ff_vf_alphamerge = { .name = "alphamerge", .description = NULL_IF_CONFIG_SMALL("Copy the luma value of the second " "input into the alpha channel of the first input."), .priv_size = sizeof(AlphaMergeContext), + .priv_class = &alphamerge_class, .query_formats = query_formats, .inputs = alphamerge_inputs, .outputs = alphamerge_outputs, .activate = activate, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, }; diff --git a/libavfilter/vf_amplify.c b/libavfilter/vf_amplify.c index 48dcb93a67c..84d9765a4cf 100644 --- a/libavfilter/vf_amplify.c +++ b/libavfilter/vf_amplify.c @@ -71,6 +71,11 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, + AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA422P16, + AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_NONE }; AVFilterFormats *formats = ff_make_format_list(pixel_fmts); @@ -231,7 +236,7 @@ static av_cold void uninit(AVFilterContext *ctx) if (s->frames) { for (i = 0; i < s->nb_frames; i++) - av_frame_free(&s->frames[i]); + av_frame_free(&s->frames[i]); } av_freep(&s->frames); } @@ -254,29 +259,37 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->frames[s->nb_inputs - 1] = in; } - out = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!out) - return AVERROR(ENOMEM); - out->pts = s->frames[0]->pts; + if (!ctx->is_disabled) { + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) + return AVERROR(ENOMEM); + av_frame_copy_props(out, s->frames[0]); - td.out = out; - td.in = s->frames; - ctx->internal->execute(ctx, amplify_frame, &td, NULL, FFMIN(s->height[1], ff_filter_get_nb_threads(ctx))); + td.out = out; + td.in = s->frames; + ctx->internal->execute(ctx, amplify_frame, &td, NULL, FFMIN(s->height[1], ff_filter_get_nb_threads(ctx))); + } else { + out = av_frame_clone(s->frames[s->radius]); + if (!out) + return AVERROR(ENOMEM); + out->pts = s->frames[0]->pts; + } return ff_filter_frame(outlink, out); } #define OFFSET(x) offsetof(AmplifyContext, 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 amplify_options[] = { { "radius", "set radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64=2}, 1, 63, .flags = FLAGS }, - { "factor", "set factor", OFFSET(factor), AV_OPT_TYPE_FLOAT, {.dbl=2}, 0, UINT16_MAX, .flags = FLAGS }, - { "threshold", "set threshold", OFFSET(threshold), AV_OPT_TYPE_FLOAT, {.dbl=10}, 0, UINT16_MAX, .flags = FLAGS }, - { "tolerance", "set tolerance", OFFSET(tolerance), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, UINT16_MAX, .flags = FLAGS }, - { "low", "set low limit for amplification", OFFSET(llimit), AV_OPT_TYPE_INT, {.i64=UINT16_MAX}, 0, UINT16_MAX, .flags = FLAGS }, - { "high", "set high limit for amplification", OFFSET(hlimit), AV_OPT_TYPE_INT, {.i64=UINT16_MAX}, 0, UINT16_MAX, .flags = FLAGS }, - { "planes", "set what planes to filter", OFFSET(planes), AV_OPT_TYPE_FLAGS, {.i64=7}, 0, 15, FLAGS }, + { "factor", "set factor", OFFSET(factor), AV_OPT_TYPE_FLOAT, {.dbl=2}, 0, UINT16_MAX, .flags = VFT }, + { "threshold", "set threshold", OFFSET(threshold), AV_OPT_TYPE_FLOAT, {.dbl=10}, 0, UINT16_MAX, .flags = VFT }, + { "tolerance", "set tolerance", OFFSET(tolerance), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, UINT16_MAX, .flags = VFT }, + { "low", "set low limit for amplification", OFFSET(llimit), AV_OPT_TYPE_INT, {.i64=UINT16_MAX}, 0, UINT16_MAX, .flags = VFT }, + { "high", "set high limit for amplification", OFFSET(hlimit), AV_OPT_TYPE_INT, {.i64=UINT16_MAX}, 0, UINT16_MAX, .flags = VFT }, + { "planes", "set what planes to filter", OFFSET(planes), AV_OPT_TYPE_FLAGS, {.i64=7}, 0, 15, VFT }, { NULL }, }; @@ -310,5 +323,6 @@ AVFilter ff_vf_amplify = { .inputs = inputs, .init = init, .uninit = uninit, - .flags = AVFILTER_FLAG_SLICE_THREADS, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vf_aspect.c b/libavfilter/vf_aspect.c index c042698ef7a..70e7fedc976 100644 --- a/libavfilter/vf_aspect.c +++ b/libavfilter/vf_aspect.c @@ -78,7 +78,7 @@ static int filter_frame(AVFilterLink *link, AVFrame *frame) static inline void compute_dar(AVRational *dar, AVRational sar, int w, int h) { if (sar.num && sar.den) { - av_reduce(&dar->num, &dar->den, sar.num * w, sar.den * h, INT_MAX); + av_reduce(&dar->num, &dar->den, sar.num * (int64_t)w, sar.den * (int64_t)h, INT_MAX); } else { av_reduce(&dar->num, &dar->den, w, h, INT_MAX); } diff --git a/libavfilter/vf_atadenoise.c b/libavfilter/vf_atadenoise.c index b7d958b8321..e1a822045f2 100644 --- a/libavfilter/vf_atadenoise.c +++ b/libavfilter/vf_atadenoise.c @@ -33,6 +33,7 @@ #define FF_BUFQUEUE_SIZE 129 #include "bufferqueue.h" +#include "atadenoise.h" #include "formats.h" #include "internal.h" #include "video.h" @@ -44,6 +45,7 @@ typedef struct ATADenoiseContext { float fthra[4], fthrb[4]; int thra[4], thrb[4]; + int algorithm; int planes; int nb_planes; @@ -57,10 +59,13 @@ typedef struct ATADenoiseContext { int available; int (*filter_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); + + ATADenoiseDSPContext dsp; } ATADenoiseContext; #define OFFSET(x) offsetof(ATADenoiseContext, x) -#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM +#define VF AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM static const AVOption atadenoise_options[] = { { "0a", "set threshold A for 1st plane", OFFSET(fthra[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.02}, 0, 0.3, FLAGS }, @@ -69,8 +74,11 @@ static const AVOption atadenoise_options[] = { { "1b", "set threshold B for 2nd plane", OFFSET(fthrb[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.04}, 0, 5.0, FLAGS }, { "2a", "set threshold A for 3rd plane", OFFSET(fthra[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.02}, 0, 0.3, FLAGS }, { "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, 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" }, { NULL } }; @@ -100,6 +108,11 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, + AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA422P16, + AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_NONE }; AVFilterFormats *formats = ff_make_format_list(pixel_fmts); @@ -125,7 +138,103 @@ typedef struct ThreadData { AVFrame *in, *out; } ThreadData; -static int filter_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +#define FILTER_ROW(type, name) \ +static void filter_row##name(const uint8_t *ssrc, uint8_t *ddst, \ + const uint8_t *ssrcf[SIZE], \ + int w, int mid, int size, \ + int thra, int thrb) \ +{ \ + const type *src = (const type *)ssrc; \ + const type **srcf = (const type **)ssrcf; \ + type *dst = (type *)ddst; \ + \ + for (int x = 0; x < w; x++) { \ + const int srcx = src[x]; \ + unsigned lsumdiff = 0, rsumdiff = 0; \ + unsigned ldiff, rdiff; \ + unsigned sum = srcx; \ + int l = 0, r = 0; \ + int srcjx, srcix; \ + \ + for (int j = mid - 1, i = mid + 1; j >= 0 && i < size; j--, i++) { \ + srcjx = srcf[j][x]; \ + \ + ldiff = FFABS(srcx - srcjx); \ + lsumdiff += ldiff; \ + if (ldiff > thra || \ + lsumdiff > thrb) \ + break; \ + l++; \ + sum += srcjx; \ + \ + srcix = srcf[i][x]; \ + \ + rdiff = FFABS(srcx - srcix); \ + rsumdiff += rdiff; \ + if (rdiff > thra || \ + rsumdiff > thrb) \ + break; \ + r++; \ + sum += srcix; \ + } \ + \ + dst[x] = (sum + ((r + l + 1) >> 1)) / (r + l + 1); \ + } \ +} + +FILTER_ROW(uint8_t, 8) +FILTER_ROW(uint16_t, 16) + +#define FILTER_ROW_SERIAL(type, name) \ +static void filter_row##name##_serial(const uint8_t *ssrc, uint8_t *ddst, \ + const uint8_t *ssrcf[SIZE], \ + int w, int mid, int size, \ + int thra, int thrb) \ +{ \ + const type *src = (const type *)ssrc; \ + const type **srcf = (const type **)ssrcf; \ + type *dst = (type *)ddst; \ + \ + for (int x = 0; x < w; x++) { \ + const int srcx = src[x]; \ + unsigned lsumdiff = 0, rsumdiff = 0; \ + unsigned ldiff, rdiff; \ + unsigned sum = srcx; \ + int l = 0, r = 0; \ + int srcjx, srcix; \ + \ + for (int j = mid - 1; j >= 0; j--) { \ + srcjx = srcf[j][x]; \ + \ + ldiff = FFABS(srcx - srcjx); \ + lsumdiff += ldiff; \ + if (ldiff > thra || \ + lsumdiff > thrb) \ + break; \ + l++; \ + sum += srcjx; \ + } \ + \ + for (int i = mid + 1; i < size; i++) { \ + srcix = srcf[i][x]; \ + \ + rdiff = FFABS(srcx - srcix); \ + rsumdiff += rdiff; \ + if (rdiff > thra || \ + rsumdiff > thrb) \ + break; \ + r++; \ + sum += srcix; \ + } \ + \ + dst[x] = (sum + ((r + l + 1) >> 1)) / (r + l + 1); \ + } \ +} + +FILTER_ROW_SERIAL(uint8_t, 8) +FILTER_ROW_SERIAL(uint16_t, 16) + +static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ATADenoiseContext *s = ctx->priv; ThreadData *td = arg; @@ -133,7 +242,7 @@ static int filter_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs AVFrame *out = td->out; const int size = s->size; const int mid = s->mid; - int p, x, y, i, j; + int p, y, i; for (p = 0; p < s->nb_planes; p++) { const int h = s->planeheight[p]; @@ -158,38 +267,7 @@ static int filter_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs srcf[i] = data[i] + slice_start * linesize[i]; for (y = slice_start; y < slice_end; y++) { - for (x = 0; x < w; x++) { - const int srcx = src[x]; - unsigned lsumdiff = 0, rsumdiff = 0; - unsigned ldiff, rdiff; - unsigned sum = srcx; - int l = 0, r = 0; - int srcjx, srcix; - - for (j = mid - 1, i = mid + 1; j >= 0 && i < size; j--, i++) { - srcjx = srcf[j][x]; - - ldiff = FFABS(srcx - srcjx); - lsumdiff += ldiff; - if (ldiff > thra || - lsumdiff > thrb) - break; - l++; - sum += srcjx; - - srcix = srcf[i][x]; - - rdiff = FFABS(srcx - srcix); - rsumdiff += rdiff; - if (rdiff > thra || - rsumdiff > thrb) - break; - r++; - sum += srcix; - } - - dst[x] = sum / (r + l + 1); - } + s->dsp.filter_row(src, dst, srcf, w, mid, size, thra, thrb); dst += out->linesize[p]; src += in->linesize[p]; @@ -202,83 +280,6 @@ static int filter_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs return 0; } -static int filter_slice16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - ATADenoiseContext *s = ctx->priv; - ThreadData *td = arg; - AVFrame *in = td->in; - AVFrame *out = td->out; - const int size = s->size; - const int mid = s->mid; - int p, x, y, i, j; - - for (p = 0; p < s->nb_planes; p++) { - const int h = s->planeheight[p]; - const int w = s->planewidth[p]; - const int slice_start = (h * jobnr) / nb_jobs; - const int slice_end = (h * (jobnr+1)) / nb_jobs; - const uint16_t *src = (uint16_t *)(in->data[p] + slice_start * in->linesize[p]); - uint16_t *dst = (uint16_t *)(out->data[p] + slice_start * out->linesize[p]); - const int thra = s->thra[p]; - const int thrb = s->thrb[p]; - const uint8_t **data = (const uint8_t **)s->data[p]; - const int *linesize = (const int *)s->linesize[p]; - const uint16_t *srcf[SIZE]; - - if (!((1 << p) & s->planes)) { - av_image_copy_plane((uint8_t *)dst, out->linesize[p], (uint8_t *)src, in->linesize[p], - w * 2, slice_end - slice_start); - continue; - } - - for (i = 0; i < s->size; i++) - srcf[i] = (const uint16_t *)(data[i] + slice_start * linesize[i]); - - for (y = slice_start; y < slice_end; y++) { - for (x = 0; x < w; x++) { - const int srcx = src[x]; - unsigned lsumdiff = 0, rsumdiff = 0; - unsigned ldiff, rdiff; - unsigned sum = srcx; - int l = 0, r = 0; - int srcjx, srcix; - - for (j = mid - 1, i = mid + 1; j >= 0 && i < size; j--, i++) { - srcjx = srcf[j][x]; - - ldiff = FFABS(srcx - srcjx); - lsumdiff += ldiff; - if (ldiff > thra || - lsumdiff > thrb) - break; - l++; - sum += srcjx; - - srcix = srcf[i][x]; - - rdiff = FFABS(srcx - srcix); - rsumdiff += rdiff; - if (rdiff > thra || - rsumdiff > thrb) - break; - r++; - sum += srcix; - } - - dst[x] = sum / (r + l + 1); - } - - dst += out->linesize[p] / 2; - src += in->linesize[p] / 2; - - for (i = 0; i < size; i++) - srcf[i] += linesize[i] / 2; - } - } - - return 0; -} - static int config_input(AVFilterLink *inlink) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); @@ -294,10 +295,11 @@ static int config_input(AVFilterLink *inlink) s->planewidth[0] = s->planewidth[3] = inlink->w; depth = desc->comp[0].depth; + s->filter_slice = filter_slice; if (depth == 8) - s->filter_slice = filter_slice8; + s->dsp.filter_row = s->algorithm == PARALLEL ? filter_row8 : filter_row8_serial; else - s->filter_slice = filter_slice16; + s->dsp.filter_row = s->algorithm == PARALLEL ? filter_row16 : filter_row16_serial; s->thra[0] = s->fthra[0] * (1 << depth) - 1; s->thra[1] = s->fthra[1] * (1 << depth) - 1; @@ -306,6 +308,9 @@ static int config_input(AVFilterLink *inlink) s->thrb[1] = s->fthrb[1] * (1 << depth) - 1; s->thrb[2] = s->fthrb[2] * (1 << depth) - 1; + if (ARCH_X86) + ff_atadenoise_init_x86(&s->dsp, depth, s->algorithm); + return 0; } @@ -405,6 +410,21 @@ static av_cold void uninit(AVFilterContext *ctx) ff_bufqueue_discard_all(&s->q); } +static int process_command(AVFilterContext *ctx, + const char *cmd, + const char *arg, + char *res, + int res_len, + int flags) +{ + int ret = ff_filter_process_command(ctx, cmd, arg, res, res_len, flags); + + if (ret < 0) + return ret; + + return config_input(ctx->inputs[0]); +} + static const AVFilterPad inputs[] = { { .name = "default", @@ -435,4 +455,5 @@ AVFilter ff_vf_atadenoise = { .inputs = inputs, .outputs = outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, + .process_command = process_command, }; diff --git a/libavfilter/vf_avgblur.c b/libavfilter/vf_avgblur.c index c7b88427fd5..7fd65eabfcf 100644 --- a/libavfilter/vf_avgblur.c +++ b/libavfilter/vf_avgblur.c @@ -46,7 +46,7 @@ typedef struct AverageBlurContext { } AverageBlurContext; #define OFFSET(x) offsetof(AverageBlurContext, x) -#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption avgblur_options[] = { { "sizeX", "set horizontal size", OFFSET(radius), AV_OPT_TYPE_INT, {.i64=1}, 1, 1024, FLAGS }, @@ -138,7 +138,6 @@ static int filter_vertically_##name(AVFilterContext *ctx, void *arg, int jobnr, float acc = 0; \ int count = 0; \ \ - ptr = buffer + x; \ src = s->buffer + x; \ \ for (i = 0; i < radius; i++) { \ @@ -149,7 +148,7 @@ static int filter_vertically_##name(AVFilterContext *ctx, void *arg, int jobnr, \ src = s->buffer + x; \ ptr = buffer + x; \ - for (i = 0; i <= radius; i++) { \ + for (i = 0; i + radius < height && i <= radius; i++) { \ acc += src[(i + radius) * width]; \ count++; \ ptr[i * linesize] = acc / count; \ @@ -238,6 +237,7 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA422P12, AV_PIX_FMT_YUVA444P12, 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, @@ -323,4 +323,5 @@ AVFilter ff_vf_avgblur = { .inputs = avgblur_inputs, .outputs = avgblur_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vf_avgblur_vulkan.c b/libavfilter/vf_avgblur_vulkan.c new file mode 100644 index 00000000000..89763345d9a --- /dev/null +++ b/libavfilter/vf_avgblur_vulkan.c @@ -0,0 +1,412 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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.h" +#include "internal.h" + +#define CGS 32 + +typedef struct AvgBlurVulkanContext { + VulkanFilterContext vkctx; + + int initialized; + FFVkExecContext *exec; + VulkanPipeline *pl_hor; + VulkanPipeline *pl_ver; + + /* Shader updators, must be in the main filter struct */ + VkDescriptorImageInfo input_images[3]; + VkDescriptorImageInfo tmp_images[3]; + VkDescriptorImageInfo output_images[3]; + + int size_x; + int size_y; + int planes; +} 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, } ) +}; + +static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) +{ + int err; + SPIRVShader *shd; + AvgBlurVulkanContext *s = ctx->priv; + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + VkSampler *sampler = ff_vk_init_sampler(ctx, 1, VK_FILTER_LINEAR); + + VulkanDescriptorSetBinding desc_i[2] = { + { + .name = "input_img", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER_ARRAY4(*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, + }, + }; + + if (!sampler) + return AVERROR_EXTERNAL; + + s->vkctx.queue_family_idx = s->vkctx.hwctx->queue_family_comp_index; + s->vkctx.queue_count = GET_QUEUE_COUNT(s->vkctx.hwctx, 0, 1, 0); + s->vkctx.cur_queue_idx = av_get_random_seed() % s->vkctx.queue_count; + + { /* 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(ctx); + if (!s->pl_hor) + return AVERROR(ENOMEM); + + shd = ff_vk_init_shader(ctx, s->pl_hor, "avgblur_compute_hor", + VK_SHADER_STAGE_COMPUTE_BIT); + + ff_vk_set_compute_shader_sizes(ctx, shd, (int [3]){ CGS, 1, 1 }); + + RET(ff_vk_add_descriptor_set(ctx, s->pl_hor, shd, desc_i, 2, 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(ctx, shd, "main")); + + RET(ff_vk_init_pipeline_layout(ctx, s->pl_hor)); + RET(ff_vk_init_compute_pipeline(ctx, s->pl_hor)); + } + + { /* 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(ctx); + if (!s->pl_ver) + return AVERROR(ENOMEM); + + shd = ff_vk_init_shader(ctx, s->pl_ver, "avgblur_compute_ver", + VK_SHADER_STAGE_COMPUTE_BIT); + + ff_vk_set_compute_shader_sizes(ctx, shd, (int [3]){ 1, CGS, 1 }); + + RET(ff_vk_add_descriptor_set(ctx, s->pl_ver, shd, desc_i, 2, 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, } ); + } + GLSLC(0, } ); + + RET(ff_vk_compile_shader(ctx, shd, "main")); + + RET(ff_vk_init_pipeline_layout(ctx, s->pl_ver)); + RET(ff_vk_init_compute_pipeline(ctx, s->pl_ver)); + } + + /* Execution context */ + RET(ff_vk_create_exec_ctx(ctx, &s->exec)); + + s->initialized = 1; + + return 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; + AVVkFrame *in = (AVVkFrame *)in_f->data[0]; + AVVkFrame *tmp = (AVVkFrame *)tmp_f->data[0]; + AVVkFrame *out = (AVVkFrame *)out_f->data[0]; + int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + + /* Update descriptors and init the exec context */ + ff_vk_start_exec_recording(avctx, s->exec); + cmd_buf = ff_vk_get_exec_buf(avctx, s->exec); + + for (int i = 0; i < planes; i++) { + RET(ff_vk_create_imageview(avctx, s->exec, &s->input_images[i].imageView, + in->img[i], + av_vkfmt_from_pixfmt(s->vkctx.input_format)[i], + ff_comp_identity_map)); + + RET(ff_vk_create_imageview(avctx, s->exec, &s->tmp_images[i].imageView, + tmp->img[i], + av_vkfmt_from_pixfmt(s->vkctx.output_format)[i], + ff_comp_identity_map)); + + RET(ff_vk_create_imageview(avctx, s->exec, &s->output_images[i].imageView, + out->img[i], + av_vkfmt_from_pixfmt(s->vkctx.output_format)[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(avctx, s->pl_hor, 0); + ff_vk_update_descriptor_set(avctx, 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, + }, + }; + + vkCmdPipelineBarrier(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(avctx, s->exec, s->pl_hor); + + vkCmdDispatch(cmd_buf, FFALIGN(s->vkctx.output_width, CGS)/CGS, + s->vkctx.output_height, 1); + + ff_vk_bind_pipeline_exec(avctx, s->exec, s->pl_ver); + + vkCmdDispatch(cmd_buf, s->vkctx.output_width, + FFALIGN(s->vkctx.output_height, CGS)/CGS, 1); + + ff_vk_add_exec_dep(avctx, s->exec, in_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + ff_vk_add_exec_dep(avctx, s->exec, out_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + + err = ff_vk_submit_exec_queue(avctx, s->exec); + if (err) + return err; + + return err; + +fail: + ff_vk_discard_exec_deps(avctx, s->exec); + return err; +} + +static int avgblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) +{ + int err; + AVFrame *tmp = NULL, *out = NULL; + AVFilterContext *ctx = link->dst; + AvgBlurVulkanContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + err = AVERROR(ENOMEM); + goto fail; + } + + tmp = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + err = AVERROR(ENOMEM); + goto fail; + } + + if (!s->initialized) + RET(init_filter(ctx, in)); + + RET(process_frames(ctx, out, tmp, in)); + + 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; +} + +static void avgblur_vulkan_uninit(AVFilterContext *avctx) +{ + AvgBlurVulkanContext *s = avctx->priv; + + ff_vk_filter_uninit(avctx); + + s->initialized = 0; +} + +#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 }, + { "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 }, +}; + +AVFILTER_DEFINE_CLASS(avgblur_vulkan); + +static const AVFilterPad avgblur_vulkan_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = &avgblur_vulkan_filter_frame, + .config_props = &ff_vk_filter_config_input, + }, + { NULL } +}; + +static const AVFilterPad avgblur_vulkan_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &ff_vk_filter_config_output, + }, + { NULL } +}; + +AVFilter ff_vf_avgblur_vulkan = { + .name = "avgblur_vulkan", + .description = NULL_IF_CONFIG_SMALL("Apply avgblur mask to input video"), + .priv_size = sizeof(AvgBlurVulkanContext), + .init = &ff_vk_filter_init, + .uninit = &avgblur_vulkan_uninit, + .query_formats = &ff_vk_filter_query_formats, + .inputs = avgblur_vulkan_inputs, + .outputs = avgblur_vulkan_outputs, + .priv_class = &avgblur_vulkan_class, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_bilateral.c b/libavfilter/vf_bilateral.c new file mode 100644 index 00000000000..3c9d8006d90 --- /dev/null +++ b/libavfilter/vf_bilateral.c @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2017 Ming Yang + * Copyright (c) 2019 Paul B Mahol + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +typedef struct BilateralContext { + const AVClass *class; + + float sigmaS; + float sigmaR; + int planes; + + int nb_planes; + int depth; + int planewidth[4]; + int planeheight[4]; + + float range_table[65536]; + + float *img_out_f; + float *img_temp; + float *map_factor_a; + float *map_factor_b; + float *slice_factor_a; + float *slice_factor_b; + float *line_factor_a; + float *line_factor_b; +} BilateralContext; + +#define OFFSET(x) offsetof(BilateralContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption bilateral_options[] = { + { "sigmaS", "set spatial sigma", OFFSET(sigmaS), AV_OPT_TYPE_FLOAT, {.dbl=0.1}, 0.0, 10, FLAGS }, + { "sigmaR", "set range sigma", OFFSET(sigmaR), AV_OPT_TYPE_FLOAT, {.dbl=0.1}, 0.0, 1, FLAGS }, + { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=1}, 0, 0xF, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(bilateral); + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, + AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, + AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, + 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_YUV440P12, + 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_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9, + AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, + 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_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_NONE + }; + + return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); +} + +static int config_input(AVFilterLink *inlink) +{ + BilateralContext *s = inlink->dst->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + float inv_sigma_range; + + s->depth = desc->comp[0].depth; + inv_sigma_range = 1.0f / (s->sigmaR * ((1 << s->depth) - 1)); + + //compute a lookup table + for (int i = 0; i < (1 << s->depth); i++) + s->range_table[i] = expf(-i * inv_sigma_range); + + s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); + s->planewidth[0] = s->planewidth[3] = inlink->w; + s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); + s->planeheight[0] = s->planeheight[3] = inlink->h; + + s->nb_planes = av_pix_fmt_count_planes(inlink->format); + + s->img_out_f = av_calloc(inlink->w * inlink->h, sizeof(float)); + s->img_temp = av_calloc(inlink->w * inlink->h, sizeof(float)); + s->map_factor_a = av_calloc(inlink->w * inlink->h, sizeof(float)); + s->map_factor_b = av_calloc(inlink->w * inlink->h, sizeof(float)); + s->slice_factor_a = av_calloc(inlink->w, sizeof(float)); + s->slice_factor_b = av_calloc(inlink->w, sizeof(float)); + s->line_factor_a = av_calloc(inlink->w, sizeof(float)); + s->line_factor_b = av_calloc(inlink->w, sizeof(float)); + + if (!s->img_out_f || + !s->img_temp || + !s->map_factor_a || + !s->map_factor_b || + !s->slice_factor_a || + !s->slice_factor_a || + !s->line_factor_a || + !s->line_factor_a) + return AVERROR(ENOMEM); + + return 0; +} + +#define BILATERAL(type, name) \ +static void bilateral_##name(BilateralContext *s, const uint8_t *ssrc, uint8_t *ddst, \ + float sigma_spatial, float sigma_range, \ + int width, int height, int src_linesize, int dst_linesize) \ +{ \ + type *dst = (type *)ddst; \ + const type *src = (const type *)ssrc; \ + float *img_out_f = s->img_out_f, *img_temp = s->img_temp; \ + float *map_factor_a = s->map_factor_a, *map_factor_b = s->map_factor_b; \ + float *slice_factor_a = s->slice_factor_a, *slice_factor_b = s->slice_factor_b; \ + float *line_factor_a = s->line_factor_a, *line_factor_b = s->line_factor_b; \ + float *range_table = s->range_table; \ + float alpha = expf(-sqrtf(2.f) / (sigma_spatial * width)); \ + float ypr, ycr, *ycy, *ypy, *xcy, fp, fc; \ + float inv_alpha_ = 1 - alpha; \ + float *ycf, *ypf, *xcf, *in_factor; \ + const type *tcy, *tpy; \ + int h1; \ + \ + for (int y = 0; y < height; y++) { \ + float *temp_factor_x, *temp_x = &img_temp[y * width]; \ + const type *in_x = &src[y * src_linesize]; \ + const type *texture_x = &src[y * src_linesize]; \ + type tpr; \ + \ + *temp_x++ = ypr = *in_x++; \ + tpr = *texture_x++; \ + \ + temp_factor_x = &map_factor_a[y * width]; \ + *temp_factor_x++ = fp = 1; \ + \ + for (int x = 1; x < width; x++) { \ + float weight, alpha_; \ + int range_dist; \ + type tcr = *texture_x++; \ + type dr = abs(tcr - tpr); \ + \ + range_dist = dr; \ + weight = range_table[range_dist]; \ + alpha_ = weight*alpha; \ + *temp_x++ = ycr = inv_alpha_*(*in_x++) + alpha_*ypr; \ + tpr = tcr; \ + ypr = ycr; \ + *temp_factor_x++ = fc = inv_alpha_ + alpha_ * fp; \ + fp = fc; \ + } \ + --temp_x; *temp_x = 0.5f*((*temp_x) + (*--in_x)); \ + tpr = *--texture_x; \ + ypr = *in_x; \ + \ + --temp_factor_x; *temp_factor_x = 0.5f*((*temp_factor_x) + 1); \ + fp = 1; \ + \ + for (int x = width - 2; x >= 0; x--) { \ + type tcr = *--texture_x; \ + type dr = abs(tcr - tpr); \ + int range_dist = dr; \ + float weight = range_table[range_dist]; \ + float alpha_ = weight * alpha; \ + \ + ycr = inv_alpha_ * (*--in_x) + alpha_ * ypr; \ + --temp_x; *temp_x = 0.5f*((*temp_x) + ycr); \ + tpr = tcr; \ + ypr = ycr; \ + \ + fc = inv_alpha_ + alpha_*fp; \ + --temp_factor_x; \ + *temp_factor_x = 0.5f*((*temp_factor_x) + fc); \ + fp = fc; \ + } \ + } \ + memcpy(img_out_f, img_temp, sizeof(float) * width); \ + \ + alpha = expf(-sqrtf(2.f) / (sigma_spatial * height)); \ + inv_alpha_ = 1 - alpha; \ + in_factor = map_factor_a; \ + memcpy(map_factor_b, in_factor, sizeof(float) * width); \ + for (int y = 1; y < height; y++) { \ + tpy = &src[(y - 1) * src_linesize]; \ + tcy = &src[y * src_linesize]; \ + xcy = &img_temp[y * width]; \ + ypy = &img_out_f[(y - 1) * width]; \ + ycy = &img_out_f[y * width]; \ + \ + xcf = &in_factor[y * width]; \ + ypf = &map_factor_b[(y - 1) * width]; \ + ycf = &map_factor_b[y * width]; \ + for (int x = 0; x < width; x++) { \ + type dr = abs((*tcy++) - (*tpy++)); \ + int range_dist = dr; \ + float weight = range_table[range_dist]; \ + float alpha_ = weight*alpha; \ + \ + *ycy++ = inv_alpha_*(*xcy++) + alpha_*(*ypy++); \ + *ycf++ = inv_alpha_*(*xcf++) + alpha_*(*ypf++); \ + } \ + } \ + h1 = height - 1; \ + ycf = line_factor_a; \ + ypf = line_factor_b; \ + memcpy(ypf, &in_factor[h1 * width], sizeof(float) * width); \ + for (int x = 0; x < width; x++) \ + map_factor_b[h1 * width + x] = 0.5f*(map_factor_b[h1 * width + x] + ypf[x]); \ + \ + ycy = slice_factor_a; \ + ypy = slice_factor_b; \ + memcpy(ypy, &img_temp[h1 * width], sizeof(float) * width); \ + for (int x = 0, k = 0; x < width; x++) { \ + int idx = h1 * width + x; \ + img_out_f[idx] = 0.5f*(img_out_f[idx] + ypy[k++]) / map_factor_b[h1 * width + x]; \ + } \ + \ + for (int y = h1 - 1; y >= 0; y--) { \ + float *ycf_, *ypf_, *factor_; \ + float *ycy_, *ypy_, *out_; \ + \ + tpy = &src[(y + 1) * src_linesize]; \ + tcy = &src[y * src_linesize]; \ + xcy = &img_temp[y * width]; \ + ycy_ = ycy; \ + ypy_ = ypy; \ + out_ = &img_out_f[y * width]; \ + \ + xcf = &in_factor[y * width]; \ + ycf_ = ycf; \ + ypf_ = ypf; \ + factor_ = &map_factor_b[y * width]; \ + for (int x = 0; x < width; x++) { \ + type dr = abs((*tcy++) - (*tpy++)); \ + int range_dist = dr; \ + float weight = range_table[range_dist]; \ + float alpha_ = weight*alpha; \ + float ycc, fcc = inv_alpha_*(*xcf++) + alpha_*(*ypf_++); \ + \ + *ycf_++ = fcc; \ + *factor_ = 0.5f * (*factor_ + fcc); \ + \ + ycc = inv_alpha_*(*xcy++) + alpha_*(*ypy_++); \ + *ycy_++ = ycc; \ + *out_ = 0.5f * (*out_ + ycc) / (*factor_); \ + out_++; \ + factor_++; \ + } \ + \ + memcpy(ypy, ycy, sizeof(float) * width); \ + memcpy(ypf, ycf, sizeof(float) * width); \ + } \ + \ + for (int i = 0; i < height; i++) \ + for (int j = 0; j < width; j++) \ + dst[j + i * dst_linesize] = img_out_f[i * width + j]; \ +} + +BILATERAL(uint8_t, byte) +BILATERAL(uint16_t, word) + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + BilateralContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *out; + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + + for (int plane = 0; plane < s->nb_planes; plane++) { + if (!(s->planes & (1 << plane))) { + av_image_copy_plane(out->data[plane], out->linesize[plane], + in->data[plane], in->linesize[plane], + s->planewidth[plane] * ((s->depth + 7) / 8), s->planeheight[plane]); + continue; + } + + if (s->depth <= 8) + bilateral_byte(s, in->data[plane], out->data[plane], s->sigmaS, s->sigmaR, + s->planewidth[plane], s->planeheight[plane], + in->linesize[plane], out->linesize[plane]); + else + bilateral_word(s, in->data[plane], out->data[plane], s->sigmaS, s->sigmaR, + s->planewidth[plane], s->planeheight[plane], + in->linesize[plane] / 2, out->linesize[plane] / 2); + } + + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + BilateralContext *s = ctx->priv; + + av_freep(&s->img_out_f); + av_freep(&s->img_temp); + av_freep(&s->map_factor_a); + av_freep(&s->map_factor_b); + av_freep(&s->slice_factor_a); + av_freep(&s->slice_factor_b); + av_freep(&s->line_factor_a); + av_freep(&s->line_factor_b); +} + +static const AVFilterPad bilateral_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad bilateral_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter ff_vf_bilateral = { + .name = "bilateral", + .description = NULL_IF_CONFIG_SMALL("Apply Bilateral filter."), + .priv_size = sizeof(BilateralContext), + .priv_class = &bilateral_class, + .uninit = uninit, + .query_formats = query_formats, + .inputs = bilateral_inputs, + .outputs = bilateral_outputs, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, +}; diff --git a/libavfilter/vf_bitplanenoise.c b/libavfilter/vf_bitplanenoise.c index 4ec3a225726..94aa24abec8 100644 --- a/libavfilter/vf_bitplanenoise.c +++ b/libavfilter/vf_bitplanenoise.c @@ -122,7 +122,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) if (s->depth <= 8) { for (plane = 0; plane < s->nb_planes; plane++) { - const int linesize = in->linesize[plane]; + const int linesize = s->planeheight[plane] > 1 ? in->linesize[plane] : 0; const int dlinesize = out->linesize[plane]; uint8_t *val = in->data[plane]; uint8_t *dst = s->filter ? out->data[plane]: NULL; @@ -151,7 +151,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) } } else { for (plane = 0; plane < s->nb_planes; plane++) { - const int linesize = in->linesize[plane] / 2; + const int linesize = s->planeheight[plane] > 1 ? in->linesize[plane] / 2 : 0; const int dlinesize = out->linesize[plane] / 2; uint16_t *val = (uint16_t *)in->data[plane]; uint16_t *dst = s->filter ? (uint16_t *)out->data[plane] : NULL; diff --git a/libavfilter/vf_blackdetect.c b/libavfilter/vf_blackdetect.c index 06ef9988d1f..4b5d8ff5618 100644 --- a/libavfilter/vf_blackdetect.c +++ b/libavfilter/vf_blackdetect.c @@ -136,7 +136,6 @@ static int request_frame(AVFilterLink *outlink) return ret; } -// TODO: document metadata static int filter_frame(AVFilterLink *inlink, AVFrame *picref) { AVFilterContext *ctx = inlink->dst; diff --git a/libavfilter/vf_blend.c b/libavfilter/vf_blend.c index d6036c10e2b..d59bed823f1 100644 --- a/libavfilter/vf_blend.c +++ b/libavfilter/vf_blend.c @@ -19,6 +19,7 @@ */ #include "libavutil/imgutils.h" +#include "libavutil/intfloat.h" #include "libavutil/eval.h" #include "libavutil/opt.h" #include "libavutil/pixfmt.h" @@ -100,11 +101,11 @@ typedef struct ThreadData { { "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" },\ - { "c0_expr", "set color component #0 expression", OFFSET(params[0].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\ - { "c1_expr", "set color component #1 expression", OFFSET(params[1].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\ - { "c2_expr", "set color component #2 expression", OFFSET(params[2].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\ - { "c3_expr", "set color component #3 expression", OFFSET(params[3].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\ - { "all_expr", "set expression for all color components", OFFSET(all_expr), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\ + { "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 },\ + { "c3_expr", "set color component #3 expression", OFFSET(params[3].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },\ + { "all_expr", "set expression for all color components", OFFSET(all_expr), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },\ { "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 },\ { "c2_opacity", "set color component #2 opacity", OFFSET(params[2].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },\ @@ -138,6 +139,9 @@ COPY(bottom, 8) COPY(top, 16) COPY(bottom, 16) +COPY(top, 32) +COPY(bottom, 32) + #undef COPY static void blend_normal_8bit(const uint8_t *top, ptrdiff_t top_linesize, @@ -184,6 +188,31 @@ static void blend_normal_16bit(const uint8_t *_top, ptrdiff_t top_linesize, } } +static void blend_normal_32bit(const uint8_t *_top, ptrdiff_t top_linesize, + const uint8_t *_bottom, ptrdiff_t bottom_linesize, + uint8_t *_dst, ptrdiff_t dst_linesize, + ptrdiff_t width, ptrdiff_t height, + FilterParams *param, double *values, int starty) +{ + const float *top = (float*)_top; + const float *bottom = (float*)_bottom; + float *dst = (float*)_dst; + const double opacity = param->opacity; + int i, j; + dst_linesize /= 4; + top_linesize /= 4; + bottom_linesize /= 4; + + for (i = 0; i < height; i++) { + for (j = 0; j < width; j++) { + dst[j] = top[j] * opacity + bottom[j] * (1. - opacity); + } + dst += dst_linesize; + top += top_linesize; + bottom += bottom_linesize; + } +} + #define DEFINE_BLEND8(name, expr) \ static void blend_## name##_8bit(const uint8_t *top, ptrdiff_t top_linesize, \ const uint8_t *bottom, ptrdiff_t bottom_linesize, \ @@ -230,6 +259,32 @@ static void blend_## name##_##depth##bit(const uint8_t *_top, ptrdiff_t top_line } \ } +#define DEFINE_BLEND32(name, expr, depth) \ +static void blend_## name##_##depth##bit(const uint8_t *_top, ptrdiff_t top_linesize,\ + const uint8_t *_bottom, ptrdiff_t bottom_linesize, \ + uint8_t *_dst, ptrdiff_t dst_linesize, \ + ptrdiff_t width, ptrdiff_t height, \ + FilterParams *param, double *values, int starty) \ +{ \ + const float *top = (const float*)_top; \ + const float *bottom = (const float*)_bottom; \ + float *dst = (float*)_dst; \ + double opacity = param->opacity; \ + int i, j; \ + dst_linesize /= 4; \ + top_linesize /= 4; \ + bottom_linesize /= 4; \ + \ + for (i = 0; i < height; i++) { \ + for (j = 0; j < width; j++) { \ + dst[j] = top[j] + ((expr) - top[j]) * opacity; \ + } \ + dst += dst_linesize; \ + top += top_linesize; \ + bottom += bottom_linesize; \ + } \ +} + #define A top[j] #define B bottom[j] @@ -443,6 +498,49 @@ DEFINE_BLEND16(xor, A ^ B, 9) DEFINE_BLEND16(vividlight, (A < 256) ? BURN(2 * A, B) : DODGE(2 * (A - 256), B), 9) DEFINE_BLEND16(linearlight,(int)av_clip_uintp2((B < 256) ? B + 2 * A - 511 : B + 2 * (A - 256), 9), 9) +#undef MULTIPLY +#undef SCREEN +#undef BURN +#undef DODGE + +#define MULTIPLY(x, a, b) ((x) * (((a) * (b)) / 1.0)) +#define SCREEN(x, a, b) (1.0 - (x) * ((1.0 - (a)) * (1.0 - (b)) / 1.0)) +#define BURN(a, b) (((a) <= 0.0) ? (a) : FFMAX(0.0, 1.0 - (1.0 - (b)) / (a))) +#define DODGE(a, b) (((a) >= 1.0) ? (a) : FFMIN(1.0, ((b) / (1.0 - (a))))) + +DEFINE_BLEND32(addition, A + B, 32) +DEFINE_BLEND32(grainmerge, A + B - 0.5, 32) +DEFINE_BLEND32(average, (A + B) / 2, 32) +DEFINE_BLEND32(subtract, A - B, 32) +DEFINE_BLEND32(multiply, A * B, 32) +DEFINE_BLEND32(multiply128, (A - 0.5) * B / 0.125 + 0.5, 32) +DEFINE_BLEND32(negation, 1.0 - FFABS(1.0 - A - B), 32) +DEFINE_BLEND32(extremity, FFABS(1.0 - A - B), 32) +DEFINE_BLEND32(difference, FFABS(A - B), 32) +DEFINE_BLEND32(grainextract, 0.5 + A - B, 32) +DEFINE_BLEND32(screen, SCREEN(1, A, B), 32) +DEFINE_BLEND32(overlay, (A < 0.5) ? MULTIPLY(2, A, B) : SCREEN(2, A, B), 32) +DEFINE_BLEND32(hardlight, (B < 0.5) ? MULTIPLY(2, B, A) : SCREEN(2, B, A), 32) +DEFINE_BLEND32(hardmix, (A < (1.0 - B)) ? 0: 1.0, 32) +DEFINE_BLEND32(heat, (A == 0) ? 0 : 1.0 - FFMIN(((1.0 - B) * (1.0 - B)) / A, 1.0), 32) +DEFINE_BLEND32(freeze, (B == 0) ? 0 : 1.0 - FFMIN(((1.0 - A) * (1.0 - A)) / B, 1.0), 32) +DEFINE_BLEND32(darken, FFMIN(A, B), 32) +DEFINE_BLEND32(lighten, FFMAX(A, B), 32) +DEFINE_BLEND32(divide, B == 0 ? 1.0 : 1.0 * A / B, 32) +DEFINE_BLEND32(dodge, DODGE(A, B), 32) +DEFINE_BLEND32(burn, BURN(A, B), 32) +DEFINE_BLEND32(softlight, (A > 0.5) ? B + (1.0 - B) * (A - 0.5) / 0.5 * (0.5 - fabs(B - 0.5) / 1.0): B - B * ((0.5 - A) / 0.5) * (0.5 - fabs(B - 0.5)/1.0), 32) +DEFINE_BLEND32(exclusion, A + B - 2 * A * B / 1.0, 32) +DEFINE_BLEND32(pinlight, (B < 0.5) ? FFMIN(A, 2 * B) : FFMAX(A, 2 * (B - 0.5)), 32) +DEFINE_BLEND32(phoenix, FFMIN(A, B) - FFMAX(A, B) + 1.0, 32) +DEFINE_BLEND32(reflect, (B == 1.0) ? B : FFMIN(1.0, (A * A / (1.0 - B))), 32) +DEFINE_BLEND32(glow, (A == 1.0) ? A : FFMIN(1.0, (B * B / (1.0 - A))), 32) +DEFINE_BLEND32(and, av_int2float(av_float2int(A) & av_float2int(B)), 32) +DEFINE_BLEND32(or, av_int2float(av_float2int(A) | av_float2int(B)), 32) +DEFINE_BLEND32(xor, av_int2float(av_float2int(A) ^ av_float2int(B)), 32) +DEFINE_BLEND32(vividlight, (A < 0.5) ? BURN(2 * A, B) : DODGE(2 * (A - 0.5), B), 32) +DEFINE_BLEND32(linearlight,(B < 0.5) ? B + 2 * A - 1.0 : B + 2 * (A - 0.5), 32) + #define DEFINE_BLEND_EXPR(type, name, div) \ static void blend_expr_## name(const uint8_t *_top, ptrdiff_t top_linesize, \ const uint8_t *_bottom, ptrdiff_t bottom_linesize, \ @@ -475,6 +573,7 @@ static void blend_expr_## name(const uint8_t *_top, ptrdiff_t top_linesize, DEFINE_BLEND_EXPR(uint8_t, 8bit, 1) DEFINE_BLEND_EXPR(uint16_t, 16bit, 2) +DEFINE_BLEND_EXPR(float, 32bit, 4) static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { @@ -575,10 +674,12 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GRAY10, AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12, + AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GRAY12, AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_GRAY16, + AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32, AV_PIX_FMT_GRAYF32, AV_PIX_FMT_NONE }; @@ -600,50 +701,51 @@ static av_cold void uninit(AVFilterContext *ctx) av_expr_free(s->params[i].e); } -#define DEFINE_INIT_BLEND_FUNC(depth, nbits) \ -static av_cold void init_blend_func_##depth##_##nbits##bit(FilterParams *param) \ -{ \ - switch (param->mode) { \ - case BLEND_ADDITION: param->blend = blend_addition_##depth##bit; break; \ - case BLEND_GRAINMERGE: param->blend = blend_grainmerge_##depth##bit; break; \ - case BLEND_AND: param->blend = blend_and_##depth##bit; break; \ - case BLEND_AVERAGE: param->blend = blend_average_##depth##bit; break; \ - case BLEND_BURN: param->blend = blend_burn_##depth##bit; break; \ - case BLEND_DARKEN: param->blend = blend_darken_##depth##bit; break; \ - case BLEND_DIFFERENCE: param->blend = blend_difference_##depth##bit; break; \ - case BLEND_GRAINEXTRACT: param->blend = blend_grainextract_##depth##bit; break; \ - case BLEND_DIVIDE: param->blend = blend_divide_##depth##bit; break; \ - case BLEND_DODGE: param->blend = blend_dodge_##depth##bit; break; \ - case BLEND_EXCLUSION: param->blend = blend_exclusion_##depth##bit; break; \ - case BLEND_EXTREMITY: param->blend = blend_extremity_##depth##bit; break; \ - case BLEND_FREEZE: param->blend = blend_freeze_##depth##bit; break; \ - case BLEND_GLOW: param->blend = blend_glow_##depth##bit; break; \ - case BLEND_HARDLIGHT: param->blend = blend_hardlight_##depth##bit; break; \ - case BLEND_HARDMIX: param->blend = blend_hardmix_##depth##bit; break; \ - case BLEND_HEAT: param->blend = blend_heat_##depth##bit; break; \ - case BLEND_LIGHTEN: param->blend = blend_lighten_##depth##bit; break; \ - case BLEND_LINEARLIGHT:param->blend = blend_linearlight_##depth##bit;break; \ - case BLEND_MULTIPLY: param->blend = blend_multiply_##depth##bit; break; \ - case BLEND_MULTIPLY128:param->blend = blend_multiply128_##depth##bit;break; \ - case BLEND_NEGATION: param->blend = blend_negation_##depth##bit; break; \ - case BLEND_NORMAL: param->blend = blend_normal_##nbits##bit; break; \ - case BLEND_OR: param->blend = blend_or_##depth##bit; break; \ - case BLEND_OVERLAY: param->blend = blend_overlay_##depth##bit; break; \ - case BLEND_PHOENIX: param->blend = blend_phoenix_##depth##bit; break; \ - case BLEND_PINLIGHT: param->blend = blend_pinlight_##depth##bit; break; \ - case BLEND_REFLECT: param->blend = blend_reflect_##depth##bit; break; \ - case BLEND_SCREEN: param->blend = blend_screen_##depth##bit; break; \ - case BLEND_SOFTLIGHT: param->blend = blend_softlight_##depth##bit; break; \ - case BLEND_SUBTRACT: param->blend = blend_subtract_##depth##bit; break; \ - case BLEND_VIVIDLIGHT: param->blend = blend_vividlight_##depth##bit; break; \ - case BLEND_XOR: param->blend = blend_xor_##depth##bit; break; \ - } \ +#define DEFINE_INIT_BLEND_FUNC(depth, nbits) \ +static av_cold void init_blend_func_##depth##_##nbits##bit(FilterParams *param) \ +{ \ + switch (param->mode) { \ + case BLEND_ADDITION: param->blend = blend_addition_##depth##bit; break; \ + case BLEND_GRAINMERGE: param->blend = blend_grainmerge_##depth##bit; break; \ + case BLEND_AND: param->blend = blend_and_##depth##bit; break; \ + case BLEND_AVERAGE: param->blend = blend_average_##depth##bit; break; \ + case BLEND_BURN: param->blend = blend_burn_##depth##bit; break; \ + case BLEND_DARKEN: param->blend = blend_darken_##depth##bit; break; \ + case BLEND_DIFFERENCE: param->blend = blend_difference_##depth##bit; break; \ + case BLEND_GRAINEXTRACT: param->blend = blend_grainextract_##depth##bit; break; \ + case BLEND_DIVIDE: param->blend = blend_divide_##depth##bit; break; \ + case BLEND_DODGE: param->blend = blend_dodge_##depth##bit; break; \ + case BLEND_EXCLUSION: param->blend = blend_exclusion_##depth##bit; break; \ + case BLEND_EXTREMITY: param->blend = blend_extremity_##depth##bit; break; \ + case BLEND_FREEZE: param->blend = blend_freeze_##depth##bit; break; \ + case BLEND_GLOW: param->blend = blend_glow_##depth##bit; break; \ + case BLEND_HARDLIGHT: param->blend = blend_hardlight_##depth##bit; break; \ + case BLEND_HARDMIX: param->blend = blend_hardmix_##depth##bit; break; \ + case BLEND_HEAT: param->blend = blend_heat_##depth##bit; break; \ + case BLEND_LIGHTEN: param->blend = blend_lighten_##depth##bit; break; \ + case BLEND_LINEARLIGHT: param->blend = blend_linearlight_##depth##bit; break; \ + case BLEND_MULTIPLY: param->blend = blend_multiply_##depth##bit; break; \ + case BLEND_MULTIPLY128: param->blend = blend_multiply128_##depth##bit; break; \ + case BLEND_NEGATION: param->blend = blend_negation_##depth##bit; break; \ + case BLEND_NORMAL: param->blend = blend_normal_##nbits##bit; break; \ + case BLEND_OR: param->blend = blend_or_##depth##bit; break; \ + case BLEND_OVERLAY: param->blend = blend_overlay_##depth##bit; break; \ + case BLEND_PHOENIX: param->blend = blend_phoenix_##depth##bit; break; \ + case BLEND_PINLIGHT: param->blend = blend_pinlight_##depth##bit; break; \ + case BLEND_REFLECT: param->blend = blend_reflect_##depth##bit; break; \ + case BLEND_SCREEN: param->blend = blend_screen_##depth##bit; break; \ + case BLEND_SOFTLIGHT: param->blend = blend_softlight_##depth##bit; break; \ + case BLEND_SUBTRACT: param->blend = blend_subtract_##depth##bit; break; \ + case BLEND_VIVIDLIGHT: param->blend = blend_vividlight_##depth##bit; break; \ + case BLEND_XOR: param->blend = blend_xor_##depth##bit; break; \ + } \ } -DEFINE_INIT_BLEND_FUNC(8, 8); -DEFINE_INIT_BLEND_FUNC(9, 16); -DEFINE_INIT_BLEND_FUNC(10, 16); -DEFINE_INIT_BLEND_FUNC(12, 16); -DEFINE_INIT_BLEND_FUNC(16, 16); +DEFINE_INIT_BLEND_FUNC(8, 8) +DEFINE_INIT_BLEND_FUNC(9, 16) +DEFINE_INIT_BLEND_FUNC(10, 16) +DEFINE_INIT_BLEND_FUNC(12, 16) +DEFINE_INIT_BLEND_FUNC(16, 16) +DEFINE_INIT_BLEND_FUNC(32, 32) void ff_blend_init(FilterParams *param, int depth) { @@ -663,15 +765,18 @@ void ff_blend_init(FilterParams *param, int depth) case 16: init_blend_func_16_16bit(param); break; + case 32: + init_blend_func_32_32bit(param); + break; } if (param->opacity == 0 && param->mode != BLEND_NORMAL) { - param->blend = depth > 8 ? blend_copytop_16 : blend_copytop_8; + param->blend = depth > 8 ? depth > 16 ? blend_copytop_32 : blend_copytop_16 : blend_copytop_8; } else if (param->mode == BLEND_NORMAL) { if (param->opacity == 1) - param->blend = depth > 8 ? blend_copytop_16 : blend_copytop_8; + param->blend = depth > 8 ? depth > 16 ? blend_copytop_32 : blend_copytop_16 : blend_copytop_8; else if (param->opacity == 0) - param->blend = depth > 8 ? blend_copybottom_16 : blend_copybottom_8; + param->blend = depth > 8 ? depth > 16 ? blend_copybottom_32 : blend_copybottom_16 : blend_copybottom_8; } if (ARCH_X86) @@ -739,7 +844,7 @@ static int config_output(AVFilterLink *outlink) NULL, NULL, NULL, NULL, 0, ctx); if (ret < 0) return ret; - param->blend = s->depth > 8 ? blend_expr_16bit : blend_expr_8bit; + param->blend = s->depth > 8 ? s->depth > 16 ? blend_expr_32bit : blend_expr_16bit : blend_expr_8bit; } } diff --git a/libavfilter/vf_bm3d.c b/libavfilter/vf_bm3d.c index 75c356728e2..e5d2b8bc632 100644 --- a/libavfilter/vf_bm3d.c +++ b/libavfilter/vf_bm3d.c @@ -153,7 +153,7 @@ static const AVOption bm3d_options[] = { { "final", "final estimate", 0, AV_OPT_TYPE_CONST, {.i64=FINAL}, 0, 0, FLAGS, "mode" }, { "ref", "have reference stream", - OFFSET(ref), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS }, + OFFSET(ref), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=7}, 0, 15, FLAGS }, { NULL } @@ -164,9 +164,8 @@ AVFILTER_DEFINE_CLASS(bm3d); static int query_formats(AVFilterContext *ctx) { static const enum AVPixelFormat pix_fmts[] = { - AV_PIX_FMT_GRAY8, - AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, - AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY16, + 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_YUV410P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, @@ -182,6 +181,11 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, + AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA422P16, + AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_NONE }; @@ -439,7 +443,7 @@ static void basic_block_filtering(BM3DContext *s, const uint8_t *src, int src_li } } - threshold[0] = s->hard_threshold * s->sigma; + threshold[0] = s->hard_threshold * s->sigma * M_SQRT2 * block_size * block_size * (1 << (s->depth - 8)) / 255.f; threshold[1] = threshold[0] * sqrtf(2.f); threshold[2] = threshold[0] * 2.f; threshold[3] = threshold[0] * sqrtf(8.f); @@ -660,7 +664,7 @@ static void do_output(BM3DContext *s, uint8_t *dst, int dst_linesize, sum_den += den; } - dstp[j] = av_clip_uint8(sum_num / sum_den); + dstp[j] = av_clip_uint8(lrintf(sum_num / sum_den)); } } } @@ -688,7 +692,7 @@ static void do_output16(BM3DContext *s, uint8_t *dst, int dst_linesize, sum_den += den; } - dstp[j] = av_clip_uintp2_c(sum_num / sum_den, depth); + dstp[j] = av_clip_uintp2_c(lrintf(sum_num / sum_den), depth); } } } @@ -706,8 +710,8 @@ static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const int plane = td->plane; const int width = s->planewidth[plane]; const int height = s->planeheight[plane]; - const int block_pos_bottom = height - s->block_size; - const int block_pos_right = width - s->block_size; + const int block_pos_bottom = FFMAX(0, height - s->block_size); + const int block_pos_right = FFMAX(0, width - s->block_size); const int slice_start = (((height + block_step - 1) / block_step) * jobnr / nb_jobs) * block_step; const int slice_end = (jobnr == nb_jobs - 1) ? block_pos_bottom + block_step : (((height + block_step - 1) / block_step) * (jobnr + 1) / nb_jobs) * block_step; @@ -748,7 +752,7 @@ static int filter_frame(AVFilterContext *ctx, AVFrame **out, AVFrame *in, AVFram av_frame_copy_props(*out, in); for (p = 0; p < s->nb_planes; p++) { - const int nb_jobs = FFMIN(s->nb_threads, s->planeheight[p] / s->block_step); + const int nb_jobs = FFMAX(1, FFMIN(s->nb_threads, s->planeheight[p] / s->block_size)); ThreadData td; if (!((1 << p) & s->planes) || ctx->is_disabled) { @@ -796,8 +800,8 @@ static int config_input(AVFilterLink *inlink) for (i = 0; i < s->nb_threads; i++) { SliceContext *sc = &s->slices[i]; - sc->num = av_calloc(s->planewidth[0] * s->planeheight[0], sizeof(FFTSample)); - sc->den = av_calloc(s->planewidth[0] * s->planeheight[0], sizeof(FFTSample)); + sc->num = av_calloc(FFALIGN(s->planewidth[0], s->block_size) * FFALIGN(s->planeheight[0], s->block_size), sizeof(FFTSample)); + sc->den = av_calloc(FFALIGN(s->planewidth[0], s->block_size) * FFALIGN(s->planeheight[0], s->block_size), sizeof(FFTSample)); if (!sc->num || !sc->den) return AVERROR(ENOMEM); @@ -857,6 +861,8 @@ static int activate(AVFilterContext *ctx) int ret, status; int64_t pts; + FF_FILTER_FORWARD_STATUS_BACK(ctx->outputs[0], ctx->inputs[0]); + if ((ret = ff_inlink_consume_frame(ctx->inputs[0], &frame)) > 0) { ret = filter_frame(ctx, &out, frame, frame); av_frame_free(&frame); diff --git a/libavfilter/vf_bwdif.c b/libavfilter/vf_bwdif.c index 37165584cf8..b6aed7a4503 100644 --- a/libavfilter/vf_bwdif.c +++ b/libavfilter/vf_bwdif.c @@ -343,8 +343,8 @@ static int config_props(AVFilterLink *link) 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 < 3) { - av_log(ctx, AV_LOG_ERROR, "Video of less than 3 columns or lines is not supported\n"); + 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"); return AVERROR(EINVAL); } diff --git a/libavfilter/vf_cas.c b/libavfilter/vf_cas.c new file mode 100644 index 00000000000..7c32ed5f9b8 --- /dev/null +++ b/libavfilter/vf_cas.c @@ -0,0 +1,299 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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/opt.h" +#include "libavutil/imgutils.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +typedef struct CASContext { + const AVClass *class; + + float strength; + int planes; + int nb_planes; + + int depth; + int planeheight[4]; + int planewidth[4]; + + AVFrame *in; + + int (*do_slice)(AVFilterContext *s, void *arg, + int jobnr, int nb_jobs); +} CASContext; + +static inline float lerpf(float v0, float v1, float f) +{ + return v0 + (v1 - v0) * f; +} + +static int cas_slice8(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs) +{ + CASContext *s = avctx->priv; + const float strength = -lerpf(16.f, 4.01f, s->strength); + AVFrame *out = arg; + AVFrame *in = s->in; + + for (int p = 0; p < s->nb_planes; p++) { + const int slice_start = (s->planeheight[p] * jobnr) / nb_jobs; + const int slice_end = (s->planeheight[p] * (jobnr+1)) / nb_jobs; + const int linesize = out->linesize[p]; + const int in_linesize = in->linesize[p]; + const int w = s->planewidth[p]; + const int w1 = w - 1; + const int h = s->planeheight[p]; + const int h1 = h - 1; + uint8_t *dst = out->data[p] + slice_start * linesize; + const uint8_t *src = in->data[p]; + + if (!((1 << p) & s->planes)) { + av_image_copy_plane(dst, linesize, src + slice_start * linesize, in_linesize, + w, slice_end - slice_start); + continue; + } + + for (int y = slice_start; y < slice_end; y++) { + const int y0 = FFMAX(y - 1, 0); + const int y1 = FFMIN(y + 1, h1); + for (int x = 0; x < w; x++) { + const int x0 = FFMAX(x - 1, 0); + const int x1 = FFMIN(x + 1, w1); + int a = src[y0 * in_linesize + x0]; + int b = src[y0 * in_linesize + x]; + int c = src[y0 * in_linesize + x1]; + int d = src[y * in_linesize + x0]; + int e = src[y * in_linesize + x]; + int f = src[y * in_linesize + x1]; + int g = src[y1 * in_linesize + x0]; + int h = src[y1 * in_linesize + x]; + int i = src[y1 * in_linesize + x1]; + int mn, mn2, mx, mx2; + float amp, weight; + + mn = FFMIN3(FFMIN3( d, e, f), b, h); + mn2 = FFMIN3(FFMIN3(mn, a, c), g, i); + + mn = mn + mn2; + + mx = FFMAX3(FFMAX3( d, e, f), b, h); + mx2 = FFMAX3(FFMAX3(mx, a, c), g, i); + + mx = mx + mx2; + + amp = sqrtf(av_clipf(FFMIN(mn, 511 - mx) / (float)mx, 0.f, 1.f)); + + weight = amp / strength; + + dst[x] = av_clip_uint8(((b + d + f + h) * weight + e) / (1.f + 4.f * weight)); + } + dst += linesize; + } + } + + return 0; +} + +static int cas_slice16(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs) +{ + CASContext *s = avctx->priv; + const float strength = -lerpf(16.f, 4.01f, s->strength); + const int max = 2 * (1 << s->depth) - 1; + AVFrame *out = arg; + AVFrame *in = s->in; + + for (int p = 0; p < s->nb_planes; p++) { + const int slice_start = (s->planeheight[p] * jobnr) / nb_jobs; + const int slice_end = (s->planeheight[p] * (jobnr+1)) / nb_jobs; + const int linesize = out->linesize[p] / 2; + const int in_linesize = in->linesize[p] / 2; + const int w = s->planewidth[p]; + const int w1 = w - 1; + const int h = s->planeheight[p]; + const int h1 = h - 1; + uint16_t *dst = (uint16_t *)out->data[p] + slice_start * linesize; + const uint16_t *src = (const uint16_t *)in->data[p]; + + if (!((1 << p) & s->planes)) { + av_image_copy_plane((uint8_t *)dst, linesize, (uint8_t *)(src + slice_start * linesize), + in_linesize, w * 2, slice_end - slice_start); + continue; + } + + for (int y = slice_start; y < slice_end; y++) { + const int y0 = FFMAX(y - 1, 0); + const int y1 = FFMIN(y + 1, h1); + for (int x = 0; x < w; x++) { + const int x0 = FFMAX(x - 1, 0); + const int x1 = FFMIN(x + 1, w1); + int a = src[y0 * in_linesize + x0]; + int b = src[y0 * in_linesize + x]; + int c = src[y0 * in_linesize + x1]; + int d = src[y * in_linesize + x0]; + int e = src[y * in_linesize + x]; + int f = src[y * in_linesize + x1]; + int g = src[y1 * in_linesize + x0]; + int h = src[y1 * in_linesize + x]; + int i = src[y1 * in_linesize + x1]; + int mn, mn2, mx, mx2; + float amp, weight; + + mn = FFMIN3(FFMIN3( d, e, f), b, h); + mn2 = FFMIN3(FFMIN3(mn, a, c), g, i); + + mn = mn + mn2; + + mx = FFMAX3(FFMAX3( d, e, f), b, h); + mx2 = FFMAX3(FFMAX3(mx, a, c), g, i); + + mx = mx + mx2; + + amp = sqrtf(av_clipf(FFMIN(mn, max - mx) / (float)mx, 0.f, 1.f)); + + weight = amp / strength; + + dst[x] = av_clip_uintp2_c(((b + d + f + h) * weight + e) / (1.f + 4.f * weight), s->depth); + } + dst += linesize; + } + } + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + CASContext *s = ctx->priv; + AVFrame *out; + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + + s->in = in; + ctx->internal->execute(ctx, s->do_slice, out, NULL, + FFMIN(in->height, ff_filter_get_nb_threads(ctx))); + av_frame_free(&in); + s->in = NULL; + + return ff_filter_frame(ctx->outputs[0], out); +} + +static av_cold int query_formats(AVFilterContext *avctx) +{ + static const enum AVPixelFormat pixel_fmts[] = { + 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_YUV410P, AV_PIX_FMT_YUV411P, + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, + AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P, + AV_PIX_FMT_YUVJ411P, + AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, + AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, + AV_PIX_FMT_YUV440P10, + 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_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, + 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_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, + AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA422P16, + AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, + AV_PIX_FMT_NONE + }; + + AVFilterFormats *formats = NULL; + + formats = ff_make_format_list(pixel_fmts); + if (!formats) + return AVERROR(ENOMEM); + + return ff_set_common_formats(avctx, formats); +} + +static av_cold int config_input(AVFilterLink *inlink) +{ + AVFilterContext *avctx = inlink->dst; + CASContext *s = avctx->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + + s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); + s->planeheight[0] = s->planeheight[3] = inlink->h; + s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); + s->planewidth[0] = s->planewidth[3] = inlink->w; + + s->depth = desc->comp[0].depth; + s->nb_planes = desc->nb_components; + s->do_slice = s->depth <= 8 ? cas_slice8 : cas_slice16; + + return 0; +} + +static const AVFilterPad cas_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = config_input, + }, + { NULL } +}; + +static const AVFilterPad cas_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +#define OFFSET(x) offsetof(CASContext, x) +#define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM + +static const AVOption cas_options[] = { + { "strength", "set the sharpening strength", OFFSET(strength), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, 1, VF }, + { "planes", "set what planes to filter", OFFSET(planes), AV_OPT_TYPE_FLAGS, {.i64=7}, 0, 15, VF }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(cas); + +AVFilter ff_vf_cas = { + .name = "cas", + .description = NULL_IF_CONFIG_SMALL("Contrast Adaptive Sharpen."), + .priv_size = sizeof(CASContext), + .priv_class = &cas_class, + .query_formats = query_formats, + .inputs = cas_inputs, + .outputs = cas_outputs, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, +}; diff --git a/libavfilter/vf_chromaber_vulkan.c b/libavfilter/vf_chromaber_vulkan.c new file mode 100644 index 00000000000..12dfebb1fef --- /dev/null +++ b/libavfilter/vf_chromaber_vulkan.c @@ -0,0 +1,347 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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.h" +#include "internal.h" + +#define CGROUPS (int [3]){ 32, 32, 1 } + +typedef struct ChromaticAberrationVulkanContext { + VulkanFilterContext vkctx; + + int initialized; + FFVkExecContext *exec; + VulkanPipeline *pl; + + /* Shader updators, must be in the main filter struct */ + VkDescriptorImageInfo input_images[3]; + VkDescriptorImageInfo output_images[3]; + + /* Push constants / options */ + struct { + float dist[2]; + } opts; +} ChromaticAberrationVulkanContext; + +static const char distort_chroma_kernel[] = { + C(0, void distort_rgb(ivec2 size, ivec2 pos) ) + C(0, { ) + C(1, const vec2 p = ((vec2(pos)/vec2(size)) - 0.5f)*2.0f; ) + C(1, const vec2 o = p * (dist - 1.0f); ) + C(0, ) + C(1, vec4 res; ) + C(1, res.r = texture(input_img[0], ((p - o)/2.0f) + 0.5f).r; ) + C(1, res.g = texture(input_img[0], ((p )/2.0f) + 0.5f).g; ) + C(1, res.b = texture(input_img[0], ((p + o)/2.0f) + 0.5f).b; ) + C(1, res.a = texture(input_img[0], ((p )/2.0f) + 0.5f).a; ) + C(1, imageStore(output_img[0], pos, res); ) + C(0, } ) + C(0, ) + C(0, void distort_chroma(int idx, ivec2 size, ivec2 pos) ) + 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, vec4 res = texture(input_img[idx], (p/2.0f) + 0.5f); ) + C(1, imageStore(output_img[idx], pos, res); ) + C(0, } ) +}; + +static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) +{ + int err; + ChromaticAberrationVulkanContext *s = ctx->priv; + + /* Create a sampler */ + VkSampler *sampler = ff_vk_init_sampler(ctx, 0, VK_FILTER_LINEAR); + if (!sampler) + return AVERROR_EXTERNAL; + + s->vkctx.queue_family_idx = s->vkctx.hwctx->queue_family_comp_index; + s->vkctx.queue_count = GET_QUEUE_COUNT(s->vkctx.hwctx, 0, 1, 0); + s->vkctx.cur_queue_idx = av_get_random_seed() % s->vkctx.queue_count; + + s->pl = ff_vk_create_pipeline(ctx); + if (!s->pl) + return AVERROR(ENOMEM); + + /* 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 */ + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + VulkanDescriptorSetBinding 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, + .samplers = DUP_SAMPLER_ARRAY4(*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, + }, + }; + + SPIRVShader *shd = ff_vk_init_shader(ctx, s->pl, "chromaber_compute", + VK_SHADER_STAGE_COMPUTE_BIT); + if (!shd) + return AVERROR(ENOMEM); + + ff_vk_set_compute_shader_sizes(ctx, shd, CGROUPS); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, vec2 dist; ); + GLSLC(0, }; ); + GLSLC(0, ); + + ff_vk_add_push_constant(ctx, s->pl, 0, sizeof(s->opts), + VK_SHADER_STAGE_COMPUTE_BIT); + + RET(ff_vk_add_descriptor_set(ctx, s->pl, shd, desc_i, 2, 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, } ); + + RET(ff_vk_compile_shader(ctx, shd, "main")); + } + + RET(ff_vk_init_pipeline_layout(ctx, s->pl)); + RET(ff_vk_init_compute_pipeline(ctx, s->pl)); + + /* Execution context */ + RET(ff_vk_create_exec_ctx(ctx, &s->exec)); + + s->initialized = 1; + + return 0; + +fail: + return err; +} + +static int process_frames(AVFilterContext *avctx, AVFrame *out_f, AVFrame *in_f) +{ + int err = 0; + VkCommandBuffer cmd_buf; + ChromaticAberrationVulkanContext *s = avctx->priv; + 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); + + /* Update descriptors and init the exec context */ + ff_vk_start_exec_recording(avctx, s->exec); + cmd_buf = ff_vk_get_exec_buf(avctx, s->exec); + + for (int i = 0; i < planes; i++) { + RET(ff_vk_create_imageview(avctx, s->exec, &s->input_images[i].imageView, + in->img[i], + av_vkfmt_from_pixfmt(s->vkctx.input_format)[i], + ff_comp_identity_map)); + + RET(ff_vk_create_imageview(avctx, s->exec, &s->output_images[i].imageView, + out->img[i], + av_vkfmt_from_pixfmt(s->vkctx.output_format)[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(avctx, 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, + }, + }; + + vkCmdPipelineBarrier(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(avctx, s->exec, s->pl); + + ff_vk_update_push_exec(avctx, s->exec, VK_SHADER_STAGE_COMPUTE_BIT, + 0, sizeof(s->opts), &s->opts); + + vkCmdDispatch(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(avctx, s->exec, in_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + ff_vk_add_exec_dep(avctx, s->exec, out_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + + err = ff_vk_submit_exec_queue(avctx, s->exec); + if (err) + return err; + + return err; + +fail: + ff_vk_discard_exec_deps(avctx, s->exec); + return err; +} + +static int chromaber_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) +{ + int err; + AVFilterContext *ctx = link->dst; + ChromaticAberrationVulkanContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + + AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + err = AVERROR(ENOMEM); + goto fail; + } + + if (!s->initialized) + RET(init_filter(ctx, in)); + + RET(process_frames(ctx, out, in)); + + err = av_frame_copy_props(out, in); + if (err < 0) + goto fail; + + av_frame_free(&in); + + return ff_filter_frame(outlink, out); + +fail: + av_frame_free(&in); + av_frame_free(&out); + return err; +} + +static void chromaber_vulkan_uninit(AVFilterContext *avctx) +{ + ChromaticAberrationVulkanContext *s = avctx->priv; + + ff_vk_filter_uninit(avctx); + + s->initialized = 0; +} + +#define OFFSET(x) offsetof(ChromaticAberrationVulkanContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) +static const AVOption chromaber_vulkan_options[] = { + { "dist_x", "Set horizontal distortion amount", OFFSET(opts.dist[0]), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, -10.0f, 10.0f, .flags = FLAGS }, + { "dist_y", "Set vertical distortion amount", OFFSET(opts.dist[1]), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, -10.0f, 10.0f, .flags = FLAGS }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(chromaber_vulkan); + +static const AVFilterPad chromaber_vulkan_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = &chromaber_vulkan_filter_frame, + .config_props = &ff_vk_filter_config_input, + }, + { NULL } +}; + +static const AVFilterPad chromaber_vulkan_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &ff_vk_filter_config_output, + }, + { NULL } +}; + +AVFilter ff_vf_chromaber_vulkan = { + .name = "chromaber_vulkan", + .description = NULL_IF_CONFIG_SMALL("Offset chroma of input video (chromatic aberration)"), + .priv_size = sizeof(ChromaticAberrationVulkanContext), + .init = &ff_vk_filter_init, + .uninit = &chromaber_vulkan_uninit, + .query_formats = &ff_vk_filter_query_formats, + .inputs = chromaber_vulkan_inputs, + .outputs = chromaber_vulkan_outputs, + .priv_class = &chromaber_vulkan_class, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_chromakey.c b/libavfilter/vf_chromakey.c index 76a5e2056a3..4b1669d0842 100644 --- a/libavfilter/vf_chromakey.c +++ b/libavfilter/vf_chromakey.c @@ -20,6 +20,7 @@ #include "libavutil/opt.h" #include "libavutil/imgutils.h" +#include "libavutil/intreadwrite.h" #include "avfilter.h" #include "formats.h" #include "internal.h" @@ -29,12 +30,15 @@ typedef struct ChromakeyContext { const AVClass *class; uint8_t chromakey_rgba[4]; - uint8_t chromakey_uv[2]; + uint16_t chromakey_uv[2]; float similarity; float blend; int is_yuv; + int depth; + int mid; + int max; int hsub_log2; int vsub_log2; @@ -52,7 +56,7 @@ static uint8_t do_chromakey_pixel(ChromakeyContext *ctx, uint8_t u[9], uint8_t v du = (int)u[i] - ctx->chromakey_uv[0]; dv = (int)v[i] - ctx->chromakey_uv[1]; - diff += sqrt((du * du + dv * dv) / (255.0 * 255.0)); + diff += sqrt((du * du + dv * dv) / (255.0 * 255.0 * 2)); } diff /= 9.0; @@ -64,6 +68,28 @@ static uint8_t do_chromakey_pixel(ChromakeyContext *ctx, uint8_t u[9], uint8_t v } } +static uint16_t do_chromakey_pixel16(ChromakeyContext *ctx, uint16_t u[9], uint16_t v[9]) +{ + double max = ctx->max; + double diff = 0.0; + int du, dv, i; + + for (i = 0; i < 9; ++i) { + du = (int)u[i] - ctx->chromakey_uv[0]; + dv = (int)v[i] - ctx->chromakey_uv[1]; + + diff += sqrt((du * du + dv * dv) / (max * max * 2)); + } + + diff /= 9.0; + + if (ctx->blend > 0.0001) { + return av_clipd((diff - ctx->similarity) / ctx->blend, 0.0, 1.0) * max; + } else { + return (diff > ctx->similarity) ? max : 0; + } +} + static av_always_inline void get_pixel_uv(AVFrame *frame, int hsub_log2, int vsub_log2, int x, int y, uint8_t *u, uint8_t *v) { if (x < 0 || x >= frame->width || y < 0 || y >= frame->height) @@ -76,6 +102,18 @@ static av_always_inline void get_pixel_uv(AVFrame *frame, int hsub_log2, int vsu *v = frame->data[2][frame->linesize[2] * y + x]; } +static av_always_inline void get_pixel16_uv(AVFrame *frame, int hsub_log2, int vsub_log2, int x, int y, uint16_t *u, uint16_t *v) +{ + if (x < 0 || x >= frame->width || y < 0 || y >= frame->height) + return; + + x >>= hsub_log2; + y >>= vsub_log2; + + *u = AV_RN16(&frame->data[1][frame->linesize[1] * y + 2 * x]); + *v = AV_RN16(&frame->data[2][frame->linesize[2] * y + 2 * x]); +} + static int do_chromakey_slice(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs) { AVFrame *frame = arg; @@ -106,6 +144,40 @@ static int do_chromakey_slice(AVFilterContext *avctx, void *arg, int jobnr, int return 0; } +static int do_chromakey16_slice(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs) +{ + AVFrame *frame = arg; + + const int slice_start = (frame->height * jobnr) / nb_jobs; + const int slice_end = (frame->height * (jobnr + 1)) / nb_jobs; + + ChromakeyContext *ctx = avctx->priv; + + int x, y, xo, yo; + uint16_t u[9], v[9]; + + for (int i = 0; i < 9; i++) { + u[i] = ctx->chromakey_uv[0]; + v[i] = ctx->chromakey_uv[1]; + } + + for (y = slice_start; y < slice_end; ++y) { + for (x = 0; x < frame->width; ++x) { + uint16_t *dst = (uint16_t *)(frame->data[3] + frame->linesize[3] * y); + + for (yo = 0; yo < 3; ++yo) { + for (xo = 0; xo < 3; ++xo) { + get_pixel16_uv(frame, ctx->hsub_log2, ctx->vsub_log2, x + xo - 1, y + yo - 1, &u[yo * 3 + xo], &v[yo * 3 + xo]); + } + } + + dst[x] = do_chromakey_pixel16(ctx, u, v); + } + } + + return 0; +} + static int do_chromahold_slice(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs) { ChromakeyContext *ctx = avctx->priv; @@ -143,6 +215,45 @@ static int do_chromahold_slice(AVFilterContext *avctx, void *arg, int jobnr, int return 0; } +static int do_chromahold16_slice(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs) +{ + ChromakeyContext *ctx = avctx->priv; + AVFrame *frame = arg; + const int slice_start = ((frame->height >> ctx->vsub_log2) * jobnr) / nb_jobs; + const int slice_end = ((frame->height >> ctx->vsub_log2) * (jobnr + 1)) / nb_jobs; + const int mid = ctx->mid; + double max = ctx->max; + + int x, y, alpha; + + for (y = slice_start; y < slice_end; ++y) { + for (x = 0; x < frame->width >> ctx->hsub_log2; ++x) { + int u = AV_RN16(&frame->data[1][frame->linesize[1] * y + 2 * x]); + int v = AV_RN16(&frame->data[2][frame->linesize[2] * y + 2 * x]); + double diff; + int du, dv; + + du = u - ctx->chromakey_uv[0]; + dv = v - ctx->chromakey_uv[1]; + + diff = sqrt((du * du + dv * dv) / (max * max)); + + alpha = diff > ctx->similarity; + if (ctx->blend > 0.0001) { + double f = 1. - av_clipd((diff - ctx->similarity) / ctx->blend, 0.0, 1.0); + + AV_WN16(&frame->data[1][frame->linesize[1] * y + 2 * x], mid + (u - mid) * f); + AV_WN16(&frame->data[2][frame->linesize[2] * y + 2 * x], mid + (v - mid) * f); + } else if (alpha) { + AV_WN16(&frame->data[1][frame->linesize[1] * y + 2 * x], mid); + AV_WN16(&frame->data[2][frame->linesize[2] * y + 2 * x], mid); + } + } + } + + return 0; +} + static int filter_frame(AVFilterLink *link, AVFrame *frame) { AVFilterContext *avctx = link->dst; @@ -159,22 +270,31 @@ static int filter_frame(AVFilterLink *link, AVFrame *frame) #define RGB_TO_U(rgb) (((- FIXNUM(0.16874) * rgb[0] - FIXNUM(0.33126) * rgb[1] + FIXNUM(0.50000) * rgb[2] + (1 << 9) - 1) >> 10) + 128) #define RGB_TO_V(rgb) ((( FIXNUM(0.50000) * rgb[0] - FIXNUM(0.41869) * rgb[1] - FIXNUM(0.08131) * rgb[2] + (1 << 9) - 1) >> 10) + 128) -static av_cold int initialize_chromakey(AVFilterContext *avctx) +static av_cold int config_output(AVFilterLink *outlink) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format); + AVFilterContext *avctx = outlink->src; ChromakeyContext *ctx = avctx->priv; + int factor; + + ctx->depth = desc->comp[0].depth; + ctx->mid = 1 << (ctx->depth - 1); + ctx->max = (1 << ctx->depth) - 1; + + factor = 1 << (ctx->depth - 8); if (ctx->is_yuv) { - ctx->chromakey_uv[0] = ctx->chromakey_rgba[1]; - ctx->chromakey_uv[1] = ctx->chromakey_rgba[2]; + ctx->chromakey_uv[0] = ctx->chromakey_rgba[1] * factor; + ctx->chromakey_uv[1] = ctx->chromakey_rgba[2] * factor; } else { - ctx->chromakey_uv[0] = RGB_TO_U(ctx->chromakey_rgba); - ctx->chromakey_uv[1] = RGB_TO_V(ctx->chromakey_rgba); + ctx->chromakey_uv[0] = RGB_TO_U(ctx->chromakey_rgba) * factor; + ctx->chromakey_uv[1] = RGB_TO_V(ctx->chromakey_rgba) * factor; } if (!strcmp(avctx->filter->name, "chromakey")) { - ctx->do_slice = do_chromakey_slice; + ctx->do_slice = ctx->depth <= 8 ? do_chromakey_slice : do_chromakey16_slice; } else { - ctx->do_slice = do_chromahold_slice; + ctx->do_slice = ctx->depth <= 8 ? do_chromahold_slice: do_chromahold16_slice; } return 0; @@ -186,6 +306,10 @@ static av_cold int query_formats(AVFilterContext *avctx) 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_YUVA422P12, AV_PIX_FMT_YUVA444P12, + AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_NONE }; @@ -196,6 +320,15 @@ static av_cold int query_formats(AVFilterContext *avctx) AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, + AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, + AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, + AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12, + AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14, + AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, + 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_YUVA422P12, AV_PIX_FMT_YUVA444P12, + AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_NONE }; @@ -220,6 +353,18 @@ static av_cold int config_input(AVFilterLink *inlink) return 0; } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + return config_output(ctx->outputs[0]); +} + static const AVFilterPad chromakey_inputs[] = { { .name = "default", @@ -235,15 +380,16 @@ static const AVFilterPad chromakey_outputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, }, { NULL } }; #define OFFSET(x) offsetof(ChromakeyContext, 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 chromakey_options[] = { - { "color", "set the chromakey key color", OFFSET(chromakey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "color", "set the chromakey key color", OFFSET(chromakey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, 0, 0, FLAGS }, { "similarity", "set the chromakey similarity value", OFFSET(similarity), AV_OPT_TYPE_FLOAT, { .dbl = 0.01 }, 0.01, 1.0, FLAGS }, { "blend", "set the chromakey key blend value", OFFSET(blend), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, 0.0, 1.0, FLAGS }, { "yuv", "color parameter is in yuv instead of rgb", OFFSET(is_yuv), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, @@ -257,15 +403,15 @@ 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, - .init = initialize_chromakey, .query_formats = query_formats, .inputs = chromakey_inputs, .outputs = chromakey_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = process_command, }; static const AVOption chromahold_options[] = { - { "color", "set the chromahold key color", OFFSET(chromakey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "color", "set the chromahold key color", OFFSET(chromakey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, 0, 0, FLAGS }, { "similarity", "set the chromahold similarity value", OFFSET(similarity), AV_OPT_TYPE_FLOAT, { .dbl = 0.01 }, 0.01, 1.0, FLAGS }, { "blend", "set the chromahold blend value", OFFSET(blend), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, 0.0, 1.0, FLAGS }, { "yuv", "color parameter is in yuv instead of rgb", OFFSET(is_yuv), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, @@ -287,6 +433,7 @@ static const AVFilterPad chromahold_outputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, }, { NULL } }; @@ -298,9 +445,9 @@ AVFilter ff_vf_chromahold = { .description = NULL_IF_CONFIG_SMALL("Turns a certain color range into gray."), .priv_size = sizeof(ChromakeyContext), .priv_class = &chromahold_class, - .init = initialize_chromakey, .query_formats = query_formats, .inputs = chromahold_inputs, .outputs = chromahold_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = process_command, }; diff --git a/libavfilter/vf_chromashift.c b/libavfilter/vf_chromashift.c index f4ac28ad18e..94de7a049e8 100644 --- a/libavfilter/vf_chromashift.c +++ b/libavfilter/vf_chromashift.c @@ -28,7 +28,6 @@ #include "avfilter.h" #include "formats.h" #include "internal.h" -#include "framesync.h" #include "video.h" typedef struct ChromaShiftContext { @@ -63,6 +62,7 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV440P10, 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_YUV440P12, + AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, @@ -400,17 +400,29 @@ static int config_input(AVFilterLink *inlink) return av_image_fill_linesizes(s->linesize, inlink->format, inlink->w); } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + return config_input(ctx->inputs[0]); +} + #define OFFSET(x) offsetof(ChromaShiftContext, 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 chromashift_options[] = { - { "cbh", "shift chroma-blue horizontally", OFFSET(cbh), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, - { "cbv", "shift chroma-blue vertically", OFFSET(cbv), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, - { "crh", "shift chroma-red horizontally", OFFSET(crh), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, - { "crv", "shift chroma-red vertically", OFFSET(crv), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, - { "edge", "set edge operation", OFFSET(edge), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags = VF, "edge" }, - { "smear", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = VF, "edge" }, - { "wrap", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = VF, "edge" }, + { "cbh", "shift chroma-blue horizontally", OFFSET(cbh), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VFR }, + { "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" }, { NULL }, }; @@ -443,20 +455,21 @@ AVFilter ff_vf_chromashift = { .outputs = outputs, .inputs = inputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = process_command, }; static const AVOption rgbashift_options[] = { - { "rh", "shift red horizontally", OFFSET(rh), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, - { "rv", "shift red vertically", OFFSET(rv), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, - { "gh", "shift green horizontally", OFFSET(gh), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, - { "gv", "shift green vertically", OFFSET(gv), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, - { "bh", "shift blue horizontally", OFFSET(bh), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, - { "bv", "shift blue vertically", OFFSET(bv), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, - { "ah", "shift alpha horizontally", OFFSET(ah), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, - { "av", "shift alpha vertically", OFFSET(av), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VF }, - { "edge", "set edge operation", OFFSET(edge), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags = VF, "edge" }, - { "smear", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = VF, "edge" }, - { "wrap", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = VF, "edge" }, + { "rh", "shift red horizontally", OFFSET(rh), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VFR }, + { "rv", "shift red vertically", OFFSET(rv), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VFR }, + { "gh", "shift green horizontally", OFFSET(gh), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VFR }, + { "gv", "shift green vertically", OFFSET(gv), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VFR }, + { "bh", "shift blue horizontally", OFFSET(bh), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VFR }, + { "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" }, { NULL }, }; @@ -471,4 +484,5 @@ AVFilter ff_vf_rgbashift = { .outputs = outputs, .inputs = inputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = process_command, }; diff --git a/libavfilter/vf_ciescope.c b/libavfilter/vf_ciescope.c index 7c0cfed061a..d5a2c8c0ad6 100644 --- a/libavfilter/vf_ciescope.c +++ b/libavfilter/vf_ciescope.c @@ -46,6 +46,7 @@ enum ColorsSystems { CIE1931system, Rec709system, Rec2020system, + DCIP3, NB_CS }; @@ -87,6 +88,7 @@ static const AVOption ciescope_options[] = { { "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" }, @@ -105,6 +107,7 @@ static const AVOption ciescope_options[] = { { "rec709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<xWhite, cs->yWhite, &wup, &wvp); - wx = wup; - wy = wvp; wx = (w - 1) * wup; wy = (h - 1) - ((int) ((h - 1) * wvp)); } else if (cie == UCS) { double wu, wv; xy_to_uv(cs->xWhite, cs->yWhite, &wu, &wv); - wx = wu; - wy = wv; wx = (w - 1) * wu; wy = (h - 1) - ((int) ((h - 1) * wv)); } else if (cie == XYY) { diff --git a/libavfilter/vf_colorbalance.c b/libavfilter/vf_colorbalance.c index fd003fdc21a..56f9d5c49cd 100644 --- a/libavfilter/vf_colorbalance.c +++ b/libavfilter/vf_colorbalance.c @@ -36,9 +36,9 @@ typedef struct ThreadData { } ThreadData; typedef struct Range { - double shadows; - double midtones; - double highlights; + float shadows; + float midtones; + float highlights; } Range; typedef struct ColorBalanceContext { @@ -46,27 +46,29 @@ typedef struct ColorBalanceContext { Range cyan_red; Range magenta_green; Range yellow_blue; - - uint16_t lut[3][65536]; + int preserve_lightness; uint8_t rgba_map[4]; + int depth; + int max; int step; - int (*apply_lut)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); + int (*color_balance)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); } ColorBalanceContext; #define OFFSET(x) offsetof(ColorBalanceContext, 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 colorbalance_options[] = { - { "rs", "set red shadows", OFFSET(cyan_red.shadows), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS }, - { "gs", "set green shadows", OFFSET(magenta_green.shadows), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS }, - { "bs", "set blue shadows", OFFSET(yellow_blue.shadows), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS }, - { "rm", "set red midtones", OFFSET(cyan_red.midtones), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS }, - { "gm", "set green midtones", OFFSET(magenta_green.midtones), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS }, - { "bm", "set blue midtones", OFFSET(yellow_blue.midtones), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS }, - { "rh", "set red highlights", OFFSET(cyan_red.highlights), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS }, - { "gh", "set green highlights", OFFSET(magenta_green.highlights), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS }, - { "bh", "set blue highlights", OFFSET(yellow_blue.highlights), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS }, + { "rs", "set red shadows", OFFSET(cyan_red.shadows), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS }, + { "gs", "set green shadows", OFFSET(magenta_green.shadows), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS }, + { "bs", "set blue shadows", OFFSET(yellow_blue.shadows), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS }, + { "rm", "set red midtones", OFFSET(cyan_red.midtones), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS }, + { "gm", "set green midtones", OFFSET(magenta_green.midtones), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS }, + { "bm", "set blue midtones", OFFSET(yellow_blue.midtones), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS }, + { "rh", "set red highlights", OFFSET(cyan_red.highlights), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS }, + { "gh", "set green highlights", OFFSET(magenta_green.highlights), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS }, + { "bh", "set blue highlights", OFFSET(yellow_blue.highlights), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS }, + { "pl", "preserve lightness", OFFSET(preserve_lightness), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, { NULL } }; @@ -96,7 +98,64 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_formats(ctx, fmts_list); } -static int apply_lut8_p(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +static float get_component(float v, float l, + float s, float m, float h) +{ + const float a = 4.f, b = 0.333f, scale = 0.7f; + + s *= av_clipf((b - l) * a + 0.5f, 0, 1) * scale; + m *= av_clipf((l - b) * a + 0.5f, 0, 1) * av_clipf((1.0 - l - b) * a + 0.5f, 0, 1) * scale; + h *= av_clipf((l + b - 1) * a + 0.5f, 0, 1) * scale; + + v += s; + v += m; + v += h; + + return av_clipf(v + 0.5f, 0, 1); +} + +static float hfun(float n, float h, float s, float l) +{ + float a = s * FFMIN(l, 1. - l); + float k = fmodf(n + h / 30.f, 12.f); + + return av_clipf(l - a * FFMAX(FFMIN3(k - 3.f, 9.f - k, 1), -1.f), 0, 1); +} + +static void preservel(float *r, float *g, float *b, float l) +{ + float max = FFMAX3(*r, *g, *b); + float min = FFMIN3(*r, *g, *b); + float h, s; + + l *= 0.5; + + if (*r == *g && *g == *b) { + h = 0.; + } else if (max == *r) { + h = 60. * (0. + (*g - *b) / (max - min)); + } else if (max == *g) { + h = 60. * (2. + (*b - *r) / (max - min)); + } else if (max == *b) { + h = 60. * (4. + (*r - *g) / (max - min)); + } else { + h = 0.; + } + if (h < 0.) + h += 360.; + + if (max == 0. || min == 1.) { + s = 0.; + } else { + s = (max - min) / (1. - FFABS(2. * l - 1)); + } + + *r = hfun(0, h, s, l); + *g = hfun(8, h, s, l); + *b = hfun(4, h, s, l); +} + +static int color_balance8_p(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ColorBalanceContext *s = ctx->priv; ThreadData *td = arg; @@ -112,13 +171,26 @@ static int apply_lut8_p(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) uint8_t *dstb = out->data[1] + slice_start * out->linesize[1]; uint8_t *dstr = out->data[2] + slice_start * out->linesize[2]; uint8_t *dsta = out->data[3] + slice_start * out->linesize[3]; + const float max = s->max; int i, j; for (i = slice_start; i < slice_end; i++) { for (j = 0; j < out->width; j++) { - dstg[j] = s->lut[G][srcg[j]]; - dstb[j] = s->lut[B][srcb[j]]; - dstr[j] = s->lut[R][srcr[j]]; + float r = srcr[j] / max; + float g = srcg[j] / max; + float b = srcb[j] / max; + const float l = FFMAX3(r, g, b) + FFMIN3(r, g, b); + + r = get_component(r, l, s->cyan_red.shadows, s->cyan_red.midtones, s->cyan_red.highlights); + g = get_component(g, l, s->magenta_green.shadows, s->magenta_green.midtones, s->magenta_green.highlights); + b = get_component(b, l, s->yellow_blue.shadows, s->yellow_blue.midtones, s->yellow_blue.highlights); + + if (s->preserve_lightness) + preservel(&r, &g, &b, l); + + dstr[j] = av_clip_uint8(r * max); + dstg[j] = av_clip_uint8(g * max); + dstb[j] = av_clip_uint8(b * max); if (in != out && out->linesize[3]) dsta[j] = srca[j]; } @@ -136,7 +208,7 @@ static int apply_lut8_p(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) return 0; } -static int apply_lut16_p(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +static int color_balance16_p(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ColorBalanceContext *s = ctx->priv; ThreadData *td = arg; @@ -152,13 +224,27 @@ static int apply_lut16_p(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs uint16_t *dstb = (uint16_t *)out->data[1] + slice_start * out->linesize[1] / 2; uint16_t *dstr = (uint16_t *)out->data[2] + slice_start * out->linesize[2] / 2; uint16_t *dsta = (uint16_t *)out->data[3] + slice_start * out->linesize[3] / 2; + const int depth = s->depth; + const float max = s->max; int i, j; for (i = slice_start; i < slice_end; i++) { for (j = 0; j < out->width; j++) { - dstg[j] = s->lut[G][srcg[j]]; - dstb[j] = s->lut[B][srcb[j]]; - dstr[j] = s->lut[R][srcr[j]]; + float r = srcr[j] / max; + float g = srcg[j] / max; + float b = srcb[j] / max; + const float l = (FFMAX3(r, g, b) + FFMIN3(r, g, b)); + + r = get_component(r, l, s->cyan_red.shadows, s->cyan_red.midtones, s->cyan_red.highlights); + g = get_component(g, l, s->magenta_green.shadows, s->magenta_green.midtones, s->magenta_green.highlights); + b = get_component(b, l, s->yellow_blue.shadows, s->yellow_blue.midtones, s->yellow_blue.highlights); + + if (s->preserve_lightness) + preservel(&r, &g, &b, l); + + dstr[j] = av_clip_uintp2_c(r * max, depth); + dstg[j] = av_clip_uintp2_c(g * max, depth); + dstb[j] = av_clip_uintp2_c(b * max, depth); if (in != out && out->linesize[3]) dsta[j] = srca[j]; } @@ -176,7 +262,7 @@ static int apply_lut16_p(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs return 0; } -static int apply_lut8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +static int color_balance8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ColorBalanceContext *s = ctx->priv; ThreadData *td = arg; @@ -190,6 +276,7 @@ static int apply_lut8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const uint8_t goffset = s->rgba_map[G]; const uint8_t boffset = s->rgba_map[B]; const uint8_t aoffset = s->rgba_map[A]; + const float max = s->max; const int step = s->step; uint8_t *dstrow; int i, j; @@ -200,9 +287,21 @@ static int apply_lut8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) uint8_t *dst = dstrow; for (j = 0; j < outlink->w * step; j += step) { - dst[j + roffset] = s->lut[R][src[j + roffset]]; - dst[j + goffset] = s->lut[G][src[j + goffset]]; - dst[j + boffset] = s->lut[B][src[j + boffset]]; + float r = src[j + roffset] / max; + float g = src[j + goffset] / max; + float b = src[j + boffset] / max; + const float l = (FFMAX3(r, g, b) + FFMIN3(r, g, b)); + + r = get_component(r, l, s->cyan_red.shadows, s->cyan_red.midtones, s->cyan_red.highlights); + g = get_component(g, l, s->magenta_green.shadows, s->magenta_green.midtones, s->magenta_green.highlights); + b = get_component(b, l, s->yellow_blue.shadows, s->yellow_blue.midtones, s->yellow_blue.highlights); + + if (s->preserve_lightness) + preservel(&r, &g, &b, l); + + dst[j + roffset] = av_clip_uint8(r * max); + dst[j + goffset] = av_clip_uint8(g * max); + dst[j + boffset] = av_clip_uint8(b * max); if (in != out && step == 4) dst[j + aoffset] = src[j + aoffset]; } @@ -214,7 +313,7 @@ static int apply_lut8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) return 0; } -static int apply_lut16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +static int color_balance16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ColorBalanceContext *s = ctx->priv; ThreadData *td = arg; @@ -229,6 +328,8 @@ static int apply_lut16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const uint8_t boffset = s->rgba_map[B]; const uint8_t aoffset = s->rgba_map[A]; const int step = s->step / 2; + const int depth = s->depth; + const float max = s->max; uint16_t *dstrow; int i, j; @@ -238,9 +339,21 @@ static int apply_lut16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) uint16_t *dst = dstrow; for (j = 0; j < outlink->w * step; j += step) { - dst[j + roffset] = s->lut[R][src[j + roffset]]; - dst[j + goffset] = s->lut[G][src[j + goffset]]; - dst[j + boffset] = s->lut[B][src[j + boffset]]; + float r = src[j + roffset] / max; + float g = src[j + goffset] / max; + float b = src[j + boffset] / max; + const float l = (FFMAX3(r, g, b) + FFMIN3(r, g, b)); + + r = get_component(r, l, s->cyan_red.shadows, s->cyan_red.midtones, s->cyan_red.highlights); + g = get_component(g, l, s->magenta_green.shadows, s->magenta_green.midtones, s->magenta_green.highlights); + b = get_component(b, l, s->yellow_blue.shadows, s->yellow_blue.midtones, s->yellow_blue.highlights); + + if (s->preserve_lightness) + preservel(&r, &g, &b, l); + + dst[j + roffset] = av_clip_uintp2_c(r * max, depth); + dst[j + goffset] = av_clip_uintp2_c(g * max, depth); + dst[j + boffset] = av_clip_uintp2_c(b * max, depth); if (in != out && step == 4) dst[j + aoffset] = src[j + aoffset]; } @@ -258,64 +371,22 @@ static int config_output(AVFilterLink *outlink) ColorBalanceContext *s = ctx->priv; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format); const int depth = desc->comp[0].depth; - const int max = 1 << depth; + const int max = (1 << depth) - 1; const int planar = av_pix_fmt_count_planes(outlink->format) > 1; - double *shadows, *midtones, *highlights, *buffer; - int i, r, g, b; - if (max == 256 && planar) { - s->apply_lut = apply_lut8_p; + s->depth = depth; + s->max = max; + + if (max == 255 && planar) { + s->color_balance = color_balance8_p; } else if (planar) { - s->apply_lut = apply_lut16_p; - } else if (max == 256) { - s->apply_lut = apply_lut8; + s->color_balance = color_balance16_p; + } else if (max == 255) { + s->color_balance = color_balance8; } else { - s->apply_lut = apply_lut16; + s->color_balance = color_balance16; } - buffer = av_malloc(max * 3 * sizeof(*buffer)); - if (!buffer) - return AVERROR(ENOMEM); - - shadows = buffer + max * 0; - midtones = buffer + max * 1; - highlights = buffer + max * 2; - - for (i = 0; i < max; i++) { - const double L = 0.333 * (max - 1); - const double M = 0.7 * (max - 1); - const double H = 1 * (max - 1); - double low = av_clipd((i - L) / (-max * 0.25) + 0.5, 0, 1) * M; - double mid = av_clipd((i - L) / ( max * 0.25) + 0.5, 0, 1) * - av_clipd((i + L - H) / (-max * 0.25) + 0.5, 0, 1) * M; - - shadows[i] = low; - midtones[i] = mid; - highlights[max - i - 1] = low; - } - - for (i = 0; i < max; i++) { - r = g = b = i; - - r = av_clip_uintp2_c(r + s->cyan_red.shadows * shadows[r], depth); - r = av_clip_uintp2_c(r + s->cyan_red.midtones * midtones[r], depth); - r = av_clip_uintp2_c(r + s->cyan_red.highlights * highlights[r], depth); - - g = av_clip_uintp2_c(g + s->magenta_green.shadows * shadows[g], depth); - g = av_clip_uintp2_c(g + s->magenta_green.midtones * midtones[g], depth); - g = av_clip_uintp2_c(g + s->magenta_green.highlights * highlights[g], depth); - - b = av_clip_uintp2_c(b + s->yellow_blue.shadows * shadows[b], depth); - b = av_clip_uintp2_c(b + s->yellow_blue.midtones * midtones[b], depth); - b = av_clip_uintp2_c(b + s->yellow_blue.highlights * highlights[b], depth); - - s->lut[R][i] = r; - s->lut[G][i] = g; - s->lut[B][i] = b; - } - - av_free(buffer); - ff_fill_rgba_map(s->rgba_map, outlink->format); s->step = av_get_padded_bits_per_pixel(desc) >> 3; @@ -343,7 +414,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) td.in = in; td.out = out; - ctx->internal->execute(ctx, s->apply_lut, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); + ctx->internal->execute(ctx, s->color_balance, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); if (in != out) av_frame_free(&in); @@ -377,4 +448,5 @@ AVFilter ff_vf_colorbalance = { .inputs = colorbalance_inputs, .outputs = colorbalance_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vf_colorchannelmixer.c b/libavfilter/vf_colorchannelmixer.c index 3a9cd37b78a..3bbe44d6ef7 100644 --- a/libavfilter/vf_colorchannelmixer.c +++ b/libavfilter/vf_colorchannelmixer.c @@ -52,7 +52,8 @@ typedef struct ColorChannelMixerContext { } ColorChannelMixerContext; #define OFFSET(x) offsetof(ColorChannelMixerContext, 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 colorchannelmixer_options[] = { { "rr", "set the red gain for the red channel", OFFSET(rr), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS }, { "rg", "set the green gain for the red channel", OFFSET(rg), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, @@ -124,7 +125,7 @@ static av_always_inline int filter_slice_rgba_planar(AVFilterContext *ctx, void const uint8_t rin = srcr[j]; const uint8_t gin = srcg[j]; const uint8_t bin = srcb[j]; - const uint8_t ain = srca[j]; + const uint8_t ain = have_alpha ? srca[j] : 0; dstr[j] = av_clip_uint8(s->lut[R][R][rin] + s->lut[R][G][gin] + @@ -183,7 +184,7 @@ static av_always_inline int filter_slice_rgba16_planar(AVFilterContext *ctx, voi const uint16_t rin = srcr[j]; const uint16_t gin = srcg[j]; const uint16_t bin = srcb[j]; - const uint16_t ain = srca[j]; + const uint16_t ain = have_alpha ? srca[j] : 0; dstr[j] = av_clip_uintp2(s->lut[R][R][rin] + s->lut[R][G][gin] + @@ -408,18 +409,20 @@ static int config_output(AVFilterLink *outlink) ColorChannelMixerContext *s = ctx->priv; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format); const int depth = desc->comp[0].depth; - int i, j, size, *buffer; + int i, j, size, *buffer = s->buffer; ff_fill_rgba_map(s->rgba_map, outlink->format); size = 1 << depth; - s->buffer = buffer = av_malloc(16 * size * sizeof(*s->buffer)); - if (!s->buffer) - return AVERROR(ENOMEM); + if (!s->buffer) { + s->buffer = buffer = av_malloc(16 * size * sizeof(*s->buffer)); + if (!s->buffer) + return AVERROR(ENOMEM); - for (i = 0; i < 4; i++) - for (j = 0; j < 4; j++, buffer += size) - s->lut[i][j] = buffer; + for (i = 0; i < 4; i++) + for (j = 0; j < 4; j++, buffer += size) + s->lut[i][j] = buffer; + } for (i = 0; i < size; i++) { s->lut[R][R][i] = lrint(i * s->rr); @@ -531,6 +534,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) return ff_filter_frame(outlink, out); } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + int ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + + if (ret < 0) + return ret; + + return config_output(ctx->outputs[0]); +} + static av_cold void uninit(AVFilterContext *ctx) { ColorChannelMixerContext *s = ctx->priv; @@ -566,4 +580,5 @@ AVFilter ff_vf_colorchannelmixer = { .inputs = colorchannelmixer_inputs, .outputs = colorchannelmixer_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = process_command, }; diff --git a/libavfilter/vf_colorconstancy.c b/libavfilter/vf_colorconstancy.c index e3bb39e51ba..eae62204b5a 100644 --- a/libavfilter/vf_colorconstancy.c +++ b/libavfilter/vf_colorconstancy.c @@ -121,7 +121,6 @@ static int set_gauss(AVFilterContext *ctx) for (; i >= 0; --i) { av_freep(&s->gauss[i]); } - av_log(ctx, AV_LOG_ERROR, "Out of memory while allocating gauss buffers.\n"); return AVERROR(ENOMEM); } } @@ -223,7 +222,6 @@ static int setup_derivative_buffers(AVFilterContext* ctx, ThreadData *td) td->data[b][p] = av_mallocz_array(s->planeheight[p] * s->planewidth[p], sizeof(*td->data[b][p])); if (!td->data[b][p]) { cleanup_derivative_buffers(td, b + 1, p); - av_log(ctx, AV_LOG_ERROR, "Out of memory while allocating derivatives buffers.\n"); return AVERROR(ENOMEM); } } @@ -280,7 +278,7 @@ static int slice_get_derivative(AVFilterContext* ctx, void* arg, int jobnr, int dst[INDX2D(r, c, width)] = 0; for (g = 0; g < filtersize; ++g) { dst[INDX2D(r, c, width)] += GAUSS(src, r, c + GINDX(filtersize, g), - in_linesize, height, width, gauss[GINDX(filtersize, g)]); + in_linesize, height, width, gauss[g]); } } } @@ -295,7 +293,7 @@ static int slice_get_derivative(AVFilterContext* ctx, void* arg, int jobnr, int dst[INDX2D(r, c, width)] = 0; for (g = 0; g < filtersize; ++g) { dst[INDX2D(r, c, width)] += GAUSS(src, r + GINDX(filtersize, g), c, - width, height, width, gauss[GINDX(filtersize, g)]); + width, height, width, gauss[g]); } } } @@ -682,24 +680,30 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFilterLink *outlink = ctx->outputs[0]; AVFrame *out; int ret; + int direct = 0; ret = illumination_estimation(ctx, in); if (ret) { + av_frame_free(&in); return ret; } if (av_frame_is_writable(in)) { + direct = 1; out = in; } else { out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) { - av_log(ctx, AV_LOG_ERROR, "Out of memory while allocating output video buffer.\n"); + av_frame_free(&in); return AVERROR(ENOMEM); } av_frame_copy_props(out, in); } chromatic_adaptation(ctx, in, out); + if (!direct) + av_frame_free(&in); + return ff_filter_frame(outlink, out); } diff --git a/libavfilter/vf_colorkey.c b/libavfilter/vf_colorkey.c index 4e37c7f0c9e..0ac847c6336 100644 --- a/libavfilter/vf_colorkey.c +++ b/libavfilter/vf_colorkey.c @@ -45,7 +45,7 @@ static uint8_t do_colorkey_pixel(ColorkeyContext *ctx, uint8_t r, uint8_t g, uin int dg = (int)g - ctx->colorkey_rgba[1]; int db = (int)b - ctx->colorkey_rgba[2]; - double diff = sqrt((dr * dr + dg * dg + db * db) / (255.0 * 255.0)); + double diff = sqrt((dr * dr + dg * dg + db * db) / (255.0 * 255.0 * 3.0)); if (ctx->blend > 0.0001) { return av_clipd((diff - ctx->similarity) / ctx->blend, 0.0, 1.0) * 255.0; @@ -199,12 +199,12 @@ static const AVFilterPad colorkey_outputs[] = { }; #define OFFSET(x) offsetof(ColorkeyContext, 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 #if CONFIG_COLORKEY_FILTER static const AVOption colorkey_options[] = { - { "color", "set the colorkey key color", OFFSET(colorkey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "color", "set the colorkey key color", OFFSET(colorkey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, 0, 0, FLAGS }, { "similarity", "set the colorkey similarity value", OFFSET(similarity), AV_OPT_TYPE_FLOAT, { .dbl = 0.01 }, 0.01, 1.0, FLAGS }, { "blend", "set the colorkey key blend value", OFFSET(blend), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, 0.0, 1.0, FLAGS }, { NULL } @@ -222,13 +222,14 @@ AVFilter ff_vf_colorkey = { .inputs = colorkey_inputs, .outputs = colorkey_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, }; #endif /* CONFIG_COLORKEY_FILTER */ #if CONFIG_COLORHOLD_FILTER static const AVOption colorhold_options[] = { - { "color", "set the colorhold key color", OFFSET(colorkey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "color", "set the colorhold key color", OFFSET(colorkey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, 0, 0, FLAGS }, { "similarity", "set the colorhold similarity value", OFFSET(similarity), AV_OPT_TYPE_FLOAT, { .dbl = 0.01 }, 0.01, 1.0, FLAGS }, { "blend", "set the colorhold blend value", OFFSET(blend), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, 0.0, 1.0, FLAGS }, { NULL } @@ -246,6 +247,7 @@ AVFilter ff_vf_colorhold = { .inputs = colorkey_inputs, .outputs = colorkey_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, }; #endif /* CONFIG_COLORHOLD_FILTER */ diff --git a/libavfilter/vf_colorkey_opencl.c b/libavfilter/vf_colorkey_opencl.c index 46a0454fbd7..a76a1dcb7c8 100644 --- a/libavfilter/vf_colorkey_opencl.c +++ b/libavfilter/vf_colorkey_opencl.c @@ -222,7 +222,7 @@ static const AVFilterPad colorkey_opencl_outputs[] = { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption colorkey_opencl_options[] = { - { "color", "set the colorkey key color", OFFSET(colorkey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "color", "set the colorkey key color", OFFSET(colorkey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, 0, 0, FLAGS }, { "similarity", "set the colorkey similarity value", OFFSET(similarity), AV_OPT_TYPE_FLOAT, { .dbl = 0.01 }, 0.01, 1.0, FLAGS }, { "blend", "set the colorkey key blend value", OFFSET(blend), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, 0.0, 1.0, FLAGS }, { NULL } diff --git a/libavfilter/vf_colorlevels.c b/libavfilter/vf_colorlevels.c index fadb39e0049..c03e288e4ac 100644 --- a/libavfilter/vf_colorlevels.c +++ b/libavfilter/vf_colorlevels.c @@ -48,7 +48,7 @@ typedef struct ColorLevelsContext { } ColorLevelsContext; #define OFFSET(x) offsetof(ColorLevelsContext, 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 colorlevels_options[] = { { "rimin", "set input red black point", OFFSET(range[R].in_min), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS }, { "gimin", "set input green black point", OFFSET(range[G].in_min), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS }, @@ -325,4 +325,5 @@ AVFilter ff_vf_colorlevels = { .inputs = colorlevels_inputs, .outputs = colorlevels_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vf_colorspace.c b/libavfilter/vf_colorspace.c index df6efffb3dc..7098dd5bea6 100644 --- a/libavfilter/vf_colorspace.c +++ b/libavfilter/vf_colorspace.c @@ -331,15 +331,15 @@ static void apply_lut(int16_t *buf[3], ptrdiff_t stride, } } -struct ThreadData { +typedef struct ThreadData { AVFrame *in, *out; ptrdiff_t in_linesize[3], out_linesize[3]; int in_ss_h, out_ss_h; -}; +} ThreadData; static int convert(AVFilterContext *ctx, void *data, int job_nr, int n_jobs) { - struct ThreadData *td = data; + const ThreadData *td = data; ColorSpaceContext *s = ctx->priv; uint8_t *in_data[3], *out_data[3]; int16_t *rgb[3]; @@ -732,7 +732,7 @@ static int create_filtergraph(AVFilterContext *ctx, return 0; } -static int init(AVFilterContext *ctx) +static av_cold int init(AVFilterContext *ctx) { ColorSpaceContext *s = ctx->priv; @@ -771,7 +771,7 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) int res; ptrdiff_t rgb_stride = FFALIGN(in->width * sizeof(int16_t), 32); unsigned rgb_sz = rgb_stride * in->height; - struct ThreadData td; + ThreadData td; if (!out) { av_frame_free(&in); @@ -780,6 +780,7 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) res = av_frame_copy_props(out, in); if (res < 0) { av_frame_free(&in); + av_frame_free(&out); return res; } @@ -839,13 +840,18 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) !s->dither_scratch_base[1][0] || !s->dither_scratch_base[1][1] || !s->dither_scratch_base[2][0] || !s->dither_scratch_base[2][1]) { uninit(ctx); + av_frame_free(&in); + av_frame_free(&out); return AVERROR(ENOMEM); } s->rgb_sz = rgb_sz; } res = create_filtergraph(ctx, in, out); - if (res < 0) + if (res < 0) { + av_frame_free(&in); + av_frame_free(&out); return res; + } s->rgb_stride = rgb_stride / sizeof(int16_t); td.in = in; td.out = out; @@ -859,8 +865,11 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) td.out_ss_h = av_pix_fmt_desc_get(out->format)->log2_chroma_h; if (s->yuv2yuv_passthrough) { res = av_frame_copy(out, in); - if (res < 0) + if (res < 0) { + av_frame_free(&in); + av_frame_free(&out); return res; + } } else { ctx->internal->execute(ctx, convert, &td, NULL, FFMIN((in->height + 1) >> 1, ff_filter_get_nb_threads(ctx))); @@ -969,6 +978,7 @@ static const AVOption colorspace_options[] = { ENUM("smpte432", AVCOL_PRI_SMPTE432, "prm"), ENUM("bt2020", AVCOL_PRI_BT2020, "prm"), ENUM("jedec-p22", AVCOL_PRI_JEDEC_P22, "prm"), + ENUM("ebu3213", AVCOL_PRI_EBU3213, "prm"), { "trc", "Output transfer characteristics", OFFSET(user_trc), AV_OPT_TYPE_INT, { .i64 = AVCOL_TRC_UNSPECIFIED }, diff --git a/libavfilter/vf_convolution.c b/libavfilter/vf_convolution.c index 1305569c888..5909feaad1c 100644 --- a/libavfilter/vf_convolution.c +++ b/libavfilter/vf_convolution.c @@ -25,48 +25,11 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "convolution.h" #include "formats.h" #include "internal.h" #include "video.h" -enum MatrixMode { - MATRIX_SQUARE, - MATRIX_ROW, - MATRIX_COLUMN, - MATRIX_NBMODES, -}; - -typedef struct ConvolutionContext { - const AVClass *class; - - char *matrix_str[4]; - float rdiv[4]; - float bias[4]; - int mode[4]; - float scale; - float delta; - int planes; - - int size[4]; - int depth; - int max; - int bpc; - int nb_planes; - int nb_threads; - int planewidth[4]; - int planeheight[4]; - int matrix[4][49]; - int matrix_length[4]; - int copy[4]; - - void (*setup[4])(int radius, const uint8_t *c[], const uint8_t *src, int stride, - int x, int width, int y, int height, int bpc); - void (*filter[4])(uint8_t *dst, int width, - float rdiv, float bias, const int *const matrix, - const uint8_t *c[], int peak, int radius, - int dstride, int stride); -} ConvolutionContext; - #define OFFSET(x) offsetof(ConvolutionContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM @@ -128,6 +91,7 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA422P12, AV_PIX_FMT_YUVA444P12, 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, @@ -152,10 +116,10 @@ static void filter16_prewitt(uint8_t *dstp, int width, int x; for (x = 0; x < width; x++) { - int suma = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[1][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) * -1 + - AV_RN16A(&c[6][2 * x]) * 1 + AV_RN16A(&c[7][2 * x]) * 1 + AV_RN16A(&c[8][2 * x]) * 1; - int sumb = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) * 1 + AV_RN16A(&c[3][2 * x]) * -1 + - AV_RN16A(&c[5][2 * x]) * 1 + AV_RN16A(&c[6][2 * x]) * -1 + AV_RN16A(&c[8][2 * x]) * 1; + float suma = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[1][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) * -1 + + AV_RN16A(&c[6][2 * x]) * 1 + AV_RN16A(&c[7][2 * x]) * 1 + AV_RN16A(&c[8][2 * x]) * 1; + float sumb = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) * 1 + AV_RN16A(&c[3][2 * x]) * -1 + + AV_RN16A(&c[5][2 * x]) * 1 + AV_RN16A(&c[6][2 * x]) * -1 + AV_RN16A(&c[8][2 * x]) * 1; dst[x] = av_clip(sqrtf(suma*suma + sumb*sumb) * scale + delta, 0, peak); } @@ -170,8 +134,8 @@ static void filter16_roberts(uint8_t *dstp, int width, int x; for (x = 0; x < width; x++) { - int suma = AV_RN16A(&c[0][2 * x]) * 1 + AV_RN16A(&c[1][2 * x]) * -1; - int sumb = AV_RN16A(&c[4][2 * x]) * 1 + AV_RN16A(&c[3][2 * x]) * -1; + float suma = AV_RN16A(&c[0][2 * x]) * 1 + AV_RN16A(&c[1][2 * x]) * -1; + float sumb = AV_RN16A(&c[4][2 * x]) * 1 + AV_RN16A(&c[3][2 * x]) * -1; dst[x] = av_clip(sqrtf(suma*suma + sumb*sumb) * scale + delta, 0, peak); } @@ -186,10 +150,10 @@ static void filter16_sobel(uint8_t *dstp, int width, int x; for (x = 0; x < width; x++) { - int suma = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[1][2 * x]) * -2 + AV_RN16A(&c[2][2 * x]) * -1 + - AV_RN16A(&c[6][2 * x]) * 1 + AV_RN16A(&c[7][2 * x]) * 2 + AV_RN16A(&c[8][2 * x]) * 1; - int sumb = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) * 1 + AV_RN16A(&c[3][2 * x]) * -2 + - AV_RN16A(&c[5][2 * x]) * 2 + AV_RN16A(&c[6][2 * x]) * -1 + AV_RN16A(&c[8][2 * x]) * 1; + float suma = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[1][2 * x]) * -2 + AV_RN16A(&c[2][2 * x]) * -1 + + AV_RN16A(&c[6][2 * x]) * 1 + AV_RN16A(&c[7][2 * x]) * 2 + AV_RN16A(&c[8][2 * x]) * 1; + float sumb = AV_RN16A(&c[0][2 * x]) * -1 + AV_RN16A(&c[2][2 * x]) * 1 + AV_RN16A(&c[3][2 * x]) * -2 + + AV_RN16A(&c[5][2 * x]) * 2 + AV_RN16A(&c[6][2 * x]) * -1 + AV_RN16A(&c[8][2 * x]) * 1; dst[x] = av_clip(sqrtf(suma*suma + sumb*sumb) * scale + delta, 0, peak); } @@ -206,10 +170,10 @@ static void filter_prewitt(uint8_t *dst, int width, int x; for (x = 0; x < width; x++) { - int suma = c0[x] * -1 + c1[x] * -1 + c2[x] * -1 + - c6[x] * 1 + c7[x] * 1 + c8[x] * 1; - int sumb = c0[x] * -1 + c2[x] * 1 + c3[x] * -1 + - c5[x] * 1 + c6[x] * -1 + c8[x] * 1; + float suma = c0[x] * -1 + c1[x] * -1 + c2[x] * -1 + + c6[x] * 1 + c7[x] * 1 + c8[x] * 1; + float sumb = c0[x] * -1 + c2[x] * 1 + c3[x] * -1 + + c5[x] * 1 + c6[x] * -1 + c8[x] * 1; dst[x] = av_clip_uint8(sqrtf(suma*suma + sumb*sumb) * scale + delta); } @@ -223,8 +187,8 @@ static void filter_roberts(uint8_t *dst, int width, int x; for (x = 0; x < width; x++) { - int suma = c[0][x] * 1 + c[1][x] * -1; - int sumb = c[4][x] * 1 + c[3][x] * -1; + float suma = c[0][x] * 1 + c[1][x] * -1; + float sumb = c[4][x] * 1 + c[3][x] * -1; dst[x] = av_clip_uint8(sqrtf(suma*suma + sumb*sumb) * scale + delta); } @@ -241,10 +205,10 @@ static void filter_sobel(uint8_t *dst, int width, int x; for (x = 0; x < width; x++) { - int suma = c0[x] * -1 + c1[x] * -2 + c2[x] * -1 + - c6[x] * 1 + c7[x] * 2 + c8[x] * 1; - int sumb = c0[x] * -1 + c2[x] * 1 + c3[x] * -2 + - c5[x] * 2 + c6[x] * -1 + c8[x] * 1; + float suma = c0[x] * -1 + c1[x] * -2 + c2[x] * -1 + + c6[x] * 1 + c7[x] * 2 + c8[x] * 1; + float sumb = c0[x] * -1 + c2[x] * 1 + c3[x] * -2 + + c5[x] * 2 + c6[x] * -1 + c8[x] * 1; dst[x] = av_clip_uint8(sqrtf(suma*suma + sumb*sumb) * scale + delta); } @@ -625,6 +589,9 @@ static int config_input(AVFilterLink *inlink) s->filter[p] = filter16_7x7; } } +#if CONFIG_CONVOLUTION_FILTER && ARCH_X86_64 + ff_convolution_init_x86(s); +#endif } else if (!strcmp(ctx->filter->name, "prewitt")) { if (s->depth > 8) for (p = 0; p < s->nb_planes; p++) diff --git a/libavfilter/vf_convolve.c b/libavfilter/vf_convolve.c index 024eb684867..90ec98715ba 100644 --- a/libavfilter/vf_convolve.c +++ b/libavfilter/vf_convolve.c @@ -590,7 +590,9 @@ static av_cold void uninit(AVFilterContext *ctx) for (j = 0; j < MAX_THREADS; j++) { av_fft_end(s->fft[i][j]); + s->fft[i][j] = NULL; av_fft_end(s->ifft[i][j]); + s->ifft[i][j] = NULL; } } diff --git a/libavfilter/vf_copy.c b/libavfilter/vf_copy.c index b0159cff00e..e82feb4f0ec 100644 --- a/libavfilter/vf_copy.c +++ b/libavfilter/vf_copy.c @@ -48,15 +48,25 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterLink *outlink = inlink->dst->outputs[0]; AVFrame *out = ff_get_video_buffer(outlink, in->width, in->height); + int ret; if (!out) { - av_frame_free(&in); - return AVERROR(ENOMEM); + ret = AVERROR(ENOMEM); + goto fail; } - av_frame_copy_props(out, in); - av_frame_copy(out, in); + + ret = av_frame_copy_props(out, in); + if (ret < 0) + goto fail; + ret = av_frame_copy(out, in); + if (ret < 0) + goto fail; av_frame_free(&in); return ff_filter_frame(outlink, out); +fail: + av_frame_free(&in); + av_frame_free(&out); + return ret; } static const AVFilterPad avfilter_vf_copy_inputs[] = { diff --git a/libavfilter/vf_coreimage.m b/libavfilter/vf_coreimage.m index 323a28caa1d..4ed5ba79209 100644 --- a/libavfilter/vf_coreimage.m +++ b/libavfilter/vf_coreimage.m @@ -486,6 +486,7 @@ static av_cold int init(AVFilterContext *fctx) av_log(ctx, AV_LOG_DEBUG, "Filter_string: %s\n", ctx->filter_string); ret = av_dict_parse_string(&filter_dict, ctx->filter_string, "@", "#", AV_DICT_MULTIKEY); // parse filter_name:all_filter_options if (ret) { + av_dict_free(&filter_dict); av_log(ctx, AV_LOG_ERROR, "Parsing of filters failed.\n"); return AVERROR(EIO); } @@ -507,6 +508,7 @@ static av_cold int init(AVFilterContext *fctx) if (strncmp(f->value, "default", 7)) { // not default ret = av_dict_parse_string(&filter_options, f->value, "=", "@", 0); // parse option_name:option_value if (ret) { + av_dict_free(&filter_options); av_log(ctx, AV_LOG_ERROR, "Parsing of filter options for \"%s\" failed.\n", f->key); return AVERROR(EIO); } diff --git a/libavfilter/vf_crop.c b/libavfilter/vf_crop.c index 9fca7a73096..3d5cb95f786 100644 --- a/libavfilter/vf_crop.c +++ b/libavfilter/vf_crop.c @@ -94,24 +94,11 @@ typedef struct CropContext { static int query_formats(AVFilterContext *ctx) { AVFilterFormats *formats = NULL; - int fmt, ret; - - for (fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) { - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); - if (desc->flags & AV_PIX_FMT_FLAG_BITSTREAM) - continue; - if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { - // Not usable if there is any subsampling but the format is - // not planar (e.g. YUYV422). - if ((desc->log2_chroma_w || desc->log2_chroma_h) && - !(desc->flags & AV_PIX_FMT_FLAG_PLANAR)) - continue; - } - ret = ff_add_format(&formats, fmt); - if (ret < 0) - return ret; - } + int ret; + ret = ff_formats_pixdesc_filter(&formats, 0, AV_PIX_FMT_FLAG_BITSTREAM | FF_PIX_FMT_FLAG_SW_FLAT_SUB); + if (ret < 0) + return ret; return ff_set_common_formats(ctx, formats); } @@ -174,10 +161,9 @@ static int config_input(AVFilterLink *link) s->vsub = pix_desc->log2_chroma_h; } - if ((ret = av_expr_parse_and_eval(&res, (expr = s->w_expr), - var_names, s->var_values, - NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) - goto fail_expr; + av_expr_parse_and_eval(&res, (expr = s->w_expr), + var_names, s->var_values, + NULL, NULL, NULL, NULL, NULL, 0, ctx); s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = res; if ((ret = av_expr_parse_and_eval(&res, (expr = s->h_expr), var_names, s->var_values, @@ -244,7 +230,7 @@ static int config_input(AVFilterLink *link) return 0; fail_expr: - av_log(NULL, AV_LOG_ERROR, "Error when evaluating the expression '%s'\n", expr); + av_log(ctx, AV_LOG_ERROR, "Error when evaluating the expression '%s'\n", expr); return ret; } @@ -371,14 +357,15 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar #define OFFSET(x) offsetof(CropContext, 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 crop_options[] = { - { "out_w", "set the width crop area expression", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "w", "set the width crop area expression", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "out_h", "set the height crop area expression", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "h", "set the height crop area expression", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "x", "set the x crop area expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "(in_w-out_w)/2"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "y", "set the y crop area expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "(in_h-out_h)/2"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "out_w", "set the width crop area expression", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, 0, 0, TFLAGS }, + { "w", "set the width crop area expression", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, 0, 0, TFLAGS }, + { "out_h", "set the height crop area expression", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, 0, 0, TFLAGS }, + { "h", "set the height crop area expression", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, 0, 0, TFLAGS }, + { "x", "set the x crop area expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "(in_w-out_w)/2"}, 0, 0, TFLAGS }, + { "y", "set the y crop area expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "(in_h-out_h)/2"}, 0, 0, TFLAGS }, { "keep_aspect", "keep aspect ratio", OFFSET(keep_aspect), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, { "exact", "do exact cropping", OFFSET(exact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, { NULL } diff --git a/libavfilter/vf_datascope.c b/libavfilter/vf_datascope.c index c9039a60f65..35f60aa2f63 100644 --- a/libavfilter/vf_datascope.c +++ b/libavfilter/vf_datascope.c @@ -35,6 +35,7 @@ typedef struct DatascopeContext { int ow, oh; int x, y; int mode; + int dformat; int axis; float opacity; @@ -54,6 +55,7 @@ typedef struct DatascopeContext { #define OFFSET(x) offsetof(DatascopeContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +#define FLAGSR AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption datascope_options[] = { { "size", "set output size", OFFSET(ow), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, FLAGS }, @@ -66,6 +68,9 @@ static const AVOption datascope_options[] = { { "color2", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "mode" }, { "axis", "draw column/row numbers", OFFSET(axis), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, { "opacity", "set background opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGS }, + { "format", "set display number format", OFFSET(dformat), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "format" }, + { "hex", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "format" }, + { "dec", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "format" }, { NULL } }; @@ -179,9 +184,10 @@ static int filter_color2(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs const int yoff = td->yoff; const int P = FFMAX(s->nb_planes, s->nb_comps); const int C = s->chars; + const int D = ((s->chars - s->dformat) >> 2) + s->dformat * 2; const int W = (outlink->w - xoff) / (C * 10); const int H = (outlink->h - yoff) / (P * 12); - const char *format[2] = {"%02X\n", "%04X\n"}; + const char *format[4] = {"%02X\n", "%04X\n", "%03d\n", "%05d\n"}; const int slice_start = (W * jobnr) / nb_jobs; const int slice_end = (W * (jobnr+1)) / nb_jobs; int x, y, p; @@ -200,7 +206,7 @@ static int filter_color2(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs for (p = 0; p < P; p++) { char text[256]; - snprintf(text, sizeof(text), format[C>>2], value[p]); + snprintf(text, sizeof(text), format[D], value[p]); draw_text(&s->draw, out, &reverse, xoff + x * C * 10 + 2, yoff + y * P * 12 + p * 10 + 2, text, 0); } } @@ -221,9 +227,10 @@ static int filter_color(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const int yoff = td->yoff; const int P = FFMAX(s->nb_planes, s->nb_comps); const int C = s->chars; + const int D = ((s->chars - s->dformat) >> 2) + s->dformat * 2; const int W = (outlink->w - xoff) / (C * 10); const int H = (outlink->h - yoff) / (P * 12); - const char *format[2] = {"%02X\n", "%04X\n"}; + const char *format[4] = {"%02X\n", "%04X\n", "%03d\n", "%05d\n"}; const int slice_start = (W * jobnr) / nb_jobs; const int slice_end = (W * (jobnr+1)) / nb_jobs; int x, y, p; @@ -238,7 +245,7 @@ static int filter_color(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) for (p = 0; p < P; p++) { char text[256]; - snprintf(text, sizeof(text), format[C>>2], value[p]); + snprintf(text, sizeof(text), format[D], value[p]); draw_text(&s->draw, out, &color, xoff + x * C * 10 + 2, yoff + y * P * 12 + p * 10 + 2, text, 0); } } @@ -259,9 +266,10 @@ static int filter_mono(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const int yoff = td->yoff; const int P = FFMAX(s->nb_planes, s->nb_comps); const int C = s->chars; + const int D = ((s->chars - s->dformat) >> 2) + s->dformat * 2; const int W = (outlink->w - xoff) / (C * 10); const int H = (outlink->h - yoff) / (P * 12); - const char *format[2] = {"%02X\n", "%04X\n"}; + const char *format[4] = {"%02X\n", "%04X\n", "%03d\n", "%05d\n"}; const int slice_start = (W * jobnr) / nb_jobs; const int slice_end = (W * (jobnr+1)) / nb_jobs; int x, y, p; @@ -275,7 +283,7 @@ static int filter_mono(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) for (p = 0; p < P; p++) { char text[256]; - snprintf(text, sizeof(text), format[C>>2], value[p]); + snprintf(text, sizeof(text), format[D], value[p]); draw_text(&s->draw, out, &s->white, xoff + x * C * 10 + 2, yoff + y * P * 12 + p * 10 + 2, text, 0); } } @@ -359,7 +367,7 @@ static int config_input(AVFilterLink *inlink) 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} ); ff_draw_color(&s->draw, &s->gray, (uint8_t[]){ 77, 77, 77, 255} ); - s->chars = (s->draw.desc->comp[0].depth + 7) / 8 * 2; + s->chars = (s->draw.desc->comp[0].depth + 7) / 8 * 2 + s->dformat; s->nb_comps = s->draw.desc->nb_components; switch (s->mode) { @@ -444,6 +452,8 @@ typedef struct PixscopeContext { FFDrawColor red; FFDrawColor *colors[4]; + uint16_t values[4][80][80]; + void (*pick_color)(FFDrawContext *draw, FFDrawColor *color, AVFrame *in, int x, int y, int *value); } PixscopeContext; @@ -518,6 +528,8 @@ static int pixscope_config_input(AVFilterLink *inlink) return 0; } +#define SQR(x) ((x)*(x)) + static int pixscope_filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; @@ -526,7 +538,7 @@ static int pixscope_filter_frame(AVFilterLink *inlink, AVFrame *in) AVFrame *out = ff_get_video_buffer(outlink, in->width, in->height); int max[4] = { 0 }, min[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX }; float average[4] = { 0 }; - double rms[4] = { 0 }; + double std[4] = { 0 }, rms[4] = { 0 }; const char rgba[4] = { 'R', 'G', 'B', 'A' }; const char yuva[4] = { 'Y', 'U', 'V', 'A' }; int x, y, X, Y, i, w, h; @@ -583,6 +595,7 @@ static int pixscope_filter_frame(AVFilterLink *inlink, AVFrame *in) ff_fill_rectangle(&s->draw, &color, out->data, out->linesize, x * w + (s->ww - 4 - (s->w * w)) / 2 + X, y * h + 2 + Y, w, h); for (i = 0; i < 4; i++) { + s->values[i][x][y] = value[i]; rms[i] += (double)value[i] * (double)value[i]; average[i] += value[i]; min[i] = FFMIN(min[i], value[i]); @@ -629,13 +642,33 @@ static int pixscope_filter_frame(AVFilterLink *inlink, AVFrame *in) average[i] /= s->w * s->h; } + for (y = 0; y < s->h; y++) { + for (x = 0; x < s->w; x++) { + for (i = 0; i < 4; i++) + std[i] += SQR(s->values[i][x][y] - average[i]); + } + } + + for (i = 0; i < 4; i++) { + std[i] /= s->w * s->h; + std[i] = sqrt(std[i]); + } + snprintf(text, sizeof(text), "CH AVG MIN MAX RMS\n"); - draw_text(&s->draw, out, &s->white, X + 28, Y + s->ww + 20, text, 0); + draw_text(&s->draw, out, &s->white, X + 28, Y + s->ww + 5, text, 0); for (i = 0; i < s->nb_comps; i++) { int c = s->rgba_map[i]; snprintf(text, sizeof(text), "%c %07.1f %05d %05d %07.1f\n", s->is_rgb ? rgba[i] : yuva[i], average[c], min[c], max[c], rms[c]); - draw_text(&s->draw, out, s->colors[i], X + 28, Y + s->ww + 20 * (i + 2), text, 0); + draw_text(&s->draw, out, s->colors[i], X + 28, Y + s->ww + 15 * (i + 1), text, 0); + } + snprintf(text, sizeof(text), "CH STD\n"); + draw_text(&s->draw, out, &s->white, X + 28, Y + s->ww + 15 * (0 + 5), text, 0); + for (i = 0; i < s->nb_comps; i++) { + int c = s->rgba_map[i]; + + snprintf(text, sizeof(text), "%c %07.2f\n", s->is_rgb ? rgba[i] : yuva[i], std[c]); + draw_text(&s->draw, out, s->colors[i], X + 28, Y + s->ww + 15 * (i + 6), text, 0); } av_frame_free(&in); @@ -720,19 +753,19 @@ typedef struct OscilloscopeContext { #define OOFFSET(x) offsetof(OscilloscopeContext, x) static const AVOption oscilloscope_options[] = { - { "x", "set scope x position", OOFFSET(xpos), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS }, - { "y", "set scope y position", OOFFSET(ypos), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS }, - { "s", "set scope size", OOFFSET(size), AV_OPT_TYPE_FLOAT, {.dbl=0.8}, 0, 1, FLAGS }, - { "t", "set scope tilt", OOFFSET(tilt), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS }, - { "o", "set trace opacity", OOFFSET(o), AV_OPT_TYPE_FLOAT, {.dbl=0.8}, 0, 1, FLAGS }, - { "tx", "set trace x position", OOFFSET(tx), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS }, - { "ty", "set trace y position", OOFFSET(ty), AV_OPT_TYPE_FLOAT, {.dbl=0.9}, 0, 1, FLAGS }, - { "tw", "set trace width", OOFFSET(twidth), AV_OPT_TYPE_FLOAT, {.dbl=0.8},.1, 1, FLAGS }, - { "th", "set trace height", OOFFSET(theight), AV_OPT_TYPE_FLOAT, {.dbl=0.3},.1, 1, FLAGS }, - { "c", "set components to trace", OOFFSET(components), AV_OPT_TYPE_INT, {.i64=7}, 0, 15, FLAGS }, - { "g", "draw trace grid", OOFFSET(grid), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, - { "st", "draw statistics", OOFFSET(statistics), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, - { "sc", "draw scope", OOFFSET(scope), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, + { "x", "set scope x position", OOFFSET(xpos), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGSR }, + { "y", "set scope y position", OOFFSET(ypos), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGSR }, + { "s", "set scope size", OOFFSET(size), AV_OPT_TYPE_FLOAT, {.dbl=0.8}, 0, 1, FLAGSR }, + { "t", "set scope tilt", OOFFSET(tilt), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGSR }, + { "o", "set trace opacity", OOFFSET(o), AV_OPT_TYPE_FLOAT, {.dbl=0.8}, 0, 1, FLAGSR }, + { "tx", "set trace x position", OOFFSET(tx), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGSR }, + { "ty", "set trace y position", OOFFSET(ty), AV_OPT_TYPE_FLOAT, {.dbl=0.9}, 0, 1, FLAGSR }, + { "tw", "set trace width", OOFFSET(twidth), AV_OPT_TYPE_FLOAT, {.dbl=0.8},.1, 1, FLAGSR }, + { "th", "set trace height", OOFFSET(theight), AV_OPT_TYPE_FLOAT, {.dbl=0.3},.1, 1, FLAGSR }, + { "c", "set components to trace", OOFFSET(components), AV_OPT_TYPE_INT, {.i64=7}, 0, 15, FLAGSR }, + { "g", "draw trace grid", OOFFSET(grid), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGSR }, + { "st", "draw statistics", OOFFSET(statistics), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGSR }, + { "sc", "draw scope", OOFFSET(scope), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGSR }, { NULL } }; @@ -830,15 +863,36 @@ static void draw_trace16(OscilloscopeContext *s, AVFrame *frame) } } -static int oscilloscope_config_input(AVFilterLink *inlink) +static void update_oscilloscope(AVFilterContext *ctx) { - OscilloscopeContext *s = inlink->dst->priv; + OscilloscopeContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; int cx, cy, size; double tilt; + ff_draw_color(&s->draw, &s->dark, (uint8_t[]){ 0, 0, 0, s->o * 255} ); + s->height = s->theight * inlink->h; + s->width = s->twidth * inlink->w; + size = hypot(inlink->w, inlink->h); + size *= s->size; + tilt = (s->tilt - 0.5) * M_PI; + cx = s->xpos * (inlink->w - 1); + cy = s->ypos * (inlink->h - 1); + s->x1 = cx - size / 2.0 * cos(tilt); + s->x2 = cx + size / 2.0 * cos(tilt); + s->y1 = cy - size / 2.0 * sin(tilt); + s->y2 = cy + size / 2.0 * sin(tilt); + s->ox = (inlink->w - s->width) * s->tx; + s->oy = (inlink->h - s->height) * s->ty; +} + +static int oscilloscope_config_input(AVFilterLink *inlink) +{ + OscilloscopeContext *s = inlink->dst->priv; + int size; + s->nb_planes = av_pix_fmt_count_planes(inlink->format); ff_draw_init(&s->draw, inlink->format, 0); - ff_draw_color(&s->draw, &s->dark, (uint8_t[]){ 0, 0, 0, s->o * 255} ); ff_draw_color(&s->draw, &s->black, (uint8_t[]){ 0, 0, 0, 255} ); ff_draw_color(&s->draw, &s->white, (uint8_t[]){ 255, 255, 255, 255} ); ff_draw_color(&s->draw, &s->green, (uint8_t[]){ 0, 255, 0, 255} ); @@ -876,24 +930,13 @@ static int oscilloscope_config_input(AVFilterLink *inlink) } s->max = (1 << s->draw.desc->comp[0].depth); - cx = s->xpos * (inlink->w - 1); - cy = s->ypos * (inlink->h - 1); - s->height = s->theight * inlink->h; - s->width = s->twidth * inlink->w; size = hypot(inlink->w, inlink->h); s->values = av_calloc(size, sizeof(*s->values)); if (!s->values) return AVERROR(ENOMEM); - size *= s->size; - tilt = (s->tilt - 0.5) * M_PI; - s->x1 = cx - size / 2.0 * cos(tilt); - s->x2 = cx + size / 2.0 * cos(tilt); - s->y1 = cy - size / 2.0 * sin(tilt); - s->y2 = cy + size / 2.0 * sin(tilt); - s->ox = (inlink->w - s->width) * s->tx; - s->oy = (inlink->h - s->height) * s->ty; + update_oscilloscope(inlink->dst); return 0; } @@ -973,7 +1016,7 @@ static int oscilloscope_filter_frame(AVFilterLink *inlink, AVFrame *frame) frame->width, frame->height, s->ox, s->oy, s->width, s->height + 20 * s->statistics); - if (s->grid) { + if (s->grid && outlink->h >= 10) { ff_fill_rectangle(&s->draw, &s->gray, frame->data, frame->linesize, s->ox, s->oy, s->width - 1, 1); @@ -1022,6 +1065,20 @@ static int oscilloscope_filter_frame(AVFilterLink *inlink, AVFrame *frame) return ff_filter_frame(outlink, frame); } +static int oscilloscope_process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + update_oscilloscope(ctx); + + return 0; +} + static const AVFilterPad oscilloscope_inputs[] = { { .name = "default", @@ -1051,4 +1108,5 @@ AVFilter ff_vf_oscilloscope = { .inputs = oscilloscope_inputs, .outputs = oscilloscope_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, + .process_command = oscilloscope_process_command, }; diff --git a/libavfilter/vf_dblur.c b/libavfilter/vf_dblur.c new file mode 100644 index 00000000000..cc127da73f8 --- /dev/null +++ b/libavfilter/vf_dblur.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2020 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/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +typedef struct DBlurContext { + const AVClass *class; + + float angle; + float radius; + int planes; + + float b0, b1, q, c, R3; + + int depth; + int planewidth[4]; + int planeheight[4]; + float *buffer; + int nb_planes; + void (*horiz_slice)(float *buffer, int width, int height, int steps, float nu, float bscale); +} DBlurContext; + +#define OFFSET(x) offsetof(DBlurContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM + +static const AVOption dblur_options[] = { + { "angle", "set angle", OFFSET(angle), AV_OPT_TYPE_FLOAT, {.dbl=45}, 0.0, 360, FLAGS }, + { "radius", "set radius", OFFSET(radius), AV_OPT_TYPE_FLOAT, {.dbl=5}, 1, 8192, FLAGS }, + { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=0xF}, 0, 0xF, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(dblur); + +#define f(n, m) (dst[(n) * width + (m)]) + +static int filter_horizontally(AVFilterContext *ctx, int width, int height) +{ + DBlurContext *s = ctx->priv; + const float b0 = s->b0; + const float b1 = s->b1; + const float q = s->q; + const float c = s->c; + float *dst = s->buffer; + float g; + + if (s->R3 > 0) { + for (int y = 1; y < height - 1; y++) { + g = q * f(0, 0) + c * f(0, 0); + for (int x = 0; x < width; x++) { + f(y, x) = b0 * f(y, x) + b1 * f(y - 1, x) + g; + g = q * f(y, x) + c * f(y - 1, x); + } + } + + for (int y = height - 2; y >= 0; y--) { + g = q * f(y, width - 1) + c * f(y, width - 1); + for (int x = width - 1; x >= 0; x--) { + f(y, x) = b0 * f(y, x) + b1 * f(y + 1, x) + g; + g = q * f(y, x) + c * f(y + 1, x); + } + } + } else { + for (int y = 1; y < height - 1; y++) { + g = q * f(0, width - 1) + c * f(0, width - 1); + for (int x = width - 1; x >= 0; x--) { + f(y, x) = b0 * f(y, x) + b1 * f(y - 1, x) + g; + g = q * f(y, x) + c * f(y - 1, x); + } + } + + for (int y = height - 2; y >= 0; y--) { + g = q * f(y, 0) + c * f(y, 0); + for (int x = 0; x < width; x++) { + f(y, x) = b0 * f(y, x) + b1 * f(y + 1, x) + g; + g = q * f(y, x) + c * f(y + 1, x); + } + } + } + + return 0; +} + +static void diriir2d(AVFilterContext *ctx, int plane) +{ + DBlurContext *s = ctx->priv; + const int width = s->planewidth[plane]; + const int height = s->planeheight[plane]; + + filter_horizontally(ctx, width, height); +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, + AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, + AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, + 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_YUV440P12, + 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_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9, + AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, + AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, + 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_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_NONE + }; + + return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); +} + +static int config_input(AVFilterLink *inlink) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + DBlurContext *s = inlink->dst->priv; + + s->depth = desc->comp[0].depth; + s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); + s->planewidth[0] = s->planewidth[3] = inlink->w; + s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); + s->planeheight[0] = s->planeheight[3] = inlink->h; + + s->nb_planes = av_pix_fmt_count_planes(inlink->format); + + s->buffer = av_malloc_array(FFALIGN(inlink->w, 16), FFALIGN(inlink->h, 16) * sizeof(*s->buffer)); + if (!s->buffer) + return AVERROR(ENOMEM); + + return 0; +} + +static void set_params(DBlurContext *s, float angle, float r) +{ + float mu, nu, R1, R2, w1, w2; + float a0, a1, a2, a3; + + angle = angle * M_PI / 180.f; + + mu = cosf(angle); + nu = sinf(angle); + R1 = (mu * r) * (mu * r); + R2 = (nu * r) * (nu * r); + s->R3 = mu * nu * r * r; + w1 = sqrtf(0.25f + R1); + w2 = sqrtf(0.25f + R2); + a0 = (w1 + 0.5f) * (w2 + 0.5f) - fabsf(s->R3); + a1 = 0.5f + w2 - a0; + a2 = 0.5f + w1 - a0; + a3 = a0 - w1 - w2; + s->b0 = 1.f / a0; + s->b1 = -a2 / a0; + s->q = -a1 / a0; + s->c = -a3 / a0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + DBlurContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *out; + int plane; + + set_params(s, s->angle, s->radius); + + if (av_frame_is_writable(in)) { + out = in; + } else { + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + } + + for (plane = 0; plane < s->nb_planes; plane++) { + const int height = s->planeheight[plane]; + const int width = s->planewidth[plane]; + float *bptr = s->buffer; + const uint8_t *src = in->data[plane]; + const uint16_t *src16 = (const uint16_t *)in->data[plane]; + uint8_t *dst = out->data[plane]; + uint16_t *dst16 = (uint16_t *)out->data[plane]; + int y, x; + + if (!(s->planes & (1 << plane))) { + if (out != in) + av_image_copy_plane(out->data[plane], out->linesize[plane], + in->data[plane], in->linesize[plane], + width * ((s->depth + 7) / 8), height); + continue; + } + + if (s->depth == 8) { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + bptr[x] = src[x]; + } + bptr += width; + src += in->linesize[plane]; + } + } else { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + bptr[x] = src16[x]; + } + bptr += width; + src16 += in->linesize[plane] / 2; + } + } + + diriir2d(ctx, plane); + + bptr = s->buffer; + if (s->depth == 8) { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + dst[x] = bptr[x]; + } + bptr += width; + dst += out->linesize[plane]; + } + } else { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + dst16[x] = bptr[x]; + } + bptr += width; + dst16 += out->linesize[plane] / 2; + } + } + } + + if (out != in) + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + DBlurContext *s = ctx->priv; + + av_freep(&s->buffer); +} + +static const AVFilterPad dblur_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad dblur_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter ff_vf_dblur = { + .name = "dblur", + .description = NULL_IF_CONFIG_SMALL("Apply Directional Blur filter."), + .priv_size = sizeof(DBlurContext), + .priv_class = &dblur_class, + .uninit = uninit, + .query_formats = query_formats, + .inputs = dblur_inputs, + .outputs = dblur_outputs, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, + .process_command = ff_filter_process_command, +}; diff --git a/libavfilter/vf_deband.c b/libavfilter/vf_deband.c index 713e80b0493..330792f532e 100644 --- a/libavfilter/vf_deband.c +++ b/libavfilter/vf_deband.c @@ -74,7 +74,8 @@ static int query_formats(AVFilterContext *ctx) DebandContext *s = ctx->priv; static const enum AVPixelFormat pix_fmts[] = { - AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY16, + 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_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, diff --git a/libavfilter/vf_deblock.c b/libavfilter/vf_deblock.c index 62e3248d461..77c681ecb8a 100644 --- a/libavfilter/vf_deblock.c +++ b/libavfilter/vf_deblock.c @@ -76,6 +76,7 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA422P12, AV_PIX_FMT_YUVA444P12, 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, diff --git a/libavfilter/vf_decimate.c b/libavfilter/vf_decimate.c index 53347c7f109..88a8b38a581 100644 --- a/libavfilter/vf_decimate.c +++ b/libavfilter/vf_decimate.c @@ -217,11 +217,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) av_frame_free(&dm->queue[i].frame); } else { AVFrame *frame = dm->queue[i].frame; + dm->queue[i].frame = NULL; if (frame->pts != AV_NOPTS_VALUE && dm->start_pts == AV_NOPTS_VALUE) dm->start_pts = frame->pts; if (dm->ppsrc) { av_frame_free(&frame); frame = dm->clean_src[i]; + dm->clean_src[i] = NULL; } frame->pts = av_rescale_q(outlink->frame_count_in, dm->ts_unit, (AVRational){1,1}) + (dm->start_pts == AV_NOPTS_VALUE ? 0 : dm->start_pts); @@ -314,7 +316,15 @@ static av_cold void decimate_uninit(AVFilterContext *ctx) av_frame_free(&dm->last); av_freep(&dm->bdiffs); + if (dm->queue) { + for (i = 0; i < dm->cycle; i++) + av_frame_free(&dm->queue[i].frame); + } av_freep(&dm->queue); + if (dm->clean_src) { + for (i = 0; i < dm->cycle; i++) + av_frame_free(&dm->clean_src[i]); + } av_freep(&dm->clean_src); for (i = 0; i < ctx->nb_inputs; i++) av_freep(&ctx->input_pads[i].name); diff --git a/libavfilter/vf_dedot.c b/libavfilter/vf_dedot.c index bb0f9e5ac8f..993ac8f74fc 100644 --- a/libavfilter/vf_dedot.c +++ b/libavfilter/vf_dedot.c @@ -71,6 +71,7 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA422P12, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_NONE }; @@ -283,7 +284,7 @@ static int activate(AVFilterContext *ctx) s->frames[i] = av_frame_clone(frame); } av_frame_free(&frame); - } else { + } else if (s->frames[3]) { s->eof_frames--; s->frames[4] = av_frame_clone(s->frames[3]); } @@ -312,7 +313,8 @@ static int activate(AVFilterContext *ctx) FFMIN(s->planeheight[2], ff_filter_get_nb_threads(ctx))); } - } + } else + av_frame_free(&out); } else if (!out) { ret = AVERROR(ENOMEM); } @@ -343,7 +345,11 @@ static int activate(AVFilterContext *ctx) if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) { if (status == AVERROR_EOF) { s->eof = 1; - s->eof_frames = 2; + s->eof_frames = !!s->frames[0] + !!s->frames[1]; + if (s->eof_frames <= 0) { + ff_outlink_set_status(outlink, AVERROR_EOF, pts); + return 0; + } ff_filter_set_ready(ctx, 10); return 0; } diff --git a/libavfilter/vf_deflicker.c b/libavfilter/vf_deflicker.c index b038b977f62..544672dd703 100644 --- a/libavfilter/vf_deflicker.c +++ b/libavfilter/vf_deflicker.c @@ -109,6 +109,10 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV440P12, AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, + AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, + AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA422P16, + AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_NONE }; AVFilterFormats *formats = ff_make_format_list(pixel_fmts); @@ -420,7 +424,10 @@ static int request_frame(AVFilterLink *outlink) ret = ff_request_frame(ctx->inputs[0]); if (ret == AVERROR_EOF && s->available > 0) { - AVFrame *buf = av_frame_clone(ff_bufqueue_peek(&s->q, s->size - 1)); + AVFrame *buf = ff_bufqueue_peek(&s->q, s->available - 1); + if (!buf) + return AVERROR(ENOMEM); + buf = av_frame_clone(buf); if (!buf) return AVERROR(ENOMEM); diff --git a/libavfilter/vf_deinterlace_vaapi.c b/libavfilter/vf_deinterlace_vaapi.c index 72d034981ab..71809eb01a8 100644 --- a/libavfilter/vf_deinterlace_vaapi.c +++ b/libavfilter/vf_deinterlace_vaapi.c @@ -239,7 +239,7 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) err = av_frame_copy_props(output_frame, input_frame); if (err < 0) - return err; + goto fail; err = ff_vaapi_vpp_init_params(avctx, ¶ms, input_frame, output_frame); diff --git a/libavfilter/vf_delogo.c b/libavfilter/vf_delogo.c index 065d0936415..e55673d5ada 100644 --- a/libavfilter/vf_delogo.c +++ b/libavfilter/vf_delogo.c @@ -31,10 +31,52 @@ #include "libavutil/imgutils.h" #include "libavutil/opt.h" #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[] = { + "x", + "y", + "w", + "h", + "n", ///< number of frame + "t", ///< timestamp expressed in seconds + NULL +}; + +enum var_name { + VAR_X, + VAR_Y, + VAR_W, + VAR_H, + VAR_N, + VAR_T, + VAR_VARS_NB +}; +#define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts) * av_q2d(tb)) + +static int set_expr(AVExpr **pexpr, const char *expr, const char *option, void *log_ctx) +{ + int ret; + AVExpr *old = NULL; + + if (*pexpr) + old = *pexpr; + ret = av_expr_parse(pexpr, expr, var_names, NULL, NULL, NULL, NULL, 0, log_ctx); + if (ret < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "Error when parsing the expression '%s' for %s\n", + expr, option); + *pexpr = old; + return ret; + } + + av_expr_free(old); + return 0; +} + /** * Apply a simple delogo algorithm to the image in src and put the @@ -126,7 +168,7 @@ static void apply_delogo(uint8_t *dst, int dst_linesize, botleft[x-logo_x1-1] + botleft[x-logo_x1+1]) * weightb; weight = (weightl + weightr + weightt + weightb) * 3U; - interp = ROUNDED_DIV(interp, weight); + interp = (interp + (weight >> 1)) / weight; if (y >= logo_y+band && y < logo_y+logo_h-band && x >= logo_x+band && x < logo_x+logo_w-band) { @@ -156,26 +198,34 @@ static void apply_delogo(uint8_t *dst, int dst_linesize, typedef struct DelogoContext { const AVClass *class; int x, y, w, h, band, show; + char *x_expr, *y_expr, *w_expr, *h_expr; + AVExpr *x_pexpr, *y_pexpr, *w_pexpr, *h_pexpr; + double var_values[VAR_VARS_NB]; } DelogoContext; #define OFFSET(x) offsetof(DelogoContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption delogo_options[]= { - { "x", "set logo x position", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS }, - { "y", "set logo y position", OFFSET(y), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS }, - { "w", "set logo width", OFFSET(w), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS }, - { "h", "set logo height", OFFSET(h), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS }, -#if LIBAVFILTER_VERSION_MAJOR < 7 - /* Actual default value for band/t is 1, set in init */ - { "band", "set delogo area band size", OFFSET(band), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, - { "t", "set delogo area band size", OFFSET(band), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, -#endif - { "show", "show delogo area", OFFSET(show), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, FLAGS }, + { "x", "set logo x position", OFFSET(x_expr), AV_OPT_TYPE_STRING, { .str = "-1" }, 0, 0, FLAGS }, + { "y", "set logo y position", OFFSET(y_expr), AV_OPT_TYPE_STRING, { .str = "-1" }, 0, 0, FLAGS }, + { "w", "set logo width", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str = "-1" }, 0, 0, FLAGS }, + { "h", "set logo height", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str = "-1" }, 0, 0, FLAGS }, + { "show", "show delogo area", OFFSET(show), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { NULL } }; AVFILTER_DEFINE_CLASS(delogo); +static av_cold void uninit(AVFilterContext *ctx) +{ + DelogoContext *s = ctx->priv; + + av_expr_free(s->x_pexpr); s->x_pexpr = NULL; + av_expr_free(s->y_pexpr); s->y_pexpr = NULL; + av_expr_free(s->w_pexpr); s->w_pexpr = NULL; + av_expr_free(s->h_pexpr); s->h_pexpr = NULL; +} + static int query_formats(AVFilterContext *ctx) { @@ -194,6 +244,18 @@ static int query_formats(AVFilterContext *ctx) static av_cold int init(AVFilterContext *ctx) { DelogoContext *s = ctx->priv; + int ret = 0; + + 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 || + (ret = set_expr(&s->w_pexpr, s->w_expr, "w", ctx)) < 0 || + (ret = set_expr(&s->h_pexpr, s->h_expr, "h", ctx)) < 0 ) + return ret; + + s->x = av_expr_eval(s->x_pexpr, s->var_values, s); + s->y = av_expr_eval(s->y_pexpr, s->var_values, s); + s->w = av_expr_eval(s->w_pexpr, s->var_values, s); + s->h = av_expr_eval(s->h_pexpr, s->var_values, s); #define CHECK_UNSET_OPT(opt) \ if (s->opt == -1) { \ @@ -205,16 +267,8 @@ static av_cold int init(AVFilterContext *ctx) CHECK_UNSET_OPT(w); CHECK_UNSET_OPT(h); -#if LIBAVFILTER_VERSION_MAJOR < 7 - if (s->band == 0) { /* Unset, use default */ - av_log(ctx, AV_LOG_WARNING, "Note: default band value was changed from 4 to 1.\n"); - s->band = 1; - } else if (s->band != 1) { - av_log(ctx, AV_LOG_WARNING, "Option band is deprecated.\n"); - } -#else s->band = 1; -#endif + av_log(ctx, AV_LOG_VERBOSE, "x:%d y:%d, w:%d h:%d band:%d show:%d\n", s->x, s->y, s->w, s->h, s->band, s->show); @@ -251,6 +305,40 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) int direct = 0; int plane; AVRational sar; + int ret; + + s->var_values[VAR_N] = inlink->frame_count_out; + s->var_values[VAR_T] = TS2T(in->pts, inlink->time_base); + s->x = av_expr_eval(s->x_pexpr, s->var_values, s); + s->y = av_expr_eval(s->y_pexpr, s->var_values, s); + s->w = av_expr_eval(s->w_pexpr, s->var_values, s); + s->h = av_expr_eval(s->h_pexpr, s->var_values, s); + + if (s->x + (s->band - 1) <= 0 || s->x + s->w - (s->band*2 - 2) > inlink->w || + s->y + (s->band - 1) <= 0 || s->y + s->h - (s->band*2 - 2) > inlink->h) { + av_log(s, AV_LOG_WARNING, "Logo area is outside of the frame," + " auto set the area inside of the frame\n"); + } + + if (s->x + (s->band - 1) <= 0) + s->x = 1 + s->band; + if (s->y + (s->band - 1) <= 0) + s->y = 1 + s->band; + if (s->x + s->w - (s->band*2 - 2) > inlink->w) + s->w = inlink->w - s->x - (s->band*2 - 2); + if (s->y + s->h - (s->band*2 - 2) > inlink->h) + s->h = inlink->h - s->y - (s->band*2 - 2); + + ret = config_input(inlink); + if (ret < 0) { + av_frame_free(&in); + return ret; + } + + s->w += s->band*2; + s->h += s->band*2; + s->x -= s->band; + s->y -= s->band; if (av_frame_is_writable(in)) { direct = 1; @@ -317,6 +405,7 @@ AVFilter ff_vf_delogo = { .priv_size = sizeof(DelogoContext), .priv_class = &delogo_class, .init = init, + .uninit = uninit, .query_formats = query_formats, .inputs = avfilter_vf_delogo_inputs, .outputs = avfilter_vf_delogo_outputs, diff --git a/libavfilter/vf_derain.c b/libavfilter/vf_derain.c index c380b401226..74322602b0c 100644 --- a/libavfilter/vf_derain.c +++ b/libavfilter/vf_derain.c @@ -34,11 +34,12 @@ typedef struct DRContext { const AVClass *class; + int filter_type; char *model_filename; DNNBackendType backend_type; DNNModule *dnn_module; DNNModel *model; - DNNInputData input; + DNNData input; DNNData output; } DRContext; @@ -46,12 +47,15 @@ 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[] = { - { "dnn_backend", "DNN backend", OFFSET(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, "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(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" }, #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, "backend" }, #endif - { "model", "path to model file", OFFSET(model_filename), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, + { "model", "path to model file", OFFSET(model_filename), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, { NULL } }; @@ -96,7 +100,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFilterLink *outlink = ctx->outputs[0]; DRContext *dr_context = ctx->priv; DNNReturnType dnn_result; - int pad_size; AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) { @@ -125,15 +128,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) out->width = dr_context->output.width; outlink->h = dr_context->output.height; outlink->w = dr_context->output.width; - pad_size = (in->height - out->height) >> 1; for (int i = 0; i < out->height; i++){ for(int j = 0; j < out->width * 3; j++){ int k = i * out->linesize[0] + j; int t = i * out->width * 3 + j; - - int t_in = (i + pad_size) * in->width * 3 + j + pad_size * 3; - out->data[0][k] = CLIP((int)((((float *)dr_context->input.data)[t_in] - dr_context->output.data[t]) * 255), 0, 255); + out->data[0][k] = CLIP((int)((((float *)dr_context->output.data)[t]) * 255), 0, 255); } } diff --git a/libavfilter/vf_deshake.c b/libavfilter/vf_deshake.c index c8480e74dd0..28a541b94a7 100644 --- a/libavfilter/vf_deshake.c +++ b/libavfilter/vf_deshake.c @@ -354,7 +354,7 @@ static av_cold int init(AVFilterContext *ctx) if (deshake->filename) deshake->fp = fopen(deshake->filename, "w"); if (deshake->fp) - fwrite("Ori x, Avg x, Fin x, Ori y, Avg y, Fin y, Ori angle, Avg angle, Fin angle, Ori zoom, Avg zoom, Fin zoom\n", sizeof(char), 104, deshake->fp); + fwrite("Ori x, Avg x, Fin x, Ori y, Avg y, Fin y, Ori angle, Avg angle, Fin angle, Ori zoom, Avg zoom, Fin zoom\n", 1, 104, deshake->fp); // Quadword align left edge of box for MMX code, adjust width if necessary // to keep right margin @@ -421,6 +421,7 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) const int chroma_width = AV_CEIL_RSHIFT(link->w, desc->log2_chroma_w); const int chroma_height = AV_CEIL_RSHIFT(link->h, desc->log2_chroma_h); int aligned; + float transform_zoom; out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) { @@ -484,7 +485,7 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) // Write statistics to file if (deshake->fp) { snprintf(tmp, 256, "%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f\n", orig.vec.x, deshake->avg.vec.x, t.vec.x, orig.vec.y, deshake->avg.vec.y, t.vec.y, orig.angle, deshake->avg.angle, t.angle, orig.zoom, deshake->avg.zoom, t.zoom); - fwrite(tmp, sizeof(char), strlen(tmp), deshake->fp); + fwrite(tmp, 1, strlen(tmp), deshake->fp); } // Turn relative current frame motion into absolute by adding it to the @@ -505,10 +506,12 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) deshake->last.angle = t.angle; deshake->last.zoom = t.zoom; + transform_zoom = 1.0 + t.zoom / 100.0; + // Generate a luma transformation matrix - avfilter_get_matrix(t.vec.x, t.vec.y, t.angle, 1.0 + t.zoom / 100.0, matrix_y); + ff_get_matrix(t.vec.x, t.vec.y, t.angle, transform_zoom, transform_zoom, matrix_y); // Generate a chroma transformation matrix - avfilter_get_matrix(t.vec.x / (link->w / chroma_width), t.vec.y / (link->h / chroma_height), t.angle, 1.0 + t.zoom / 100.0, matrix_uv); + ff_get_matrix(t.vec.x / (link->w / chroma_width), t.vec.y / (link->h / chroma_height), t.angle, transform_zoom, transform_zoom, matrix_uv); // Transform the luma and chroma planes ret = deshake->transform(link->dst, link->w, link->h, chroma_width, chroma_height, matrix_y, matrix_uv, INTERPOLATE_BILINEAR, deshake->edge, in, out); diff --git a/libavfilter/vf_deshake_opencl.c b/libavfilter/vf_deshake_opencl.c new file mode 100644 index 00000000000..4f1bb09362a --- /dev/null +++ b/libavfilter/vf_deshake_opencl.c @@ -0,0 +1,2202 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 (C) 2000-2008, Intel Corporation, all rights reserved. + * Copyright (C) 2009, Willow Garage Inc., all rights reserved. + * Copyright (C) 2013, OpenCV Foundation, all rights reserved. + * Third party copyrights are property of their respective owners. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistribution's of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistribution's 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. + * + * * The name of the copyright holders may not 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 Intel Corporation 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 +#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/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" +#include "video.h" + +/* +This filter matches feature points between frames (dealing with outliers) and then +uses the matches to estimate an affine transform between frames. This transform is +decomposed into various values (translation, scale, rotation) and the values are +summed relative to the start of the video to obtain on absolute camera position +for each frame. This "camera path" is then smoothed via a gaussian filter, resulting +in a new path that is turned back into an affine transform and applied to each +frame to render it. + +High-level overview: + +All of the work to extract motion data from frames occurs in queue_frame. Motion data +is buffered in a smoothing window, so queue_frame simply computes the absolute camera +positions and places them in ringbuffers. + +filter_frame is responsible for looking at the absolute camera positions currently +in the ringbuffers, applying the gaussian filter, and then transforming the frames. +*/ + +// Number of bits for BRIEF descriptors +#define BREIFN 512 +// Size of the patch from which a BRIEF descriptor is extracted +// This is the size used in OpenCV +#define BRIEF_PATCH_SIZE 31 +#define BRIEF_PATCH_SIZE_HALF (BRIEF_PATCH_SIZE / 2) + +#define MATCHES_CONTIG_SIZE 2000 + +#define ROUNDED_UP_DIV(a, b) ((a + (b - 1)) / b) + +typedef struct PointPair { + // Previous frame + cl_float2 p1; + // Current frame + cl_float2 p2; +} PointPair; + +typedef struct MotionVector { + PointPair p; + // Used to mark vectors as potential outliers + cl_int should_consider; +} MotionVector; + +// Denotes the indices for the different types of motion in the ringbuffers array +enum RingbufferIndices { + RingbufX, + RingbufY, + RingbufRot, + RingbufScaleX, + RingbufScaleY, + + // Should always be last + RingbufCount +}; + +// Struct that holds data for drawing point match debug data +typedef struct DebugMatches { + MotionVector *matches; + // The points used to calculate the affine transform for a frame + MotionVector model_matches[3]; + + int num_matches; + // For cases where we couldn't calculate a model + int num_model_matches; +} DebugMatches; + +// Groups together the ringbuffers that store absolute distortion / position values +// for each frame +typedef struct AbsoluteFrameMotion { + // Array with the various ringbuffers, indexed via the RingbufferIndices enum + AVFifoBuffer *ringbuffers[RingbufCount]; + + // Offset to get to the current frame being processed + // (not in bytes) + int curr_frame_offset; + // Keeps track of where the start and end of contiguous motion data is (to + // deal with cases where no motion data is found between two frames) + int data_start_offset; + int data_end_offset; + + AVFifoBuffer *debug_matches; +} AbsoluteFrameMotion; + +// Takes care of freeing the arrays within the DebugMatches inside of the +// debug_matches ringbuffer and then freeing the buffer itself. +static void free_debug_matches(AbsoluteFrameMotion *afm) { + DebugMatches dm; + + if (!afm->debug_matches) { + return; + } + + while (av_fifo_size(afm->debug_matches) > 0) { + av_fifo_generic_read( + afm->debug_matches, + &dm, + sizeof(DebugMatches), + NULL + ); + + av_freep(&dm.matches); + } + + av_fifo_freep(&afm->debug_matches); +} + +// Stores the translation, scale, rotation, and skew deltas between two frames +typedef struct FrameDelta { + cl_float2 translation; + float rotation; + cl_float2 scale; + cl_float2 skew; +} FrameDelta; + +typedef struct SimilarityMatrix { + // The 2x3 similarity matrix + double matrix[6]; +} SimilarityMatrix; + +typedef struct CropInfo { + // The top left corner of the bounding box for the crop + cl_float2 top_left; + // The bottom right corner of the bounding box for the crop + cl_float2 bottom_right; +} CropInfo; + +// Returned from function that determines start and end values for iteration +// around the current frame in a ringbuffer +typedef struct IterIndices { + int start; + int end; +} IterIndices; + +typedef struct DeshakeOpenCLContext { + OpenCLFilterContext ocf; + // Whether or not the above `OpenCLFilterContext` has been initialized + int initialized; + + // These variables are used in the activate callback + int64_t duration; + int eof; + + // State for random number generation + AVLFG alfg; + + // FIFO frame queue used to buffer future frames for processing + FFFrameQueue fq; + // Ringbuffers for frame positions + AbsoluteFrameMotion abs_motion; + + // The number of frames' motion to consider before and after the frame we are + // smoothing + int smooth_window; + // The number of the frame we are currently processing + int curr_frame; + + // Stores a 1d array of normalised gaussian kernel values for convolution + float *gauss_kernel; + + // Buffer for error values used in RANSAC code + float *ransac_err; + + // Information regarding how to crop the smoothed luminance (or RGB) planes + CropInfo crop_y; + // Information regarding how to crop the smoothed chroma planes + CropInfo crop_uv; + + // Whether or not we are processing YUV input (as oppposed to RGB) + int is_yuv; + // The underlying format of the hardware surfaces + int sw_format; + + // Buffer to copy `matches` into for the CPU to work with + MotionVector *matches_host; + MotionVector *matches_contig_host; + + MotionVector *inliers; + + cl_command_queue command_queue; + cl_kernel kernel_grayscale; + cl_kernel kernel_harris_response; + cl_kernel kernel_refine_features; + cl_kernel kernel_brief_descriptors; + cl_kernel kernel_match_descriptors; + cl_kernel kernel_transform; + cl_kernel kernel_crop_upscale; + + // Stores a frame converted to grayscale + cl_mem grayscale; + // Stores the harris response for a frame (measure of "cornerness" for each pixel) + cl_mem harris_buf; + + // Detected features after non-maximum suppression and sub-pixel refinement + cl_mem refined_features; + // Saved from the previous frame + cl_mem prev_refined_features; + + // BRIEF sampling pattern that is randomly initialized + cl_mem brief_pattern; + // Feature point descriptors for the current frame + cl_mem descriptors; + // Feature point descriptors for the previous frame + cl_mem prev_descriptors; + // Vectors between points in current and previous frame + cl_mem matches; + cl_mem matches_contig; + // Holds the matrix to transform luminance (or RGB) with + cl_mem transform_y; + // Holds the matrix to transform chroma with + cl_mem transform_uv; + + // Configurable options + + int tripod_mode; + int debug_on; + int should_crop; + + // Whether or not feature points should be refined at a sub-pixel level + cl_int refine_features; + // If the user sets a value other than the default, 0, this percentage is + // translated into a sigma value ranging from 0.5 to 40.0 + float smooth_percent; + // This number is multiplied by the video frame rate to determine the size + // of the smooth window + float smooth_window_multiplier; + + // Debug stuff + + cl_kernel kernel_draw_debug_info; + cl_mem debug_matches; + cl_mem debug_model_matches; + + // These store the total time spent executing the different kernels in nanoseconds + unsigned long long grayscale_time; + unsigned long long harris_response_time; + unsigned long long refine_features_time; + unsigned long long brief_descriptors_time; + unsigned long long match_descriptors_time; + unsigned long long transform_time; + unsigned long long crop_upscale_time; + + // Time spent copying matched features from the device to the host + unsigned long long read_buf_time; +} DeshakeOpenCLContext; + +// Returns a random uniformly-distributed number in [low, high] +static int rand_in(int low, int high, AVLFG *alfg) { + return (av_lfg_get(alfg) % (high - low)) + low; +} + +// Returns the average execution time for an event given the total time and the +// number of frames processed. +static double averaged_event_time_ms(unsigned long long total_time, int num_frames) { + return (double)total_time / (double)num_frames / 1000000.0; +} + +// The following code is loosely ported from OpenCV + +// Estimates affine transform from 3 point pairs +// model is a 2x3 matrix: +// a b c +// d e f +static void run_estimate_kernel(const MotionVector *point_pairs, double *model) +{ + // src points + double x1 = point_pairs[0].p.p1.s[0]; + double y1 = point_pairs[0].p.p1.s[1]; + double x2 = point_pairs[1].p.p1.s[0]; + double y2 = point_pairs[1].p.p1.s[1]; + double x3 = point_pairs[2].p.p1.s[0]; + double y3 = point_pairs[2].p.p1.s[1]; + + // dest points + double X1 = point_pairs[0].p.p2.s[0]; + double Y1 = point_pairs[0].p.p2.s[1]; + double X2 = point_pairs[1].p.p2.s[0]; + double Y2 = point_pairs[1].p.p2.s[1]; + double X3 = point_pairs[2].p.p2.s[0]; + double Y3 = point_pairs[2].p.p2.s[1]; + + double d = 1.0 / ( x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2) ); + + model[0] = d * ( X1*(y2-y3) + X2*(y3-y1) + X3*(y1-y2) ); + model[1] = d * ( X1*(x3-x2) + X2*(x1-x3) + X3*(x2-x1) ); + model[2] = d * ( X1*(x2*y3 - x3*y2) + X2*(x3*y1 - x1*y3) + X3*(x1*y2 - x2*y1) ); + + model[3] = d * ( Y1*(y2-y3) + Y2*(y3-y1) + Y3*(y1-y2) ); + model[4] = d * ( Y1*(x3-x2) + Y2*(x1-x3) + Y3*(x2-x1) ); + model[5] = d * ( Y1*(x2*y3 - x3*y2) + Y2*(x3*y1 - x1*y3) + Y3*(x1*y2 - x2*y1) ); +} + +// Checks that the 3 points in the given array are not collinear +static int points_not_collinear(const cl_float2 **points) +{ + int j, k, i = 2; + + for (j = 0; j < i; j++) { + double dx1 = points[j]->s[0] - points[i]->s[0]; + double dy1 = points[j]->s[1] - points[i]->s[1]; + + for (k = 0; k < j; k++) { + double dx2 = points[k]->s[0] - points[i]->s[0]; + double dy2 = points[k]->s[1] - points[i]->s[1]; + + // Assuming a 3840 x 2160 video with a point at (0, 0) and one at + // (3839, 2159), this prevents a third point from being within roughly + // 0.5 of a pixel of the line connecting the two on both axes + if (fabs(dx2*dy1 - dy2*dx1) <= 1.0) { + return 0; + } + } + } + + return 1; +} + +// Checks a subset of 3 point pairs to make sure that the points are not collinear +// and not too close to each other +static int check_subset(const MotionVector *pairs_subset) +{ + const cl_float2 *prev_points[] = { + &pairs_subset[0].p.p1, + &pairs_subset[1].p.p1, + &pairs_subset[2].p.p1 + }; + + const cl_float2 *curr_points[] = { + &pairs_subset[0].p.p2, + &pairs_subset[1].p.p2, + &pairs_subset[2].p.p2 + }; + + return points_not_collinear(prev_points) && points_not_collinear(curr_points); +} + +// Selects a random subset of 3 points from point_pairs and places them in pairs_subset +static int get_subset( + AVLFG *alfg, + const MotionVector *point_pairs, + const int num_point_pairs, + MotionVector *pairs_subset, + int max_attempts +) { + int idx[3]; + int i = 0, j, iters = 0; + + for (; iters < max_attempts; iters++) { + for (i = 0; i < 3 && iters < max_attempts;) { + int idx_i = 0; + + for (;;) { + idx_i = idx[i] = rand_in(0, num_point_pairs, alfg); + + for (j = 0; j < i; j++) { + if (idx_i == idx[j]) { + break; + } + } + + if (j == i) { + break; + } + } + + pairs_subset[i] = point_pairs[idx[i]]; + i++; + } + + if (i == 3 && !check_subset(pairs_subset)) { + continue; + } + break; + } + + return i == 3 && iters < max_attempts; +} + +// Computes the error for each of the given points based on the given model. +static void compute_error( + const MotionVector *point_pairs, + const int num_point_pairs, + const double *model, + float *err +) { + double F0 = model[0], F1 = model[1], F2 = model[2]; + double F3 = model[3], F4 = model[4], F5 = model[5]; + + for (int i = 0; i < num_point_pairs; i++) { + const cl_float2 *f = &point_pairs[i].p.p1; + const cl_float2 *t = &point_pairs[i].p.p2; + + double a = F0*f->s[0] + F1*f->s[1] + F2 - t->s[0]; + double b = F3*f->s[0] + F4*f->s[1] + F5 - t->s[1]; + + err[i] = a*a + b*b; + } +} + +// Determines which of the given point matches are inliers for the given model +// based on the specified threshold. +// +// err must be an array of num_point_pairs length +static int find_inliers( + MotionVector *point_pairs, + const int num_point_pairs, + const double *model, + float *err, + double thresh +) { + float t = (float)(thresh * thresh); + int i, n = num_point_pairs, num_inliers = 0; + + compute_error(point_pairs, num_point_pairs, model, err); + + for (i = 0; i < n; i++) { + if (err[i] <= t) { + // This is an inlier + point_pairs[i].should_consider = 1; + num_inliers += 1; + } else { + point_pairs[i].should_consider = 0; + } + } + + return num_inliers; +} + +// Determines the number of iterations required to achieve the desired confidence level. +// +// The equation used to determine the number of iterations to do is: +// 1 - confidence = (1 - inlier_probability^num_points)^num_iters +// +// Solving for num_iters: +// +// num_iters = log(1 - confidence) / log(1 - inlier_probability^num_points) +// +// A more in-depth explanation can be found at https://en.wikipedia.org/wiki/Random_sample_consensus +// under the 'Parameters' heading +static int ransac_update_num_iters(double confidence, double num_outliers, int max_iters) +{ + double num, denom; + + confidence = av_clipd(confidence, 0.0, 1.0); + num_outliers = av_clipd(num_outliers, 0.0, 1.0); + + // avoid inf's & nan's + num = FFMAX(1.0 - confidence, DBL_MIN); + denom = 1.0 - pow(1.0 - num_outliers, 3); + if (denom < DBL_MIN) { + return 0; + } + + num = log(num); + denom = log(denom); + + return denom >= 0 || -num >= max_iters * (-denom) ? max_iters : (int)round(num / denom); +} + +// Estimates an affine transform between the given pairs of points using RANdom +// SAmple Consensus +static int estimate_affine_2d( + DeshakeOpenCLContext *deshake_ctx, + MotionVector *point_pairs, + DebugMatches *debug_matches, + const int num_point_pairs, + double *model_out, + const double threshold, + const int max_iters, + const double confidence +) { + int result = 0; + double best_model[6], model[6]; + MotionVector pairs_subset[3], best_pairs[3]; + + int iter, niters = FFMAX(max_iters, 1); + int good_count, max_good_count = 0; + + // We need at least 3 points to build a model from + if (num_point_pairs < 3) { + return 0; + } else if (num_point_pairs == 3) { + // There are only 3 points, so RANSAC doesn't apply here + run_estimate_kernel(point_pairs, model_out); + + for (int i = 0; i < 3; ++i) { + point_pairs[i].should_consider = 1; + } + + return 1; + } + + for (iter = 0; iter < niters; ++iter) { + int found = get_subset(&deshake_ctx->alfg, point_pairs, num_point_pairs, pairs_subset, 10000); + + if (!found) { + if (iter == 0) { + return 0; + } + + break; + } + + run_estimate_kernel(pairs_subset, model); + good_count = find_inliers(point_pairs, num_point_pairs, model, deshake_ctx->ransac_err, threshold); + + if (good_count > FFMAX(max_good_count, 2)) { + for (int mi = 0; mi < 6; ++mi) { + best_model[mi] = model[mi]; + } + + for (int pi = 0; pi < 3; pi++) { + best_pairs[pi] = pairs_subset[pi]; + } + + max_good_count = good_count; + niters = ransac_update_num_iters( + confidence, + (double)(num_point_pairs - good_count) / num_point_pairs, + niters + ); + } + } + + if (max_good_count > 0) { + for (int mi = 0; mi < 6; ++mi) { + model_out[mi] = best_model[mi]; + } + + for (int pi = 0; pi < 3; ++pi) { + debug_matches->model_matches[pi] = best_pairs[pi]; + } + debug_matches->num_model_matches = 3; + + // Find the inliers again for the best model for debugging + find_inliers(point_pairs, num_point_pairs, best_model, deshake_ctx->ransac_err, threshold); + result = 1; + } + + return result; +} + +// "Wiggles" the first point in best_pairs around a tiny bit in order to decrease the +// total error +static void optimize_model( + DeshakeOpenCLContext *deshake_ctx, + MotionVector *best_pairs, + MotionVector *inliers, + const int num_inliers, + float best_err, + double *model_out +) { + float move_x_val = 0.01; + float move_y_val = 0.01; + int move_x = 1; + float old_move_x_val = 0; + double model[6]; + int last_changed = 0; + + for (int iters = 0; iters < 200; iters++) { + float total_err = 0; + + if (move_x) { + best_pairs[0].p.p2.s[0] += move_x_val; + } else { + best_pairs[0].p.p2.s[0] += move_y_val; + } + + run_estimate_kernel(best_pairs, model); + compute_error(inliers, num_inliers, model, deshake_ctx->ransac_err); + + for (int j = 0; j < num_inliers; j++) { + total_err += deshake_ctx->ransac_err[j]; + } + + if (total_err < best_err) { + for (int mi = 0; mi < 6; ++mi) { + model_out[mi] = model[mi]; + } + + best_err = total_err; + last_changed = iters; + } else { + // Undo the change + if (move_x) { + best_pairs[0].p.p2.s[0] -= move_x_val; + } else { + best_pairs[0].p.p2.s[0] -= move_y_val; + } + + if (iters - last_changed > 4) { + // We've already improved the model as much as we can + break; + } + + old_move_x_val = move_x_val; + + if (move_x) { + move_x_val *= -1; + } else { + move_y_val *= -1; + } + + if (old_move_x_val < 0) { + move_x = 0; + } else { + move_x = 1; + } + } + } +} + +// Uses a process similar to that of RANSAC to find a transform that minimizes +// the total error for a set of point matches determined to be inliers +// +// (Pick random subsets, compute model, find total error, iterate until error +// is minimized.) +static int minimize_error( + DeshakeOpenCLContext *deshake_ctx, + MotionVector *inliers, + DebugMatches *debug_matches, + const int num_inliers, + double *model_out, + const int max_iters +) { + int result = 0; + float best_err = FLT_MAX; + double best_model[6], model[6]; + MotionVector pairs_subset[3], best_pairs[3]; + + for (int i = 0; i < max_iters; i++) { + float total_err = 0; + int found = get_subset(&deshake_ctx->alfg, inliers, num_inliers, pairs_subset, 10000); + + if (!found) { + if (i == 0) { + return 0; + } + + break; + } + + run_estimate_kernel(pairs_subset, model); + compute_error(inliers, num_inliers, model, deshake_ctx->ransac_err); + + for (int j = 0; j < num_inliers; j++) { + total_err += deshake_ctx->ransac_err[j]; + } + + if (total_err < best_err) { + for (int mi = 0; mi < 6; ++mi) { + best_model[mi] = model[mi]; + } + + for (int pi = 0; pi < 3; pi++) { + best_pairs[pi] = pairs_subset[pi]; + } + + best_err = total_err; + } + } + + for (int mi = 0; mi < 6; ++mi) { + model_out[mi] = best_model[mi]; + } + + for (int pi = 0; pi < 3; ++pi) { + debug_matches->model_matches[pi] = best_pairs[pi]; + } + debug_matches->num_model_matches = 3; + result = 1; + + optimize_model(deshake_ctx, best_pairs, inliers, num_inliers, best_err, model_out); + return result; +} + +// End code from OpenCV + +// Decomposes a similarity matrix into translation, rotation, scale, and skew +// +// See http://frederic-wang.fr/decomposition-of-2d-transform-matrices.html +static FrameDelta decompose_transform(double *model) +{ + FrameDelta ret; + + double a = model[0]; + double c = model[1]; + double e = model[2]; + double b = model[3]; + double d = model[4]; + double f = model[5]; + double delta = a * d - b * c; + + memset(&ret, 0, sizeof(ret)); + + ret.translation.s[0] = e; + ret.translation.s[1] = f; + + // This is the QR method + if (a != 0 || b != 0) { + double r = hypot(a, b); + + ret.rotation = FFSIGN(b) * acos(a / r); + ret.scale.s[0] = r; + ret.scale.s[1] = delta / r; + ret.skew.s[0] = atan((a * c + b * d) / (r * r)); + ret.skew.s[1] = 0; + } else if (c != 0 || d != 0) { + double s = sqrt(c * c + d * d); + + ret.rotation = M_PI / 2 - FFSIGN(d) * acos(-c / s); + ret.scale.s[0] = delta / s; + ret.scale.s[1] = s; + ret.skew.s[0] = 0; + ret.skew.s[1] = atan((a * c + b * d) / (s * s)); + } // otherwise there is only translation + + return ret; +} + +// Move valid vectors from the 2d buffer into a 1d buffer where they are contiguous +static int make_vectors_contig( + DeshakeOpenCLContext *deshake_ctx, + int size_y, + int size_x +) { + int num_vectors = 0; + + for (int i = 0; i < size_y; ++i) { + for (int j = 0; j < size_x; ++j) { + MotionVector v = deshake_ctx->matches_host[j + i * size_x]; + + if (v.should_consider) { + deshake_ctx->matches_contig_host[num_vectors] = v; + ++num_vectors; + } + + // Make sure we do not exceed the amount of space we allocated for these vectors + if (num_vectors == MATCHES_CONTIG_SIZE - 1) { + return num_vectors; + } + } + } + return num_vectors; +} + +// Returns the gaussian kernel value for the given x coordinate and sigma value +static float gaussian_for(int x, float sigma) { + return 1.0f / expf(((float)x * (float)x) / (2.0f * sigma * sigma)); +} + +// Makes a normalized gaussian kernel of the given length for the given sigma +// and places it in gauss_kernel +static void make_gauss_kernel(float *gauss_kernel, float length, float sigma) +{ + float gauss_sum = 0; + int window_half = length / 2; + + for (int i = 0; i < length; ++i) { + float val = gaussian_for(i - window_half, sigma); + + gauss_sum += val; + gauss_kernel[i] = val; + } + + // Normalize the gaussian values + for (int i = 0; i < length; ++i) { + gauss_kernel[i] /= gauss_sum; + } +} + +// Returns indices to start and end iteration at in order to iterate over a window +// of length size centered at the current frame in a ringbuffer +// +// Always returns numbers that result in a window of length size, even if that +// means specifying negative indices or indices past the end of the values in the +// ringbuffers. Make sure you clip indices appropriately within your loop. +static IterIndices start_end_for(DeshakeOpenCLContext *deshake_ctx, int length) { + IterIndices indices; + + indices.start = deshake_ctx->abs_motion.curr_frame_offset - (length / 2); + indices.end = deshake_ctx->abs_motion.curr_frame_offset + (length / 2) + (length % 2); + + return indices; +} + +// Sets val to the value in the given ringbuffer at the given offset, taking care of +// clipping the offset into the appropriate range +static void ringbuf_float_at( + DeshakeOpenCLContext *deshake_ctx, + AVFifoBuffer *values, + float *val, + int offset +) { + int clip_start, clip_end, offset_clipped; + if (deshake_ctx->abs_motion.data_end_offset != -1) { + clip_end = deshake_ctx->abs_motion.data_end_offset; + } else { + // This expression represents the last valid index in the buffer, + // which we use repeatedly at the end of the video. + clip_end = deshake_ctx->smooth_window - (av_fifo_space(values) / sizeof(float)) - 1; + } + + if (deshake_ctx->abs_motion.data_start_offset != -1) { + clip_start = deshake_ctx->abs_motion.data_start_offset; + } else { + // Negative indices will occur at the start of the video, and we want + // them to be clipped to 0 in order to repeatedly use the position of + // the first frame. + clip_start = 0; + } + + offset_clipped = av_clip( + offset, + clip_start, + clip_end + ); + + av_fifo_generic_peek_at( + values, + val, + offset_clipped * sizeof(float), + sizeof(float), + NULL + ); +} + +// Returns smoothed current frame value of the given buffer of floats based on the +// given Gaussian kernel and its length (also the window length, centered around the +// current frame) and the "maximum value" of the motion. +// +// This "maximum value" should be the width / height of the image in the case of +// translation and an empirically chosen constant for rotation / scale. +// +// The sigma chosen to generate the final gaussian kernel with used to smooth the +// camera path is either hardcoded (set by user, deshake_ctx->smooth_percent) or +// adaptively chosen. +static float smooth( + DeshakeOpenCLContext *deshake_ctx, + float *gauss_kernel, + int length, + float max_val, + AVFifoBuffer *values +) { + float new_large_s = 0, new_small_s = 0, new_best = 0, old, diff_between, + percent_of_max, inverted_percent; + IterIndices indices = start_end_for(deshake_ctx, length); + float large_sigma = 40.0f; + float small_sigma = 2.0f; + float best_sigma; + + if (deshake_ctx->smooth_percent) { + best_sigma = (large_sigma - 0.5f) * deshake_ctx->smooth_percent + 0.5f; + } else { + // Strategy to adaptively smooth trajectory: + // + // 1. Smooth path with large and small sigma values + // 2. Take the absolute value of the difference between them + // 3. Get a percentage by putting the difference over the "max value" + // 4, Invert the percentage + // 5. Calculate a new sigma value weighted towards the larger sigma value + // 6. Determine final smoothed trajectory value using that sigma + + make_gauss_kernel(gauss_kernel, length, large_sigma); + for (int i = indices.start, j = 0; i < indices.end; ++i, ++j) { + ringbuf_float_at(deshake_ctx, values, &old, i); + new_large_s += old * gauss_kernel[j]; + } + + make_gauss_kernel(gauss_kernel, length, small_sigma); + for (int i = indices.start, j = 0; i < indices.end; ++i, ++j) { + ringbuf_float_at(deshake_ctx, values, &old, i); + new_small_s += old * gauss_kernel[j]; + } + + diff_between = fabsf(new_large_s - new_small_s); + percent_of_max = diff_between / max_val; + inverted_percent = 1 - percent_of_max; + best_sigma = large_sigma * powf(inverted_percent, 40); + } + + make_gauss_kernel(gauss_kernel, length, best_sigma); + for (int i = indices.start, j = 0; i < indices.end; ++i, ++j) { + ringbuf_float_at(deshake_ctx, values, &old, i); + new_best += old * gauss_kernel[j]; + } + + return new_best; +} + +// Returns the position of the given point after the transform is applied +static cl_float2 transformed_point(float x, float y, float *transform) { + cl_float2 ret; + + ret.s[0] = x * transform[0] + y * transform[1] + transform[2]; + ret.s[1] = x * transform[3] + y * transform[4] + transform[5]; + + return ret; +} + +// Creates an affine transform that scales from the center of a frame +static void transform_center_scale( + float x_shift, + float y_shift, + float angle, + float scale_x, + float scale_y, + float center_w, + float center_h, + float *matrix +) { + cl_float2 center_s; + float center_s_w, center_s_h; + + ff_get_matrix( + 0, + 0, + 0, + scale_x, + scale_y, + matrix + ); + + center_s = transformed_point(center_w, center_h, matrix); + center_s_w = center_w - center_s.s[0]; + center_s_h = center_h - center_s.s[1]; + + ff_get_matrix( + x_shift + center_s_w, + y_shift + center_s_h, + angle, + scale_x, + scale_y, + matrix + ); +} + +// Determines the crop necessary to eliminate black borders from a smoothed frame +// and updates target crop accordingly +static void update_needed_crop( + CropInfo* crop, + float *transform, + float frame_width, + float frame_height +) { + float new_width, new_height, adjusted_width, adjusted_height, adjusted_x, adjusted_y; + + cl_float2 top_left = transformed_point(0, 0, transform); + cl_float2 top_right = transformed_point(frame_width, 0, transform); + cl_float2 bottom_left = transformed_point(0, frame_height, transform); + cl_float2 bottom_right = transformed_point(frame_width, frame_height, transform); + float ar_h = frame_height / frame_width; + float ar_w = frame_width / frame_height; + + if (crop->bottom_right.s[0] == 0) { + // The crop hasn't been set to the original size of the plane + crop->bottom_right.s[0] = frame_width; + crop->bottom_right.s[1] = frame_height; + } + + crop->top_left.s[0] = FFMAX3( + crop->top_left.s[0], + top_left.s[0], + bottom_left.s[0] + ); + + crop->top_left.s[1] = FFMAX3( + crop->top_left.s[1], + top_left.s[1], + top_right.s[1] + ); + + crop->bottom_right.s[0] = FFMIN3( + crop->bottom_right.s[0], + bottom_right.s[0], + top_right.s[0] + ); + + crop->bottom_right.s[1] = FFMIN3( + crop->bottom_right.s[1], + bottom_right.s[1], + bottom_left.s[1] + ); + + // Make sure our potentially new bounding box has the same aspect ratio + new_height = crop->bottom_right.s[1] - crop->top_left.s[1]; + new_width = crop->bottom_right.s[0] - crop->top_left.s[0]; + + adjusted_width = new_height * ar_w; + adjusted_x = crop->bottom_right.s[0] - adjusted_width; + + if (adjusted_x >= crop->top_left.s[0]) { + crop->top_left.s[0] = adjusted_x; + } else { + adjusted_height = new_width * ar_h; + adjusted_y = crop->bottom_right.s[1] - adjusted_height; + crop->top_left.s[1] = adjusted_y; + } +} + +static av_cold void deshake_opencl_uninit(AVFilterContext *avctx) +{ + DeshakeOpenCLContext *ctx = avctx->priv; + cl_int cle; + + for (int i = 0; i < RingbufCount; i++) + av_fifo_freep(&ctx->abs_motion.ringbuffers[i]); + + if (ctx->debug_on) + free_debug_matches(&ctx->abs_motion); + + if (ctx->gauss_kernel) + av_freep(&ctx->gauss_kernel); + + if (ctx->ransac_err) + av_freep(&ctx->ransac_err); + + if (ctx->matches_host) + av_freep(&ctx->matches_host); + + if (ctx->matches_contig_host) + av_freep(&ctx->matches_contig_host); + + if (ctx->inliers) + av_freep(&ctx->inliers); + + ff_framequeue_free(&ctx->fq); + + CL_RELEASE_KERNEL(ctx->kernel_grayscale); + CL_RELEASE_KERNEL(ctx->kernel_harris_response); + CL_RELEASE_KERNEL(ctx->kernel_refine_features); + CL_RELEASE_KERNEL(ctx->kernel_brief_descriptors); + CL_RELEASE_KERNEL(ctx->kernel_match_descriptors); + CL_RELEASE_KERNEL(ctx->kernel_crop_upscale); + if (ctx->debug_on) + CL_RELEASE_KERNEL(ctx->kernel_draw_debug_info); + + CL_RELEASE_QUEUE(ctx->command_queue); + + if (!ctx->is_yuv) + CL_RELEASE_MEMORY(ctx->grayscale); + CL_RELEASE_MEMORY(ctx->harris_buf); + CL_RELEASE_MEMORY(ctx->refined_features); + CL_RELEASE_MEMORY(ctx->prev_refined_features); + CL_RELEASE_MEMORY(ctx->brief_pattern); + CL_RELEASE_MEMORY(ctx->descriptors); + CL_RELEASE_MEMORY(ctx->prev_descriptors); + CL_RELEASE_MEMORY(ctx->matches); + CL_RELEASE_MEMORY(ctx->matches_contig); + CL_RELEASE_MEMORY(ctx->transform_y); + CL_RELEASE_MEMORY(ctx->transform_uv); + if (ctx->debug_on) { + CL_RELEASE_MEMORY(ctx->debug_matches); + CL_RELEASE_MEMORY(ctx->debug_model_matches); + } + + ff_opencl_filter_uninit(avctx); +} + +static int deshake_opencl_init(AVFilterContext *avctx) +{ + DeshakeOpenCLContext *ctx = avctx->priv; + AVFilterLink *outlink = avctx->outputs[0]; + AVFilterLink *inlink = avctx->inputs[0]; + // Pointer to the host-side pattern buffer to be initialized and then copied + // to the GPU + PointPair *pattern_host = NULL; + cl_int cle; + int err; + cl_ulong8 zeroed_ulong8; + FFFrameQueueGlobal fqg; + cl_image_format grayscale_format; + cl_image_desc grayscale_desc; + cl_command_queue_properties queue_props; + + const enum AVPixelFormat disallowed_formats[14] = { + AV_PIX_FMT_GBRP, + AV_PIX_FMT_GBRP9BE, + AV_PIX_FMT_GBRP9LE, + AV_PIX_FMT_GBRP10BE, + AV_PIX_FMT_GBRP10LE, + AV_PIX_FMT_GBRP16BE, + AV_PIX_FMT_GBRP16LE, + AV_PIX_FMT_GBRAP, + AV_PIX_FMT_GBRAP16BE, + AV_PIX_FMT_GBRAP16LE, + AV_PIX_FMT_GBRAP12BE, + AV_PIX_FMT_GBRAP12LE, + AV_PIX_FMT_GBRAP10BE, + AV_PIX_FMT_GBRAP10LE + }; + + // Number of elements for an array + const int image_grid_32 = ROUNDED_UP_DIV(outlink->h, 32) * ROUNDED_UP_DIV(outlink->w, 32); + + const int descriptor_buf_size = image_grid_32 * (BREIFN / 8); + const int features_buf_size = image_grid_32 * sizeof(cl_float2); + + const AVHWFramesContext *hw_frames_ctx = (AVHWFramesContext*)inlink->hw_frames_ctx->data; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(hw_frames_ctx->sw_format); + + av_assert0(hw_frames_ctx); + av_assert0(desc); + + ff_framequeue_global_init(&fqg); + ff_framequeue_init(&ctx->fq, &fqg); + ctx->eof = 0; + ctx->smooth_window = (int)(av_q2d(avctx->inputs[0]->frame_rate) * ctx->smooth_window_multiplier); + ctx->curr_frame = 0; + + memset(&zeroed_ulong8, 0, sizeof(cl_ulong8)); + + ctx->gauss_kernel = av_malloc_array(ctx->smooth_window, sizeof(float)); + if (!ctx->gauss_kernel) { + err = AVERROR(ENOMEM); + goto fail; + } + + ctx->ransac_err = av_malloc_array(MATCHES_CONTIG_SIZE, sizeof(float)); + if (!ctx->ransac_err) { + err = AVERROR(ENOMEM); + goto fail; + } + + for (int i = 0; i < RingbufCount; i++) { + ctx->abs_motion.ringbuffers[i] = av_fifo_alloc_array( + ctx->smooth_window, + sizeof(float) + ); + + if (!ctx->abs_motion.ringbuffers[i]) { + err = AVERROR(ENOMEM); + goto fail; + } + } + + if (ctx->debug_on) { + ctx->abs_motion.debug_matches = av_fifo_alloc_array( + ctx->smooth_window / 2, + sizeof(DebugMatches) + ); + + if (!ctx->abs_motion.debug_matches) { + err = AVERROR(ENOMEM); + goto fail; + } + } + + ctx->abs_motion.curr_frame_offset = 0; + ctx->abs_motion.data_start_offset = -1; + ctx->abs_motion.data_end_offset = -1; + + pattern_host = av_malloc_array(BREIFN, sizeof(PointPair)); + if (!pattern_host) { + err = AVERROR(ENOMEM); + goto fail; + } + + ctx->matches_host = av_malloc_array(image_grid_32, sizeof(MotionVector)); + if (!ctx->matches_host) { + err = AVERROR(ENOMEM); + goto fail; + } + + ctx->matches_contig_host = av_malloc_array(MATCHES_CONTIG_SIZE, sizeof(MotionVector)); + if (!ctx->matches_contig_host) { + err = AVERROR(ENOMEM); + goto fail; + } + + ctx->inliers = av_malloc_array(MATCHES_CONTIG_SIZE, sizeof(MotionVector)); + if (!ctx->inliers) { + err = AVERROR(ENOMEM); + goto fail; + } + + // Initializing the patch pattern for building BREIF descriptors with + av_lfg_init(&ctx->alfg, 234342424); + for (int i = 0; i < BREIFN; ++i) { + PointPair pair; + + for (int j = 0; j < 2; ++j) { + pair.p1.s[j] = rand_in(-BRIEF_PATCH_SIZE_HALF, BRIEF_PATCH_SIZE_HALF + 1, &ctx->alfg); + pair.p2.s[j] = rand_in(-BRIEF_PATCH_SIZE_HALF, BRIEF_PATCH_SIZE_HALF + 1, &ctx->alfg); + } + + pattern_host[i] = pair; + } + + for (int i = 0; i < 14; i++) { + if (ctx->sw_format == disallowed_formats[i]) { + av_log(avctx, AV_LOG_ERROR, "unsupported format in deshake_opencl.\n"); + err = AVERROR(ENOSYS); + goto fail; + } + } + + if (desc->flags & AV_PIX_FMT_FLAG_RGB) { + ctx->is_yuv = 0; + } else { + ctx->is_yuv = 1; + } + ctx->sw_format = hw_frames_ctx->sw_format; + + err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_deshake, 1); + if (err < 0) + goto fail; + + if (ctx->debug_on) { + queue_props = CL_QUEUE_PROFILING_ENABLE; + } else { + queue_props = 0; + } + ctx->command_queue = clCreateCommandQueue( + ctx->ocf.hwctx->context, + ctx->ocf.hwctx->device_id, + queue_props, + &cle + ); + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to create OpenCL command queue %d.\n", cle); + + CL_CREATE_KERNEL(ctx, grayscale); + CL_CREATE_KERNEL(ctx, harris_response); + CL_CREATE_KERNEL(ctx, refine_features); + CL_CREATE_KERNEL(ctx, brief_descriptors); + CL_CREATE_KERNEL(ctx, match_descriptors); + CL_CREATE_KERNEL(ctx, transform); + CL_CREATE_KERNEL(ctx, crop_upscale); + if (ctx->debug_on) + CL_CREATE_KERNEL(ctx, draw_debug_info); + + if (!ctx->is_yuv) { + grayscale_format.image_channel_order = CL_R; + grayscale_format.image_channel_data_type = CL_FLOAT; + + grayscale_desc = (cl_image_desc) { + .image_type = CL_MEM_OBJECT_IMAGE2D, + .image_width = outlink->w, + .image_height = outlink->h, + .image_depth = 0, + .image_array_size = 0, + .image_row_pitch = 0, + .image_slice_pitch = 0, + .num_mip_levels = 0, + .num_samples = 0, + .buffer = NULL, + }; + + ctx->grayscale = clCreateImage( + ctx->ocf.hwctx->context, + 0, + &grayscale_format, + &grayscale_desc, + NULL, + &cle + ); + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to create grayscale image: %d.\n", cle); + } + + CL_CREATE_BUFFER(ctx, harris_buf, outlink->h * outlink->w * sizeof(float)); + CL_CREATE_BUFFER(ctx, refined_features, features_buf_size); + CL_CREATE_BUFFER(ctx, prev_refined_features, features_buf_size); + CL_CREATE_BUFFER_FLAGS( + ctx, + brief_pattern, + CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, + BREIFN * sizeof(PointPair), + pattern_host + ); + CL_CREATE_BUFFER(ctx, descriptors, descriptor_buf_size); + CL_CREATE_BUFFER(ctx, prev_descriptors, descriptor_buf_size); + CL_CREATE_BUFFER(ctx, matches, image_grid_32 * sizeof(MotionVector)); + CL_CREATE_BUFFER(ctx, matches_contig, MATCHES_CONTIG_SIZE * sizeof(MotionVector)); + CL_CREATE_BUFFER(ctx, transform_y, 9 * sizeof(float)); + CL_CREATE_BUFFER(ctx, transform_uv, 9 * sizeof(float)); + if (ctx->debug_on) { + CL_CREATE_BUFFER(ctx, debug_matches, MATCHES_CONTIG_SIZE * sizeof(MotionVector)); + CL_CREATE_BUFFER(ctx, debug_model_matches, 3 * sizeof(MotionVector)); + } + + ctx->initialized = 1; + av_freep(&pattern_host); + + return 0; + +fail: + av_freep(&pattern_host); + return err; +} + +// Logs debug information about the transform data +static void transform_debug(AVFilterContext *avctx, float *new_vals, float *old_vals, int curr_frame) { + av_log(avctx, AV_LOG_VERBOSE, + "Frame %d:\n" + "\tframe moved from: %f x, %f y\n" + "\t to: %f x, %f y\n" + "\t rotated from: %f degrees\n" + "\t to: %f degrees\n" + "\t scaled from: %f x, %f y\n" + "\t to: %f x, %f y\n" + "\n" + "\tframe moved by: %f x, %f y\n" + "\t rotated by: %f degrees\n" + "\t scaled by: %f x, %f y\n", + curr_frame, + old_vals[RingbufX], old_vals[RingbufY], + new_vals[RingbufX], new_vals[RingbufY], + old_vals[RingbufRot] * (180.0 / M_PI), + new_vals[RingbufRot] * (180.0 / M_PI), + old_vals[RingbufScaleX], old_vals[RingbufScaleY], + new_vals[RingbufScaleX], new_vals[RingbufScaleY], + old_vals[RingbufX] - new_vals[RingbufX], old_vals[RingbufY] - new_vals[RingbufY], + old_vals[RingbufRot] * (180.0 / M_PI) - new_vals[RingbufRot] * (180.0 / M_PI), + new_vals[RingbufScaleX] / old_vals[RingbufScaleX], new_vals[RingbufScaleY] / old_vals[RingbufScaleY] + ); +} + +// Uses the buffered motion information to determine a transform that smooths the +// given frame and applies it +static int filter_frame(AVFilterLink *link, AVFrame *input_frame) +{ + AVFilterContext *avctx = link->dst; + AVFilterLink *outlink = avctx->outputs[0]; + DeshakeOpenCLContext *deshake_ctx = avctx->priv; + AVFrame *cropped_frame = NULL, *transformed_frame = NULL; + int err; + cl_int cle; + float new_vals[RingbufCount]; + float old_vals[RingbufCount]; + // Luma (in the case of YUV) transform, or just the transform in the case of RGB + float transform_y[9]; + // Chroma transform + float transform_uv[9]; + // Luma crop transform (or RGB) + float transform_crop_y[9]; + // Chroma crop transform + float transform_crop_uv[9]; + float transform_debug_rgb[9]; + size_t global_work[2]; + int64_t duration; + cl_mem src, transformed, dst; + cl_mem transforms[3]; + CropInfo crops[3]; + cl_event transform_event, crop_upscale_event; + DebugMatches debug_matches; + cl_int num_model_matches; + + const float center_w = (float)input_frame->width / 2; + const float center_h = (float)input_frame->height / 2; + + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(deshake_ctx->sw_format); + const int chroma_width = AV_CEIL_RSHIFT(input_frame->width, desc->log2_chroma_w); + const int chroma_height = AV_CEIL_RSHIFT(input_frame->height, desc->log2_chroma_h); + + const float center_w_chroma = (float)chroma_width / 2; + const float center_h_chroma = (float)chroma_height / 2; + + const float luma_w_over_chroma_w = ((float)input_frame->width / (float)chroma_width); + const float luma_h_over_chroma_h = ((float)input_frame->height / (float)chroma_height); + + if (deshake_ctx->debug_on) { + av_fifo_generic_read( + deshake_ctx->abs_motion.debug_matches, + &debug_matches, + sizeof(DebugMatches), + NULL + ); + } + + if (input_frame->pkt_duration) { + duration = input_frame->pkt_duration; + } else { + duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); + } + deshake_ctx->duration = input_frame->pts + duration; + + // Get the absolute transform data for this frame + for (int i = 0; i < RingbufCount; i++) { + av_fifo_generic_peek_at( + deshake_ctx->abs_motion.ringbuffers[i], + &old_vals[i], + deshake_ctx->abs_motion.curr_frame_offset * sizeof(float), + sizeof(float), + NULL + ); + } + + if (deshake_ctx->tripod_mode) { + // If tripod mode is turned on we simply undo all motion relative to the + // first frame + + new_vals[RingbufX] = 0.0f; + new_vals[RingbufY] = 0.0f; + new_vals[RingbufRot] = 0.0f; + new_vals[RingbufScaleX] = 1.0f; + new_vals[RingbufScaleY] = 1.0f; + } else { + // Tripod mode is off and we need to smooth a moving camera + + new_vals[RingbufX] = smooth( + deshake_ctx, + deshake_ctx->gauss_kernel, + deshake_ctx->smooth_window, + input_frame->width, + deshake_ctx->abs_motion.ringbuffers[RingbufX] + ); + new_vals[RingbufY] = smooth( + deshake_ctx, + deshake_ctx->gauss_kernel, + deshake_ctx->smooth_window, + input_frame->height, + deshake_ctx->abs_motion.ringbuffers[RingbufY] + ); + new_vals[RingbufRot] = smooth( + deshake_ctx, + deshake_ctx->gauss_kernel, + deshake_ctx->smooth_window, + M_PI / 4, + deshake_ctx->abs_motion.ringbuffers[RingbufRot] + ); + new_vals[RingbufScaleX] = smooth( + deshake_ctx, + deshake_ctx->gauss_kernel, + deshake_ctx->smooth_window, + 2.0f, + deshake_ctx->abs_motion.ringbuffers[RingbufScaleX] + ); + new_vals[RingbufScaleY] = smooth( + deshake_ctx, + deshake_ctx->gauss_kernel, + deshake_ctx->smooth_window, + 2.0f, + deshake_ctx->abs_motion.ringbuffers[RingbufScaleY] + ); + } + + transform_center_scale( + old_vals[RingbufX] - new_vals[RingbufX], + old_vals[RingbufY] - new_vals[RingbufY], + old_vals[RingbufRot] - new_vals[RingbufRot], + new_vals[RingbufScaleX] / old_vals[RingbufScaleX], + new_vals[RingbufScaleY] / old_vals[RingbufScaleY], + center_w, + center_h, + transform_y + ); + + transform_center_scale( + (old_vals[RingbufX] - new_vals[RingbufX]) / luma_w_over_chroma_w, + (old_vals[RingbufY] - new_vals[RingbufY]) / luma_h_over_chroma_h, + old_vals[RingbufRot] - new_vals[RingbufRot], + new_vals[RingbufScaleX] / old_vals[RingbufScaleX], + new_vals[RingbufScaleY] / old_vals[RingbufScaleY], + center_w_chroma, + center_h_chroma, + transform_uv + ); + + CL_BLOCKING_WRITE_BUFFER(deshake_ctx->command_queue, deshake_ctx->transform_y, 9 * sizeof(float), transform_y, NULL); + CL_BLOCKING_WRITE_BUFFER(deshake_ctx->command_queue, deshake_ctx->transform_uv, 9 * sizeof(float), transform_uv, NULL); + + if (deshake_ctx->debug_on) + transform_debug(avctx, new_vals, old_vals, deshake_ctx->curr_frame); + + cropped_frame = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!cropped_frame) { + err = AVERROR(ENOMEM); + goto fail; + } + + transformed_frame = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!transformed_frame) { + err = AVERROR(ENOMEM); + goto fail; + } + + transforms[0] = deshake_ctx->transform_y; + transforms[1] = transforms[2] = deshake_ctx->transform_uv; + + for (int p = 0; p < FF_ARRAY_ELEMS(transformed_frame->data); p++) { + // Transform all of the planes appropriately + src = (cl_mem)input_frame->data[p]; + transformed = (cl_mem)transformed_frame->data[p]; + + if (!transformed) + break; + + err = ff_opencl_filter_work_size_from_image(avctx, global_work, input_frame, p, 0); + if (err < 0) + goto fail; + + CL_RUN_KERNEL_WITH_ARGS( + deshake_ctx->command_queue, + deshake_ctx->kernel_transform, + global_work, + NULL, + &transform_event, + { sizeof(cl_mem), &src }, + { sizeof(cl_mem), &transformed }, + { sizeof(cl_mem), &transforms[p] }, + ); + } + + if (deshake_ctx->debug_on && !deshake_ctx->is_yuv && debug_matches.num_matches > 0) { + CL_BLOCKING_WRITE_BUFFER( + deshake_ctx->command_queue, + deshake_ctx->debug_matches, + debug_matches.num_matches * sizeof(MotionVector), + debug_matches.matches, + NULL + ); + + CL_BLOCKING_WRITE_BUFFER( + deshake_ctx->command_queue, + deshake_ctx->debug_model_matches, + debug_matches.num_model_matches * sizeof(MotionVector), + debug_matches.model_matches, + NULL + ); + + num_model_matches = debug_matches.num_model_matches; + + // Invert the transform + transform_center_scale( + new_vals[RingbufX] - old_vals[RingbufX], + new_vals[RingbufY] - old_vals[RingbufY], + new_vals[RingbufRot] - old_vals[RingbufRot], + old_vals[RingbufScaleX] / new_vals[RingbufScaleX], + old_vals[RingbufScaleY] / new_vals[RingbufScaleY], + center_w, + center_h, + transform_debug_rgb + ); + + CL_BLOCKING_WRITE_BUFFER(deshake_ctx->command_queue, deshake_ctx->transform_y, 9 * sizeof(float), transform_debug_rgb, NULL); + + transformed = (cl_mem)transformed_frame->data[0]; + CL_RUN_KERNEL_WITH_ARGS( + deshake_ctx->command_queue, + deshake_ctx->kernel_draw_debug_info, + (size_t[]){ debug_matches.num_matches }, + NULL, + NULL, + { sizeof(cl_mem), &transformed }, + { sizeof(cl_mem), &deshake_ctx->debug_matches }, + { sizeof(cl_mem), &deshake_ctx->debug_model_matches }, + { sizeof(cl_int), &num_model_matches }, + { sizeof(cl_mem), &deshake_ctx->transform_y } + ); + } + + if (deshake_ctx->should_crop) { + // Generate transforms for cropping + transform_center_scale( + (old_vals[RingbufX] - new_vals[RingbufX]) / 5, + (old_vals[RingbufY] - new_vals[RingbufY]) / 5, + (old_vals[RingbufRot] - new_vals[RingbufRot]) / 5, + new_vals[RingbufScaleX] / old_vals[RingbufScaleX], + new_vals[RingbufScaleY] / old_vals[RingbufScaleY], + center_w, + center_h, + transform_crop_y + ); + update_needed_crop(&deshake_ctx->crop_y, transform_crop_y, input_frame->width, input_frame->height); + + transform_center_scale( + (old_vals[RingbufX] - new_vals[RingbufX]) / (5 * luma_w_over_chroma_w), + (old_vals[RingbufY] - new_vals[RingbufY]) / (5 * luma_h_over_chroma_h), + (old_vals[RingbufRot] - new_vals[RingbufRot]) / 5, + new_vals[RingbufScaleX] / old_vals[RingbufScaleX], + new_vals[RingbufScaleY] / old_vals[RingbufScaleY], + center_w_chroma, + center_h_chroma, + transform_crop_uv + ); + update_needed_crop(&deshake_ctx->crop_uv, transform_crop_uv, chroma_width, chroma_height); + + crops[0] = deshake_ctx->crop_y; + crops[1] = crops[2] = deshake_ctx->crop_uv; + + for (int p = 0; p < FF_ARRAY_ELEMS(cropped_frame->data); p++) { + // Crop all of the planes appropriately + dst = (cl_mem)cropped_frame->data[p]; + transformed = (cl_mem)transformed_frame->data[p]; + + if (!dst) + break; + + err = ff_opencl_filter_work_size_from_image(avctx, global_work, input_frame, p, 0); + if (err < 0) + goto fail; + + CL_RUN_KERNEL_WITH_ARGS( + deshake_ctx->command_queue, + deshake_ctx->kernel_crop_upscale, + global_work, + NULL, + &crop_upscale_event, + { sizeof(cl_mem), &transformed }, + { sizeof(cl_mem), &dst }, + { sizeof(cl_float2), &crops[p].top_left }, + { sizeof(cl_float2), &crops[p].bottom_right }, + ); + } + } + + if (deshake_ctx->curr_frame < deshake_ctx->smooth_window / 2) { + // This means we are somewhere at the start of the video. We need to + // increment the current frame offset until it reaches the center of + // the ringbuffers (as the current frame will be located there for + // the rest of the video). + // + // The end of the video is taken care of by draining motion data + // one-by-one out of the buffer, causing the (at that point fixed) + // offset to move towards later frames' data. + ++deshake_ctx->abs_motion.curr_frame_offset; + } + + if (deshake_ctx->abs_motion.data_end_offset != -1) { + // Keep the end offset in sync with the frame it's supposed to be + // positioned at + --deshake_ctx->abs_motion.data_end_offset; + + if (deshake_ctx->abs_motion.data_end_offset == deshake_ctx->abs_motion.curr_frame_offset - 1) { + // The end offset would be the start of the new video sequence; flip to + // start offset + deshake_ctx->abs_motion.data_end_offset = -1; + deshake_ctx->abs_motion.data_start_offset = deshake_ctx->abs_motion.curr_frame_offset; + } + } else if (deshake_ctx->abs_motion.data_start_offset != -1) { + // Keep the start offset in sync with the frame it's supposed to be + // positioned at + --deshake_ctx->abs_motion.data_start_offset; + } + + if (deshake_ctx->debug_on) { + deshake_ctx->transform_time += ff_opencl_get_event_time(transform_event); + if (deshake_ctx->should_crop) { + deshake_ctx->crop_upscale_time += ff_opencl_get_event_time(crop_upscale_event); + } + } + + ++deshake_ctx->curr_frame; + + if (deshake_ctx->debug_on) + av_freep(&debug_matches.matches); + + if (deshake_ctx->should_crop) { + err = av_frame_copy_props(cropped_frame, input_frame); + if (err < 0) + goto fail; + + av_frame_free(&transformed_frame); + av_frame_free(&input_frame); + return ff_filter_frame(outlink, cropped_frame); + + } else { + err = av_frame_copy_props(transformed_frame, input_frame); + if (err < 0) + goto fail; + + av_frame_free(&cropped_frame); + av_frame_free(&input_frame); + return ff_filter_frame(outlink, transformed_frame); + } + +fail: + clFinish(deshake_ctx->command_queue); + + if (deshake_ctx->debug_on) + if (debug_matches.matches) + av_freep(&debug_matches.matches); + + av_frame_free(&input_frame); + av_frame_free(&transformed_frame); + av_frame_free(&cropped_frame); + return err; +} + +// Add the given frame to the frame queue to eventually be processed. +// +// Also determines the motion from the previous frame and updates the stored +// motion information accordingly. +static int queue_frame(AVFilterLink *link, AVFrame *input_frame) +{ + AVFilterContext *avctx = link->dst; + DeshakeOpenCLContext *deshake_ctx = avctx->priv; + int err; + int num_vectors; + int num_inliers = 0; + cl_int cle; + FrameDelta relative; + SimilarityMatrix model; + size_t global_work[2]; + size_t harris_global_work[2]; + size_t grid_32_global_work[2]; + int grid_32_h, grid_32_w; + size_t local_work[2]; + cl_mem src, temp; + float prev_vals[5]; + float new_vals[5]; + cl_event grayscale_event, harris_response_event, refine_features_event, + brief_event, match_descriptors_event, read_buf_event; + DebugMatches debug_matches; + + num_vectors = 0; + + local_work[0] = 8; + local_work[1] = 8; + + err = ff_opencl_filter_work_size_from_image(avctx, global_work, input_frame, 0, 0); + if (err < 0) + goto fail; + + err = ff_opencl_filter_work_size_from_image(avctx, harris_global_work, input_frame, 0, 8); + if (err < 0) + goto fail; + + err = ff_opencl_filter_work_size_from_image(avctx, grid_32_global_work, input_frame, 0, 32); + if (err < 0) + goto fail; + + // We want a single work-item for each 32x32 block of pixels in the input frame + grid_32_global_work[0] /= 32; + grid_32_global_work[1] /= 32; + + grid_32_h = ROUNDED_UP_DIV(input_frame->height, 32); + grid_32_w = ROUNDED_UP_DIV(input_frame->width, 32); + + if (deshake_ctx->is_yuv) { + deshake_ctx->grayscale = (cl_mem)input_frame->data[0]; + } else { + src = (cl_mem)input_frame->data[0]; + + CL_RUN_KERNEL_WITH_ARGS( + deshake_ctx->command_queue, + deshake_ctx->kernel_grayscale, + global_work, + NULL, + &grayscale_event, + { sizeof(cl_mem), &src }, + { sizeof(cl_mem), &deshake_ctx->grayscale } + ); + } + + CL_RUN_KERNEL_WITH_ARGS( + deshake_ctx->command_queue, + deshake_ctx->kernel_harris_response, + harris_global_work, + local_work, + &harris_response_event, + { sizeof(cl_mem), &deshake_ctx->grayscale }, + { sizeof(cl_mem), &deshake_ctx->harris_buf } + ); + + CL_RUN_KERNEL_WITH_ARGS( + deshake_ctx->command_queue, + deshake_ctx->kernel_refine_features, + grid_32_global_work, + NULL, + &refine_features_event, + { sizeof(cl_mem), &deshake_ctx->grayscale }, + { sizeof(cl_mem), &deshake_ctx->harris_buf }, + { sizeof(cl_mem), &deshake_ctx->refined_features }, + { sizeof(cl_int), &deshake_ctx->refine_features } + ); + + CL_RUN_KERNEL_WITH_ARGS( + deshake_ctx->command_queue, + deshake_ctx->kernel_brief_descriptors, + grid_32_global_work, + NULL, + &brief_event, + { sizeof(cl_mem), &deshake_ctx->grayscale }, + { sizeof(cl_mem), &deshake_ctx->refined_features }, + { sizeof(cl_mem), &deshake_ctx->descriptors }, + { sizeof(cl_mem), &deshake_ctx->brief_pattern} + ); + + if (av_fifo_size(deshake_ctx->abs_motion.ringbuffers[RingbufX]) == 0) { + // This is the first frame we've been given to queue, meaning there is + // no previous frame to match descriptors to + + goto no_motion_data; + } + + CL_RUN_KERNEL_WITH_ARGS( + deshake_ctx->command_queue, + deshake_ctx->kernel_match_descriptors, + grid_32_global_work, + NULL, + &match_descriptors_event, + { sizeof(cl_mem), &deshake_ctx->prev_refined_features }, + { sizeof(cl_mem), &deshake_ctx->refined_features }, + { sizeof(cl_mem), &deshake_ctx->descriptors }, + { sizeof(cl_mem), &deshake_ctx->prev_descriptors }, + { sizeof(cl_mem), &deshake_ctx->matches } + ); + + cle = clEnqueueReadBuffer( + deshake_ctx->command_queue, + deshake_ctx->matches, + CL_TRUE, + 0, + grid_32_h * grid_32_w * sizeof(MotionVector), + deshake_ctx->matches_host, + 0, + NULL, + &read_buf_event + ); + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to read matches to host: %d.\n", cle); + + num_vectors = make_vectors_contig(deshake_ctx, grid_32_h, grid_32_w); + + if (num_vectors < 10) { + // Not enough matches to get reliable motion data for this frame + // + // From this point on all data is relative to this frame rather than the + // original frame. We have to make sure that we don't mix values that were + // relative to the original frame with the new values relative to this + // frame when doing the gaussian smoothing. We keep track of where the old + // values end using this data_end_offset field in order to accomplish + // that goal. + // + // If no motion data is present for multiple frames in a short window of + // time, we leave the end where it was to avoid mixing 0s in with the + // old data (and just treat them all as part of the new values) + if (deshake_ctx->abs_motion.data_end_offset == -1) { + deshake_ctx->abs_motion.data_end_offset = + av_fifo_size(deshake_ctx->abs_motion.ringbuffers[RingbufX]) / sizeof(float) - 1; + } + + goto no_motion_data; + } + + if (!estimate_affine_2d( + deshake_ctx, + deshake_ctx->matches_contig_host, + &debug_matches, + num_vectors, + model.matrix, + 10.0, + 3000, + 0.999999999999 + )) { + goto no_motion_data; + } + + for (int i = 0; i < num_vectors; i++) { + if (deshake_ctx->matches_contig_host[i].should_consider) { + deshake_ctx->inliers[num_inliers] = deshake_ctx->matches_contig_host[i]; + num_inliers++; + } + } + + if (!minimize_error( + deshake_ctx, + deshake_ctx->inliers, + &debug_matches, + num_inliers, + model.matrix, + 400 + )) { + goto no_motion_data; + } + + + relative = decompose_transform(model.matrix); + + // Get the absolute transform data for the previous frame + for (int i = 0; i < RingbufCount; i++) { + av_fifo_generic_peek_at( + deshake_ctx->abs_motion.ringbuffers[i], + &prev_vals[i], + av_fifo_size(deshake_ctx->abs_motion.ringbuffers[i]) - sizeof(float), + sizeof(float), + NULL + ); + } + + new_vals[RingbufX] = prev_vals[RingbufX] + relative.translation.s[0]; + new_vals[RingbufY] = prev_vals[RingbufY] + relative.translation.s[1]; + new_vals[RingbufRot] = prev_vals[RingbufRot] + relative.rotation; + new_vals[RingbufScaleX] = prev_vals[RingbufScaleX] / relative.scale.s[0]; + new_vals[RingbufScaleY] = prev_vals[RingbufScaleY] / relative.scale.s[1]; + + if (deshake_ctx->debug_on) { + if (!deshake_ctx->is_yuv) { + deshake_ctx->grayscale_time += ff_opencl_get_event_time(grayscale_event); + } + deshake_ctx->harris_response_time += ff_opencl_get_event_time(harris_response_event); + deshake_ctx->refine_features_time += ff_opencl_get_event_time(refine_features_event); + deshake_ctx->brief_descriptors_time += ff_opencl_get_event_time(brief_event); + deshake_ctx->match_descriptors_time += ff_opencl_get_event_time(match_descriptors_event); + deshake_ctx->read_buf_time += ff_opencl_get_event_time(read_buf_event); + } + + goto end; + +no_motion_data: + new_vals[RingbufX] = 0.0f; + new_vals[RingbufY] = 0.0f; + new_vals[RingbufRot] = 0.0f; + new_vals[RingbufScaleX] = 1.0f; + new_vals[RingbufScaleY] = 1.0f; + + for (int i = 0; i < num_vectors; i++) { + deshake_ctx->matches_contig_host[i].should_consider = 0; + } + debug_matches.num_model_matches = 0; + + if (deshake_ctx->debug_on) { + av_log(avctx, AV_LOG_VERBOSE, + "\n[ALERT] No motion data found in queue_frame, motion reset to 0\n\n" + ); + } + + goto end; + +end: + // Swap the descriptor buffers (we don't need the previous frame's descriptors + // again so we will use that space for the next frame's descriptors) + temp = deshake_ctx->prev_descriptors; + deshake_ctx->prev_descriptors = deshake_ctx->descriptors; + deshake_ctx->descriptors = temp; + + // Same for the refined features + temp = deshake_ctx->prev_refined_features; + deshake_ctx->prev_refined_features = deshake_ctx->refined_features; + deshake_ctx->refined_features = temp; + + if (deshake_ctx->debug_on) { + if (num_vectors == 0) { + debug_matches.matches = NULL; + } else { + debug_matches.matches = av_malloc_array(num_vectors, sizeof(MotionVector)); + + if (!debug_matches.matches) { + err = AVERROR(ENOMEM); + goto fail; + } + } + + for (int i = 0; i < num_vectors; i++) { + debug_matches.matches[i] = deshake_ctx->matches_contig_host[i]; + } + debug_matches.num_matches = num_vectors; + + av_fifo_generic_write( + deshake_ctx->abs_motion.debug_matches, + &debug_matches, + sizeof(DebugMatches), + NULL + ); + } + + for (int i = 0; i < RingbufCount; i++) { + av_fifo_generic_write( + deshake_ctx->abs_motion.ringbuffers[i], + &new_vals[i], + sizeof(float), + NULL + ); + } + + return ff_framequeue_add(&deshake_ctx->fq, input_frame); + +fail: + clFinish(deshake_ctx->command_queue); + av_frame_free(&input_frame); + return err; +} + +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + DeshakeOpenCLContext *deshake_ctx = ctx->priv; + AVFrame *frame = NULL; + int ret, status; + int64_t pts; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + if (!deshake_ctx->eof) { + ret = ff_inlink_consume_frame(inlink, &frame); + if (ret < 0) + return ret; + if (ret > 0) { + if (!frame->hw_frames_ctx) + return AVERROR(EINVAL); + + if (!deshake_ctx->initialized) { + ret = deshake_opencl_init(ctx); + if (ret < 0) + return ret; + } + + // If there is no more space in the ringbuffers, remove the oldest + // values to make room for the new ones + if (av_fifo_space(deshake_ctx->abs_motion.ringbuffers[RingbufX]) == 0) { + for (int i = 0; i < RingbufCount; i++) { + av_fifo_drain(deshake_ctx->abs_motion.ringbuffers[i], sizeof(float)); + } + } + ret = queue_frame(inlink, frame); + if (ret < 0) + return ret; + if (ret >= 0) { + // See if we have enough buffered frames to process one + // + // "enough" is half the smooth window of queued frames into the future + if (ff_framequeue_queued_frames(&deshake_ctx->fq) >= deshake_ctx->smooth_window / 2) { + return filter_frame(inlink, ff_framequeue_take(&deshake_ctx->fq)); + } + } + } + } + + if (!deshake_ctx->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) { + if (status == AVERROR_EOF) { + deshake_ctx->eof = 1; + } + } + + if (deshake_ctx->eof) { + // Finish processing the rest of the frames in the queue. + while(ff_framequeue_queued_frames(&deshake_ctx->fq) != 0) { + for (int i = 0; i < RingbufCount; i++) { + av_fifo_drain(deshake_ctx->abs_motion.ringbuffers[i], sizeof(float)); + } + + ret = filter_frame(inlink, ff_framequeue_take(&deshake_ctx->fq)); + if (ret < 0) { + return ret; + } + } + + if (deshake_ctx->debug_on) { + av_log(ctx, AV_LOG_VERBOSE, + "Average kernel execution times:\n" + "\t grayscale: %0.3f ms\n" + "\t harris_response: %0.3f ms\n" + "\t refine_features: %0.3f ms\n" + "\tbrief_descriptors: %0.3f ms\n" + "\tmatch_descriptors: %0.3f ms\n" + "\t transform: %0.3f ms\n" + "\t crop_upscale: %0.3f ms\n" + "Average buffer read times:\n" + "\t features buf: %0.3f ms\n", + averaged_event_time_ms(deshake_ctx->grayscale_time, deshake_ctx->curr_frame), + averaged_event_time_ms(deshake_ctx->harris_response_time, deshake_ctx->curr_frame), + averaged_event_time_ms(deshake_ctx->refine_features_time, deshake_ctx->curr_frame), + averaged_event_time_ms(deshake_ctx->brief_descriptors_time, deshake_ctx->curr_frame), + averaged_event_time_ms(deshake_ctx->match_descriptors_time, deshake_ctx->curr_frame), + averaged_event_time_ms(deshake_ctx->transform_time, deshake_ctx->curr_frame), + averaged_event_time_ms(deshake_ctx->crop_upscale_time, deshake_ctx->curr_frame), + averaged_event_time_ms(deshake_ctx->read_buf_time, deshake_ctx->curr_frame) + ); + } + + ff_outlink_set_status(outlink, AVERROR_EOF, deshake_ctx->duration); + return 0; + } + + if (!deshake_ctx->eof) { + FF_FILTER_FORWARD_WANTED(outlink, inlink); + } + + return FFERROR_NOT_READY; +} + +static const AVFilterPad deshake_opencl_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &ff_opencl_filter_config_input, + }, + { NULL } +}; + +static const AVFilterPad deshake_opencl_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &ff_opencl_filter_config_output, + }, + { NULL } +}; + +#define OFFSET(x) offsetof(DeshakeOpenCLContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM + +static const AVOption deshake_opencl_options[] = { + { + "tripod", "simulates a tripod by preventing any camera movement whatsoever " + "from the original frame", + OFFSET(tripod_mode), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS + }, + { + "debug", "turn on additional debugging information", + OFFSET(debug_on), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS + }, + { + "adaptive_crop", "attempt to subtly crop borders to reduce mirrored content", + OFFSET(should_crop), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS + }, + { + "refine_features", "refine feature point locations at a sub-pixel level", + OFFSET(refine_features), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS + }, + { + "smooth_strength", "smoothing strength (0 attempts to adaptively determine optimal strength)", + OFFSET(smooth_percent), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, 0.0f, 1.0f, FLAGS + }, + { + "smooth_window_multiplier", "multiplier for number of frames to buffer for motion data", + OFFSET(smooth_window_multiplier), AV_OPT_TYPE_FLOAT, {.dbl = 2.0}, 0.1, 10.0, FLAGS + }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(deshake_opencl); + +AVFilter ff_vf_deshake_opencl = { + .name = "deshake_opencl", + .description = NULL_IF_CONFIG_SMALL("Feature-point based video stabilization filter"), + .priv_size = sizeof(DeshakeOpenCLContext), + .priv_class = &deshake_opencl_class, + .init = &ff_opencl_filter_init, + .uninit = &deshake_opencl_uninit, + .query_formats = &ff_opencl_filter_query_formats, + .activate = activate, + .inputs = deshake_opencl_inputs, + .outputs = deshake_opencl_outputs, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE +}; diff --git a/libavfilter/vf_dnn_processing.c b/libavfilter/vf_dnn_processing.c new file mode 100644 index 00000000000..cf589acedf7 --- /dev/null +++ b/libavfilter/vf_dnn_processing.c @@ -0,0 +1,520 @@ +/* + * 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 + * 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 "avfilter.h" +#include "dnn_interface.h" +#include "formats.h" +#include "internal.h" +#include "libswscale/swscale.h" + +typedef struct DnnProcessingContext { + const AVClass *class; + + char *model_filename; + DNNBackendType backend_type; + char *model_inputname; + char *model_outputname; + + DNNModule *dnn_module; + DNNModel *model; + + // input & output of the model at execution time + DNNData input; + DNNData output; + + struct SwsContext *sws_gray8_to_grayf32; + struct SwsContext *sws_grayf32_to_gray8; + struct SwsContext *sws_uv_scale; + int sws_uv_height; +} DnnProcessingContext; + +#define OFFSET(x) offsetof(DnnProcessingContext, 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 }, 0, 1, FLAGS, "backend" }, + { "native", "native backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "backend" }, +#if (CONFIG_LIBTENSORFLOW == 1) + { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "backend" }, +#endif + { "model", "path to model file", OFFSET(model_filename), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, + { "input", "input name of the model", OFFSET(model_inputname), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, + { "output", "output name of the model", OFFSET(model_outputname), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(dnn_processing); + +static av_cold int init(AVFilterContext *context) +{ + DnnProcessingContext *ctx = context->priv; + + if (!ctx->model_filename) { + av_log(ctx, AV_LOG_ERROR, "model file for network is not specified\n"); + return AVERROR(EINVAL); + } + if (!ctx->model_inputname) { + av_log(ctx, AV_LOG_ERROR, "input name of the model network is not specified\n"); + return AVERROR(EINVAL); + } + if (!ctx->model_outputname) { + av_log(ctx, AV_LOG_ERROR, "output name of the model network is not specified\n"); + return AVERROR(EINVAL); + } + + ctx->dnn_module = ff_get_dnn_module(ctx->backend_type); + if (!ctx->dnn_module) { + av_log(ctx, AV_LOG_ERROR, "could not create DNN module for requested backend\n"); + return AVERROR(ENOMEM); + } + if (!ctx->dnn_module->load_model) { + av_log(ctx, AV_LOG_ERROR, "load_model for network is not specified\n"); + return AVERROR(EINVAL); + } + + ctx->model = (ctx->dnn_module->load_model)(ctx->model_filename); + if (!ctx->model) { + av_log(ctx, AV_LOG_ERROR, "could not load DNN model\n"); + return AVERROR(EINVAL); + } + + return 0; +} + +static int query_formats(AVFilterContext *context) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, + AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAYF32, + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, + AV_PIX_FMT_NONE + }; + AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); + return ff_set_common_formats(context, fmts_list); +} + +#define LOG_FORMAT_CHANNEL_MISMATCH() \ + av_log(ctx, AV_LOG_ERROR, \ + "the frame's format %s does not match " \ + "the model input channel %d\n", \ + av_get_pix_fmt_name(fmt), \ + model_input->channels); + +static int check_modelinput_inlink(const DNNData *model_input, const AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + enum AVPixelFormat fmt = inlink->format; + + // the design is to add explicit scale filter before this filter + if (model_input->height != -1 && model_input->height != inlink->h) { + av_log(ctx, AV_LOG_ERROR, "the model requires frame height %d but got %d\n", + model_input->height, inlink->h); + return AVERROR(EIO); + } + if (model_input->width != -1 && model_input->width != inlink->w) { + av_log(ctx, AV_LOG_ERROR, "the model requires frame width %d but got %d\n", + model_input->width, inlink->w); + return AVERROR(EIO); + } + + switch (fmt) { + case AV_PIX_FMT_RGB24: + case AV_PIX_FMT_BGR24: + if (model_input->channels != 3) { + LOG_FORMAT_CHANNEL_MISMATCH(); + return AVERROR(EIO); + } + if (model_input->dt != DNN_FLOAT && model_input->dt != DNN_UINT8) { + av_log(ctx, AV_LOG_ERROR, "only support dnn models with input data type as float32 and uint8.\n"); + return AVERROR(EIO); + } + return 0; + case AV_PIX_FMT_GRAY8: + if (model_input->channels != 1) { + LOG_FORMAT_CHANNEL_MISMATCH(); + return AVERROR(EIO); + } + if (model_input->dt != DNN_UINT8) { + av_log(ctx, AV_LOG_ERROR, "only support dnn models with input data type uint8.\n"); + return AVERROR(EIO); + } + return 0; + case AV_PIX_FMT_GRAYF32: + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUV444P: + case AV_PIX_FMT_YUV410P: + case AV_PIX_FMT_YUV411P: + if (model_input->channels != 1) { + LOG_FORMAT_CHANNEL_MISMATCH(); + return AVERROR(EIO); + } + if (model_input->dt != DNN_FLOAT) { + av_log(ctx, AV_LOG_ERROR, "only support dnn models with input data type float32.\n"); + return AVERROR(EIO); + } + return 0; + default: + av_log(ctx, AV_LOG_ERROR, "%s not supported.\n", av_get_pix_fmt_name(fmt)); + return AVERROR(EIO); + } + + return 0; +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *context = inlink->dst; + DnnProcessingContext *ctx = context->priv; + DNNReturnType result; + DNNData model_input; + int check; + + result = ctx->model->get_input(ctx->model->model, &model_input, ctx->model_inputname); + if (result != DNN_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "could not get input from the model\n"); + return AVERROR(EIO); + } + + check = check_modelinput_inlink(&model_input, inlink); + if (check != 0) { + return check; + } + + ctx->input.width = inlink->w; + ctx->input.height = inlink->h; + ctx->input.channels = model_input.channels; + ctx->input.dt = model_input.dt; + + result = (ctx->model->set_input_output)(ctx->model->model, + &ctx->input, ctx->model_inputname, + (const char **)&ctx->model_outputname, 1); + if (result != DNN_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "could not set input and output for the model\n"); + return AVERROR(EIO); + } + + return 0; +} + +static int prepare_sws_context(AVFilterLink *outlink) +{ + AVFilterContext *context = outlink->src; + DnnProcessingContext *ctx = context->priv; + AVFilterLink *inlink = context->inputs[0]; + enum AVPixelFormat fmt = inlink->format; + DNNDataType input_dt = ctx->input.dt; + DNNDataType output_dt = ctx->output.dt; + + switch (fmt) { + case AV_PIX_FMT_RGB24: + case AV_PIX_FMT_BGR24: + if (input_dt == DNN_FLOAT) { + ctx->sws_gray8_to_grayf32 = sws_getContext(inlink->w * 3, + inlink->h, + AV_PIX_FMT_GRAY8, + inlink->w * 3, + inlink->h, + AV_PIX_FMT_GRAYF32, + 0, NULL, NULL, NULL); + } + if (output_dt == DNN_FLOAT) { + ctx->sws_grayf32_to_gray8 = sws_getContext(outlink->w * 3, + outlink->h, + AV_PIX_FMT_GRAYF32, + outlink->w * 3, + outlink->h, + AV_PIX_FMT_GRAY8, + 0, NULL, NULL, NULL); + } + return 0; + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUV444P: + case AV_PIX_FMT_YUV410P: + case AV_PIX_FMT_YUV411P: + av_assert0(input_dt == DNN_FLOAT); + av_assert0(output_dt == DNN_FLOAT); + ctx->sws_gray8_to_grayf32 = sws_getContext(inlink->w, + inlink->h, + AV_PIX_FMT_GRAY8, + inlink->w, + inlink->h, + AV_PIX_FMT_GRAYF32, + 0, NULL, NULL, NULL); + ctx->sws_grayf32_to_gray8 = sws_getContext(outlink->w, + outlink->h, + AV_PIX_FMT_GRAYF32, + outlink->w, + outlink->h, + AV_PIX_FMT_GRAY8, + 0, NULL, NULL, NULL); + + if (inlink->w != outlink->w || inlink->h != outlink->h) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); + int sws_src_h = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); + int sws_src_w = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); + int sws_dst_h = AV_CEIL_RSHIFT(outlink->h, desc->log2_chroma_h); + int sws_dst_w = AV_CEIL_RSHIFT(outlink->w, desc->log2_chroma_w); + ctx->sws_uv_scale = sws_getContext(sws_src_w, sws_src_h, AV_PIX_FMT_GRAY8, + sws_dst_w, sws_dst_h, AV_PIX_FMT_GRAY8, + SWS_BICUBIC, NULL, NULL, NULL); + ctx->sws_uv_height = sws_src_h; + } + return 0; + default: + //do nothing + break; + } + + return 0; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *context = outlink->src; + DnnProcessingContext *ctx = context->priv; + DNNReturnType result; + + // have a try run in case that the dnn model resize the frame + result = (ctx->dnn_module->execute_model)(ctx->model, &ctx->output, 1); + if (result != DNN_SUCCESS){ + av_log(ctx, AV_LOG_ERROR, "failed to execute model\n"); + return AVERROR(EIO); + } + + outlink->w = ctx->output.width; + outlink->h = ctx->output.height; + + prepare_sws_context(outlink); + + return 0; +} + +static int copy_from_frame_to_dnn(DnnProcessingContext *ctx, const AVFrame *frame) +{ + int bytewidth = av_image_get_linesize(frame->format, frame->width, 0); + DNNData *dnn_input = &ctx->input; + + switch (frame->format) { + case AV_PIX_FMT_RGB24: + case AV_PIX_FMT_BGR24: + if (dnn_input->dt == DNN_FLOAT) { + sws_scale(ctx->sws_gray8_to_grayf32, (const uint8_t **)frame->data, frame->linesize, + 0, frame->height, (uint8_t * const*)(&dnn_input->data), + (const int [4]){frame->width * 3 * sizeof(float), 0, 0, 0}); + } else { + av_assert0(dnn_input->dt == DNN_UINT8); + av_image_copy_plane(dnn_input->data, bytewidth, + frame->data[0], frame->linesize[0], + bytewidth, frame->height); + } + return 0; + case AV_PIX_FMT_GRAY8: + case AV_PIX_FMT_GRAYF32: + av_image_copy_plane(dnn_input->data, bytewidth, + frame->data[0], frame->linesize[0], + bytewidth, frame->height); + return 0; + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUV444P: + case AV_PIX_FMT_YUV410P: + case AV_PIX_FMT_YUV411P: + sws_scale(ctx->sws_gray8_to_grayf32, (const uint8_t **)frame->data, frame->linesize, + 0, frame->height, (uint8_t * const*)(&dnn_input->data), + (const int [4]){frame->width * sizeof(float), 0, 0, 0}); + return 0; + default: + return AVERROR(EIO); + } + + return 0; +} + +static int copy_from_dnn_to_frame(DnnProcessingContext *ctx, AVFrame *frame) +{ + int bytewidth = av_image_get_linesize(frame->format, frame->width, 0); + DNNData *dnn_output = &ctx->output; + + switch (frame->format) { + case AV_PIX_FMT_RGB24: + case AV_PIX_FMT_BGR24: + if (dnn_output->dt == DNN_FLOAT) { + sws_scale(ctx->sws_grayf32_to_gray8, (const uint8_t *[4]){(const uint8_t *)dnn_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); + + } else { + av_assert0(dnn_output->dt == DNN_UINT8); + av_image_copy_plane(frame->data[0], frame->linesize[0], + dnn_output->data, bytewidth, + bytewidth, frame->height); + } + return 0; + case AV_PIX_FMT_GRAY8: + // it is possible that data type of dnn output is float32, + // need to add support for such case when needed. + av_assert0(dnn_output->dt == DNN_UINT8); + av_image_copy_plane(frame->data[0], frame->linesize[0], + dnn_output->data, bytewidth, + bytewidth, frame->height); + return 0; + case AV_PIX_FMT_GRAYF32: + av_assert0(dnn_output->dt == DNN_FLOAT); + av_image_copy_plane(frame->data[0], frame->linesize[0], + dnn_output->data, bytewidth, + bytewidth, frame->height); + return 0; + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUV444P: + case AV_PIX_FMT_YUV410P: + case AV_PIX_FMT_YUV411P: + sws_scale(ctx->sws_grayf32_to_gray8, (const uint8_t *[4]){(const uint8_t *)dnn_output->data, 0, 0, 0}, + (const int[4]){frame->width * sizeof(float), 0, 0, 0}, + 0, frame->height, (uint8_t * const*)frame->data, frame->linesize); + return 0; + default: + return AVERROR(EIO); + } + + return 0; +} + +static av_always_inline int isPlanarYUV(enum AVPixelFormat pix_fmt) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); + av_assert0(desc); + return !(desc->flags & AV_PIX_FMT_FLAG_RGB) && desc->nb_components == 3; +} + +static int copy_uv_planes(DnnProcessingContext *ctx, AVFrame *out, const AVFrame *in) +{ + const AVPixFmtDescriptor *desc; + int uv_height; + + if (!ctx->sws_uv_scale) { + av_assert0(in->height == out->height && in->width == out->width); + desc = av_pix_fmt_desc_get(in->format); + uv_height = AV_CEIL_RSHIFT(in->height, desc->log2_chroma_h); + for (int i = 1; i < 3; ++i) { + int bytewidth = av_image_get_linesize(in->format, in->width, i); + av_image_copy_plane(out->data[i], out->linesize[i], + in->data[i], in->linesize[i], + bytewidth, uv_height); + } + } else { + sws_scale(ctx->sws_uv_scale, (const uint8_t **)(in->data + 1), in->linesize + 1, + 0, ctx->sws_uv_height, out->data + 1, out->linesize + 1); + sws_scale(ctx->sws_uv_scale, (const uint8_t **)(in->data + 2), in->linesize + 2, + 0, ctx->sws_uv_height, out->data + 2, out->linesize + 2); + } + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *context = inlink->dst; + AVFilterLink *outlink = context->outputs[0]; + DnnProcessingContext *ctx = context->priv; + DNNReturnType dnn_result; + AVFrame *out; + + copy_from_frame_to_dnn(ctx, in); + + dnn_result = (ctx->dnn_module->execute_model)(ctx->model, &ctx->output, 1); + if (dnn_result != DNN_SUCCESS){ + av_log(ctx, AV_LOG_ERROR, "failed to execute model\n"); + av_frame_free(&in); + return AVERROR(EIO); + } + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + + av_frame_copy_props(out, in); + copy_from_dnn_to_frame(ctx, out); + + if (isPlanarYUV(in->format)) + copy_uv_planes(ctx, out, in); + + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + DnnProcessingContext *context = ctx->priv; + + sws_freeContext(context->sws_gray8_to_grayf32); + sws_freeContext(context->sws_grayf32_to_gray8); + sws_freeContext(context->sws_uv_scale); + + if (context->dnn_module) + (context->dnn_module->free_model)(&context->model); + + av_freep(&context->dnn_module); +} + +static const AVFilterPad dnn_processing_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad dnn_processing_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, + { NULL } +}; + +AVFilter ff_vf_dnn_processing = { + .name = "dnn_processing", + .description = NULL_IF_CONFIG_SMALL("Apply DNN processing filter to the input."), + .priv_size = sizeof(DnnProcessingContext), + .init = init, + .uninit = uninit, + .query_formats = query_formats, + .inputs = dnn_processing_inputs, + .outputs = dnn_processing_outputs, + .priv_class = &dnn_processing_class, +}; diff --git a/libavfilter/vf_drawbox.c b/libavfilter/vf_drawbox.c index c9cb63dbd13..21d520e5293 100644 --- a/libavfilter/vf_drawbox.c +++ b/libavfilter/vf_drawbox.c @@ -208,6 +208,12 @@ static int config_input(AVFilterLink *inlink) return ret; } +static av_pure av_always_inline int pixel_belongs_to_box(DrawBoxContext *s, int x, int y) +{ + return (y - s->y < s->thickness) || (s->y + s->h - 1 - y < s->thickness) || + (x - s->x < s->thickness) || (s->x + s->w - 1 - x < s->thickness); +} + static int filter_frame(AVFilterLink *inlink, AVFrame *frame) { DrawBoxContext *s = inlink->dst->priv; @@ -225,13 +231,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) if (s->invert_color) { for (x = FFMAX(xb, 0); x < xb + s->w && x < frame->width; x++) - if ((y - yb < s->thickness) || (yb + s->h - 1 - y < s->thickness) || - (x - xb < s->thickness) || (xb + s->w - 1 - x < s->thickness)) + if (pixel_belongs_to_box(s, x, y)) row[0][x] = 0xff - row[0][x]; } else { for (x = FFMAX(xb, 0); x < xb + s->w && x < frame->width; x++) { - if ((y - yb < s->thickness) || (yb + s->h - 1 - y < s->thickness) || - (x - xb < s->thickness) || (xb + s->w - 1 - x < s->thickness)) { + if (pixel_belongs_to_box(s, x, y)) { row[0][x ] = s->yuv_color[Y]; row[1][x >> s->hsub] = s->yuv_color[U]; row[2][x >> s->hsub] = s->yuv_color[V]; @@ -250,15 +254,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) if (s->invert_color) { for (x = FFMAX(xb, 0); x < xb + s->w && x < frame->width; x++) - if ((y - yb < s->thickness) || (yb + s->h - 1 - y < s->thickness) || - (x - xb < s->thickness) || (xb + s->w - 1 - x < s->thickness)) + if (pixel_belongs_to_box(s, x, y)) row[0][x] = 0xff - row[0][x]; } else { for (x = FFMAX(xb, 0); x < xb + s->w && x < frame->width; x++) { double alpha = (double)s->yuv_color[A] / 255; - if ((y - yb < s->thickness) || (yb + s->h - 1 - y < s->thickness) || - (x - xb < s->thickness) || (xb + s->w - 1 - x < s->thickness)) { + if (pixel_belongs_to_box(s, x, y)) { row[0][x ] = (1 - alpha) * row[0][x ] + alpha * s->yuv_color[Y]; row[1][x >> s->hsub] = (1 - alpha) * row[1][x >> s->hsub] + alpha * s->yuv_color[U]; row[2][x >> s->hsub] = (1 - alpha) * row[2][x >> s->hsub] + alpha * s->yuv_color[V]; @@ -271,22 +273,55 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) return ff_filter_frame(inlink->dst->outputs[0], frame); } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, char *res, int res_len, int flags) +{ + AVFilterLink *inlink = ctx->inputs[0]; + DrawBoxContext *s = ctx->priv; + int old_x = s->x; + int old_y = s->y; + int old_w = s->w; + int old_h = s->h; + int old_t = s->thickness; + int old_r = s->replace; + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + ret = init(ctx); + if (ret < 0) + goto end; + ret = config_input(inlink); +end: + if (ret < 0) { + s->x = old_x; + s->y = old_y; + s->w = old_w; + s->h = old_h; + s->thickness = old_t; + s->replace = old_r; + } + + return ret; +} + #define OFFSET(x) offsetof(DrawBoxContext, x) -#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM #if CONFIG_DRAWBOX_FILTER static const AVOption drawbox_options[] = { - { "x", "set horizontal position of the left box edge", OFFSET(x_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "y", "set vertical position of the top box edge", OFFSET(y_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "width", "set width of the box", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "w", "set width of the box", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "height", "set height of the box", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "h", "set height of the box", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "color", "set color of the box", OFFSET(color_str), AV_OPT_TYPE_STRING, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "c", "set color of the box", OFFSET(color_str), AV_OPT_TYPE_STRING, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "thickness", "set the box thickness", OFFSET(t_expr), AV_OPT_TYPE_STRING, { .str="3" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "t", "set the box thickness", OFFSET(t_expr), AV_OPT_TYPE_STRING, { .str="3" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "x", "set horizontal position of the left box edge", OFFSET(x_expr), AV_OPT_TYPE_STRING, { .str="0" }, 0, 0, FLAGS }, + { "y", "set vertical position of the top box edge", OFFSET(y_expr), AV_OPT_TYPE_STRING, { .str="0" }, 0, 0, FLAGS }, + { "width", "set width of the box", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str="0" }, 0, 0, FLAGS }, + { "w", "set width of the box", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str="0" }, 0, 0, FLAGS }, + { "height", "set height of the box", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str="0" }, 0, 0, FLAGS }, + { "h", "set height of the box", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str="0" }, 0, 0, FLAGS }, + { "color", "set color of the box", OFFSET(color_str), AV_OPT_TYPE_STRING, { .str = "black" }, 0, 0, FLAGS }, + { "c", "set color of the box", OFFSET(color_str), AV_OPT_TYPE_STRING, { .str = "black" }, 0, 0, FLAGS }, + { "thickness", "set the box thickness", OFFSET(t_expr), AV_OPT_TYPE_STRING, { .str="3" }, 0, 0, FLAGS }, + { "t", "set the box thickness", OFFSET(t_expr), AV_OPT_TYPE_STRING, { .str="3" }, 0, 0, FLAGS }, { "replace", "replace color & alpha", OFFSET(replace), AV_OPT_TYPE_BOOL, { .i64=0 }, 0, 1, FLAGS }, { NULL } }; @@ -321,6 +356,7 @@ AVFilter ff_vf_drawbox = { .query_formats = query_formats, .inputs = drawbox_inputs, .outputs = drawbox_outputs, + .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; #endif /* CONFIG_DRAWBOX_FILTER */ @@ -410,16 +446,16 @@ static int drawgrid_filter_frame(AVFilterLink *inlink, AVFrame *frame) } static const AVOption drawgrid_options[] = { - { "x", "set horizontal offset", OFFSET(x_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "y", "set vertical offset", OFFSET(y_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "width", "set width of grid cell", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "w", "set width of grid cell", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "height", "set height of grid cell", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "h", "set height of grid cell", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "color", "set color of the grid", OFFSET(color_str), AV_OPT_TYPE_STRING, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "c", "set color of the grid", OFFSET(color_str), AV_OPT_TYPE_STRING, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "thickness", "set grid line thickness", OFFSET(t_expr), AV_OPT_TYPE_STRING, {.str="1"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "t", "set grid line thickness", OFFSET(t_expr), AV_OPT_TYPE_STRING, {.str="1"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "x", "set horizontal offset", OFFSET(x_expr), AV_OPT_TYPE_STRING, { .str="0" }, 0, 0, FLAGS }, + { "y", "set vertical offset", OFFSET(y_expr), AV_OPT_TYPE_STRING, { .str="0" }, 0, 0, FLAGS }, + { "width", "set width of grid cell", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str="0" }, 0, 0, FLAGS }, + { "w", "set width of grid cell", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str="0" }, 0, 0, FLAGS }, + { "height", "set height of grid cell", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str="0" }, 0, 0, FLAGS }, + { "h", "set height of grid cell", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str="0" }, 0, 0, FLAGS }, + { "color", "set color of the grid", OFFSET(color_str), AV_OPT_TYPE_STRING, { .str = "black" }, 0, 0, FLAGS }, + { "c", "set color of the grid", OFFSET(color_str), AV_OPT_TYPE_STRING, { .str = "black" }, 0, 0, FLAGS }, + { "thickness", "set grid line thickness", OFFSET(t_expr), AV_OPT_TYPE_STRING, {.str="1"}, 0, 0, FLAGS }, + { "t", "set grid line thickness", OFFSET(t_expr), AV_OPT_TYPE_STRING, {.str="1"}, 0, 0, FLAGS }, { "replace", "replace color & alpha", OFFSET(replace), AV_OPT_TYPE_BOOL, { .i64=0 }, 0, 1, FLAGS }, { NULL } }; @@ -455,6 +491,7 @@ AVFilter ff_vf_drawgrid = { .inputs = drawgrid_inputs, .outputs = drawgrid_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, + .process_command = process_command, }; #endif /* CONFIG_DRAWGRID_FILTER */ diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c index 8f4badbdb50..abe1ca6c35c 100644 --- a/libavfilter/vf_drawtext.c +++ b/libavfilter/vf_drawtext.c @@ -209,20 +209,20 @@ typedef struct DrawTextContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption drawtext_options[]= { - {"fontfile", "set font file", OFFSET(fontfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS}, - {"text", "set text", OFFSET(text), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS}, - {"textfile", "set text file", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS}, - {"fontcolor", "set foreground color", OFFSET(fontcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS}, - {"fontcolor_expr", "set foreground color expression", OFFSET(fontcolor_expr), AV_OPT_TYPE_STRING, {.str=""}, CHAR_MIN, CHAR_MAX, FLAGS}, - {"boxcolor", "set box color", OFFSET(boxcolor.rgba), AV_OPT_TYPE_COLOR, {.str="white"}, CHAR_MIN, CHAR_MAX, FLAGS}, - {"bordercolor", "set border color", OFFSET(bordercolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS}, - {"shadowcolor", "set shadow color", OFFSET(shadowcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, CHAR_MIN, CHAR_MAX, 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, 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}, + {"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}, CHAR_MIN, CHAR_MAX , FLAGS}, - {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX, FLAGS}, - {"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_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}, @@ -237,7 +237,7 @@ static const AVOption drawtext_options[]= { {"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"}, - {"timecode", "set initial timecode", OFFSET(tc_opt_string), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS}, + {"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}, @@ -829,6 +829,7 @@ static int config_input(AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; DrawTextContext *s = ctx->priv; + char *expr; int ret; ff_draw_init(&s->dc, inlink->format, FF_DRAW_PROCESS_ALPHA); @@ -854,14 +855,15 @@ static int config_input(AVFilterLink *inlink) av_expr_free(s->a_pexpr); s->x_pexpr = s->y_pexpr = s->a_pexpr = NULL; - if ((ret = av_expr_parse(&s->x_pexpr, s->x_expr, var_names, + if ((ret = av_expr_parse(&s->x_pexpr, expr = s->x_expr, var_names, NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 || - (ret = av_expr_parse(&s->y_pexpr, s->y_expr, var_names, + (ret = av_expr_parse(&s->y_pexpr, expr = s->y_expr, var_names, NULL, NULL, fun2_names, fun2, 0, ctx)) < 0 || - (ret = av_expr_parse(&s->a_pexpr, s->a_expr, var_names, - NULL, NULL, fun2_names, fun2, 0, ctx)) < 0) - + (ret = av_expr_parse(&s->a_pexpr, expr = s->a_expr, var_names, + NULL, NULL, fun2_names, fun2, 0, ctx)) < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to parse expression: %s \n", expr); return AVERROR(EINVAL); + } return 0; } @@ -1083,10 +1085,12 @@ static int func_eval_expr_int_format(AVFilterContext *ctx, AVBPrint *bp, 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); } +#endif if (argc == 3) av_strlcatf(fmt_str, sizeof(fmt_str), "0%u", positions); @@ -1223,7 +1227,8 @@ static int draw_glyphs(DrawTextContext *s, AVFrame *frame, for (i = 0, p = text; *p; i++) { FT_Bitmap bitmap; Glyph dummy = { 0 }; - GET_UTF8(code, *p++, continue;); + 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') @@ -1361,7 +1366,8 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, /* load and cache glyphs */ for (i = 0, p = text; *p; i++) { - GET_UTF8(code, *p++, continue;); + GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_invalid;); +continue_on_invalid: /* get glyph */ dummy.code = code; @@ -1384,7 +1390,8 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, /* compute and save position for each glyph */ glyph = NULL; for (i = 0, p = text; *p; i++) { - GET_UTF8(code, *p++, continue;); + 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') diff --git a/libavfilter/vf_edgedetect.c b/libavfilter/vf_edgedetect.c index a0ddcbbf5c6..a5614ea63bb 100644 --- a/libavfilter/vf_edgedetect.c +++ b/libavfilter/vf_edgedetect.c @@ -150,10 +150,13 @@ static void gaussian_blur(AVFilterContext *ctx, int w, int h, int i, j; memcpy(dst, src, w); dst += dst_linesize; src += src_linesize; - memcpy(dst, src, w); dst += dst_linesize; src += src_linesize; + if (h > 1) { + memcpy(dst, src, w); dst += dst_linesize; src += src_linesize; + } for (j = 2; j < h - 2; j++) { dst[0] = src[0]; - dst[1] = src[1]; + if (w > 1) + dst[1] = src[1]; for (i = 2; i < w - 2; i++) { /* Gaussian mask of size 5x5 with sigma = 1.4 */ dst[i] = ((src[-2*src_linesize + i-2] + src[2*src_linesize + i-2]) * 2 @@ -174,14 +177,19 @@ static void gaussian_blur(AVFilterContext *ctx, int w, int h, + src[i+1] * 12 + src[i+2] * 5) / 159; } - dst[i ] = src[i ]; - dst[i + 1] = src[i + 1]; + if (w > 2) + dst[i ] = src[i ]; + if (w > 3) + dst[i + 1] = src[i + 1]; dst += dst_linesize; src += src_linesize; } - memcpy(dst, src, w); dst += dst_linesize; src += src_linesize; - memcpy(dst, src, w); + if (h > 2) { + memcpy(dst, src, w); dst += dst_linesize; src += src_linesize; + } + if (h > 3) + memcpy(dst, src, w); } enum { @@ -208,7 +216,7 @@ static int get_rounded_direction(int gx, int gy) if (gx < 0) gx = -gx, gy = -gy; - gy <<= 16; + gy *= (1 << 16); tanpi8gx = 27146 * gx; tan3pi8gx = 158218 * gx; if (gy > -tan3pi8gx && gy < -tanpi8gx) return DIRECTION_45UP; diff --git a/libavfilter/vf_elbg.c b/libavfilter/vf_elbg.c index 396af82f775..5bccb5f58d9 100644 --- a/libavfilter/vf_elbg.c +++ b/libavfilter/vf_elbg.c @@ -36,7 +36,7 @@ typedef struct ELBGContext { const AVClass *class; AVLFG lfg; - unsigned int lfg_seed; + int64_t lfg_seed; int max_steps_nb; int *codeword; int codeword_length; @@ -56,8 +56,8 @@ static const AVOption elbg_options[] = { { "l", "set codebook length", OFFSET(codebook_length), AV_OPT_TYPE_INT, { .i64 = 256 }, 1, INT_MAX, FLAGS }, { "nb_steps", "set max number of steps used to compute the mapping", OFFSET(max_steps_nb), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, FLAGS }, { "n", "set max number of steps used to compute the mapping", OFFSET(max_steps_nb), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, FLAGS }, - { "seed", "set the random seed", OFFSET(lfg_seed), AV_OPT_TYPE_INT, {.i64 = -1}, -1, UINT32_MAX, FLAGS }, - { "s", "set the random seed", OFFSET(lfg_seed), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, UINT32_MAX, FLAGS }, + { "seed", "set the random seed", OFFSET(lfg_seed), AV_OPT_TYPE_INT64, {.i64 = -1}, -1, UINT32_MAX, FLAGS }, + { "s", "set the random seed", OFFSET(lfg_seed), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, UINT32_MAX, FLAGS }, { "pal8", "set the pal8 output", OFFSET(pal8), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { NULL } }; @@ -178,8 +178,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); uint32_t *pal; - if (!out) + if (!out) { + av_frame_free(&frame); return AVERROR(ENOMEM); + } out->pts = frame->pts; av_frame_free(&frame); pal = (uint32_t *)out->data[1]; diff --git a/libavfilter/vf_eq.c b/libavfilter/vf_eq.c index 2c4c7e4d544..c8c3d5b3404 100644 --- a/libavfilter/vf_eq.c +++ b/libavfilter/vf_eq.c @@ -174,12 +174,18 @@ static int set_expr(AVExpr **pexpr, const char *expr, const char *option, void * return 0; } +void ff_eq_init(EQContext *eq) +{ + eq->process = process_c; + if (ARCH_X86) + ff_eq_init_x86(eq); +} + static int initialize(AVFilterContext *ctx) { EQContext *eq = ctx->priv; int ret; - - eq->process = process_c; + ff_eq_init(eq); if ((ret = set_expr(&eq->contrast_pexpr, eq->contrast_expr, "contrast", ctx)) < 0 || (ret = set_expr(&eq->brightness_pexpr, eq->brightness_expr, "brightness", ctx)) < 0 || @@ -191,9 +197,6 @@ static int initialize(AVFilterContext *ctx) (ret = set_expr(&eq->gamma_weight_pexpr, eq->gamma_weight_expr, "gamma_weight", ctx)) < 0 ) return ret; - if (ARCH_X86) - ff_eq_init_x86(eq); - if (eq->eval_mode == EVAL_MODE_INIT) { set_gamma(eq); set_contrast(eq); @@ -204,7 +207,7 @@ static int initialize(AVFilterContext *ctx) return 0; } -static void uninit(AVFilterContext *ctx) +static av_cold void uninit(AVFilterContext *ctx) { EQContext *eq = ctx->priv; @@ -350,24 +353,24 @@ static const AVFilterPad eq_outputs[] = { #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 static const AVOption eq_options[] = { { "contrast", "set the contrast adjustment, negative values give a negative image", - OFFSET(contrast_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS }, + OFFSET(contrast_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, 0, 0, TFLAGS }, { "brightness", "set the brightness adjustment", - OFFSET(brightness_expr), AV_OPT_TYPE_STRING, {.str = "0.0"}, CHAR_MIN, CHAR_MAX, FLAGS }, + OFFSET(brightness_expr), AV_OPT_TYPE_STRING, {.str = "0.0"}, 0, 0, TFLAGS }, { "saturation", "set the saturation adjustment", - OFFSET(saturation_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS }, + OFFSET(saturation_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, 0, 0, TFLAGS }, { "gamma", "set the initial gamma value", - OFFSET(gamma_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS }, + OFFSET(gamma_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, 0, 0, TFLAGS }, { "gamma_r", "gamma value for red", - OFFSET(gamma_r_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS }, + OFFSET(gamma_r_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, 0, 0, TFLAGS }, { "gamma_g", "gamma value for green", - OFFSET(gamma_g_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS }, + OFFSET(gamma_g_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, 0, 0, TFLAGS }, { "gamma_b", "gamma value for blue", - OFFSET(gamma_b_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, CHAR_MIN, CHAR_MAX, FLAGS }, + 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"}, CHAR_MIN, CHAR_MAX, FLAGS }, + 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" }, { "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" }, @@ -387,5 +390,5 @@ AVFilter ff_vf_eq = { .query_formats = query_formats, .init = initialize, .uninit = uninit, - .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_eq.h b/libavfilter/vf_eq.h index fa49d46e5cf..cd0cd75f08c 100644 --- a/libavfilter/vf_eq.h +++ b/libavfilter/vf_eq.h @@ -100,6 +100,7 @@ typedef struct EQContext { enum EvalMode { EVAL_MODE_INIT, EVAL_MODE_FRAME, EVAL_MODE_NB } eval_mode; } EQContext; +void ff_eq_init(EQContext *eq); void ff_eq_init_x86(EQContext *eq); #endif /* AVFILTER_EQ_H */ diff --git a/libavfilter/vf_fade.c b/libavfilter/vf_fade.c index 17eca109b6c..58d7f8cd49f 100644 --- a/libavfilter/vf_fade.c +++ b/libavfilter/vf_fade.c @@ -54,15 +54,20 @@ typedef struct FadeContext { int type; int factor, fade_per_frame; int start_frame, nb_frames; - int hsub, vsub, bpp; + int hsub, vsub, bpp, depth; unsigned int black_level, black_level_scaled; + uint8_t is_rgb; uint8_t is_packed_rgb; uint8_t rgba_map[4]; int alpha; + int is_planar; uint64_t start_time, duration; enum {VF_FADE_WAITING=0, VF_FADE_FADING, VF_FADE_DONE} fade_state; uint8_t color_rgba[4]; ///< fade color int black_fade; ///< if color_rgba is black + int (*filter_slice_luma)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); + int (*filter_slice_chroma)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); + int (*filter_slice_alpha)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); } FadeContext; static av_cold int init(AVFilterContext *ctx) @@ -107,23 +112,40 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, + AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, + AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, + 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_YUV440P12, + 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_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9, + AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, + AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, + AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_NONE }; static const enum AVPixelFormat pix_fmts_rgb[] = { AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, + AV_PIX_FMT_GBRP, AV_PIX_FMT_NONE }; static const enum AVPixelFormat pix_fmts_alpha[] = { 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_YUVA422P12, AV_PIX_FMT_YUVA444P12, + AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_NONE }; static const enum AVPixelFormat pix_fmts_rgba[] = { AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_NONE }; AVFilterFormats *fmts_list; @@ -148,32 +170,18 @@ const static enum AVPixelFormat studio_level_pix_fmts[] = { AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, + 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_YUV440P12, + 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_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9, + AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, + AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, + AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_NONE }; -static int config_props(AVFilterLink *inlink) -{ - FadeContext *s = inlink->dst->priv; - const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(inlink->format); - - s->hsub = pixdesc->log2_chroma_w; - s->vsub = pixdesc->log2_chroma_h; - - s->bpp = pixdesc->flags & AV_PIX_FMT_FLAG_PLANAR ? - 1 : - av_get_bits_per_pixel(pixdesc) >> 3; - s->alpha &= !!(pixdesc->flags & AV_PIX_FMT_FLAG_ALPHA); - s->is_packed_rgb = ff_fill_rgba_map(s->rgba_map, inlink->format) >= 0; - - /* use CCIR601/709 black level for studio-level pixel non-alpha components */ - s->black_level = - ff_fmt_is_in(inlink->format, studio_level_pix_fmts) && !s->alpha ? 16 : 0; - /* 32768 = 1 << 15, it is an integer representation - * of 0.5 and is for rounding. */ - s->black_level_scaled = (s->black_level << 16) + 32768; - return 0; -} - static av_always_inline void filter_rgb(FadeContext *s, const AVFrame *frame, int slice_start, int slice_end, int do_alpha, int step) @@ -199,6 +207,29 @@ static av_always_inline void filter_rgb(FadeContext *s, const AVFrame *frame, } } +static av_always_inline void filter_rgb_planar(FadeContext *s, const AVFrame *frame, + int slice_start, int slice_end, + int do_alpha) +{ + int i, j; + const uint8_t *c = s->color_rgba; + + for (i = slice_start; i < slice_end; i++) { + uint8_t *pg = frame->data[0] + i * frame->linesize[0]; + uint8_t *pb = frame->data[1] + i * frame->linesize[1]; + uint8_t *pr = frame->data[2] + i * frame->linesize[2]; + uint8_t *pa = frame->data[3] + i * frame->linesize[3]; + for (j = 0; j < frame->width; j++) { +#define INTERPP(c_name, c_idx) av_clip_uint8(((c[c_idx]<<16) + ((int)c_name - (int)c[c_idx]) * s->factor + (1<<15)) >> 16) + pr[j] = INTERPP(pr[j], 0); + pg[j] = INTERPP(pg[j], 1); + pb[j] = INTERPP(pb[j], 2); + if (do_alpha) + pa[j] = INTERPP(pa[j], 3); + } + } +} + static int filter_slice_rgb(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { @@ -207,7 +238,11 @@ static int filter_slice_rgb(AVFilterContext *ctx, void *arg, int jobnr, int slice_start = (frame->height * jobnr ) / nb_jobs; int slice_end = (frame->height * (jobnr+1)) / nb_jobs; - if (s->alpha) filter_rgb(s, frame, slice_start, slice_end, 1, 4); + if (s->is_planar && s->alpha) + filter_rgb_planar(s, frame, slice_start, slice_end, 1); + else if (s->is_planar) + filter_rgb_planar(s, frame, slice_start, slice_end, 0); + else if (s->alpha) filter_rgb(s, frame, slice_start, slice_end, 1, 4); else if (s->bpp == 3) filter_rgb(s, frame, slice_start, slice_end, 0, 3); else if (s->bpp == 4) filter_rgb(s, frame, slice_start, slice_end, 0, 4); else av_assert0(0); @@ -224,14 +259,41 @@ static int filter_slice_luma(AVFilterContext *ctx, void *arg, int jobnr, int slice_end = (frame->height * (jobnr+1)) / nb_jobs; int i, j; - for (i = slice_start; i < slice_end; i++) { - uint8_t *p = frame->data[0] + i * frame->linesize[0]; - for (j = 0; j < frame->width * s->bpp; j++) { - /* s->factor is using 16 lower-order bits for decimal - * places. 32768 = 1 << 15, it is an integer representation - * of 0.5 and is for rounding. */ - *p = ((*p - s->black_level) * s->factor + s->black_level_scaled) >> 16; - p++; + for (int k = 0; k < 1 + 2 * (s->is_planar && s->is_rgb); k++) { + for (i = slice_start; i < slice_end; i++) { + uint8_t *p = frame->data[k] + i * frame->linesize[k]; + for (j = 0; j < frame->width * s->bpp; j++) { + /* s->factor is using 16 lower-order bits for decimal + * places. 32768 = 1 << 15, it is an integer representation + * of 0.5 and is for rounding. */ + *p = ((*p - s->black_level) * s->factor + s->black_level_scaled) >> 16; + p++; + } + } + } + + return 0; +} + +static int filter_slice_luma16(AVFilterContext *ctx, void *arg, int jobnr, + int nb_jobs) +{ + FadeContext *s = ctx->priv; + AVFrame *frame = arg; + int slice_start = (frame->height * jobnr ) / nb_jobs; + int slice_end = (frame->height * (jobnr+1)) / nb_jobs; + int i, j; + + for (int k = 0; k < 1 + 2 * (s->is_planar && s->is_rgb); k++) { + for (i = slice_start; i < slice_end; i++) { + uint16_t *p = (uint16_t *)(frame->data[k] + i * frame->linesize[k]); + for (j = 0; j < frame->width * s->bpp; j++) { + /* s->factor is using 16 lower-order bits for decimal + * places. 32768 = 1 << 15, it is an integer representation + * of 0.5 and is for rounding. */ + *p = ((*p - s->black_level) * s->factor + s->black_level_scaled) >> 16; + p++; + } } } @@ -265,6 +327,32 @@ static int filter_slice_chroma(AVFilterContext *ctx, void *arg, int jobnr, return 0; } +static int filter_slice_chroma16(AVFilterContext *ctx, void *arg, int jobnr, + int nb_jobs) +{ + FadeContext *s = ctx->priv; + AVFrame *frame = arg; + int i, j, plane; + const int width = AV_CEIL_RSHIFT(frame->width, s->hsub); + const int height= AV_CEIL_RSHIFT(frame->height, s->vsub); + const int mid = 1 << (s->depth - 1); + const int add = ((mid << 1) + 1) << 15; + int slice_start = (height * jobnr ) / nb_jobs; + int slice_end = FFMIN(((height * (jobnr+1)) / nb_jobs), frame->height); + + for (plane = 1; plane < 3; plane++) { + for (i = slice_start; i < slice_end; i++) { + uint16_t *p = (uint16_t *)(frame->data[plane] + i * frame->linesize[plane]); + for (j = 0; j < width; j++) { + *p = ((*p - mid) * s->factor + add) >> 16; + p++; + } + } + } + + return 0; +} + static int filter_slice_alpha(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { @@ -290,6 +378,64 @@ static int filter_slice_alpha(AVFilterContext *ctx, void *arg, int jobnr, return 0; } +static int filter_slice_alpha16(AVFilterContext *ctx, void *arg, int jobnr, + int nb_jobs) +{ + FadeContext *s = ctx->priv; + AVFrame *frame = arg; + int plane = s->is_packed_rgb ? 0 : A; + int slice_start = (frame->height * jobnr ) / nb_jobs; + int slice_end = (frame->height * (jobnr+1)) / nb_jobs; + int i, j; + + for (i = slice_start; i < slice_end; i++) { + uint16_t *p = (uint16_t *)(frame->data[plane] + i * frame->linesize[plane]) + s->is_packed_rgb*s->rgba_map[A]; + int step = s->is_packed_rgb ? 4 : 1; + for (j = 0; j < frame->width; j++) { + /* s->factor is using 16 lower-order bits for decimal + * places. 32768 = 1 << 15, it is an integer representation + * of 0.5 and is for rounding. */ + *p = ((*p - s->black_level) * s->factor + s->black_level_scaled) >> 16; + p += step; + } + } + + return 0; +} + +static int config_props(AVFilterLink *inlink) +{ + FadeContext *s = inlink->dst->priv; + const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(inlink->format); + + s->hsub = pixdesc->log2_chroma_w; + s->vsub = pixdesc->log2_chroma_h; + + ff_fill_rgba_map(s->rgba_map, inlink->format); + + s->depth = pixdesc->comp[0].depth; + s->bpp = pixdesc->flags & AV_PIX_FMT_FLAG_PLANAR ? + 1 : + av_get_bits_per_pixel(pixdesc) >> 3; + s->alpha &= !!(pixdesc->flags & AV_PIX_FMT_FLAG_ALPHA); + s->is_planar = pixdesc->flags & AV_PIX_FMT_FLAG_PLANAR; + s->is_rgb = pixdesc->flags & AV_PIX_FMT_FLAG_RGB; + s->is_packed_rgb = !s->is_planar && s->is_rgb; + + /* use CCIR601/709 black level for studio-level pixel non-alpha components */ + s->black_level = + ff_fmt_is_in(inlink->format, studio_level_pix_fmts) && !s->alpha ? 16 * (1 << (s->depth - 8)): 0; + /* 32768 = 1 << 15, it is an integer representation + * of 0.5 and is for rounding. */ + s->black_level_scaled = (s->black_level << 16) + 32768; + + s->filter_slice_luma = s->depth <= 8 ? filter_slice_luma : filter_slice_luma16; + s->filter_slice_chroma = s->depth <= 8 ? filter_slice_chroma : filter_slice_chroma16; + s->filter_slice_alpha = s->depth <= 8 ? filter_slice_alpha : filter_slice_alpha16; + + return 0; +} + static int filter_frame(AVFilterLink *inlink, AVFrame *frame) { AVFilterContext *ctx = inlink->dst; @@ -346,19 +492,19 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) if (s->factor < UINT16_MAX) { if (s->alpha) { - ctx->internal->execute(ctx, filter_slice_alpha, frame, NULL, + ctx->internal->execute(ctx, s->filter_slice_alpha, frame, NULL, FFMIN(frame->height, ff_filter_get_nb_threads(ctx))); - } else if (s->is_packed_rgb && !s->black_fade) { + } else if (s->is_rgb && !s->black_fade) { ctx->internal->execute(ctx, filter_slice_rgb, frame, NULL, FFMIN(frame->height, ff_filter_get_nb_threads(ctx))); } else { /* luma, or rgb plane in case of black */ - ctx->internal->execute(ctx, filter_slice_luma, frame, NULL, + ctx->internal->execute(ctx, s->filter_slice_luma, frame, NULL, FFMIN(frame->height, ff_filter_get_nb_threads(ctx))); - if (frame->data[1] && frame->data[2]) { + if (frame->data[1] && frame->data[2] && !s->is_rgb) { /* chroma planes */ - ctx->internal->execute(ctx, filter_slice_chroma, frame, NULL, + ctx->internal->execute(ctx, s->filter_slice_chroma, frame, NULL, FFMIN(frame->height, ff_filter_get_nb_threads(ctx))); } } @@ -372,18 +518,18 @@ 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", "'in' or 'out' for fade-in/fade-out", OFFSET(type), AV_OPT_TYPE_INT, { .i64 = FADE_IN }, FADE_IN, FADE_OUT, FLAGS, "type" }, - { "t", "'in' or 'out' for fade-in/fade-out", OFFSET(type), AV_OPT_TYPE_INT, { .i64 = FADE_IN }, FADE_IN, FADE_OUT, FLAGS, "type" }, - { "in", "fade-in", 0, AV_OPT_TYPE_CONST, { .i64 = FADE_IN }, .unit = "type" }, - { "out", "fade-out", 0, AV_OPT_TYPE_CONST, { .i64 = FADE_OUT }, .unit = "type" }, + { "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" }, + { "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.", OFFSET(start_frame), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, { "s", "Number of the first frame to which to apply the effect.", OFFSET(start_frame), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, { "nb_frames", "Number of frames to which the effect should be applied.", - OFFSET(nb_frames), AV_OPT_TYPE_INT, { .i64 = 25 }, 0, INT_MAX, FLAGS }, + OFFSET(nb_frames), AV_OPT_TYPE_INT, { .i64 = 25 }, 1, INT_MAX, FLAGS }, { "n", "Number of frames to which the effect should be applied.", - OFFSET(nb_frames), AV_OPT_TYPE_INT, { .i64 = 25 }, 0, INT_MAX, FLAGS }, + OFFSET(nb_frames), AV_OPT_TYPE_INT, { .i64 = 25 }, 1, INT_MAX, FLAGS }, { "alpha", "fade alpha if it is available on the input", OFFSET(alpha), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, FLAGS }, { "start_time", "Number of seconds of the beginning of the effect.", OFFSET(start_time), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT64_MAX, FLAGS }, @@ -393,8 +539,8 @@ static const AVOption fade_options[] = { OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT64_MAX, FLAGS }, { "d", "Duration of the effect in seconds.", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT64_MAX, FLAGS }, - { "color", "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "black"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "c", "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "black"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "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 }, { NULL } }; diff --git a/libavfilter/vf_fftdnoiz.c b/libavfilter/vf_fftdnoiz.c index 7ee7dbc19b6..856d716be53 100644 --- a/libavfilter/vf_fftdnoiz.c +++ b/libavfilter/vf_fftdnoiz.c @@ -129,6 +129,11 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, + AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA422P16, + AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_NONE }; AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); @@ -156,7 +161,7 @@ static void export_row8(FFTComplex *src, uint8_t *dst, int rw, float scale, int int j; for (j = 0; j < rw; j++) - dst[j] = av_clip_uint8(src[j].re * scale); + dst[j] = av_clip_uint8(src[j].re * scale + 0.5f); } static void import_row16(FFTComplex *dst, uint8_t *srcp, int rw) @@ -176,7 +181,7 @@ static void export_row16(FFTComplex *src, uint8_t *dstp, int rw, float scale, in int j; for (j = 0; j < rw; j++) - dst[j] = av_clip_uintp2_c(src[j].re * scale, depth); + dst[j] = av_clip_uintp2_c(src[j].re * scale + 0.5f, depth); } static int config_input(AVFilterLink *inlink) diff --git a/libavfilter/vf_fftfilt.c b/libavfilter/vf_fftfilt.c index af44b1e22ee..d091cd830de 100644 --- a/libavfilter/vf_fftfilt.c +++ b/libavfilter/vf_fftfilt.c @@ -81,9 +81,9 @@ static const AVOption fftfilt_options[] = { { "dc_Y", "adjust gain in Y plane", OFFSET(dc[Y]), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1000, FLAGS }, { "dc_U", "adjust gain in U plane", OFFSET(dc[U]), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1000, FLAGS }, { "dc_V", "adjust gain in V plane", OFFSET(dc[V]), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1000, FLAGS }, - { "weight_Y", "set luminance expression in Y plane", OFFSET(weight_str[Y]), AV_OPT_TYPE_STRING, {.str = "1"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "weight_U", "set chrominance expression in U plane", OFFSET(weight_str[U]), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "weight_V", "set chrominance expression in V plane", OFFSET(weight_str[V]), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "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" }, { "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" }, diff --git a/libavfilter/vf_fieldhint.c b/libavfilter/vf_fieldhint.c index 3cfeb20a382..c120bb01c35 100644 --- a/libavfilter/vf_fieldhint.c +++ b/libavfilter/vf_fieldhint.c @@ -65,7 +65,7 @@ static av_cold int init(AVFilterContext *ctx) av_log(ctx, AV_LOG_ERROR, "Hint file must be set.\n"); return AVERROR(EINVAL); } - s->hint = fopen(s->hint_file_str, "r"); + s->hint = av_fopen_utf8(s->hint_file_str, "r"); if (!s->hint) { ret = AVERROR(errno); av_log(ctx, AV_LOG_ERROR, "%s: %s\n", s->hint_file_str, av_err2str(ret)); @@ -117,7 +117,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFrame *out, *top, *bottom; char buf[1024] = { 0 }; int64_t tf, bf; - char hint = '='; + int tfactor = 0, bfactor = 1; + char hint = '=', field = '='; int p; av_frame_free(&s->frame[0]); @@ -137,6 +138,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->line++; if (buf[0] == '#' || buf[0] == ';') { continue; + } else if (sscanf(buf, "%"PRId64",%"PRId64" %c %c", &tf, &bf, &hint, &field) == 4) { + ; } else if (sscanf(buf, "%"PRId64",%"PRId64" %c", &tf, &bf, &hint) == 3) { ; } else if (sscanf(buf, "%"PRId64",%"PRId64"", &tf, &bf) == 2) { @@ -185,6 +188,23 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) av_assert0(0); } + switch (field) { + case 'b': + tfactor = 1; + top = bottom; + break; + case 't': + bfactor = 0; + bottom = top; + break; + case '=': + break; + default: + av_log(ctx, AV_LOG_ERROR, "Invalid field: %c.\n", field); + av_frame_free(&out); + return AVERROR(EINVAL); + } + switch (hint) { case '+': out->interlaced_frame = 1; @@ -194,6 +214,14 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) break; case '=': break; + case 'b': + tfactor = 1; + top = bottom; + break; + case 't': + bfactor = 0; + bottom = top; + break; default: av_log(ctx, AV_LOG_ERROR, "Invalid hint: %c.\n", hint); av_frame_free(&out); @@ -203,13 +231,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) for (p = 0; p < s->nb_planes; p++) { av_image_copy_plane(out->data[p], out->linesize[p] * 2, - top->data[p], + top->data[p] + tfactor * top->linesize[p], top->linesize[p] * 2, s->planewidth[p], (s->planeheight[p] + 1) / 2); av_image_copy_plane(out->data[p] + out->linesize[p], out->linesize[p] * 2, - bottom->data[p] + bottom->linesize[p], + bottom->data[p] + bfactor * bottom->linesize[p], bottom->linesize[p] * 2, s->planewidth[p], (s->planeheight[p] + 1) / 2); diff --git a/libavfilter/vf_fieldmatch.c b/libavfilter/vf_fieldmatch.c index 5a73eb43b8a..fa4aa8239cc 100644 --- a/libavfilter/vf_fieldmatch.c +++ b/libavfilter/vf_fieldmatch.c @@ -679,7 +679,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFilterLink *outlink = ctx->outputs[0]; FieldMatchContext *fm = ctx->priv; int combs[] = { -1, -1, -1, -1, -1 }; - int order, field, i, match, sc = 0; + int order, field, i, match, sc = 0, ret = 0; const int *fxo; AVFrame *gen_frames[] = { NULL, NULL, NULL, NULL, NULL }; AVFrame *dst; @@ -725,16 +725,20 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) if (i > mN && fm->combdbg == COMBDBG_PCN) break; gen_frames[i] = create_weave_frame(ctx, i, field, fm->prv, fm->src, fm->nxt); - if (!gen_frames[i]) - return AVERROR(ENOMEM); + if (!gen_frames[i]) { + ret = AVERROR(ENOMEM); + goto fail; + } combs[i] = calc_combed_score(fm, gen_frames[i]); } av_log(ctx, AV_LOG_INFO, "COMBS: %3d %3d %3d %3d %3d\n", combs[0], combs[1], combs[2], combs[3], combs[4]); } else { gen_frames[mC] = av_frame_clone(fm->src); - if (!gen_frames[mC]) - return AVERROR(ENOMEM); + if (!gen_frames[mC]) { + ret = AVERROR(ENOMEM); + goto fail; + } } /* p/c selection and optional 3-way p/c/n matches */ @@ -801,10 +805,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) gen_frames[match] = NULL; } } - if (!dst) - return AVERROR(ENOMEM); - for (i = 0; i < FF_ARRAY_ELEMS(gen_frames); i++) - av_frame_free(&gen_frames[i]); + if (!dst) { + ret = AVERROR(ENOMEM); + goto fail; + } /* mark the frame we are unable to match properly as interlaced so a proper * de-interlacer can take the relay */ @@ -819,7 +823,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) " match=%d combed=%s\n", sc, combs[0], combs[1], combs[2], combs[3], combs[4], fm->combpel, match, dst->interlaced_frame ? "YES" : "NO"); - return ff_filter_frame(outlink, dst); +fail: + for (i = 0; i < FF_ARRAY_ELEMS(gen_frames); i++) + av_frame_free(&gen_frames[i]); + + if (ret >= 0) + return ff_filter_frame(outlink, dst); + return ret; } static int activate(AVFilterContext *ctx) @@ -829,6 +839,8 @@ static int activate(AVFilterContext *ctx) int ret = 0, status; int64_t pts; + FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); + if ((fm->got_frame[INPUT_MAIN] == 0) && (ret = ff_inlink_consume_frame(ctx->inputs[INPUT_MAIN], &frame)) > 0) { ret = filter_frame(ctx->inputs[INPUT_MAIN], frame); @@ -938,7 +950,7 @@ static int config_input(AVFilterLink *inlink) fm->tpitchy = FFALIGN(w, 16); fm->tpitchuv = FFALIGN(w >> 1, 16); - fm->tbuffer = av_malloc(h/2 * fm->tpitchy); + fm->tbuffer = av_calloc((h/2 + 4) * fm->tpitchy, sizeof(*fm->tbuffer)); fm->c_array = av_malloc((((w + fm->blockx/2)/fm->blockx)+1) * (((h + fm->blocky/2)/fm->blocky)+1) * 4 * sizeof(*fm->c_array)); diff --git a/libavfilter/vf_fieldorder.c b/libavfilter/vf_fieldorder.c index ca55ff1f668..5707151f1be 100644 --- a/libavfilter/vf_fieldorder.c +++ b/libavfilter/vf_fieldorder.c @@ -108,8 +108,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) s->dst_tff ? "up" : "down"); h = frame->height; for (plane = 0; plane < 4 && frame->data[plane] && frame->linesize[plane]; plane++) { - dst_line_step = out->linesize[plane]; - src_line_step = frame->linesize[plane]; + dst_line_step = out->linesize[plane] * (h > 2); + src_line_step = frame->linesize[plane] * (h > 2); line_size = s->line_size[plane]; dst = out->data[plane]; src = frame->data[plane]; diff --git a/libavfilter/vf_fillborders.c b/libavfilter/vf_fillborders.c index 134458702d8..a5a0cb365c0 100644 --- a/libavfilter/vf_fillborders.c +++ b/libavfilter/vf_fillborders.c @@ -69,6 +69,7 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA422P12, AV_PIX_FMT_YUVA444P12, 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, @@ -291,6 +292,20 @@ static int config_input(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; + if (inlink->w < s->left + s->right || + inlink->w <= s->left || + inlink->w <= s->right || + inlink->h < s->top + s->bottom || + inlink->h <= s->top || + inlink->h <= s->bottom || + inlink->w < s->left * 2 || + inlink->w < s->right * 2 || + inlink->h < s->top * 2 || + inlink->h < s->bottom * 2) { + av_log(ctx, AV_LOG_ERROR, "Borders are bigger than input frame size.\n"); + return AVERROR(EINVAL); + } + s->borders[0].left = s->borders[3].left = s->left; s->borders[0].right = s->borders[3].right = s->right; s->borders[0].top = s->borders[3].top = s->top; @@ -306,20 +321,6 @@ static int config_input(AVFilterLink *inlink) s->borders[2].top = s->top >> desc->log2_chroma_h; s->borders[2].bottom = s->bottom >> desc->log2_chroma_h; - if (inlink->w < s->left + s->right || - inlink->w <= s->left || - inlink->w <= s->right || - inlink->h < s->top + s->bottom || - inlink->h <= s->top || - inlink->h <= s->bottom || - inlink->w < s->left * 2 || - inlink->w < s->right * 2 || - inlink->h < s->top * 2 || - inlink->h < s->bottom * 2) { - av_log(ctx, AV_LOG_ERROR, "Borders are bigger than input frame size.\n"); - return AVERROR(EINVAL); - } - switch (s->mode) { case FM_SMEAR: s->fillborders = s->depth <= 8 ? smear_borders8 : smear_borders16; break; case FM_MIRROR: s->fillborders = s->depth <= 8 ? mirror_borders8 : mirror_borders16; break; @@ -345,8 +346,20 @@ static int config_input(AVFilterLink *inlink) return 0; } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + return config_input(ctx->inputs[0]); +} + #define OFFSET(x) offsetof(FillBordersContext, x) -#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption fillborders_options[] = { { "left", "set the left fill border", OFFSET(left), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, @@ -391,4 +404,5 @@ AVFilter ff_vf_fillborders = { .inputs = fillborders_inputs, .outputs = fillborders_outputs, .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 d7e6579af71..b5f8fbcba6d 100644 --- a/libavfilter/vf_find_rect.c +++ b/libavfilter/vf_find_rect.c @@ -22,7 +22,6 @@ * @todo switch to dualinput */ -#include "libavutil/avassert.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "internal.h" @@ -81,7 +80,7 @@ static AVFrame *downscale(AVFrame *in) frame->width = (in->width + 1) / 2; frame->height = (in->height+ 1) / 2; - if (av_frame_get_buffer(frame, 32) < 0) { + if (av_frame_get_buffer(frame, 0) < 0) { av_frame_free(&frame); return NULL; } @@ -159,7 +158,7 @@ static float search(FOCContext *foc, int pass, int maxpass, int xmin, int xmax, if (pass + 1 <= maxpass) { int sub_x, sub_y; - search(foc, pass+1, maxpass, xmin>>1, (xmax+1)>>1, ymin>>1, (ymax+1)>>1, &sub_x, &sub_y, 1.0); + search(foc, pass+1, maxpass, xmin>>1, (xmax+1)>>1, ymin>>1, (ymax+1)>>1, &sub_x, &sub_y, 2.0); xmin = FFMAX(xmin, 2*sub_x - 4); xmax = FFMIN(xmax, 2*sub_x + 4); ymin = FFMAX(ymin, 2*sub_y - 4); @@ -169,7 +168,6 @@ static float search(FOCContext *foc, int pass, int maxpass, int xmin, int xmax, for (y = ymin; y <= ymax; y++) { for (x = xmin; x <= xmax; x++) { float score = compare(foc->haystack_frame[pass], foc->needle_frame[pass], x, y); - av_assert0(score != 0); if (score < best_score) { best_score = score; *best_x = x; @@ -198,7 +196,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) FFMIN(foc->xmax, foc->last_x + 8), FFMAX(foc->ymin, foc->last_y - 8), FFMIN(foc->ymax, foc->last_y + 8), - &best_x, &best_y, 1.0); + &best_x, &best_y, 2.0); best_score = search(foc, 0, foc->mipmaps - 1, foc->xmin, foc->xmax, foc->ymin, foc->ymax, &best_x, &best_y, best_score); diff --git a/libavfilter/vf_floodfill.c b/libavfilter/vf_floodfill.c index 323dd0e2fa1..11c614a089f 100644 --- a/libavfilter/vf_floodfill.c +++ b/libavfilter/vf_floodfill.c @@ -34,9 +34,11 @@ typedef struct FloodfillContext { const AVClass *class; int x, y; - int s0, s1, s2, s3; - int d0, d1, d2, d3; + int s[4]; + int S[4]; + int d[4]; + int nb_planes; int back, front; Points *points; @@ -238,12 +240,12 @@ static int config_input(AVFilterLink *inlink) const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); AVFilterContext *ctx = inlink->dst; FloodfillContext *s = ctx->priv; - int nb_planes = av_pix_fmt_count_planes(inlink->format); int depth; + s->nb_planes = av_pix_fmt_count_planes(inlink->format); depth = desc->comp[0].depth; if (depth == 8) { - switch (nb_planes) { + switch (s->nb_planes) { case 1: s->set_pixel = set_pixel1; s->is_same = is_same1; s->pick_pixel = pick_pixel1; break; @@ -255,7 +257,7 @@ static int config_input(AVFilterLink *inlink) s->pick_pixel = pick_pixel4; break; } } else { - switch (nb_planes) { + switch (s->nb_planes) { case 1: s->set_pixel = set_pixel1_16; s->is_same = is_same1_16; s->pick_pixel = pick_pixel1_16; break; @@ -280,30 +282,42 @@ static int filter_frame(AVFilterLink *link, AVFrame *frame) { AVFilterContext *ctx = link->dst; FloodfillContext *s = ctx->priv; - const unsigned d0 = s->d0; - const unsigned d1 = s->d1; - const unsigned d2 = s->d2; - const unsigned d3 = s->d3; - int s0 = s->s0; - int s1 = s->s1; - int s2 = s->s2; - int s3 = s->s3; + const unsigned d0 = s->d[0]; + const unsigned d1 = s->d[1]; + const unsigned d2 = s->d[2]; + const unsigned d3 = s->d[3]; + int s0 = s->s[0]; + int s1 = s->s[1]; + int s2 = s->s[2]; + int s3 = s->s[3]; const int w = frame->width; const int h = frame->height; - int ret; - - if (ret = av_frame_make_writable(frame)) - return ret; + int i, ret; if (is_inside(s->x, s->y, w, h)) { s->pick_pixel(frame, s->x, s->y, &s0, &s1, &s2, &s3); + s->S[0] = s0; + s->S[1] = s1; + s->S[2] = s2; + s->S[3] = s3; + for (i = 0; i < s->nb_planes; i++) { + if (s->S[i] != s->d[i]) + break; + } + + if (i == s->nb_planes) + goto end; + if (s->is_same(frame, s->x, s->y, s0, s1, s2, s3)) { s->points[s->front].x = s->x; s->points[s->front].y = s->y; s->front++; } + if (ret = av_frame_make_writable(frame)) + return ret; + while (s->front > s->back) { int x, y; @@ -337,34 +351,20 @@ static int filter_frame(AVFilterLink *link, AVFrame *frame) } } +end: return ff_filter_frame(ctx->outputs[0], frame); } static av_cold int query_formats(AVFilterContext *ctx) { static const enum AVPixelFormat pixel_fmts[] = { - AV_PIX_FMT_GRAY8, - AV_PIX_FMT_YUV444P, - AV_PIX_FMT_YUVA444P, - AV_PIX_FMT_GBRP, - 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_GBRP16, - AV_PIX_FMT_GBRAP16, - AV_PIX_FMT_GBRAP, - AV_PIX_FMT_YUV444P9, - AV_PIX_FMT_YUVA444P9, - AV_PIX_FMT_YUV444P10, - AV_PIX_FMT_YUVA444P10, - AV_PIX_FMT_YUV444P12, - AV_PIX_FMT_YUV444P14, - AV_PIX_FMT_GRAY16, - AV_PIX_FMT_YUV444P16, - AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16, + AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P, + AV_PIX_FMT_GBRP, 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_GBRP16, + AV_PIX_FMT_GBRAP16, AV_PIX_FMT_GBRAP, + AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUVA444P10, + AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_NONE }; AVFilterFormats *formats; @@ -405,16 +405,16 @@ static const AVFilterPad floodfill_outputs[] = { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption floodfill_options[] = { - { "x", "set pixel x coordinate", OFFSET(x), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, - { "y", "set pixel y coordinate", OFFSET(y), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, - { "s0", "set source #0 component value", OFFSET(s0), AV_OPT_TYPE_INT, {.i64=0},-1, UINT16_MAX, FLAGS }, - { "s1", "set source #1 component value", OFFSET(s1), AV_OPT_TYPE_INT, {.i64=0},-1, UINT16_MAX, FLAGS }, - { "s2", "set source #2 component value", OFFSET(s2), AV_OPT_TYPE_INT, {.i64=0},-1, UINT16_MAX, FLAGS }, - { "s3", "set source #3 component value", OFFSET(s3), AV_OPT_TYPE_INT, {.i64=0},-1, UINT16_MAX, FLAGS }, - { "d0", "set destination #0 component value", OFFSET(d0), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, - { "d1", "set destination #1 component value", OFFSET(d1), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, - { "d2", "set destination #2 component value", OFFSET(d2), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, - { "d3", "set destination #3 component value", OFFSET(d3), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, + { "x", "set pixel x coordinate", OFFSET(x), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, + { "y", "set pixel y coordinate", OFFSET(y), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, + { "s0", "set source #0 component value", OFFSET(s[0]), AV_OPT_TYPE_INT, {.i64=0},-1, UINT16_MAX, FLAGS }, + { "s1", "set source #1 component value", OFFSET(s[1]), AV_OPT_TYPE_INT, {.i64=0},-1, UINT16_MAX, FLAGS }, + { "s2", "set source #2 component value", OFFSET(s[2]), AV_OPT_TYPE_INT, {.i64=0},-1, UINT16_MAX, FLAGS }, + { "s3", "set source #3 component value", OFFSET(s[3]), AV_OPT_TYPE_INT, {.i64=0},-1, UINT16_MAX, FLAGS }, + { "d0", "set destination #0 component value", OFFSET(d[0]), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, + { "d1", "set destination #1 component value", OFFSET(d[1]), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, + { "d2", "set destination #2 component value", OFFSET(d[2]), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, + { "d3", "set destination #3 component value", OFFSET(d[3]), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, { NULL } }; diff --git a/libavfilter/vf_fps.c b/libavfilter/vf_fps.c index 6b99f20d2bb..cf1e36726ae 100644 --- a/libavfilter/vf_fps.c +++ b/libavfilter/vf_fps.c @@ -256,7 +256,7 @@ static int write_frame(AVFilterContext *ctx, FPSContext *s, AVFilterLink *outlin av_log(ctx, AV_LOG_DEBUG, "Writing frame with pts %"PRId64" to pts %"PRId64"\n", s->frames[0]->pts, frame->pts); s->cur_frame_out++; - + *again = 1; return ff_filter_frame(outlink, frame); } } diff --git a/libavfilter/vf_framepack.c b/libavfilter/vf_framepack.c index 12a29964c47..b5d877ca992 100644 --- a/libavfilter/vf_framepack.c +++ b/libavfilter/vf_framepack.c @@ -33,6 +33,7 @@ #include "libavutil/stereo3d.h" #include "avfilter.h" +#include "filters.h" #include "formats.h" #include "internal.h" #include "video.h" @@ -48,8 +49,6 @@ typedef struct FramepackContext { enum AVStereo3DType format; ///< frame pack type output AVFrame *input_views[2]; ///< input frames - - int64_t double_pts; ///< new pts for frameseq mode } FramepackContext; static const enum AVPixelFormat formats_supported[] = { @@ -120,8 +119,6 @@ static int config_output(AVFilterLink *outlink) case AV_STEREO3D_FRAMESEQUENCE: time_base.den *= 2; frame_rate.num *= 2; - - s->double_pts = AV_NOPTS_VALUE; break; case AV_STEREO3D_COLUMNS: case AV_STEREO3D_SIDEBYSIDE: @@ -269,39 +266,6 @@ static av_always_inline void spatial_frame_pack(AVFilterLink *outlink, } } -static int try_push_frame(AVFilterContext *ctx); - -static int filter_frame_left(AVFilterLink *inlink, AVFrame *frame) -{ - FramepackContext *s = inlink->dst->priv; - s->input_views[LEFT] = frame; - return try_push_frame(inlink->dst); -} - -static int filter_frame_right(AVFilterLink *inlink, AVFrame *frame) -{ - FramepackContext *s = inlink->dst->priv; - s->input_views[RIGHT] = frame; - return try_push_frame(inlink->dst); -} - -static int request_frame(AVFilterLink *outlink) -{ - AVFilterContext *ctx = outlink->src; - FramepackContext *s = ctx->priv; - int ret, i; - - /* get a frame on the either input, stop as soon as a video ends */ - for (i = 0; i < 2; i++) { - if (!s->input_views[i]) { - ret = ff_request_frame(ctx->inputs[i]); - if (ret < 0) - return ret; - } - } - return 0; -} - static int try_push_frame(AVFilterContext *ctx) { FramepackContext *s = ctx->priv; @@ -312,12 +276,12 @@ static int try_push_frame(AVFilterContext *ctx) if (!(s->input_views[0] && s->input_views[1])) return 0; if (s->format == AV_STEREO3D_FRAMESEQUENCE) { - if (s->double_pts == AV_NOPTS_VALUE) - s->double_pts = s->input_views[LEFT]->pts; + int64_t pts = s->input_views[0]->pts; for (i = 0; i < 2; i++) { // set correct timestamps - s->input_views[i]->pts = s->double_pts++; + if (pts != AV_NOPTS_VALUE) + s->input_views[i]->pts = i == 0 ? pts * 2 : pts * 2 + av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); // set stereo3d side data stereo = av_stereo3d_create_side_data(s->input_views[i]); @@ -363,21 +327,64 @@ static int try_push_frame(AVFilterContext *ctx) } } +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *outlink = ctx->outputs[0]; + FramepackContext *s = ctx->priv; + int ret; + + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); + + if (!s->input_views[0]) { + ret = ff_inlink_consume_frame(ctx->inputs[0], &s->input_views[0]); + if (ret < 0) + return ret; + } + + if (!s->input_views[1]) { + ret = ff_inlink_consume_frame(ctx->inputs[1], &s->input_views[1]); + if (ret < 0) + return ret; + } + + if (s->input_views[0] && s->input_views[1]) + return try_push_frame(ctx); + + FF_FILTER_FORWARD_STATUS(ctx->inputs[0], outlink); + 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; + } + + return FFERROR_NOT_READY; +} + #define OFFSET(x) offsetof(FramepackContext, x) -#define V AV_OPT_FLAG_VIDEO_PARAM +#define VF AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM static const AVOption framepack_options[] = { { "format", "Frame pack output format", OFFSET(format), AV_OPT_TYPE_INT, - { .i64 = AV_STEREO3D_SIDEBYSIDE }, 0, INT_MAX, .flags = V, .unit = "format" }, + { .i64 = AV_STEREO3D_SIDEBYSIDE }, 0, INT_MAX, .flags = VF, .unit = "format" }, { "sbs", "Views are packed next to each other", 0, AV_OPT_TYPE_CONST, - { .i64 = AV_STEREO3D_SIDEBYSIDE }, INT_MIN, INT_MAX, .flags = V, .unit = "format" }, + { .i64 = AV_STEREO3D_SIDEBYSIDE }, INT_MIN, INT_MAX, .flags = VF, .unit = "format" }, { "tab", "Views are packed on top of each other", 0, AV_OPT_TYPE_CONST, - { .i64 = AV_STEREO3D_TOPBOTTOM }, INT_MIN, INT_MAX, .flags = V, .unit = "format" }, + { .i64 = AV_STEREO3D_TOPBOTTOM }, INT_MIN, INT_MAX, .flags = VF, .unit = "format" }, { "frameseq", "Views are one after the other", 0, AV_OPT_TYPE_CONST, - { .i64 = AV_STEREO3D_FRAMESEQUENCE }, INT_MIN, INT_MAX, .flags = V, .unit = "format" }, + { .i64 = AV_STEREO3D_FRAMESEQUENCE }, INT_MIN, INT_MAX, .flags = VF, .unit = "format" }, { "lines", "Views are interleaved by lines", 0, AV_OPT_TYPE_CONST, - { .i64 = AV_STEREO3D_LINES }, INT_MIN, INT_MAX, .flags = V, .unit = "format" }, + { .i64 = AV_STEREO3D_LINES }, INT_MIN, INT_MAX, .flags = VF, .unit = "format" }, { "columns", "Views are interleaved by columns", 0, AV_OPT_TYPE_CONST, - { .i64 = AV_STEREO3D_COLUMNS }, INT_MIN, INT_MAX, .flags = V, .unit = "format" }, + { .i64 = AV_STEREO3D_COLUMNS }, INT_MIN, INT_MAX, .flags = VF, .unit = "format" }, { NULL }, }; @@ -387,14 +394,10 @@ static const AVFilterPad framepack_inputs[] = { { .name = "left", .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = filter_frame_left, - .needs_fifo = 1, }, { .name = "right", .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = filter_frame_right, - .needs_fifo = 1, }, { NULL } }; @@ -404,7 +407,6 @@ static const AVFilterPad framepack_outputs[] = { .name = "packed", .type = AVMEDIA_TYPE_VIDEO, .config_props = config_output, - .request_frame = request_frame, }, { NULL } }; @@ -417,5 +419,6 @@ AVFilter ff_vf_framepack = { .query_formats = query_formats, .inputs = framepack_inputs, .outputs = framepack_outputs, + .activate = activate, .uninit = framepack_uninit, }; diff --git a/libavfilter/vf_framerate.c b/libavfilter/vf_framerate.c index 06e463e4d77..6c8d01c94bb 100644 --- a/libavfilter/vf_framerate.c +++ b/libavfilter/vf_framerate.c @@ -51,7 +51,7 @@ static const AVOption framerate_options[] = { {"interp_start", "point to start linear interpolation", OFFSET(interp_start), AV_OPT_TYPE_INT, {.i64=15}, 0, 255, V|F }, {"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, INT_MAX, 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" }, @@ -95,29 +95,22 @@ static int filter_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs) { FrameRateContext *s = ctx->priv; ThreadData *td = arg; + AVFrame *work = s->work; + AVFrame *src1 = td->copy_src1; + AVFrame *src2 = td->copy_src2; uint16_t src1_factor = td->src1_factor; uint16_t src2_factor = td->src2_factor; int plane; - for (plane = 0; plane < 4 && td->copy_src1->data[plane] && td->copy_src2->data[plane]; plane++) { - int cpy_line_width = s->line_size[plane]; - uint8_t *cpy_src1_data = td->copy_src1->data[plane]; - int cpy_src1_line_size = td->copy_src1->linesize[plane]; - uint8_t *cpy_src2_data = td->copy_src2->data[plane]; - int cpy_src2_line_size = td->copy_src2->linesize[plane]; - int cpy_src_h = (plane > 0 && plane < 3) ? (td->copy_src1->height >> s->vsub) : (td->copy_src1->height); - uint8_t *cpy_dst_data = s->work->data[plane]; - int cpy_dst_line_size = s->work->linesize[plane]; - const int start = (cpy_src_h * job ) / nb_jobs; - const int end = (cpy_src_h * (job+1)) / nb_jobs; - cpy_src1_data += start * cpy_src1_line_size; - cpy_src2_data += start * cpy_src2_line_size; - cpy_dst_data += start * cpy_dst_line_size; - - s->blend(cpy_src1_data, cpy_src1_line_size, - cpy_src2_data, cpy_src2_line_size, - cpy_dst_data, cpy_dst_line_size, - cpy_line_width, end - start, + for (plane = 0; plane < 4 && src1->data[plane] && src2->data[plane]; plane++) { + const int start = (s->height[plane] * job ) / nb_jobs; + const int end = (s->height[plane] * (job+1)) / nb_jobs; + uint8_t *src1_data = src1->data[plane] + start * src1->linesize[plane]; + uint8_t *src2_data = src2->data[plane] + start * src2->linesize[plane]; + uint8_t *dst_data = work->data[plane] + start * work->linesize[plane]; + + s->blend(src1_data, src1->linesize[plane], src2_data, src2->linesize[plane], + dst_data, work->linesize[plane], s->line_size[plane], end - start, src1_factor, src2_factor, s->blend_factor_max >> 1); } @@ -242,44 +235,38 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_formats(ctx, fmts_list); } -static void blend_frames_c(BLEND_FUNC_PARAMS) -{ - int line, pixel; - for (line = 0; line < height; line++) { - for (pixel = 0; pixel < width; pixel++) - dst[pixel] = ((src1[pixel] * factor1) + (src2[pixel] * factor2) + half) >> BLEND_FACTOR_DEPTH8; - src1 += src1_linesize; - src2 += src2_linesize; - dst += dst_linesize; - } -} - -static void blend_frames16_c(BLEND_FUNC_PARAMS) -{ - int line, pixel; - uint16_t *dstw = (uint16_t *)dst; - uint16_t *src1w = (uint16_t *)src1; - uint16_t *src2w = (uint16_t *)src2; - width /= 2; - src1_linesize /= 2; - src2_linesize /= 2; - dst_linesize /= 2; - for (line = 0; line < height; line++) { - for (pixel = 0; pixel < width; pixel++) - dstw[pixel] = ((src1w[pixel] * factor1) + (src2w[pixel] * factor2) + half) >> BLEND_FACTOR_DEPTH16; - src1w += src1_linesize; - src2w += src2_linesize; - dstw += dst_linesize; - } +#define BLEND_FRAME_FUNC(nbits) \ +static void blend_frames##nbits##_c(BLEND_FUNC_PARAMS) \ +{ \ + int line, pixel; \ + uint##nbits##_t *dstw = (uint##nbits##_t *)dst; \ + uint##nbits##_t *src1w = (uint##nbits##_t *)src1; \ + uint##nbits##_t *src2w = (uint##nbits##_t *)src2; \ + int bytes = nbits / 8; \ + width /= bytes; \ + src1_linesize /= bytes; \ + src2_linesize /= bytes; \ + dst_linesize /= bytes; \ + for (line = 0; line < height; line++) { \ + for (pixel = 0; pixel < width; pixel++) \ + dstw[pixel] = ((src1w[pixel] * factor1) + \ + (src2w[pixel] * factor2) + half) \ + >> BLEND_FACTOR_DEPTH(nbits); \ + src1w += src1_linesize; \ + src2w += src2_linesize; \ + dstw += dst_linesize; \ + } \ } +BLEND_FRAME_FUNC(8) +BLEND_FRAME_FUNC(16) void ff_framerate_init(FrameRateContext *s) { if (s->bitdepth == 8) { - s->blend_factor_max = 1 << BLEND_FACTOR_DEPTH8; - s->blend = blend_frames_c; + s->blend_factor_max = 1 << BLEND_FACTOR_DEPTH(8); + s->blend = blend_frames8_c; } else { - s->blend_factor_max = 1 << BLEND_FACTOR_DEPTH16; + s->blend_factor_max = 1 << BLEND_FACTOR_DEPTH(16); s->blend = blend_frames16_c; } if (ARCH_X86) @@ -293,13 +280,13 @@ static int config_input(AVFilterLink *inlink) const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format); int plane; + s->vsub = pix_desc->log2_chroma_h; for (plane = 0; plane < 4; plane++) { - s->line_size[plane] = av_image_get_linesize(inlink->format, inlink->w, - plane); + s->line_size[plane] = av_image_get_linesize(inlink->format, inlink->w, plane); + s->height[plane] = inlink->h >> ((plane == 1 || plane == 2) ? s->vsub : 0); } s->bitdepth = pix_desc->comp[0].depth; - s->vsub = pix_desc->log2_chroma_h; s->sad = ff_scene_sad_get_fn(s->bitdepth == 8 ? 8 : 16); if (!s->sad) diff --git a/libavfilter/vf_freezeframes.c b/libavfilter/vf_freezeframes.c new file mode 100644 index 00000000000..b6cd5dba68c --- /dev/null +++ b/libavfilter/vf_freezeframes.c @@ -0,0 +1,167 @@ +/* + * 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/avstring.h" +#include "libavutil/common.h" +#include "libavutil/internal.h" +#include "libavutil/opt.h" + +#include "avfilter.h" +#include "filters.h" +#include "internal.h" +#include "video.h" + +typedef struct FreezeFramesContext { + const AVClass *class; + int64_t first, last, replace; + + AVFrame *replace_frame; +} FreezeFramesContext; + +#define OFFSET(x) offsetof(FreezeFramesContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) + +static const AVOption freezeframes_options[] = { + { "first", "set first frame to freeze", OFFSET(first), AV_OPT_TYPE_INT64, {.i64=0}, 0, INT64_MAX, FLAGS }, + { "last", "set last frame to freeze", OFFSET(last), AV_OPT_TYPE_INT64, {.i64=0}, 0, INT64_MAX, FLAGS }, + { "replace", "set frame to replace", OFFSET(replace), AV_OPT_TYPE_INT64, {.i64=0}, 0, INT64_MAX, FLAGS }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(freezeframes); + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AVFilterLink *sourcelink = ctx->inputs[0]; + AVFilterLink *replacelink = ctx->inputs[1]; + + if (sourcelink->w != replacelink->w || sourcelink->h != replacelink->h) { + av_log(ctx, AV_LOG_ERROR, + "Input frame sizes do not match (%dx%d vs %dx%d).\n", + sourcelink->w, sourcelink->h, + replacelink->w, replacelink->h); + return AVERROR(EINVAL); + } + + outlink->w = sourcelink->w; + outlink->h = sourcelink->h; + outlink->time_base = sourcelink->time_base; + outlink->sample_aspect_ratio = sourcelink->sample_aspect_ratio; + outlink->frame_rate = sourcelink->frame_rate; + + return 0; +} + +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *outlink = ctx->outputs[0]; + FreezeFramesContext *s = ctx->priv; + AVFrame *frame = NULL; + int drop = ctx->inputs[0]->frame_count_out >= s->first && + ctx->inputs[0]->frame_count_out <= s->last; + int replace = ctx->inputs[1]->frame_count_out == s->replace; + int ret; + + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); + + if (drop && s->replace_frame) { + ret = ff_inlink_consume_frame(ctx->inputs[0], &frame); + if (ret < 0) + return ret; + + if (frame) { + int64_t dropped_pts = frame->pts; + + av_frame_free(&frame); + frame = av_frame_clone(s->replace_frame); + if (!frame) + return AVERROR(ENOMEM); + frame->pts = dropped_pts; + return ff_filter_frame(outlink, frame); + } + } else if (!drop) { + ret = ff_inlink_consume_frame(ctx->inputs[0], &frame); + if (ret < 0) + return ret; + + if (frame) + return ff_filter_frame(outlink, frame); + } + + ret = ff_inlink_consume_frame(ctx->inputs[1], &frame); + if (ret < 0) + return ret; + if (replace && frame) { + s->replace_frame = frame; + } else if (frame) { + av_frame_free(&frame); + } + + FF_FILTER_FORWARD_STATUS(ctx->inputs[0], outlink); + FF_FILTER_FORWARD_STATUS(ctx->inputs[1], outlink); + + if (!drop || (drop && s->replace_frame)) + FF_FILTER_FORWARD_WANTED(outlink, ctx->inputs[0]); + if (!s->replace_frame) + FF_FILTER_FORWARD_WANTED(outlink, ctx->inputs[1]); + + return FFERROR_NOT_READY; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + FreezeFramesContext *s = ctx->priv; + + av_frame_free(&s->replace_frame); +} + +static const AVFilterPad freezeframes_inputs[] = { + { + .name = "source", + .type = AVMEDIA_TYPE_VIDEO, + }, + { + .name = "replace", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL }, +}; + +static const AVFilterPad freezeframes_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, + { NULL }, +}; + +AVFilter ff_vf_freezeframes = { + .name = "freezeframes", + .description = NULL_IF_CONFIG_SMALL("Freeze video frames."), + .priv_size = sizeof(FreezeFramesContext), + .priv_class = &freezeframes_class, + .inputs = freezeframes_inputs, + .outputs = freezeframes_outputs, + .activate = activate, + .uninit = uninit, +}; diff --git a/libavfilter/vf_gblur.c b/libavfilter/vf_gblur.c index e71b33da800..2e587f6a0af 100644 --- a/libavfilter/vf_gblur.c +++ b/libavfilter/vf_gblur.c @@ -35,7 +35,7 @@ #include "video.h" #define OFFSET(x) offsetof(GBlurContext, x) -#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption gblur_options[] = { { "sigma", "set sigma", OFFSET(sigma), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0.0, 1024, FLAGS }, @@ -157,6 +157,7 @@ static int filter_postscale(AVFilterContext *ctx, void *arg, int jobnr, int nb_j { GBlurContext *s = ctx->priv; ThreadData *td = arg; + const float max = (1 << s->depth) - 1; const int height = td->height; const int width = td->width; const int64_t numpixels = width * (int64_t)height; @@ -166,8 +167,10 @@ static int filter_postscale(AVFilterContext *ctx, void *arg, int jobnr, int nb_j float *buffer = s->buffer; unsigned i; - for (i = slice_start; i < slice_end; i++) + for (i = slice_start; i < slice_end; i++) { buffer[i] *= postscale; + buffer[i] = av_clipf(buffer[i], 0.f, max); + } return 0; } @@ -205,6 +208,7 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA422P12, AV_PIX_FMT_YUVA444P12, 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, @@ -236,7 +240,7 @@ static int config_input(AVFilterLink *inlink) s->nb_planes = av_pix_fmt_count_planes(inlink->format); - s->buffer = av_malloc_array(inlink->w, inlink->h * sizeof(*s->buffer)); + s->buffer = av_malloc_array(FFALIGN(inlink->w, 16), FFALIGN(inlink->h, 16) * sizeof(*s->buffer)); if (!s->buffer) return AVERROR(ENOMEM); @@ -379,4 +383,5 @@ AVFilter ff_vf_gblur = { .inputs = gblur_inputs, .outputs = gblur_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vf_geq.c b/libavfilter/vf_geq.c index 91eb9685f9f..d7abf96a686 100644 --- a/libavfilter/vf_geq.c +++ b/libavfilter/vf_geq.c @@ -4,19 +4,19 @@ * * This file is part of FFmpeg. * - * FFmpeg is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with FFmpeg; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** @@ -33,12 +33,21 @@ #include "libavutil/pixdesc.h" #include "internal.h" +#define MAX_NB_THREADS 32 +#define NB_PLANES 4 + +enum InterpolationMethods { + INTERP_NEAREST, + INTERP_BILINEAR, + NB_INTERP +}; + static const char *const var_names[] = { "X", "Y", "W", "H", "N", "SW", "SH", "T", NULL }; enum { VAR_X, VAR_Y, VAR_W, VAR_H, VAR_N, VAR_SW, VAR_SH, VAR_T, VAR_VARS_NB }; typedef struct GEQContext { const AVClass *class; - AVExpr *e[4]; ///< expressions for each plane + AVExpr *e[NB_PLANES][MAX_NB_THREADS]; ///< expressions for each plane and thread char *expr_str[4+3]; ///< expression strings for each plane AVFrame *picref; ///< current input buffer uint8_t *dst; ///< reference pointer to the 8bits output @@ -46,8 +55,12 @@ typedef struct GEQContext { double values[VAR_VARS_NB]; ///< expression values int hsub, vsub; ///< chroma subsampling int planes; ///< number of planes + int interpolation; int is_rgb; int bps; + + double *pixel_sums[NB_PLANES]; + int needs_sum[NB_PLANES]; } GEQContext; enum { Y = 0, U, V, A, G, B, R }; @@ -56,20 +69,26 @@ enum { Y = 0, U, V, A, G, B, R }; #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM static const AVOption geq_options[] = { - { "lum_expr", "set luminance expression", OFFSET(expr_str[Y]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "lum", "set luminance expression", OFFSET(expr_str[Y]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "cb_expr", "set chroma blue expression", OFFSET(expr_str[U]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "cb", "set chroma blue expression", OFFSET(expr_str[U]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "cr_expr", "set chroma red expression", OFFSET(expr_str[V]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "cr", "set chroma red expression", OFFSET(expr_str[V]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "alpha_expr", "set alpha expression", OFFSET(expr_str[A]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "a", "set alpha expression", OFFSET(expr_str[A]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "red_expr", "set red expression", OFFSET(expr_str[R]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "r", "set red expression", OFFSET(expr_str[R]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "green_expr", "set green expression", OFFSET(expr_str[G]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "g", "set green expression", OFFSET(expr_str[G]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "blue_expr", "set blue expression", OFFSET(expr_str[B]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "b", "set blue expression", OFFSET(expr_str[B]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "lum_expr", "set luminance expression", OFFSET(expr_str[Y]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + { "lum", "set luminance expression", OFFSET(expr_str[Y]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + { "cb_expr", "set chroma blue expression", OFFSET(expr_str[U]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + { "cb", "set chroma blue expression", OFFSET(expr_str[U]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + { "cr_expr", "set chroma red expression", OFFSET(expr_str[V]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + { "cr", "set chroma red expression", OFFSET(expr_str[V]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + { "alpha_expr", "set alpha expression", OFFSET(expr_str[A]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + { "a", "set alpha expression", OFFSET(expr_str[A]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + { "red_expr", "set red expression", OFFSET(expr_str[R]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + { "r", "set red expression", OFFSET(expr_str[R]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + { "green_expr", "set green expression", OFFSET(expr_str[G]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, + { "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" }, {NULL}, }; @@ -88,24 +107,108 @@ static inline double getpix(void *priv, double x, double y, int plane) if (!src) return 0; - xi = x = av_clipf(x, 0, w - 2); - yi = y = av_clipf(y, 0, h - 2); + if (geq->interpolation == INTERP_BILINEAR) { + xi = x = av_clipd(x, 0, w - 2); + yi = y = av_clipd(y, 0, h - 2); - x -= xi; - y -= yi; + x -= xi; + y -= yi; - if (geq->bps > 8) { - const uint16_t *src16 = (const uint16_t*)src; - linesize /= 2; + if (geq->bps > 8) { + const uint16_t *src16 = (const uint16_t*)src; + linesize /= 2; - return (1-y)*((1-x)*src16[xi + yi * linesize] + x*src16[xi + 1 + yi * linesize]) - + y *((1-x)*src16[xi + (yi+1) * linesize] + x*src16[xi + 1 + (yi+1) * linesize]); + return (1-y)*((1-x)*src16[xi + yi * linesize] + x*src16[xi + 1 + yi * linesize]) + + y *((1-x)*src16[xi + (yi+1) * linesize] + x*src16[xi + 1 + (yi+1) * linesize]); + } else { + return (1-y)*((1-x)*src[xi + yi * linesize] + x*src[xi + 1 + yi * linesize]) + + y *((1-x)*src[xi + (yi+1) * linesize] + x*src[xi + 1 + (yi+1) * linesize]); + } } else { - return (1-y)*((1-x)*src[xi + yi * linesize] + x*src[xi + 1 + yi * linesize]) - + y *((1-x)*src[xi + (yi+1) * linesize] + x*src[xi + 1 + (yi+1) * linesize]); + xi = av_clipd(x, 0, w - 1); + yi = av_clipd(y, 0, h - 1); + + if (geq->bps > 8) { + const uint16_t *src16 = (const uint16_t*)src; + linesize /= 2; + + return src16[xi + yi * linesize]; + } else { + return src[xi + yi * linesize]; + } } } +static int calculate_sums(GEQContext *geq, int plane, int w, int h) +{ + int xi, yi; + AVFrame *picref = geq->picref; + const uint8_t *src = picref->data[plane]; + int linesize = picref->linesize[plane]; + + if (!geq->pixel_sums[plane]) + geq->pixel_sums[plane] = av_malloc_array(w, h * sizeof (*geq->pixel_sums[plane])); + if (!geq->pixel_sums[plane]) + return AVERROR(ENOMEM); + if (geq->bps > 8) + linesize /= 2; + for (yi = 0; yi < h; yi ++) { + if (geq->bps > 8) { + const uint16_t *src16 = (const uint16_t*)src; + double linesum = 0; + + for (xi = 0; xi < w; xi ++) { + linesum += src16[xi + yi * linesize]; + geq->pixel_sums[plane][xi + yi * w] = linesum; + } + } else { + double linesum = 0; + + for (xi = 0; xi < w; xi ++) { + linesum += src[xi + yi * linesize]; + geq->pixel_sums[plane][xi + yi * w] = linesum; + } + } + if (yi) + for (xi = 0; xi < w; xi ++) { + geq->pixel_sums[plane][xi + yi * w] += geq->pixel_sums[plane][xi + yi * w - w]; + } + } + return 0; +} + +static inline double getpix_integrate_internal(GEQContext *geq, int x, int y, int plane, int w, int h) +{ + if (x > w - 1) { + double boundary = getpix_integrate_internal(geq, w - 1, y, plane, w, h); + return 2*boundary - getpix_integrate_internal(geq, 2*(w - 1) - x, y, plane, w, h); + } else if (y > h - 1) { + double boundary = getpix_integrate_internal(geq, x, h - 1, plane, w, h); + return 2*boundary - getpix_integrate_internal(geq, x, 2*(h - 1) - y, plane, w, h); + } else if (x < 0) { + if (x == -1) return 0; + return - getpix_integrate_internal(geq, -x-2, y, plane, w, h); + } else if (y < 0) { + if (y == -1) return 0; + return - getpix_integrate_internal(geq, x, -y-2, plane, w, h); + } + + return geq->pixel_sums[plane][x + y * w]; +} + +static inline double getpix_integrate(void *priv, double x, double y, int plane) { + GEQContext *geq = priv; + AVFrame *picref = geq->picref; + const uint8_t *src = picref->data[plane]; + const int w = (plane == 1 || plane == 2) ? AV_CEIL_RSHIFT(picref->width, geq->hsub) : picref->width; + const int h = (plane == 1 || plane == 2) ? AV_CEIL_RSHIFT(picref->height, geq->vsub) : picref->height; + + if (!src) + return 0; + + return getpix_integrate_internal(geq, lrint(av_clipd(x, -w, 2*w)), lrint(av_clipd(y, -h, 2*h)), plane, w, h); +} + //TODO: cubic interpolate //TODO: keep the last few frames static double lum(void *priv, double x, double y) { return getpix(priv, x, y, 0); } @@ -113,6 +216,11 @@ static double cb(void *priv, double x, double y) { return getpix(priv, x, y, 1) static double cr(void *priv, double x, double y) { return getpix(priv, x, y, 2); } static double alpha(void *priv, double x, double y) { return getpix(priv, x, y, 3); } +static double lumsum(void *priv, double x, double y) { return getpix_integrate(priv, x, y, 0); } +static double cbsum(void *priv, double x, double y) { return getpix_integrate(priv, x, y, 1); } +static double crsub(void *priv, double x, double y) { return getpix_integrate(priv, x, y, 2); } +static double alphasum(void *priv, double x, double y) { return getpix_integrate(priv, x, y, 3); } + static av_cold int geq_init(AVFilterContext *ctx) { GEQContext *geq = ctx->priv; @@ -161,17 +269,35 @@ static av_cold int geq_init(AVFilterContext *ctx) goto end; } - for (plane = 0; plane < 4; plane++) { - static double (*p[])(void *, double, double) = { lum, cb, cr, alpha }; - static const char *const func2_yuv_names[] = { "lum", "cb", "cr", "alpha", "p", NULL }; - static const char *const func2_rgb_names[] = { "g", "b", "r", "alpha", "p", NULL }; + for (plane = 0; plane < NB_PLANES; plane++) { + static double (*p[])(void *, double, double) = { + lum , cb , cr , alpha , + lumsum, cbsum, crsub, alphasum, + }; + static const char *const func2_yuv_names[] = { + "lum" , "cb" , "cr" , "alpha" , "p", + "lumsum", "cbsum", "crsum", "alphasum", "psum", + NULL }; + static const char *const func2_rgb_names[] = { + "g" , "b" , "r" , "alpha" , "p", + "gsum", "bsum", "rsum", "alphasum", "psum", + NULL }; const char *const *func2_names = geq->is_rgb ? func2_rgb_names : func2_yuv_names; - double (*func2[])(void *, double, double) = { lum, cb, cr, alpha, p[plane], NULL }; + double (*func2[])(void *, double, double) = { + lum , cb , cr , alpha , p[plane], + lumsum, cbsum, crsub, alphasum, p[plane + 4], + NULL }; + int counter[10] = {0}; + + for (int i = 0; i < MAX_NB_THREADS; i++) { + ret = av_expr_parse(&geq->e[plane][i], geq->expr_str[plane < 3 && geq->is_rgb ? plane+4 : plane], var_names, + NULL, NULL, func2_names, func2, 0, ctx); + if (ret < 0) + goto end; + } - ret = av_expr_parse(&geq->e[plane], geq->expr_str[plane < 3 && geq->is_rgb ? plane+4 : plane], var_names, - NULL, NULL, func2_names, func2, 0, ctx); - if (ret < 0) - break; + av_expr_count_func(geq->e[plane][0], counter, FF_ARRAY_ELEMS(counter), 2); + geq->needs_sum[plane] = counter[5] + counter[6] + counter[7] + counter[8] + counter[9]; } end: @@ -252,8 +378,6 @@ static int slice_geq_filter(AVFilterContext *ctx, void *arg, int jobnr, int nb_j const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr+1)) / nb_jobs; int x, y; - uint8_t *ptr; - uint16_t *ptr16; double values[VAR_VARS_NB]; values[VAR_W] = geq->values[VAR_W]; @@ -264,25 +388,25 @@ static int slice_geq_filter(AVFilterContext *ctx, void *arg, int jobnr, int nb_j values[VAR_T] = geq->values[VAR_T]; if (geq->bps == 8) { + uint8_t *ptr = geq->dst + linesize * slice_start; for (y = slice_start; y < slice_end; y++) { - ptr = geq->dst + linesize * y; values[VAR_Y] = y; for (x = 0; x < width; x++) { values[VAR_X] = x; - ptr[x] = av_expr_eval(geq->e[plane], values, geq); + ptr[x] = av_expr_eval(geq->e[plane][jobnr], values, geq); } ptr += linesize; } - } - else { + } else { + uint16_t *ptr16 = geq->dst16 + (linesize/2) * slice_start; for (y = slice_start; y < slice_end; y++) { - ptr16 = geq->dst16 + (linesize/2) * y; values[VAR_Y] = y; for (x = 0; x < width; x++) { values[VAR_X] = x; - ptr16[x] = av_expr_eval(geq->e[plane], values, geq); + ptr16[x] = av_expr_eval(geq->e[plane][jobnr], values, geq); } + ptr16 += linesize/2; } } @@ -293,7 +417,7 @@ static int geq_filter_frame(AVFilterLink *inlink, AVFrame *in) { int plane; AVFilterContext *ctx = inlink->dst; - const int nb_threads = ff_filter_get_nb_threads(ctx); + const int nb_threads = FFMIN(MAX_NB_THREADS, ff_filter_get_nb_threads(ctx)); GEQContext *geq = ctx->priv; AVFilterLink *outlink = inlink->dst->outputs[0]; AVFrame *out; @@ -328,6 +452,9 @@ static int geq_filter_frame(AVFilterLink *inlink, AVFrame *in) td.plane = plane; td.linesize = linesize; + if (geq->needs_sum[plane]) + calculate_sums(geq, plane, width, height); + ctx->internal->execute(ctx, slice_geq_filter, &td, NULL, FFMIN(height, nb_threads)); } @@ -340,8 +467,11 @@ static av_cold void geq_uninit(AVFilterContext *ctx) int i; GEQContext *geq = ctx->priv; - for (i = 0; i < FF_ARRAY_ELEMS(geq->e); i++) - av_expr_free(geq->e[i]); + for (i = 0; i < NB_PLANES; i++) + for (int j = 0; j < MAX_NB_THREADS; j++) + av_expr_free(geq->e[i][j]); + for (i = 0; i < NB_PLANES; i++) + av_freep(&geq->pixel_sums); } static const AVFilterPad geq_inputs[] = { diff --git a/libavfilter/vf_histogram.c b/libavfilter/vf_histogram.c index 5185992de60..db1962edc19 100644 --- a/libavfilter/vf_histogram.c +++ b/libavfilter/vf_histogram.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013 Paul B Mahol + * Copyright (c) 2012-2019 Paul B Mahol * * This file is part of FFmpeg. * @@ -19,6 +19,7 @@ */ #include "libavutil/avassert.h" +#include "libavutil/colorspace.h" #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" @@ -31,13 +32,19 @@ typedef struct HistogramContext { const AVClass *class; ///< AVClass context for log and options purpose + int thistogram; + int envelope; unsigned histogram[256*256]; int histogram_size; + int width; + int x_pos; int mult; int ncomp; int dncomp; uint8_t bg_color[4]; uint8_t fg_color[4]; + uint8_t envelope_rgba[4]; + uint8_t envelope_color[4]; int level_height; int scale_height; int display_mode; @@ -48,25 +55,30 @@ typedef struct HistogramContext { float bgopacity; int planewidth[4]; int planeheight[4]; + int start[4]; + AVFrame *out; } HistogramContext; #define OFFSET(x) offsetof(HistogramContext, x) #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" }, \ + { "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}, + static const AVOption histogram_options[] = { { "level_height", "set level height", OFFSET(level_height), AV_OPT_TYPE_INT, {.i64=200}, 50, 2048, FLAGS}, { "scale_height", "set scale height", OFFSET(scale_height), AV_OPT_TYPE_INT, {.i64=12}, 0, 40, FLAGS}, - { "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" }, - { "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}, + COMMON_OPTIONS { "fgopacity", "set foreground opacity", OFFSET(fgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.7}, 0, 1, FLAGS}, { "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}, @@ -87,6 +99,7 @@ static const enum AVPixelFormat levels_in_pix_fmts[] = { 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_YUV440P12, + AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, @@ -110,7 +123,7 @@ static const enum AVPixelFormat levels_out_yuv10_pix_fmts[] = { }; static const enum AVPixelFormat levels_out_yuv12_pix_fmts[] = { - AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_NONE }; @@ -192,12 +205,12 @@ static const uint8_t white_gbrp_color[4] = { 255, 255, 255, 255 }; static int config_input(AVFilterLink *inlink) { - HistogramContext *h = inlink->dst->priv; + HistogramContext *s = inlink->dst->priv; - h->desc = av_pix_fmt_desc_get(inlink->format); - h->ncomp = h->desc->nb_components; - h->histogram_size = 1 << h->desc->comp[0].depth; - h->mult = h->histogram_size / 256; + s->desc = av_pix_fmt_desc_get(inlink->format); + s->ncomp = s->desc->nb_components; + s->histogram_size = 1 << s->desc->comp[0].depth; + s->mult = s->histogram_size / 256; switch (inlink->format) { case AV_PIX_FMT_GBRAP12: @@ -207,21 +220,29 @@ static int config_input(AVFilterLink *inlink) case AV_PIX_FMT_GBRP9: case AV_PIX_FMT_GBRAP: case AV_PIX_FMT_GBRP: - memcpy(h->bg_color, black_gbrp_color, 4); - memcpy(h->fg_color, white_gbrp_color, 4); + memcpy(s->bg_color, black_gbrp_color, 4); + memcpy(s->fg_color, white_gbrp_color, 4); + s->start[0] = s->start[1] = s->start[2] = s->start[3] = 0; + memcpy(s->envelope_color, s->envelope_rgba, 4); break; default: - memcpy(h->bg_color, black_yuva_color, 4); - memcpy(h->fg_color, white_yuva_color, 4); + memcpy(s->bg_color, black_yuva_color, 4); + memcpy(s->fg_color, white_yuva_color, 4); + s->start[0] = s->start[3] = 0; + s->start[1] = s->start[2] = s->histogram_size / 2; + s->envelope_color[0] = RGB_TO_Y_BT709(s->envelope_rgba[0], s->envelope_rgba[1], s->envelope_rgba[2]); + s->envelope_color[1] = RGB_TO_U_BT709(s->envelope_rgba[0], s->envelope_rgba[1], s->envelope_rgba[2], 0); + s->envelope_color[2] = RGB_TO_V_BT709(s->envelope_rgba[0], s->envelope_rgba[1], s->envelope_rgba[2], 0); + s->envelope_color[3] = s->envelope_rgba[3]; } - h->fg_color[3] = h->fgopacity * 255; - h->bg_color[3] = h->bgopacity * 255; + s->fg_color[3] = s->fgopacity * 255; + s->bg_color[3] = s->bgopacity * 255; - h->planeheight[1] = h->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, h->desc->log2_chroma_h); - h->planeheight[0] = h->planeheight[3] = inlink->h; - h->planewidth[1] = h->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, h->desc->log2_chroma_w); - h->planewidth[0] = h->planewidth[3] = inlink->w; + s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h); + s->planeheight[0] = s->planeheight[3] = inlink->h; + s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, s->desc->log2_chroma_w); + s->planewidth[0] = s->planewidth[3] = inlink->w; return 0; } @@ -229,18 +250,29 @@ static int config_input(AVFilterLink *inlink) static int config_output(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; - HistogramContext *h = ctx->priv; + HistogramContext *s = ctx->priv; int ncomp = 0, i; - for (i = 0; i < h->ncomp; i++) { - if ((1 << i) & h->components) + if (!strcmp(ctx->filter->name, "thistogram")) + s->thistogram = 1; + + for (i = 0; i < s->ncomp; i++) { + if ((1 << i) & s->components) ncomp++; } - outlink->w = h->histogram_size * FFMAX(ncomp * (h->display_mode == 1), 1); - outlink->h = (h->level_height + h->scale_height) * FFMAX(ncomp * (h->display_mode == 2), 1); - h->odesc = av_pix_fmt_desc_get(outlink->format); - h->dncomp = h->odesc->nb_components; + if (s->thistogram) { + if (!s->width) + s->width = ctx->inputs[0]->w; + outlink->w = s->width * FFMAX(ncomp * (s->display_mode == 1), 1); + outlink->h = s->histogram_size * FFMAX(ncomp * (s->display_mode == 2), 1); + } else { + outlink->w = s->histogram_size * FFMAX(ncomp * (s->display_mode == 1), 1); + outlink->h = (s->level_height + s->scale_height) * FFMAX(ncomp * (s->display_mode == 2), 1); + } + + s->odesc = av_pix_fmt_desc_get(outlink->format); + s->dncomp = s->odesc->nb_components; outlink->sample_aspect_ratio = (AVRational){1,1}; return 0; @@ -248,111 +280,179 @@ static int config_output(AVFilterLink *outlink) static int filter_frame(AVFilterLink *inlink, AVFrame *in) { - HistogramContext *h = inlink->dst->priv; + HistogramContext *s = inlink->dst->priv; AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; - AVFrame *out; + AVFrame *out = s->out; int i, j, k, l, m; - out = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!out) { - av_frame_free(&in); - return AVERROR(ENOMEM); - } - - out->pts = in->pts; - - for (k = 0; k < 4 && out->data[k]; k++) { - const int is_chroma = (k == 1 || k == 2); - const int dst_h = AV_CEIL_RSHIFT(outlink->h, (is_chroma ? h->odesc->log2_chroma_h : 0)); - const int dst_w = AV_CEIL_RSHIFT(outlink->w, (is_chroma ? h->odesc->log2_chroma_w : 0)); - - if (h->histogram_size <= 256) { - for (i = 0; i < dst_h ; i++) - memset(out->data[h->odesc->comp[k].plane] + - i * out->linesize[h->odesc->comp[k].plane], - h->bg_color[k], dst_w); - } else { - const int mult = h->mult; + if (!s->thistogram || !out) { + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + s->out = out; + + for (k = 0; k < 4 && out->data[k]; k++) { + const int is_chroma = (k == 1 || k == 2); + const int dst_h = AV_CEIL_RSHIFT(outlink->h, (is_chroma ? s->odesc->log2_chroma_h : 0)); + const int dst_w = AV_CEIL_RSHIFT(outlink->w, (is_chroma ? s->odesc->log2_chroma_w : 0)); + + if (s->histogram_size <= 256) { + for (i = 0; i < dst_h ; i++) + memset(out->data[s->odesc->comp[k].plane] + + i * out->linesize[s->odesc->comp[k].plane], + s->bg_color[k], dst_w); + } else { + const int mult = s->mult; - for (i = 0; i < dst_h ; i++) - for (j = 0; j < dst_w; j++) - AV_WN16(out->data[h->odesc->comp[k].plane] + - i * out->linesize[h->odesc->comp[k].plane] + j * 2, - h->bg_color[k] * mult); + for (i = 0; i < dst_h ; i++) + for (j = 0; j < dst_w; j++) + AV_WN16(out->data[s->odesc->comp[k].plane] + + i * out->linesize[s->odesc->comp[k].plane] + j * 2, + s->bg_color[k] * mult); + } } } - for (m = 0, k = 0; k < h->ncomp; k++) { - const int p = h->desc->comp[k].plane; - const int height = h->planeheight[p]; - const int width = h->planewidth[p]; + for (m = 0, k = 0; k < s->ncomp; k++) { + const int p = s->desc->comp[k].plane; + const int max_value = s->histogram_size - 1 - s->start[p]; + const int height = s->planeheight[p]; + const int width = s->planewidth[p]; double max_hval_log; unsigned max_hval = 0; - int start, startx; + int starty, startx; - if (!((1 << k) & h->components)) + if (!((1 << k) & s->components)) continue; - startx = m * h->histogram_size * (h->display_mode == 1); - start = m++ * (h->level_height + h->scale_height) * (h->display_mode == 2); + if (s->thistogram) { + starty = m * s->histogram_size * (s->display_mode == 2); + startx = m++ * s->width * (s->display_mode == 1); + } else { + startx = m * s->histogram_size * (s->display_mode == 1); + starty = m++ * (s->level_height + s->scale_height) * (s->display_mode == 2); + } - if (h->histogram_size <= 256) { + if (s->histogram_size <= 256) { for (i = 0; i < height; i++) { const uint8_t *src = in->data[p] + i * in->linesize[p]; for (j = 0; j < width; j++) - h->histogram[src[j]]++; + s->histogram[src[j]]++; } } else { for (i = 0; i < height; i++) { const uint16_t *src = (const uint16_t *)(in->data[p] + i * in->linesize[p]); for (j = 0; j < width; j++) - h->histogram[src[j]]++; + s->histogram[src[j]]++; } } - for (i = 0; i < h->histogram_size; i++) - max_hval = FFMAX(max_hval, h->histogram[i]); + for (i = 0; i < s->histogram_size; i++) + max_hval = FFMAX(max_hval, s->histogram[i]); max_hval_log = log2(max_hval + 1); - for (i = 0; i < h->histogram_size; i++) { - int col_height; - - if (h->levels_mode) - col_height = lrint(h->level_height * (1. - (log2(h->histogram[i] + 1) / max_hval_log))); - else - col_height = h->level_height - (h->histogram[i] * (int64_t)h->level_height + max_hval - 1) / max_hval; - - if (h->histogram_size <= 256) { - for (j = h->level_height - 1; j >= col_height; j--) { - if (h->display_mode) { - for (l = 0; l < h->dncomp; l++) - out->data[l][(j + start) * out->linesize[l] + startx + i] = h->fg_color[l]; - } else { - out->data[p][(j + start) * out->linesize[p] + startx + i] = 255; + if (s->thistogram) { + int minh = s->histogram_size - 1, maxh = 0; + + for (int i = 0; i < s->histogram_size; i++) { + int idx = s->histogram_size - i - 1; + int value = s->start[p]; + + if (s->envelope && s->histogram[idx]) { + minh = FFMIN(minh, i); + maxh = FFMAX(maxh, i); + } + + if (s->levels_mode) + value += lrint(max_value * (log2(s->histogram[idx] + 1) / max_hval_log)); + else + value += lrint(max_value * s->histogram[idx] / (float)max_hval); + + if (s->histogram_size <= 256) { + s->out->data[p][(i + starty) * s->out->linesize[p] + startx + s->x_pos] = value; + } else { + AV_WN16(s->out->data[p] + (i + starty) * s->out->linesize[p] + startx * 2 + s->x_pos * 2, value); + } + } + + if (s->envelope) { + if (s->histogram_size <= 256) { + s->out->data[0][(minh + starty) * s->out->linesize[p] + startx + s->x_pos] = s->envelope_color[0]; + s->out->data[0][(maxh + starty) * s->out->linesize[p] + startx + s->x_pos] = s->envelope_color[0]; + if (s->dncomp >= 3) { + s->out->data[1][(minh + starty) * s->out->linesize[p] + startx + s->x_pos] = s->envelope_color[1]; + s->out->data[2][(minh + starty) * s->out->linesize[p] + startx + s->x_pos] = s->envelope_color[2]; + s->out->data[1][(maxh + starty) * s->out->linesize[p] + startx + s->x_pos] = s->envelope_color[1]; + s->out->data[2][(maxh + starty) * s->out->linesize[p] + startx + s->x_pos] = s->envelope_color[2]; + } + } else { + const int mult = s->mult; + + AV_WN16(s->out->data[0] + (minh + starty) * s->out->linesize[p] + startx * 2 + s->x_pos * 2, s->envelope_color[0] * mult); + AV_WN16(s->out->data[0] + (maxh + starty) * s->out->linesize[p] + startx * 2 + s->x_pos * 2, s->envelope_color[0] * mult); + if (s->dncomp >= 3) { + AV_WN16(s->out->data[1] + (minh + starty) * s->out->linesize[p] + startx * 2 + s->x_pos * 2, s->envelope_color[1] * mult); + AV_WN16(s->out->data[2] + (minh + starty) * s->out->linesize[p] + startx * 2 + s->x_pos * 2, s->envelope_color[2] * mult); + AV_WN16(s->out->data[1] + (maxh + starty) * s->out->linesize[p] + startx * 2 + s->x_pos * 2, s->envelope_color[1] * mult); + AV_WN16(s->out->data[2] + (maxh + starty) * s->out->linesize[p] + startx * 2 + s->x_pos * 2, s->envelope_color[2] * mult); } } - for (j = h->level_height + h->scale_height - 1; j >= h->level_height; j--) - out->data[p][(j + start) * out->linesize[p] + startx + i] = i; - } else { - const int mult = h->mult; - - for (j = h->level_height - 1; j >= col_height; j--) { - if (h->display_mode) { - for (l = 0; l < h->dncomp; l++) - AV_WN16(out->data[l] + (j + start) * out->linesize[l] + startx * 2 + i * 2, h->fg_color[l] * mult); - } else { - AV_WN16(out->data[p] + (j + start) * out->linesize[p] + startx * 2 + i * 2, 255 * mult); + } + } else { + for (i = 0; i < s->histogram_size; i++) { + int col_height; + + if (s->levels_mode) + col_height = lrint(s->level_height * (1. - (log2(s->histogram[i] + 1) / max_hval_log))); + else + col_height = s->level_height - (s->histogram[i] * (int64_t)s->level_height + max_hval - 1) / max_hval; + + if (s->histogram_size <= 256) { + for (j = s->level_height - 1; j >= col_height; j--) { + if (s->display_mode) { + for (l = 0; l < s->dncomp; l++) + out->data[l][(j + starty) * out->linesize[l] + startx + i] = s->fg_color[l]; + } else { + out->data[p][(j + starty) * out->linesize[p] + startx + i] = 255; + } + } + for (j = s->level_height + s->scale_height - 1; j >= s->level_height; j--) + out->data[p][(j + starty) * out->linesize[p] + startx + i] = i; + } else { + const int mult = s->mult; + + for (j = s->level_height - 1; j >= col_height; j--) { + if (s->display_mode) { + for (l = 0; l < s->dncomp; l++) + AV_WN16(out->data[l] + (j + starty) * out->linesize[l] + startx * 2 + i * 2, s->fg_color[l] * mult); + } else { + AV_WN16(out->data[p] + (j + starty) * out->linesize[p] + startx * 2 + i * 2, 255 * mult); + } } + for (j = s->level_height + s->scale_height - 1; j >= s->level_height; j--) + AV_WN16(out->data[p] + (j + starty) * out->linesize[p] + startx * 2 + i * 2, i); } - for (j = h->level_height + h->scale_height - 1; j >= h->level_height; j--) - AV_WN16(out->data[p] + (j + start) * out->linesize[p] + startx * 2 + i * 2, i); } } - memset(h->histogram, 0, h->histogram_size * sizeof(unsigned)); + memset(s->histogram, 0, s->histogram_size * sizeof(unsigned)); } + out->pts = in->pts; av_frame_free(&in); + s->x_pos++; + if (s->x_pos >= s->width) + s->x_pos = 0; + + if (s->thistogram) { + AVFrame *clone = av_frame_clone(out); + + if (!clone) + return AVERROR(ENOMEM); + return ff_filter_frame(outlink, clone); + } return ff_filter_frame(outlink, out); } @@ -375,6 +475,8 @@ static const AVFilterPad outputs[] = { { NULL } }; +#if CONFIG_HISTOGRAM_FILTER + AVFilter ff_vf_histogram = { .name = "histogram", .description = NULL_IF_CONFIG_SMALL("Compute and draw a histogram."), @@ -384,3 +486,34 @@ AVFilter ff_vf_histogram = { .outputs = outputs, .priv_class = &histogram_class, }; + +#endif /* CONFIG_HISTOGRAM_FILTER */ + +#if CONFIG_THISTOGRAM_FILTER + +static const AVOption thistogram_options[] = { + { "width", "set width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, 8192, FLAGS}, + { "w", "set width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, 8192, FLAGS}, + COMMON_OPTIONS + { "bgopacity", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.9}, 0, 1, FLAGS}, + { "b", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.9}, 0, 1, FLAGS}, + { "envelope", "display envelope", OFFSET(envelope), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, + { "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 }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(thistogram); + +AVFilter ff_vf_thistogram = { + .name = "thistogram", + .description = NULL_IF_CONFIG_SMALL("Compute and draw a temporal histogram."), + .priv_size = sizeof(HistogramContext), + .query_formats = query_formats, + .inputs = inputs, + .outputs = outputs, + .priv_class = &thistogram_class, +}; + +#endif /* CONFIG_THISTOGRAM_FILTER */ diff --git a/libavfilter/vf_hqdn3d.c b/libavfilter/vf_hqdn3d.c index d6c14bb3d88..803f90c28c3 100644 --- a/libavfilter/vf_hqdn3d.c +++ b/libavfilter/vf_hqdn3d.c @@ -160,6 +160,8 @@ static int denoise_depth(HQDN3DContext *s, case 8: ret = denoise_depth(__VA_ARGS__, 8); break; \ case 9: ret = denoise_depth(__VA_ARGS__, 9); break; \ case 10: ret = denoise_depth(__VA_ARGS__, 10); break; \ + case 12: ret = denoise_depth(__VA_ARGS__, 12); break; \ + case 14: ret = denoise_depth(__VA_ARGS__, 14); break; \ case 16: ret = denoise_depth(__VA_ARGS__, 16); break; \ } \ if (ret < 0) { \ @@ -170,13 +172,10 @@ static int denoise_depth(HQDN3DContext *s, } \ } while (0) -static int16_t *precalc_coefs(double dist25, int depth) +static void precalc_coefs(double dist25, int depth, int16_t *ct) { int i; double gamma, simil, C; - int16_t *ct = av_malloc((512<coefs[1]); av_freep(&s->coefs[2]); av_freep(&s->coefs[3]); - av_freep(&s->line); + av_freep(&s->line[0]); + av_freep(&s->line[1]); + av_freep(&s->line[2]); av_freep(&s->frame_prev[0]); av_freep(&s->frame_prev[1]); av_freep(&s->frame_prev[2]); @@ -232,25 +232,16 @@ static av_cold void uninit(AVFilterContext *ctx) static int query_formats(AVFilterContext *ctx) { 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, - AV_PIX_FMT_YUVJ420P, - AV_PIX_FMT_YUVJ422P, - AV_PIX_FMT_YUVJ444P, - AV_PIX_FMT_YUVJ440P, - AV_PIX_FMT_YUV420P9, - AV_PIX_FMT_YUV422P9, - AV_PIX_FMT_YUV444P9, - AV_PIX_FMT_YUV420P10, - AV_PIX_FMT_YUV422P10, - AV_PIX_FMT_YUV444P10, - AV_PIX_FMT_YUV420P16, - AV_PIX_FMT_YUV422P16, - AV_PIX_FMT_YUV444P16, + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, + AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, + AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, + AV_PIX_FMT_YUV440P10, + 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_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, AV_PIX_FMT_NONE }; AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); @@ -259,42 +250,79 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_formats(ctx, fmts_list); } +static void calc_coefs(AVFilterContext *ctx) +{ + HQDN3DContext *s = ctx->priv; + + for (int i = 0; i < 4; i++) + precalc_coefs(s->strength[i], s->depth, s->coefs[i]); +} + static int config_input(AVFilterLink *inlink) { + AVFilterContext *ctx = inlink->dst; HQDN3DContext *s = inlink->dst->priv; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); - int i; + int i, depth; uninit(inlink->dst); s->hsub = desc->log2_chroma_w; s->vsub = desc->log2_chroma_h; - s->depth = desc->comp[0].depth; + s->depth = depth = desc->comp[0].depth; - s->line = av_malloc_array(inlink->w, sizeof(*s->line)); - if (!s->line) - return AVERROR(ENOMEM); + for (i = 0; i < 3; i++) { + s->line[i] = av_malloc_array(inlink->w, sizeof(*s->line[i])); + if (!s->line[i]) + return AVERROR(ENOMEM); + } for (i = 0; i < 4; i++) { - s->coefs[i] = precalc_coefs(s->strength[i], s->depth); + s->coefs[i] = av_malloc((512<coefs[i]) return AVERROR(ENOMEM); } + calc_coefs(ctx); + if (ARCH_X86) ff_hqdn3d_init_x86(s); return 0; } +typedef struct ThreadData { + AVFrame *in, *out; + int direct; +} ThreadData; + +static int do_denoise(AVFilterContext *ctx, void *data, int job_nr, int n_jobs) +{ + HQDN3DContext *s = ctx->priv; + const ThreadData *td = data; + AVFrame *out = td->out; + AVFrame *in = td->in; + int direct = td->direct; + + denoise(s, in->data[job_nr], out->data[job_nr], + s->line[job_nr], &s->frame_prev[job_nr], + AV_CEIL_RSHIFT(in->width, (!!job_nr * s->hsub)), + AV_CEIL_RSHIFT(in->height, (!!job_nr * s->vsub)), + in->linesize[job_nr], out->linesize[job_nr], + s->coefs[job_nr ? CHROMA_SPATIAL : LUMA_SPATIAL], + s->coefs[job_nr ? CHROMA_TMP : LUMA_TMP]); + + return 0; +} + static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; - HQDN3DContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; AVFrame *out; - int c, direct = av_frame_is_writable(in) && !ctx->is_disabled; + int direct = av_frame_is_writable(in) && !ctx->is_disabled; + ThreadData td; if (direct) { out = in; @@ -308,15 +336,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) av_frame_copy_props(out, in); } - for (c = 0; c < 3; c++) { - denoise(s, in->data[c], out->data[c], - s->line, &s->frame_prev[c], - AV_CEIL_RSHIFT(in->width, (!!c * s->hsub)), - AV_CEIL_RSHIFT(in->height, (!!c * s->vsub)), - in->linesize[c], out->linesize[c], - s->coefs[c ? CHROMA_SPATIAL : LUMA_SPATIAL], - s->coefs[c ? CHROMA_TMP : LUMA_TMP]); - } + td.in = in; + td.out = out; + td.direct = direct; + /* one thread per plane */ + ctx->internal->execute(ctx, do_denoise, &td, NULL, 3); if (ctx->is_disabled) { av_frame_free(&out); @@ -329,8 +353,22 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) return ff_filter_frame(outlink, out); } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + calc_coefs(ctx); + + return 0; +} + #define OFFSET(x) offsetof(HQDN3DContext, x) -#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM static const AVOption hqdn3d_options[] = { { "luma_spatial", "spatial luma strength", OFFSET(strength[LUMA_SPATIAL]), AV_OPT_TYPE_DOUBLE, { .dbl = 0.0 }, 0, DBL_MAX, FLAGS }, { "chroma_spatial", "spatial chroma strength", OFFSET(strength[CHROMA_SPATIAL]), AV_OPT_TYPE_DOUBLE, { .dbl = 0.0 }, 0, DBL_MAX, FLAGS }, @@ -370,5 +408,6 @@ AVFilter ff_vf_hqdn3d = { .query_formats = query_formats, .inputs = avfilter_vf_hqdn3d_inputs, .outputs = avfilter_vf_hqdn3d_outputs, - .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, + .process_command = process_command, }; diff --git a/libavfilter/vf_hqdn3d.h b/libavfilter/vf_hqdn3d.h index 03a79a108c5..3279bbcc772 100644 --- a/libavfilter/vf_hqdn3d.h +++ b/libavfilter/vf_hqdn3d.h @@ -31,7 +31,7 @@ typedef struct HQDN3DContext { const AVClass *class; int16_t *coefs[4]; - uint16_t *line; + uint16_t *line[3]; uint16_t *frame_prev[3]; double strength[4]; int hsub, vsub; diff --git a/libavfilter/vf_hqx.c b/libavfilter/vf_hqx.c index 16a1be7bd4d..4f768c7a130 100644 --- a/libavfilter/vf_hqx.c +++ b/libavfilter/vf_hqx.c @@ -523,7 +523,7 @@ static av_cold int init(AVFilterContext *ctx) int startg = FFMAX3(-bg, -rg, 0); int endg = FFMIN3(255-bg, 255-rg, 255); uint32_t y = (uint32_t)(( 299*rg + 1000*startg + 114*bg)/1000); - c = bg + (rg<<16) + 0x010101 * startg; + c = bg + rg * (1 << 16) + 0x010101 * startg; for (g = startg; g <= endg; g++) { hqx->rgbtoyuv[c] = ((y++) << 16) + (u << 8) + v; c+= 0x010101; diff --git a/libavfilter/vf_hue.c b/libavfilter/vf_hue.c index 323333b33cf..b75ef210587 100644 --- a/libavfilter/vf_hue.c +++ b/libavfilter/vf_hue.c @@ -86,7 +86,7 @@ typedef struct HueContext { } HueContext; #define OFFSET(x) offsetof(HueContext, x) -#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption hue_options[] = { { "h", "set the hue angle degrees expression", OFFSET(hue_deg_expr), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, @@ -136,7 +136,7 @@ static inline void create_chrominance_lut(HueContext *h, const int32_t c, */ for (i = 0; i < 256; i++) { for (j = 0; j < 256; j++) { - /* Normalize the components from range [16;140] to [-112;112] */ + /* Normalize the components from range [16;240] to [-112;112] */ u = i - 128; v = j - 128; /* diff --git a/libavfilter/vf_hwupload.c b/libavfilter/vf_hwupload.c index 50bc7e10f6e..7c5dd497b0e 100644 --- a/libavfilter/vf_hwupload.c +++ b/libavfilter/vf_hwupload.c @@ -32,10 +32,11 @@ typedef struct HWUploadContext { const AVClass *class; AVBufferRef *hwdevice_ref; - AVHWDeviceContext *hwdevice; AVBufferRef *hwframes_ref; AVHWFramesContext *hwframes; + + char *device_type; } HWUploadContext; static int hwupload_query_formats(AVFilterContext *avctx) @@ -46,17 +47,27 @@ static int hwupload_query_formats(AVFilterContext *avctx) AVFilterFormats *input_formats = NULL; int err, i; - if (!avctx->hw_device_ctx) { + if (ctx->hwdevice_ref) { + /* We already have a specified device. */ + } else if (avctx->hw_device_ctx) { + if (ctx->device_type) { + err = av_hwdevice_ctx_create_derived( + &ctx->hwdevice_ref, + av_hwdevice_find_type_by_name(ctx->device_type), + avctx->hw_device_ctx, 0); + if (err < 0) + return err; + } else { + ctx->hwdevice_ref = av_buffer_ref(avctx->hw_device_ctx); + if (!ctx->hwdevice_ref) + return AVERROR(ENOMEM); + } + } else { av_log(ctx, AV_LOG_ERROR, "A hardware device reference is required " "to upload frames to.\n"); return AVERROR(EINVAL); } - ctx->hwdevice_ref = av_buffer_ref(avctx->hw_device_ctx); - if (!ctx->hwdevice_ref) - return AVERROR(ENOMEM); - ctx->hwdevice = (AVHWDeviceContext*)ctx->hwdevice_ref->data; - constraints = av_hwdevice_get_hwframe_constraints(ctx->hwdevice_ref, NULL); if (!constraints) { err = AVERROR(EINVAL); @@ -127,7 +138,13 @@ static int hwupload_config_output(AVFilterLink *outlink) av_get_pix_fmt_name(inlink->format)); ctx->hwframes->format = outlink->format; - ctx->hwframes->sw_format = inlink->format; + if (inlink->hw_frames_ctx) { + AVHWFramesContext *in_hwframe_ctx = + (AVHWFramesContext*)inlink->hw_frames_ctx->data; + ctx->hwframes->sw_format = in_hwframe_ctx->sw_format; + } else { + ctx->hwframes->sw_format = inlink->format; + } ctx->hwframes->width = inlink->w; ctx->hwframes->height = inlink->h; @@ -200,13 +217,21 @@ static av_cold void hwupload_uninit(AVFilterContext *avctx) av_buffer_unref(&ctx->hwdevice_ref); } -static const AVClass hwupload_class = { - .class_name = "hwupload", - .item_name = av_default_item_name, - .option = NULL, - .version = LIBAVUTIL_VERSION_INT, +#define OFFSET(x) offsetof(HWUploadContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM) +static const AVOption hwupload_options[] = { + { + "derive_device", "Derive a new device of this type", + OFFSET(device_type), AV_OPT_TYPE_STRING, + { .str = NULL }, 0, 0, FLAGS + }, + { + NULL + } }; +AVFILTER_DEFINE_CLASS(hwupload); + static const AVFilterPad hwupload_inputs[] = { { .name = "default", diff --git a/libavfilter/vf_hwupload_cuda.c b/libavfilter/vf_hwupload_cuda.c index 4d83e6c8f27..8ee0825859c 100644 --- a/libavfilter/vf_hwupload_cuda.c +++ b/libavfilter/vf_hwupload_cuda.c @@ -60,6 +60,9 @@ static int cudaupload_query_formats(AVFilterContext *ctx) AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_P010, AV_PIX_FMT_P016, AV_PIX_FMT_YUV444P16, AV_PIX_FMT_0RGB32, AV_PIX_FMT_0BGR32, +#if CONFIG_VULKAN + AV_PIX_FMT_VULKAN, +#endif AV_PIX_FMT_NONE, }; static const enum AVPixelFormat output_pix_fmts[] = { @@ -97,7 +100,12 @@ static int cudaupload_config_output(AVFilterLink *outlink) hwframe_ctx = (AVHWFramesContext*)s->hwframe->data; hwframe_ctx->format = AV_PIX_FMT_CUDA; - hwframe_ctx->sw_format = inlink->format; + if (inlink->hw_frames_ctx) { + AVHWFramesContext *in_hwframe_ctx = (AVHWFramesContext*)inlink->hw_frames_ctx->data; + hwframe_ctx->sw_format = in_hwframe_ctx->sw_format; + } else { + hwframe_ctx->sw_format = inlink->format; + } hwframe_ctx->width = inlink->w; hwframe_ctx->height = inlink->h; diff --git a/libavfilter/vf_il.c b/libavfilter/vf_il.c index ae0cc1938a3..6cd5f89f767 100644 --- a/libavfilter/vf_il.c +++ b/libavfilter/vf_il.c @@ -46,7 +46,7 @@ typedef struct IlContext { } IlContext; #define OFFSET(x) offsetof(IlContext, 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 il_options[] = { {"luma_mode", "select luma mode", OFFSET(luma_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, "luma_mode"}, @@ -210,4 +210,5 @@ AVFilter ff_vf_il = { .outputs = outputs, .priv_class = &il_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vf_interlace.c b/libavfilter/vf_interlace.c deleted file mode 100644 index 24c422ded26..00000000000 --- a/libavfilter/vf_interlace.c +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright (c) 2003 Michael Zucchi - * Copyright (c) 2010 Baptiste Coudurier - * Copyright (c) 2011 Stefano Sabatini - * Copyright (c) 2013 Vittorio Giovara - * Copyright (c) 2017 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 General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with FFmpeg; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/** - * @file - * progressive to interlaced content filter, inspired by heavy debugging of tinterlace filter - */ - -#include "libavutil/common.h" -#include "libavutil/opt.h" -#include "libavutil/imgutils.h" -#include "libavutil/avassert.h" - -#include "formats.h" -#include "avfilter.h" -#include "interlace.h" -#include "internal.h" -#include "video.h" - -#define OFFSET(x) offsetof(InterlaceContext, x) -#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM -static const AVOption interlace_options[] = { - { "scan", "scanning mode", OFFSET(scan), - AV_OPT_TYPE_INT, {.i64 = MODE_TFF }, 0, 1, .flags = FLAGS, .unit = "scan" }, - { "tff", "top field first", 0, - AV_OPT_TYPE_CONST, {.i64 = MODE_TFF }, INT_MIN, INT_MAX, .flags = FLAGS, .unit = "scan" }, - { "bff", "bottom field first", 0, - AV_OPT_TYPE_CONST, {.i64 = MODE_BFF }, INT_MIN, INT_MAX, .flags = FLAGS, .unit = "scan" }, - { "lowpass", "set vertical low-pass filter", OFFSET(lowpass), - AV_OPT_TYPE_INT, {.i64 = VLPF_LIN }, 0, 2, .flags = FLAGS, .unit = "lowpass" }, - { "off", "disable vertical low-pass filter", 0, - AV_OPT_TYPE_CONST, {.i64 = VLPF_OFF }, INT_MIN, INT_MAX, .flags = FLAGS, .unit = "lowpass" }, - { "linear", "linear vertical low-pass filter", 0, - AV_OPT_TYPE_CONST, {.i64 = VLPF_LIN }, INT_MIN, INT_MAX, .flags = FLAGS, .unit = "lowpass" }, - { "complex", "complex vertical low-pass filter", 0, - AV_OPT_TYPE_CONST, {.i64 = VLPF_CMP }, INT_MIN, INT_MAX, .flags = FLAGS, .unit = "lowpass" }, - { NULL } -}; - -AVFILTER_DEFINE_CLASS(interlace); - -static void lowpass_line_c(uint8_t *dstp, ptrdiff_t linesize, - const uint8_t *srcp, ptrdiff_t mref, - ptrdiff_t pref, int clip_max) -{ - const uint8_t *srcp_above = srcp + mref; - const uint8_t *srcp_below = srcp + pref; - int i; - for (i = 0; i < linesize; i++) { - // this calculation is an integer representation of - // '0.5 * current + 0.25 * above + 0.25 * below' - // '1 +' is for rounding. - dstp[i] = (1 + srcp[i] + srcp[i] + srcp_above[i] + srcp_below[i]) >> 2; - } -} - -static void lowpass_line_c_16(uint8_t *dst8, ptrdiff_t linesize, - const uint8_t *src8, ptrdiff_t mref, - ptrdiff_t pref, int clip_max) -{ - uint16_t *dstp = (uint16_t *)dst8; - const uint16_t *srcp = (const uint16_t *)src8; - const uint16_t *srcp_above = srcp + mref / 2; - const uint16_t *srcp_below = srcp + pref / 2; - int i, src_x; - for (i = 0; i < linesize; i++) { - // this calculation is an integer representation of - // '0.5 * current + 0.25 * above + 0.25 * below' - // '1 +' is for rounding. - src_x = av_le2ne16(srcp[i]) << 1; - dstp[i] = av_le2ne16((1 + src_x + av_le2ne16(srcp_above[i]) - + av_le2ne16(srcp_below[i])) >> 2); - } -} - -static void lowpass_line_complex_c(uint8_t *dstp, ptrdiff_t linesize, - const uint8_t *srcp, ptrdiff_t mref, - ptrdiff_t pref, int clip_max) -{ - const uint8_t *srcp_above = srcp + mref; - const uint8_t *srcp_below = srcp + pref; - const uint8_t *srcp_above2 = srcp + mref * 2; - const uint8_t *srcp_below2 = srcp + pref * 2; - int i, src_x, src_ab; - for (i = 0; i < linesize; i++) { - // this calculation is an integer representation of - // '0.75 * current + 0.25 * above + 0.25 * below - 0.125 * above2 - 0.125 * below2' - // '4 +' is for rounding. - src_x = srcp[i] << 1; - src_ab = srcp_above[i] + srcp_below[i]; - dstp[i] = av_clip_uint8((4 + ((srcp[i] + src_x + src_ab) << 1) - - srcp_above2[i] - srcp_below2[i]) >> 3); - // Prevent over-sharpening: - // dst must not exceed src when the average of above and below - // is less than src. And the other way around. - if (src_ab > src_x) { - if (dstp[i] < srcp[i]) - dstp[i] = srcp[i]; - } else if (dstp[i] > srcp[i]) - dstp[i] = srcp[i]; - } -} - -static void lowpass_line_complex_c_16(uint8_t *dst8, ptrdiff_t linesize, - const uint8_t *src8, ptrdiff_t mref, - ptrdiff_t pref, int clip_max) -{ - uint16_t *dstp = (uint16_t *)dst8; - const uint16_t *srcp = (const uint16_t *)src8; - const uint16_t *srcp_above = srcp + mref / 2; - const uint16_t *srcp_below = srcp + pref / 2; - const uint16_t *srcp_above2 = srcp + mref; - const uint16_t *srcp_below2 = srcp + pref; - int i, dst_le, src_le, src_x, src_ab; - for (i = 0; i < linesize; i++) { - // this calculation is an integer representation of - // '0.75 * current + 0.25 * above + 0.25 * below - 0.125 * above2 - 0.125 * below2' - // '4 +' is for rounding. - src_le = av_le2ne16(srcp[i]); - src_x = src_le << 1; - src_ab = av_le2ne16(srcp_above[i]) + av_le2ne16(srcp_below[i]); - dst_le = av_clip((4 + ((src_le + src_x + src_ab) << 1) - - av_le2ne16(srcp_above2[i]) - - av_le2ne16(srcp_below2[i])) >> 3, 0, clip_max); - // Prevent over-sharpening: - // dst must not exceed src when the average of above and below - // is less than src. And the other way around. - if (src_ab > src_x) { - if (dst_le < src_le) - dstp[i] = av_le2ne16(src_le); - else - dstp[i] = av_le2ne16(dst_le); - } else if (dst_le > src_le) { - dstp[i] = av_le2ne16(src_le); - } else - dstp[i] = av_le2ne16(dst_le); - } -} - -static const enum AVPixelFormat formats_supported[] = { - AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, - AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, - AV_PIX_FMT_YUV420P10LE, AV_PIX_FMT_YUV422P10LE, AV_PIX_FMT_YUV444P10LE, - AV_PIX_FMT_YUV420P12LE, AV_PIX_FMT_YUV422P12LE, AV_PIX_FMT_YUV444P12LE, - AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, - AV_PIX_FMT_YUVA420P10LE, AV_PIX_FMT_YUVA422P10LE, AV_PIX_FMT_YUVA444P10LE, - AV_PIX_FMT_GRAY8, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, - AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_NONE -}; - -static int query_formats(AVFilterContext *ctx) -{ - AVFilterFormats *fmts_list = ff_make_format_list(formats_supported); - if (!fmts_list) - return AVERROR(ENOMEM); - return ff_set_common_formats(ctx, fmts_list); -} - -static av_cold void uninit(AVFilterContext *ctx) -{ - InterlaceContext *s = ctx->priv; - - av_frame_free(&s->cur); - av_frame_free(&s->next); -} - -void ff_interlace_init(InterlaceContext *s, int depth) -{ - if (s->lowpass) { - if (s->lowpass == VLPF_LIN) { - if (depth > 8) - s->lowpass_line = lowpass_line_c_16; - else - s->lowpass_line = lowpass_line_c; - } else if (s->lowpass == VLPF_CMP) { - if (depth > 8) - s->lowpass_line = lowpass_line_complex_c_16; - else - s->lowpass_line = lowpass_line_complex_c; - } - if (ARCH_X86) - ff_interlace_init_x86(s, depth); - } -} - -static int config_out_props(AVFilterLink *outlink) -{ - AVFilterContext *ctx = outlink->src; - AVFilterLink *inlink = outlink->src->inputs[0]; - InterlaceContext *s = ctx->priv; - - if (inlink->h < 2) { - av_log(ctx, AV_LOG_ERROR, "input video height is too small\n"); - return AVERROR_INVALIDDATA; - } - - if (!s->lowpass) - av_log(ctx, AV_LOG_WARNING, "Lowpass filter is disabled, " - "the resulting video will be aliased rather than interlaced.\n"); - - // same input size - outlink->w = inlink->w; - outlink->h = inlink->h; - outlink->time_base = inlink->time_base; - outlink->frame_rate = inlink->frame_rate; - // half framerate - outlink->time_base.num *= 2; - outlink->frame_rate.den *= 2; - - s->csp = av_pix_fmt_desc_get(outlink->format); - ff_interlace_init(s, s->csp->comp[0].depth); - - av_log(ctx, AV_LOG_VERBOSE, "%s interlacing %s lowpass filter\n", - s->scan == MODE_TFF ? "tff" : "bff", (s->lowpass) ? "with" : "without"); - - return 0; -} - -static void copy_picture_field(InterlaceContext *s, - AVFrame *src_frame, AVFrame *dst_frame, - AVFilterLink *inlink, enum FieldType field_type, - int lowpass) -{ - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); - int hsub = desc->log2_chroma_w; - int vsub = desc->log2_chroma_h; - int plane, j; - - for (plane = 0; plane < desc->nb_components; plane++) { - int cols = (plane == 1 || plane == 2) ? -(-inlink->w) >> hsub : inlink->w; - int lines = (plane == 1 || plane == 2) ? AV_CEIL_RSHIFT(inlink->h, vsub) : inlink->h; - uint8_t *dstp = dst_frame->data[plane]; - const uint8_t *srcp = src_frame->data[plane]; - int srcp_linesize = src_frame->linesize[plane] * 2; - int dstp_linesize = dst_frame->linesize[plane] * 2; - int clip_max = (1 << s->csp->comp[plane].depth) - 1; - - av_assert0(cols >= 0 || lines >= 0); - - lines = (lines + (field_type == FIELD_UPPER)) / 2; - if (field_type == FIELD_LOWER) { - srcp += src_frame->linesize[plane]; - dstp += dst_frame->linesize[plane]; - } - if (lowpass) { - int x = 0; - if (lowpass == VLPF_CMP) - x = 1; - for (j = lines; j > 0; j--) { - ptrdiff_t pref = src_frame->linesize[plane]; - ptrdiff_t mref = -pref; - if (j >= (lines - x)) - mref = 0; - else if (j <= (1 + x)) - pref = 0; - s->lowpass_line(dstp, cols, srcp, mref, pref, clip_max); - dstp += dstp_linesize; - srcp += srcp_linesize; - } - } else { - if (s->csp->comp[plane].depth > 8) - cols *= 2; - av_image_copy_plane(dstp, dstp_linesize, srcp, srcp_linesize, cols, lines); - } - } -} - -static int filter_frame(AVFilterLink *inlink, AVFrame *buf) -{ - AVFilterContext *ctx = inlink->dst; - AVFilterLink *outlink = ctx->outputs[0]; - InterlaceContext *s = ctx->priv; - AVFrame *out; - int tff, ret; - - av_frame_free(&s->cur); - s->cur = s->next; - s->next = buf; - - /* we need at least two frames */ - if (!s->cur || !s->next) - return 0; - - if (s->cur->interlaced_frame) { - av_log(ctx, AV_LOG_WARNING, - "video is already interlaced, adjusting framerate only\n"); - out = av_frame_clone(s->cur); - if (!out) - return AVERROR(ENOMEM); - out->pts /= 2; // adjust pts to new framerate - ret = ff_filter_frame(outlink, out); - return ret; - } - - tff = (s->scan == MODE_TFF); - out = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!out) - return AVERROR(ENOMEM); - - av_frame_copy_props(out, s->cur); - out->interlaced_frame = 1; - out->top_field_first = tff; - out->pts /= 2; // adjust pts to new framerate - - /* copy upper/lower field from cur */ - copy_picture_field(s, s->cur, out, inlink, tff ? FIELD_UPPER : FIELD_LOWER, s->lowpass); - av_frame_free(&s->cur); - - /* copy lower/upper field from next */ - copy_picture_field(s, s->next, out, inlink, tff ? FIELD_LOWER : FIELD_UPPER, s->lowpass); - av_frame_free(&s->next); - - ret = ff_filter_frame(outlink, out); - - return ret; -} - -static const AVFilterPad inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = filter_frame, - }, - { NULL } -}; - -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_out_props, - }, - { NULL } -}; - -AVFilter ff_vf_interlace = { - .name = "interlace", - .description = NULL_IF_CONFIG_SMALL("Convert progressive video into interlaced."), - .uninit = uninit, - .priv_class = &interlace_class, - .priv_size = sizeof(InterlaceContext), - .query_formats = query_formats, - .inputs = inputs, - .outputs = outputs, -}; diff --git a/libavfilter/vf_lagfun.c b/libavfilter/vf_lagfun.c index 329c7465e14..1fbc889e35f 100644 --- a/libavfilter/vf_lagfun.c +++ b/libavfilter/vf_lagfun.c @@ -137,7 +137,7 @@ static int lagfun_frame16(AVFilterContext *ctx, void *arg, int jobnr, int nb_job } for (int y = slice_start; y < slice_end; y++) { - for (int x = 0; x < s->linesize[p]; x++) + for (int x = 0; x < s->linesize[p] / 2; x++) dst[x] = FFMAX(src[x], osrc[x] * decay); src += in->linesize[p] / 2; diff --git a/libavfilter/vf_lenscorrection.c b/libavfilter/vf_lenscorrection.c index 239fe195bd0..bb8ea3734fb 100644 --- a/libavfilter/vf_lenscorrection.c +++ b/libavfilter/vf_lenscorrection.c @@ -36,8 +36,8 @@ typedef struct LenscorrectionCtx { const AVClass *av_class; - unsigned int width; - unsigned int height; + int width; + int height; int hsub, vsub; int nb_planes; double cx, cy, k1, k2; @@ -65,7 +65,7 @@ typedef struct ThreadData { static int filter_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs) { - ThreadData *td = (ThreadData*)arg; + ThreadData *td = arg; AVFrame *in = td->in; AVFrame *out = td->out; @@ -155,10 +155,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) for (plane = 0; plane < rect->nb_planes; ++plane) { int hsub = plane == 1 || plane == 2 ? rect->hsub : 0; int vsub = plane == 1 || plane == 2 ? rect->vsub : 0; - int hdiv = 1 << hsub; - int vdiv = 1 << vsub; - int w = rect->width / hdiv; - int h = rect->height / vdiv; + int w = AV_CEIL_RSHIFT(rect->width, hsub); + int h = AV_CEIL_RSHIFT(rect->height, vsub); int xcenter = rect->cx * w; int ycenter = rect->cy * h; int k1 = rect->k1 * (1<<24); diff --git a/libavfilter/vf_libvmaf.c b/libavfilter/vf_libvmaf.c index 249e50c7200..14c3216b3a8 100644 --- a/libavfilter/vf_libvmaf.c +++ b/libavfilter/vf_libvmaf.c @@ -110,6 +110,7 @@ FRAMESYNC_DEFINE_CLASS(libvmaf, LIBVMAFContext, fs); const type *main_ptr = (const type *) s->gmain->data[0]; \ \ float *ptr = ref_data; \ + float factor = 1.f / (1 << (bits - 8)); \ \ int h = s->height; \ int w = s->width; \ @@ -118,7 +119,7 @@ FRAMESYNC_DEFINE_CLASS(libvmaf, LIBVMAFContext, fs); \ for (i = 0; i < h; i++) { \ for ( j = 0; j < w; j++) { \ - ptr[j] = (float)ref_ptr[j]; \ + ptr[j] = ref_ptr[j] * factor; \ } \ ref_ptr += ref_stride / sizeof(*ref_ptr); \ ptr += stride / sizeof(*ptr); \ @@ -128,7 +129,7 @@ FRAMESYNC_DEFINE_CLASS(libvmaf, LIBVMAFContext, fs); \ for (i = 0; i < h; i++) { \ for (j = 0; j < w; j++) { \ - ptr[j] = (float)main_ptr[j]; \ + ptr[j] = main_ptr[j] * factor; \ } \ main_ptr += main_stride / sizeof(*main_ptr); \ ptr += stride / sizeof(*ptr); \ @@ -234,6 +235,9 @@ static av_cold int init(AVFilterContext *ctx) s->gref = av_frame_alloc(); s->gmain = av_frame_alloc(); + if (!s->gref || !s->gmain) + return AVERROR(ENOMEM); + s->error = 0; s->vmaf_thread_created = 0; diff --git a/libavfilter/vf_limiter.c b/libavfilter/vf_limiter.c index 6f340069da6..8db28fee645 100644 --- a/libavfilter/vf_limiter.c +++ b/libavfilter/vf_limiter.c @@ -81,6 +81,7 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA422P12, AV_PIX_FMT_YUVA444P12, 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, diff --git a/libavfilter/vf_lumakey.c b/libavfilter/vf_lumakey.c index 418538f929d..c182e9045bd 100644 --- a/libavfilter/vf_lumakey.c +++ b/libavfilter/vf_lumakey.c @@ -28,12 +28,13 @@ typedef struct LumakeyContext { const AVClass *class; - int threshold; - int tolerance; - int softness; + double threshold; + double tolerance; + double softness; int white; int black; + int so; int max; int (*do_lumakey_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); @@ -47,7 +48,7 @@ static int do_lumakey_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_ const int slice_end = (frame->height * (jobnr + 1)) / nb_jobs; uint8_t *alpha = frame->data[3] + slice_start * frame->linesize[3]; const uint8_t *luma = frame->data[0] + slice_start * frame->linesize[0]; - const int so = s->softness; + const int so = s->so; const int w = s->white; const int b = s->black; int x, y; @@ -79,7 +80,7 @@ static int do_lumakey_slice16(AVFilterContext *ctx, void *arg, int jobnr, int nb const int slice_end = (frame->height * (jobnr + 1)) / nb_jobs; uint16_t *alpha = (uint16_t *)(frame->data[3] + slice_start * frame->linesize[3]); const uint16_t *luma = (const uint16_t *)(frame->data[0] + slice_start * frame->linesize[0]); - const int so = s->softness; + const int so = s->so; const int w = s->white; const int b = s->black; const int m = s->max; @@ -113,14 +114,16 @@ static int config_input(AVFilterLink *inlink) depth = desc->comp[0].depth; if (depth == 8) { - s->white = av_clip_uint8(s->threshold + s->tolerance); - s->black = av_clip_uint8(s->threshold - s->tolerance); + s->white = av_clip_uint8((s->threshold + s->tolerance) * 255); + s->black = av_clip_uint8((s->threshold - s->tolerance) * 255); s->do_lumakey_slice = do_lumakey_slice8; + s->so = s->softness * 255; } else { s->max = (1 << depth) - 1; - s->white = av_clip(s->threshold + s->tolerance, 0, s->max); - s->black = av_clip(s->threshold - s->tolerance, 0, s->max); + s->white = av_clip((s->threshold + s->tolerance) * s->max, 0, s->max); + s->black = av_clip((s->threshold - s->tolerance) * s->max, 0, s->max); s->do_lumakey_slice = do_lumakey_slice16; + s->so = s->softness * s->max; } return 0; @@ -147,6 +150,7 @@ static av_cold int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA420P9, 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_NONE }; @@ -159,6 +163,18 @@ static av_cold int query_formats(AVFilterContext *ctx) return ff_set_common_formats(ctx, formats); } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + return config_input(ctx->inputs[0]); +} + static const AVFilterPad lumakey_inputs[] = { { .name = "default", @@ -178,12 +194,12 @@ static const AVFilterPad lumakey_outputs[] = { }; #define OFFSET(x) offsetof(LumakeyContext, 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 lumakey_options[] = { - { "threshold", "set the threshold value", OFFSET(threshold), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, - { "tolerance", "set the tolerance value", OFFSET(tolerance), AV_OPT_TYPE_INT, {.i64=1}, 0, UINT16_MAX, FLAGS }, - { "softness", "set the softness value", OFFSET(softness), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, + { "threshold", "set the threshold value", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1, FLAGS }, + { "tolerance", "set the tolerance value", OFFSET(tolerance), AV_OPT_TYPE_DOUBLE, {.dbl=0.01}, 0, 1, FLAGS }, + { "softness", "set the softness value", OFFSET(softness), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1, FLAGS }, { NULL } }; @@ -198,4 +214,5 @@ AVFilter ff_vf_lumakey = { .inputs = lumakey_inputs, .outputs = lumakey_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = process_command, }; diff --git a/libavfilter/vf_lut2.c b/libavfilter/vf_lut2.c index 721de7d2107..7a204f5f1df 100644 --- a/libavfilter/vf_lut2.c +++ b/libavfilter/vf_lut2.c @@ -125,11 +125,12 @@ static av_cold void uninit(AVFilterContext *ctx) #define BIT12_FMTS \ AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12, \ + AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, \ AV_PIX_FMT_GRAY12, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRP12, #define BIT14_FMTS \ AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14, \ - AV_PIX_FMT_GRAY12, AV_PIX_FMT_GBRP14, + AV_PIX_FMT_GRAY14, AV_PIX_FMT_GBRP14, #define BIT16_FMTS \ AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, \ diff --git a/libavfilter/vf_lut3d.c b/libavfilter/vf_lut3d.c index a525039baa6..6730a424ced 100644 --- a/libavfilter/vf_lut3d.c +++ b/libavfilter/vf_lut3d.c @@ -24,9 +24,12 @@ * 3D Lookup table filter */ +#include "float.h" + #include "libavutil/opt.h" #include "libavutil/file.h" #include "libavutil/intreadwrite.h" +#include "libavutil/intfloat.h" #include "libavutil/avassert.h" #include "libavutil/pixdesc.h" #include "libavutil/avstring.h" @@ -55,7 +58,16 @@ struct rgbvec { /* 3D LUT don't often go up to level 32, but it is common to have a Hald CLUT * of 512x512 (64x64x64) */ -#define MAX_LEVEL 128 +#define MAX_LEVEL 256 +#define PRELUT_SIZE 65536 + +typedef struct Lut3DPreLut { + int size; + float min[3]; + float max[3]; + float scale[3]; + float* lut[3]; +} Lut3DPreLut; typedef struct LUT3DContext { const AVClass *class; @@ -65,13 +77,16 @@ typedef struct LUT3DContext { int step; avfilter_action_func *interp; struct rgbvec scale; - struct rgbvec lut[MAX_LEVEL][MAX_LEVEL][MAX_LEVEL]; + struct rgbvec *lut; int lutsize; + int lutsize2; + Lut3DPreLut prelut; #if CONFIG_HALDCLUT_FILTER uint8_t clut_rgba_map[4]; int clut_step; int clut_bits; int clut_planar; + int clut_float; int clut_width; FFFrameSync fs; #endif @@ -90,6 +105,30 @@ typedef struct ThreadData { { "tetrahedral", "interpolate values using a tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TETRAHEDRAL}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, \ { NULL } +#define EXPONENT_MASK 0x7F800000 +#define MANTISSA_MASK 0x007FFFFF +#define SIGN_MASK 0x7FFFFFFF + +static inline float sanitizef(float f) +{ + union av_intfloat32 t; + t.f = f; + + if ((t.i & EXPONENT_MASK) == EXPONENT_MASK) { + if ((t.i & MANTISSA_MASK) != 0) { + // NAN + return 0.0f; + } else if (t.i & SIGN_MASK) { + // -INF + return FLT_MIN; + } else { + // +INF + return FLT_MAX; + } + } + return f; +} + static inline float lerpf(float v0, float v1, float f) { return v0 + (v1 - v0) * f; @@ -113,7 +152,7 @@ static inline struct rgbvec lerp(const struct rgbvec *v0, const struct rgbvec *v static inline struct rgbvec interp_nearest(const LUT3DContext *lut3d, const struct rgbvec *s) { - return lut3d->lut[NEAR(s->r)][NEAR(s->g)][NEAR(s->b)]; + return lut3d->lut[NEAR(s->r) * lut3d->lutsize2 + NEAR(s->g) * lut3d->lutsize + NEAR(s->b)]; } /** @@ -123,17 +162,19 @@ static inline struct rgbvec interp_nearest(const LUT3DContext *lut3d, static inline struct rgbvec interp_trilinear(const LUT3DContext *lut3d, const struct rgbvec *s) { + const int lutsize2 = lut3d->lutsize2; + const int lutsize = lut3d->lutsize; const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)}; const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)}; const struct rgbvec d = {s->r - prev[0], s->g - prev[1], s->b - prev[2]}; - const struct rgbvec c000 = lut3d->lut[prev[0]][prev[1]][prev[2]]; - const struct rgbvec c001 = lut3d->lut[prev[0]][prev[1]][next[2]]; - const struct rgbvec c010 = lut3d->lut[prev[0]][next[1]][prev[2]]; - const struct rgbvec c011 = lut3d->lut[prev[0]][next[1]][next[2]]; - const struct rgbvec c100 = lut3d->lut[next[0]][prev[1]][prev[2]]; - const struct rgbvec c101 = lut3d->lut[next[0]][prev[1]][next[2]]; - const struct rgbvec c110 = lut3d->lut[next[0]][next[1]][prev[2]]; - const struct rgbvec c111 = lut3d->lut[next[0]][next[1]][next[2]]; + const struct rgbvec c000 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + prev[2]]; + const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]]; + const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]]; + const struct rgbvec c011 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + next[2]]; + const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]]; + const struct rgbvec c101 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + next[2]]; + const struct rgbvec c110 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + prev[2]]; + const struct rgbvec c111 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + next[2]]; const struct rgbvec c00 = lerp(&c000, &c100, d.r); const struct rgbvec c10 = lerp(&c010, &c110, d.r); const struct rgbvec c01 = lerp(&c001, &c101, d.r); @@ -151,48 +192,50 @@ static inline struct rgbvec interp_trilinear(const LUT3DContext *lut3d, static inline struct rgbvec interp_tetrahedral(const LUT3DContext *lut3d, const struct rgbvec *s) { + const int lutsize2 = lut3d->lutsize2; + const int lutsize = lut3d->lutsize; const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)}; const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)}; const struct rgbvec d = {s->r - prev[0], s->g - prev[1], s->b - prev[2]}; - const struct rgbvec c000 = lut3d->lut[prev[0]][prev[1]][prev[2]]; - const struct rgbvec c111 = lut3d->lut[next[0]][next[1]][next[2]]; + const struct rgbvec c000 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + prev[2]]; + const struct rgbvec c111 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + next[2]]; struct rgbvec c; if (d.r > d.g) { if (d.g > d.b) { - const struct rgbvec c100 = lut3d->lut[next[0]][prev[1]][prev[2]]; - const struct rgbvec c110 = lut3d->lut[next[0]][next[1]][prev[2]]; + const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]]; + const struct rgbvec c110 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + prev[2]]; c.r = (1-d.r) * c000.r + (d.r-d.g) * c100.r + (d.g-d.b) * c110.r + (d.b) * c111.r; c.g = (1-d.r) * c000.g + (d.r-d.g) * c100.g + (d.g-d.b) * c110.g + (d.b) * c111.g; c.b = (1-d.r) * c000.b + (d.r-d.g) * c100.b + (d.g-d.b) * c110.b + (d.b) * c111.b; } else if (d.r > d.b) { - const struct rgbvec c100 = lut3d->lut[next[0]][prev[1]][prev[2]]; - const struct rgbvec c101 = lut3d->lut[next[0]][prev[1]][next[2]]; + const struct rgbvec c100 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + prev[2]]; + const struct rgbvec c101 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + next[2]]; c.r = (1-d.r) * c000.r + (d.r-d.b) * c100.r + (d.b-d.g) * c101.r + (d.g) * c111.r; c.g = (1-d.r) * c000.g + (d.r-d.b) * c100.g + (d.b-d.g) * c101.g + (d.g) * c111.g; c.b = (1-d.r) * c000.b + (d.r-d.b) * c100.b + (d.b-d.g) * c101.b + (d.g) * c111.b; } else { - const struct rgbvec c001 = lut3d->lut[prev[0]][prev[1]][next[2]]; - const struct rgbvec c101 = lut3d->lut[next[0]][prev[1]][next[2]]; + const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]]; + const struct rgbvec c101 = lut3d->lut[next[0] * lutsize2 + prev[1] * lutsize + next[2]]; c.r = (1-d.b) * c000.r + (d.b-d.r) * c001.r + (d.r-d.g) * c101.r + (d.g) * c111.r; c.g = (1-d.b) * c000.g + (d.b-d.r) * c001.g + (d.r-d.g) * c101.g + (d.g) * c111.g; c.b = (1-d.b) * c000.b + (d.b-d.r) * c001.b + (d.r-d.g) * c101.b + (d.g) * c111.b; } } else { if (d.b > d.g) { - const struct rgbvec c001 = lut3d->lut[prev[0]][prev[1]][next[2]]; - const struct rgbvec c011 = lut3d->lut[prev[0]][next[1]][next[2]]; + const struct rgbvec c001 = lut3d->lut[prev[0] * lutsize2 + prev[1] * lutsize + next[2]]; + const struct rgbvec c011 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + next[2]]; c.r = (1-d.b) * c000.r + (d.b-d.g) * c001.r + (d.g-d.r) * c011.r + (d.r) * c111.r; c.g = (1-d.b) * c000.g + (d.b-d.g) * c001.g + (d.g-d.r) * c011.g + (d.r) * c111.g; c.b = (1-d.b) * c000.b + (d.b-d.g) * c001.b + (d.g-d.r) * c011.b + (d.r) * c111.b; } else if (d.b > d.r) { - const struct rgbvec c010 = lut3d->lut[prev[0]][next[1]][prev[2]]; - const struct rgbvec c011 = lut3d->lut[prev[0]][next[1]][next[2]]; + const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]]; + const struct rgbvec c011 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + next[2]]; c.r = (1-d.g) * c000.r + (d.g-d.b) * c010.r + (d.b-d.r) * c011.r + (d.r) * c111.r; c.g = (1-d.g) * c000.g + (d.g-d.b) * c010.g + (d.b-d.r) * c011.g + (d.r) * c111.g; c.b = (1-d.g) * c000.b + (d.g-d.b) * c010.b + (d.b-d.r) * c011.b + (d.r) * c111.b; } else { - const struct rgbvec c010 = lut3d->lut[prev[0]][next[1]][prev[2]]; - const struct rgbvec c110 = lut3d->lut[next[0]][next[1]][prev[2]]; + const struct rgbvec c010 = lut3d->lut[prev[0] * lutsize2 + next[1] * lutsize + prev[2]]; + const struct rgbvec c110 = lut3d->lut[next[0] * lutsize2 + next[1] * lutsize + prev[2]]; c.r = (1-d.g) * c000.r + (d.g-d.r) * c010.r + (d.r-d.b) * c110.r + (d.b) * c111.r; c.g = (1-d.g) * c000.g + (d.g-d.r) * c010.g + (d.r-d.b) * c110.g + (d.b) * c111.g; c.b = (1-d.g) * c000.b + (d.g-d.r) * c010.b + (d.r-d.b) * c110.b + (d.b) * c111.b; @@ -201,11 +244,40 @@ static inline struct rgbvec interp_tetrahedral(const LUT3DContext *lut3d, return c; } +static inline float prelut_interp_1d_linear(const Lut3DPreLut *prelut, + int idx, const float s) +{ + const int lut_max = prelut->size - 1; + const float scaled = (s - prelut->min[idx]) * prelut->scale[idx]; + const float x = av_clipf(scaled, 0.0f, lut_max); + const int prev = PREV(x); + const int next = FFMIN((int)(x) + 1, lut_max); + const float p = prelut->lut[idx][prev]; + const float n = prelut->lut[idx][next]; + const float d = x - (float)prev; + return lerpf(p, n, d); +} + +static inline struct rgbvec apply_prelut(const Lut3DPreLut *prelut, + const struct rgbvec *s) +{ + struct rgbvec c; + + if (prelut->size <= 0) + return *s; + + c.r = prelut_interp_1d_linear(prelut, 0, s->r); + c.g = prelut_interp_1d_linear(prelut, 1, s->g); + c.b = prelut_interp_1d_linear(prelut, 2, s->b); + return c; +} + #define DEFINE_INTERP_FUNC_PLANAR(name, nbits, depth) \ static int interp_##nbits##_##name##_p##depth(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \ { \ int x, y; \ const LUT3DContext *lut3d = ctx->priv; \ + const Lut3DPreLut *prelut = &lut3d->prelut; \ const ThreadData *td = arg; \ const AVFrame *in = td->in; \ const AVFrame *out = td->out; \ @@ -220,9 +292,11 @@ static int interp_##nbits##_##name##_p##depth(AVFilterContext *ctx, void *arg, i const uint8_t *srcbrow = in->data[1] + slice_start * in->linesize[1]; \ const uint8_t *srcrrow = in->data[2] + slice_start * in->linesize[2]; \ const uint8_t *srcarow = in->data[3] + slice_start * in->linesize[3]; \ - const float scale_r = (lut3d->scale.r / ((1<lutsize - 1); \ - const float scale_g = (lut3d->scale.g / ((1<lutsize - 1); \ - const float scale_b = (lut3d->scale.b / ((1<lutsize - 1); \ + const float lut_max = lut3d->lutsize - 1; \ + const float scale_f = 1.0f / ((1<scale.r * lut_max; \ + const float scale_g = lut3d->scale.g * lut_max; \ + const float scale_b = lut3d->scale.b * lut_max; \ \ for (y = slice_start; y < slice_end; y++) { \ uint##nbits##_t *dstg = (uint##nbits##_t *)grow; \ @@ -234,9 +308,13 @@ static int interp_##nbits##_##name##_p##depth(AVFilterContext *ctx, void *arg, i const uint##nbits##_t *srcr = (const uint##nbits##_t *)srcrrow; \ const uint##nbits##_t *srca = (const uint##nbits##_t *)srcarow; \ for (x = 0; x < in->width; x++) { \ - const struct rgbvec scaled_rgb = {srcr[x] * scale_r, \ - srcg[x] * scale_g, \ - srcb[x] * scale_b}; \ + const struct rgbvec rgb = {srcr[x] * scale_f, \ + srcg[x] * scale_f, \ + srcb[x] * scale_f}; \ + const struct rgbvec prelut_rgb = apply_prelut(prelut, &rgb); \ + const struct rgbvec scaled_rgb = {av_clipf(prelut_rgb.r * scale_r, 0, lut_max), \ + av_clipf(prelut_rgb.g * scale_g, 0, lut_max), \ + av_clipf(prelut_rgb.b * scale_b, 0, lut_max)}; \ struct rgbvec vec = interp_##name(lut3d, &scaled_rgb); \ dstr[x] = av_clip_uintp2(vec.r * (float)((1<priv; \ + const Lut3DPreLut *prelut = &lut3d->prelut; \ + const ThreadData *td = arg; \ + const AVFrame *in = td->in; \ + const AVFrame *out = td->out; \ + const int direct = out == in; \ + const int slice_start = (in->height * jobnr ) / nb_jobs; \ + const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \ + uint8_t *grow = out->data[0] + slice_start * out->linesize[0]; \ + uint8_t *brow = out->data[1] + slice_start * out->linesize[1]; \ + uint8_t *rrow = out->data[2] + slice_start * out->linesize[2]; \ + uint8_t *arow = out->data[3] + slice_start * out->linesize[3]; \ + const uint8_t *srcgrow = in->data[0] + slice_start * in->linesize[0]; \ + const uint8_t *srcbrow = in->data[1] + slice_start * in->linesize[1]; \ + const uint8_t *srcrrow = in->data[2] + slice_start * in->linesize[2]; \ + const uint8_t *srcarow = in->data[3] + slice_start * in->linesize[3]; \ + const float lut_max = lut3d->lutsize - 1; \ + const float scale_r = lut3d->scale.r * lut_max; \ + const float scale_g = lut3d->scale.g * lut_max; \ + const float scale_b = lut3d->scale.b * lut_max; \ + \ + for (y = slice_start; y < slice_end; y++) { \ + float *dstg = (float *)grow; \ + float *dstb = (float *)brow; \ + float *dstr = (float *)rrow; \ + float *dsta = (float *)arow; \ + const float *srcg = (const float *)srcgrow; \ + const float *srcb = (const float *)srcbrow; \ + const float *srcr = (const float *)srcrrow; \ + const float *srca = (const float *)srcarow; \ + for (x = 0; x < in->width; x++) { \ + const struct rgbvec rgb = {sanitizef(srcr[x]), \ + sanitizef(srcg[x]), \ + sanitizef(srcb[x])}; \ + const struct rgbvec prelut_rgb = apply_prelut(prelut, &rgb); \ + const struct rgbvec scaled_rgb = {av_clipf(prelut_rgb.r * scale_r, 0, lut_max), \ + av_clipf(prelut_rgb.g * scale_g, 0, lut_max), \ + av_clipf(prelut_rgb.b * scale_b, 0, lut_max)}; \ + struct rgbvec vec = interp_##name(lut3d, &scaled_rgb); \ + dstr[x] = vec.r; \ + dstg[x] = vec.g; \ + dstb[x] = vec.b; \ + if (!direct && in->linesize[3]) \ + dsta[x] = srca[x]; \ + } \ + grow += out->linesize[0]; \ + brow += out->linesize[1]; \ + rrow += out->linesize[2]; \ + arow += out->linesize[3]; \ + srcgrow += in->linesize[0]; \ + srcbrow += in->linesize[1]; \ + srcrrow += in->linesize[2]; \ + srcarow += in->linesize[3]; \ + } \ + return 0; \ +} + +DEFINE_INTERP_FUNC_PLANAR_FLOAT(nearest, 32) +DEFINE_INTERP_FUNC_PLANAR_FLOAT(trilinear, 32) +DEFINE_INTERP_FUNC_PLANAR_FLOAT(tetrahedral, 32) + #define DEFINE_INTERP_FUNC(name, nbits) \ static int interp_##nbits##_##name(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \ { \ int x, y; \ const LUT3DContext *lut3d = ctx->priv; \ + const Lut3DPreLut *prelut = &lut3d->prelut; \ const ThreadData *td = arg; \ const AVFrame *in = td->in; \ const AVFrame *out = td->out; \ @@ -298,17 +442,23 @@ static int interp_##nbits##_##name(AVFilterContext *ctx, void *arg, int jobnr, i const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \ uint8_t *dstrow = out->data[0] + slice_start * out->linesize[0]; \ const uint8_t *srcrow = in ->data[0] + slice_start * in ->linesize[0]; \ - const float scale_r = (lut3d->scale.r / ((1<lutsize - 1); \ - const float scale_g = (lut3d->scale.g / ((1<lutsize - 1); \ - const float scale_b = (lut3d->scale.b / ((1<lutsize - 1); \ + const float lut_max = lut3d->lutsize - 1; \ + const float scale_f = 1.0f / ((1<scale.r * lut_max; \ + const float scale_g = lut3d->scale.g * lut_max; \ + const float scale_b = lut3d->scale.b * lut_max; \ \ for (y = slice_start; y < slice_end; y++) { \ uint##nbits##_t *dst = (uint##nbits##_t *)dstrow; \ const uint##nbits##_t *src = (const uint##nbits##_t *)srcrow; \ for (x = 0; x < in->width * step; x += step) { \ - const struct rgbvec scaled_rgb = {src[x + r] * scale_r, \ - src[x + g] * scale_g, \ - src[x + b] * scale_b}; \ + const struct rgbvec rgb = {src[x + r] * scale_f, \ + src[x + g] * scale_f, \ + src[x + b] * scale_f}; \ + const struct rgbvec prelut_rgb = apply_prelut(prelut, &rgb); \ + const struct rgbvec scaled_rgb = {av_clipf(prelut_rgb.r * scale_r, 0, lut_max), \ + av_clipf(prelut_rgb.g * scale_g, 0, lut_max), \ + av_clipf(prelut_rgb.b * scale_b, 0, lut_max)}; \ struct rgbvec vec = interp_##name(lut3d, &scaled_rgb); \ dst[x + r] = av_clip_uint##nbits(vec.r * (float)((1< 0; max--) { + if ((c = fgetc(f)) == EOF) + break; + + if (av_isspace(c)) + break; + + *p++ = c; + } + + *p = 0; + if (p == dst) + return NULL; + return p; +} + #define NEXT_LINE(loop_cond) do { \ if (!fgets(line, sizeof(line), f)) { \ av_log(ctx, AV_LOG_ERROR, "Unexpected EOF\n"); \ @@ -346,30 +530,73 @@ static int skip_line(const char *p) } \ } while (loop_cond) +#define NEXT_LINE_OR_GOTO(loop_cond, label) do { \ + if (!fgets(line, sizeof(line), f)) { \ + av_log(ctx, AV_LOG_ERROR, "Unexpected EOF\n"); \ + ret = AVERROR_INVALIDDATA; \ + goto label; \ + } \ +} while (loop_cond) + +static int allocate_3dlut(AVFilterContext *ctx, int lutsize, int prelut) +{ + LUT3DContext *lut3d = ctx->priv; + int i; + if (lutsize < 2 || lutsize > MAX_LEVEL) { + av_log(ctx, AV_LOG_ERROR, "Too large or invalid 3D LUT size\n"); + return AVERROR(EINVAL); + } + + av_freep(&lut3d->lut); + lut3d->lut = av_malloc_array(lutsize * lutsize * lutsize, sizeof(*lut3d->lut)); + if (!lut3d->lut) + return AVERROR(ENOMEM); + + if (prelut) { + lut3d->prelut.size = PRELUT_SIZE; + for (i = 0; i < 3; i++) { + av_freep(&lut3d->prelut.lut[i]); + lut3d->prelut.lut[i] = av_malloc_array(PRELUT_SIZE, sizeof(*lut3d->prelut.lut[0])); + if (!lut3d->prelut.lut[i]) + return AVERROR(ENOMEM); + } + } else { + lut3d->prelut.size = 0; + for (i = 0; i < 3; i++) { + av_freep(&lut3d->prelut.lut[i]); + } + } + lut3d->lutsize = lutsize; + lut3d->lutsize2 = lutsize * lutsize; + return 0; +} + /* Basically r g and b float values on each line, with a facultative 3DLUTSIZE * directive; seems to be generated by Davinci */ static int parse_dat(AVFilterContext *ctx, FILE *f) { LUT3DContext *lut3d = ctx->priv; char line[MAX_LINE_SIZE]; - int i, j, k, size; + int ret, i, j, k, size, size2; lut3d->lutsize = size = 33; + size2 = size * size; NEXT_LINE(skip_line(line)); if (!strncmp(line, "3DLUTSIZE ", 10)) { size = strtol(line + 10, NULL, 0); - if (size < 2 || size > MAX_LEVEL) { - av_log(ctx, AV_LOG_ERROR, "Too large or invalid 3D LUT size\n"); - return AVERROR(EINVAL); - } - lut3d->lutsize = size; + NEXT_LINE(skip_line(line)); } + + ret = allocate_3dlut(ctx, size, 0); + if (ret < 0) + return ret; + for (k = 0; k < size; k++) { for (j = 0; j < size; j++) { for (i = 0; i < size; i++) { - struct rgbvec *vec = &lut3d->lut[k][j][i]; + struct rgbvec *vec = &lut3d->lut[k * size2 + j * size + i]; if (k != 0 || j != 0 || i != 0) NEXT_LINE(skip_line(line)); if (av_sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b) != 3) @@ -390,18 +617,18 @@ static int parse_cube(AVFilterContext *ctx, FILE *f) while (fgets(line, sizeof(line), f)) { if (!strncmp(line, "LUT_3D_SIZE", 11)) { - int i, j, k; + int ret, i, j, k; const int size = strtol(line + 12, NULL, 0); + const int size2 = size * size; + + ret = allocate_3dlut(ctx, size, 0); + if (ret < 0) + return ret; - if (size < 2 || size > MAX_LEVEL) { - av_log(ctx, AV_LOG_ERROR, "Too large or invalid 3D LUT size\n"); - return AVERROR(EINVAL); - } - lut3d->lutsize = size; for (k = 0; k < size; k++) { for (j = 0; j < size; j++) { for (i = 0; i < size; i++) { - struct rgbvec *vec = &lut3d->lut[i][j][k]; + struct rgbvec *vec = &lut3d->lut[i * size2 + j * size + k]; do { try_again: @@ -442,17 +669,23 @@ static int parse_3dl(AVFilterContext *ctx, FILE *f) { char line[MAX_LINE_SIZE]; LUT3DContext *lut3d = ctx->priv; - int i, j, k; + int ret, i, j, k; const int size = 17; + const int size2 = 17 * 17; const float scale = 16*16*16; lut3d->lutsize = size; + + ret = allocate_3dlut(ctx, size, 0); + if (ret < 0) + return ret; + NEXT_LINE(skip_line(line)); for (k = 0; k < size; k++) { for (j = 0; j < size; j++) { for (i = 0; i < size; i++) { int r, g, b; - struct rgbvec *vec = &lut3d->lut[k][j][i]; + struct rgbvec *vec = &lut3d->lut[k * size2 + j * size + i]; NEXT_LINE(skip_line(line)); if (av_sscanf(line, "%d %d %d", &r, &g, &b) != 3) @@ -471,7 +704,7 @@ static int parse_m3d(AVFilterContext *ctx, FILE *f) { LUT3DContext *lut3d = ctx->priv; float scale; - int i, j, k, size, in = -1, out = -1; + int ret, i, j, k, size, size2, in = -1, out = -1; char line[MAX_LINE_SIZE]; uint8_t rgb_map[3] = {0, 1, 2}; @@ -510,12 +743,18 @@ static int parse_m3d(AVFilterContext *ctx, FILE *f) } for (size = 1; size*size*size < in; size++); lut3d->lutsize = size; + size2 = size * size; + + ret = allocate_3dlut(ctx, size, 0); + if (ret < 0) + return ret; + scale = 1. / (out - 1); for (k = 0; k < size; k++) { for (j = 0; j < size; j++) { for (i = 0; i < size; i++) { - struct rgbvec *vec = &lut3d->lut[k][j][i]; + struct rgbvec *vec = &lut3d->lut[k * size2 + j * size + i]; float val[3]; NEXT_LINE(0); @@ -530,6 +769,44 @@ static int parse_m3d(AVFilterContext *ctx, FILE *f) return 0; } +static int nearest_sample_index(float *data, float x, int low, int hi) +{ + int mid; + if (x < data[low]) + return low; + + if (x > data[hi]) + return hi; + + for (;;) { + av_assert0(x >= data[low]); + av_assert0(x <= data[hi]); + av_assert0((hi-low) > 0); + + if (hi - low == 1) + return low; + + mid = (low + hi) / 2; + + if (x < data[mid]) + hi = mid; + else + low = mid; + } + + return 0; +} + +#define NEXT_FLOAT_OR_GOTO(value, label) \ + if (!fget_next_word(line, sizeof(line) ,f)) { \ + ret = AVERROR_INVALIDDATA; \ + goto label; \ + } \ + if (av_sscanf(line, "%f", &value) != 1) { \ + ret = AVERROR_INVALIDDATA; \ + goto label; \ + } + static int parse_cinespace(AVFilterContext *ctx, FILE *f) { LUT3DContext *lut3d = ctx->priv; @@ -538,22 +815,30 @@ static int parse_cinespace(AVFilterContext *ctx, FILE *f) float in_max[3] = {1.0, 1.0, 1.0}; float out_min[3] = {0.0, 0.0, 0.0}; float out_max[3] = {1.0, 1.0, 1.0}; - int inside_metadata = 0, size; + int inside_metadata = 0, size, size2; + int prelut = 0; + int ret = 0; - NEXT_LINE(skip_line(line)); + int prelut_sizes[3] = {0, 0, 0}; + float *in_prelut[3] = {NULL, NULL, NULL}; + float *out_prelut[3] = {NULL, NULL, NULL}; + + NEXT_LINE_OR_GOTO(skip_line(line), end); if (strncmp(line, "CSPLUTV100", 10)) { av_log(ctx, AV_LOG_ERROR, "Not cineSpace LUT format\n"); - return AVERROR(EINVAL); + ret = AVERROR(EINVAL); + goto end; } - NEXT_LINE(skip_line(line)); + NEXT_LINE_OR_GOTO(skip_line(line), end); if (strncmp(line, "3D", 2)) { av_log(ctx, AV_LOG_ERROR, "Not 3D LUT format\n"); - return AVERROR(EINVAL); + ret = AVERROR(EINVAL); + goto end; } while (1) { - NEXT_LINE(skip_line(line)); + NEXT_LINE_OR_GOTO(skip_line(line), end); if (!strncmp(line, "BEGIN METADATA", 14)) { inside_metadata = 1; @@ -569,43 +854,108 @@ static int parse_cinespace(AVFilterContext *ctx, FILE *f) for (int i = 0; i < 3; i++) { int npoints = strtol(line, NULL, 0); - if (npoints != 2) { + if (npoints > 2) { + float v,last; + + if (npoints > PRELUT_SIZE) { + av_log(ctx, AV_LOG_ERROR, "Prelut size too large.\n"); + ret = AVERROR_INVALIDDATA; + goto end; + } + + if (in_prelut[i] || out_prelut[i]) { + av_log(ctx, AV_LOG_ERROR, "Invalid file has multiple preluts.\n"); + ret = AVERROR_INVALIDDATA; + goto end; + } + + in_prelut[i] = (float*)av_malloc(npoints * sizeof(float)); + out_prelut[i] = (float*)av_malloc(npoints * sizeof(float)); + if (!in_prelut[i] || !out_prelut[i]) { + ret = AVERROR(ENOMEM); + goto end; + } + + prelut_sizes[i] = npoints; + in_min[i] = FLT_MAX; + in_max[i] = FLT_MIN; + out_min[i] = FLT_MAX; + out_max[i] = FLT_MIN; + + last = FLT_MIN; + + for (int j = 0; j < npoints; j++) { + NEXT_FLOAT_OR_GOTO(v, end) + in_min[i] = FFMIN(in_min[i], v); + in_max[i] = FFMAX(in_max[i], v); + in_prelut[i][j] = v; + if (v < last) { + av_log(ctx, AV_LOG_ERROR, "Invalid file, non increasing prelut.\n"); + ret = AVERROR(ENOMEM); + goto end; + } + last = v; + } + + for (int j = 0; j < npoints; j++) { + NEXT_FLOAT_OR_GOTO(v, end) + out_min[i] = FFMIN(out_min[i], v); + out_max[i] = FFMAX(out_max[i], v); + out_prelut[i][j] = v; + } + + } else if (npoints == 2) { + NEXT_LINE_OR_GOTO(skip_line(line), end); + if (av_sscanf(line, "%f %f", &in_min[i], &in_max[i]) != 2) { + ret = AVERROR_INVALIDDATA; + goto end; + } + NEXT_LINE_OR_GOTO(skip_line(line), end); + if (av_sscanf(line, "%f %f", &out_min[i], &out_max[i]) != 2) { + ret = AVERROR_INVALIDDATA; + goto end; + } + + } else { av_log(ctx, AV_LOG_ERROR, "Unsupported number of pre-lut points.\n"); - return AVERROR_PATCHWELCOME; + ret = AVERROR_PATCHWELCOME; + goto end; } - NEXT_LINE(skip_line(line)); - if (av_sscanf(line, "%f %f", &in_min[i], &in_max[i]) != 2) - return AVERROR_INVALIDDATA; - NEXT_LINE(skip_line(line)); - if (av_sscanf(line, "%f %f", &out_min[i], &out_max[i]) != 2) - return AVERROR_INVALIDDATA; - NEXT_LINE(skip_line(line)); + NEXT_LINE_OR_GOTO(skip_line(line), end); } - if (av_sscanf(line, "%d %d %d", &size_r, &size_g, &size_b) != 3) - return AVERROR(EINVAL); + if (av_sscanf(line, "%d %d %d", &size_r, &size_g, &size_b) != 3) { + ret = AVERROR(EINVAL); + goto end; + } if (size_r != size_g || size_r != size_b) { av_log(ctx, AV_LOG_ERROR, "Unsupported size combination: %dx%dx%d.\n", size_r, size_g, size_b); - return AVERROR_PATCHWELCOME; + ret = AVERROR_PATCHWELCOME; + goto end; } size = size_r; - if (size < 2 || size > MAX_LEVEL) { - av_log(ctx, AV_LOG_ERROR, "Too large or invalid 3D LUT size\n"); - return AVERROR(EINVAL); - } + size2 = size * size; - lut3d->lutsize = size; + if (prelut_sizes[0] && prelut_sizes[1] && prelut_sizes[2]) + prelut = 1; + + ret = allocate_3dlut(ctx, size, prelut); + if (ret < 0) + return ret; for (int k = 0; k < size; k++) { for (int j = 0; j < size; j++) { for (int i = 0; i < size; i++) { - struct rgbvec *vec = &lut3d->lut[i][j][k]; - if (k != 0 || j != 0 || i != 0) - NEXT_LINE(skip_line(line)); - if (av_sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b) != 3) - return AVERROR_INVALIDDATA; + struct rgbvec *vec = &lut3d->lut[i * size2 + j * size + k]; + + NEXT_LINE_OR_GOTO(skip_line(line), end); + if (av_sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b) != 3) { + ret = AVERROR_INVALIDDATA; + goto end; + } + vec->r *= out_max[0] - out_min[0]; vec->g *= out_max[1] - out_min[1]; vec->b *= out_max[2] - out_min[2]; @@ -617,29 +967,68 @@ static int parse_cinespace(AVFilterContext *ctx, FILE *f) } } - lut3d->scale.r = av_clipf(1. / (in_max[0] - in_min[0]), 0.f, 1.f); - lut3d->scale.g = av_clipf(1. / (in_max[1] - in_min[1]), 0.f, 1.f); - lut3d->scale.b = av_clipf(1. / (in_max[2] - in_min[2]), 0.f, 1.f); + if (prelut) { + for (int c = 0; c < 3; c++) { - return 0; + lut3d->prelut.min[c] = in_min[c]; + lut3d->prelut.max[c] = in_max[c]; + lut3d->prelut.scale[c] = (1.0f / (float)(in_max[c] - in_min[c])) * (lut3d->prelut.size - 1); + + for (int i = 0; i < lut3d->prelut.size; ++i) { + float mix = (float) i / (float)(lut3d->prelut.size - 1); + float x = lerpf(in_min[c], in_max[c], mix), a, b; + + int idx = nearest_sample_index(in_prelut[c], x, 0, prelut_sizes[c]-1); + av_assert0(idx + 1 < prelut_sizes[c]); + + a = out_prelut[c][idx + 0]; + b = out_prelut[c][idx + 1]; + mix = x - in_prelut[c][idx]; + + lut3d->prelut.lut[c][i] = sanitizef(lerpf(a, b, mix)); + } + } + lut3d->scale.r = 1.00f; + lut3d->scale.g = 1.00f; + lut3d->scale.b = 1.00f; + + } else { + lut3d->scale.r = av_clipf(1. / (in_max[0] - in_min[0]), 0.f, 1.f); + lut3d->scale.g = av_clipf(1. / (in_max[1] - in_min[1]), 0.f, 1.f); + lut3d->scale.b = av_clipf(1. / (in_max[2] - in_min[2]), 0.f, 1.f); + } + +end: + for (int c = 0; c < 3; c++) { + av_freep(&in_prelut[c]); + av_freep(&out_prelut[c]); + } + return ret; } -static void set_identity_matrix(LUT3DContext *lut3d, int size) +static int set_identity_matrix(AVFilterContext *ctx, int size) { - int i, j, k; + LUT3DContext *lut3d = ctx->priv; + int ret, i, j, k; + const int size2 = size * size; const float c = 1. / (size - 1); - lut3d->lutsize = size; + ret = allocate_3dlut(ctx, size, 0); + if (ret < 0) + return ret; + for (k = 0; k < size; k++) { for (j = 0; j < size; j++) { for (i = 0; i < size; i++) { - struct rgbvec *vec = &lut3d->lut[k][j][i]; + struct rgbvec *vec = &lut3d->lut[k * size2 + j * size + i]; vec->r = k * c; vec->g = j * c; vec->b = i * c; } } } + + return 0; } static int query_formats(AVFilterContext *ctx) @@ -657,7 +1046,8 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRP14, - AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, + AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, + AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32, AV_PIX_FMT_NONE }; AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); @@ -668,39 +1058,19 @@ static int query_formats(AVFilterContext *ctx) static int config_input(AVFilterLink *inlink) { - int depth, is16bit = 0, planar = 0; + int depth, is16bit, isfloat, planar; LUT3DContext *lut3d = inlink->dst->priv; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); depth = desc->comp[0].depth; - - switch (inlink->format) { - case AV_PIX_FMT_RGB48: - case AV_PIX_FMT_BGR48: - case AV_PIX_FMT_RGBA64: - case AV_PIX_FMT_BGRA64: - is16bit = 1; - break; - case AV_PIX_FMT_GBRP9: - case AV_PIX_FMT_GBRP10: - case AV_PIX_FMT_GBRP12: - case AV_PIX_FMT_GBRP14: - case AV_PIX_FMT_GBRP16: - case AV_PIX_FMT_GBRAP10: - case AV_PIX_FMT_GBRAP12: - case AV_PIX_FMT_GBRAP16: - is16bit = 1; - case AV_PIX_FMT_GBRP: - case AV_PIX_FMT_GBRAP: - planar = 1; - break; - } - + is16bit = desc->comp[0].depth > 8; + planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR; + isfloat = desc->flags & AV_PIX_FMT_FLAG_FLOAT; ff_fill_rgba_map(lut3d->rgba_map, inlink->format); lut3d->step = av_get_padded_bits_per_pixel(desc) >> (3 + is16bit); #define SET_FUNC(name) do { \ - if (planar) { \ + if (planar && !isfloat) { \ switch (depth) { \ case 8: lut3d->interp = interp_8_##name##_p8; break; \ case 9: lut3d->interp = interp_16_##name##_p9; break; \ @@ -709,6 +1079,7 @@ static int config_input(AVFilterLink *inlink) case 14: lut3d->interp = interp_16_##name##_p14; break; \ case 16: lut3d->interp = interp_16_##name##_p16; break; \ } \ + } else if (isfloat) { lut3d->interp = interp_##name##_pf32; \ } else if (is16bit) { lut3d->interp = interp_16_##name; \ } else { lut3d->interp = interp_8_##name; } \ } while (0) @@ -780,11 +1151,10 @@ static av_cold int lut3d_init(AVFilterContext *ctx) lut3d->scale.r = lut3d->scale.g = lut3d->scale.b = 1.f; if (!lut3d->file) { - set_identity_matrix(lut3d, 32); - return 0; + return set_identity_matrix(ctx, 32); } - f = fopen(lut3d->file, "r"); + f = av_fopen_utf8(lut3d->file, "r"); if (!f) { ret = AVERROR(errno); av_log(ctx, AV_LOG_ERROR, "%s: %s\n", lut3d->file, av_err2str(ret)); @@ -824,6 +1194,17 @@ static av_cold int lut3d_init(AVFilterContext *ctx) return ret; } +static av_cold void lut3d_uninit(AVFilterContext *ctx) +{ + LUT3DContext *lut3d = ctx->priv; + int i; + av_freep(&lut3d->lut); + + for (i = 0; i < 3; i++) { + av_freep(&lut3d->prelut.lut[i]); + } +} + static const AVFilterPad lut3d_inputs[] = { { .name = "default", @@ -847,6 +1228,7 @@ AVFilter ff_vf_lut3d = { .description = NULL_IF_CONFIG_SMALL("Adjust colors using a 3D LUT."), .priv_size = sizeof(LUT3DContext), .init = lut3d_init, + .uninit = lut3d_uninit, .query_formats = query_formats, .inputs = lut3d_inputs, .outputs = lut3d_outputs, @@ -865,6 +1247,7 @@ static void update_clut_packed(LUT3DContext *lut3d, const AVFrame *frame) const int step = lut3d->clut_step; const uint8_t *rgba_map = lut3d->clut_rgba_map; const int level = lut3d->lutsize; + const int level2 = lut3d->lutsize2; #define LOAD_CLUT(nbits) do { \ int i, j, k, x = 0, y = 0; \ @@ -874,7 +1257,7 @@ static void update_clut_packed(LUT3DContext *lut3d, const AVFrame *frame) for (i = 0; i < level; i++) { \ const uint##nbits##_t *src = (const uint##nbits##_t *) \ (data + y*linesize + x*step); \ - struct rgbvec *vec = &lut3d->lut[i][j][k]; \ + struct rgbvec *vec = &lut3d->lut[i * level2 + j * level + k]; \ vec->r = src[rgba_map[0]] / (float)((1<<(nbits)) - 1); \ vec->g = src[rgba_map[1]] / (float)((1<<(nbits)) - 1); \ vec->b = src[rgba_map[2]] / (float)((1<<(nbits)) - 1); \ @@ -903,6 +1286,7 @@ static void update_clut_planar(LUT3DContext *lut3d, const AVFrame *frame) const int rlinesize = frame->linesize[2]; const int w = lut3d->clut_width; const int level = lut3d->lutsize; + const int level2 = lut3d->lutsize2; #define LOAD_CLUT_PLANAR(nbits, depth) do { \ int i, j, k, x = 0, y = 0; \ @@ -916,7 +1300,7 @@ static void update_clut_planar(LUT3DContext *lut3d, const AVFrame *frame) (datab + y*blinesize); \ const uint##nbits##_t *rsrc = (const uint##nbits##_t *) \ (datar + y*rlinesize); \ - struct rgbvec *vec = &lut3d->lut[i][j][k]; \ + struct rgbvec *vec = &lut3d->lut[i * level2 + j * level + k]; \ vec->r = gsrc[x] / (float)((1<<(depth)) - 1); \ vec->g = bsrc[x] / (float)((1<<(depth)) - 1); \ vec->b = rsrc[x] / (float)((1<<(depth)) - 1); \ @@ -939,6 +1323,39 @@ static void update_clut_planar(LUT3DContext *lut3d, const AVFrame *frame) } } +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 int w = lut3d->clut_width; + const int level = lut3d->lutsize; + const int level2 = lut3d->lutsize2; + + int i, j, k, x = 0, y = 0; + + for (k = 0; k < level; k++) { + for (j = 0; j < level; j++) { + for (i = 0; i < level; i++) { + const float *gsrc = (const float *)(datag + y*glinesize); + const float *bsrc = (const float *)(datab + y*blinesize); + const float *rsrc = (const float *)(datar + y*rlinesize); + struct rgbvec *vec = &lut3d->lut[i * level2 + j * level + k]; + vec->r = rsrc[x]; + vec->g = gsrc[x]; + vec->b = bsrc[x]; + if (++x == w) { + x = 0; + y++; + } + } + } + } +} + static int config_output(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; @@ -973,6 +1390,7 @@ static int config_clut(AVFilterLink *inlink) lut3d->clut_bits = desc->comp[0].depth; lut3d->clut_planar = av_pix_fmt_count_planes(inlink->format) > 1; + lut3d->clut_float = desc->flags & AV_PIX_FMT_FLAG_FLOAT; lut3d->clut_step = av_get_padded_bits_per_pixel(desc) >> 3; ff_fill_rgba_map(lut3d->clut_rgba_map, inlink->format); @@ -1001,9 +1419,8 @@ static int config_clut(AVFilterLink *inlink) max_clut_level, max_clut_size, max_clut_size); return AVERROR(EINVAL); } - lut3d->lutsize = level; - return 0; + return allocate_3dlut(ctx, level, 0); } static int update_apply_clut(FFFrameSync *fs) @@ -1019,7 +1436,9 @@ static int update_apply_clut(FFFrameSync *fs) return ret; if (!second) return ff_filter_frame(ctx->outputs[0], master); - if (lut3d->clut_planar) + if (lut3d->clut_float) + update_clut_float(ctx->priv, second); + else if (lut3d->clut_planar) update_clut_planar(ctx->priv, second); else update_clut_packed(ctx->priv, second); @@ -1039,6 +1458,7 @@ static av_cold void haldclut_uninit(AVFilterContext *ctx) { LUT3DContext *lut3d = ctx->priv; ff_framesync_uninit(&lut3d->fs); + av_freep(&lut3d->lut); } static const AVOption haldclut_options[] = { @@ -1446,6 +1866,72 @@ DEFINE_INTERP_FUNC_PLANAR_1D(cosine, 16, 16) DEFINE_INTERP_FUNC_PLANAR_1D(cubic, 16, 16) DEFINE_INTERP_FUNC_PLANAR_1D(spline, 16, 16) +#define DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(name, depth) \ +static int interp_1d_##name##_pf##depth(AVFilterContext *ctx, \ + void *arg, int jobnr, \ + int nb_jobs) \ +{ \ + int x, y; \ + const LUT1DContext *lut1d = ctx->priv; \ + const ThreadData *td = arg; \ + const AVFrame *in = td->in; \ + const AVFrame *out = td->out; \ + const int direct = out == in; \ + const int slice_start = (in->height * jobnr ) / nb_jobs; \ + const int slice_end = (in->height * (jobnr+1)) / nb_jobs; \ + uint8_t *grow = out->data[0] + slice_start * out->linesize[0]; \ + uint8_t *brow = out->data[1] + slice_start * out->linesize[1]; \ + uint8_t *rrow = out->data[2] + slice_start * out->linesize[2]; \ + uint8_t *arow = out->data[3] + slice_start * out->linesize[3]; \ + const uint8_t *srcgrow = in->data[0] + slice_start * in->linesize[0]; \ + const uint8_t *srcbrow = in->data[1] + slice_start * in->linesize[1]; \ + const uint8_t *srcrrow = in->data[2] + slice_start * in->linesize[2]; \ + const uint8_t *srcarow = in->data[3] + slice_start * in->linesize[3]; \ + const float lutsize = lut1d->lutsize - 1; \ + const float scale_r = lut1d->scale.r * lutsize; \ + const float scale_g = lut1d->scale.g * lutsize; \ + const float scale_b = lut1d->scale.b * lutsize; \ + \ + for (y = slice_start; y < slice_end; y++) { \ + float *dstg = (float *)grow; \ + float *dstb = (float *)brow; \ + float *dstr = (float *)rrow; \ + float *dsta = (float *)arow; \ + const float *srcg = (const float *)srcgrow; \ + const float *srcb = (const float *)srcbrow; \ + const float *srcr = (const float *)srcrrow; \ + const float *srca = (const float *)srcarow; \ + for (x = 0; x < in->width; x++) { \ + float r = av_clipf(sanitizef(srcr[x]) * scale_r, 0.0f, lutsize); \ + float g = av_clipf(sanitizef(srcg[x]) * scale_g, 0.0f, lutsize); \ + float b = av_clipf(sanitizef(srcb[x]) * scale_b, 0.0f, lutsize); \ + r = interp_1d_##name(lut1d, 0, r); \ + g = interp_1d_##name(lut1d, 1, g); \ + b = interp_1d_##name(lut1d, 2, b); \ + dstr[x] = r; \ + dstg[x] = g; \ + dstb[x] = b; \ + if (!direct && in->linesize[3]) \ + dsta[x] = srca[x]; \ + } \ + grow += out->linesize[0]; \ + brow += out->linesize[1]; \ + rrow += out->linesize[2]; \ + arow += out->linesize[3]; \ + srcgrow += in->linesize[0]; \ + srcbrow += in->linesize[1]; \ + srcrrow += in->linesize[2]; \ + srcarow += in->linesize[3]; \ + } \ + return 0; \ +} + +DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(nearest, 32) +DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(linear, 32) +DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(cosine, 32) +DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(cubic, 32) +DEFINE_INTERP_FUNC_PLANAR_1D_FLOAT(spline, 32) + #define DEFINE_INTERP_FUNC_1D(name, nbits) \ static int interp_1d_##nbits##_##name(AVFilterContext *ctx, void *arg, \ int jobnr, int nb_jobs) \ @@ -1506,39 +1992,19 @@ DEFINE_INTERP_FUNC_1D(spline, 16) static int config_input_1d(AVFilterLink *inlink) { - int depth, is16bit = 0, planar = 0; + int depth, is16bit, isfloat, planar; LUT1DContext *lut1d = inlink->dst->priv; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); depth = desc->comp[0].depth; - - switch (inlink->format) { - case AV_PIX_FMT_RGB48: - case AV_PIX_FMT_BGR48: - case AV_PIX_FMT_RGBA64: - case AV_PIX_FMT_BGRA64: - is16bit = 1; - break; - case AV_PIX_FMT_GBRP9: - case AV_PIX_FMT_GBRP10: - case AV_PIX_FMT_GBRP12: - case AV_PIX_FMT_GBRP14: - case AV_PIX_FMT_GBRP16: - case AV_PIX_FMT_GBRAP10: - case AV_PIX_FMT_GBRAP12: - case AV_PIX_FMT_GBRAP16: - is16bit = 1; - case AV_PIX_FMT_GBRP: - case AV_PIX_FMT_GBRAP: - planar = 1; - break; - } - + is16bit = desc->comp[0].depth > 8; + planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR; + isfloat = desc->flags & AV_PIX_FMT_FLAG_FLOAT; ff_fill_rgba_map(lut1d->rgba_map, inlink->format); lut1d->step = av_get_padded_bits_per_pixel(desc) >> (3 + is16bit); #define SET_FUNC_1D(name) do { \ - if (planar) { \ + if (planar && !isfloat) { \ switch (depth) { \ case 8: lut1d->interp = interp_1d_8_##name##_p8; break; \ case 9: lut1d->interp = interp_1d_16_##name##_p9; break; \ @@ -1547,6 +2013,7 @@ static int config_input_1d(AVFilterLink *inlink) case 14: lut1d->interp = interp_1d_16_##name##_p14; break; \ case 16: lut1d->interp = interp_1d_16_##name##_p16; break; \ } \ + } else if (isfloat) { lut1d->interp = interp_1d_##name##_pf32; \ } else if (is16bit) { lut1d->interp = interp_1d_16_##name; \ } else { lut1d->interp = interp_1d_8_##name; } \ } while (0) @@ -1578,7 +2045,7 @@ static av_cold int lut1d_init(AVFilterContext *ctx) return 0; } - f = fopen(lut1d->file, "r"); + f = av_fopen_utf8(lut1d->file, "r"); if (!f) { ret = AVERROR(errno); av_log(ctx, AV_LOG_ERROR, "%s: %s\n", lut1d->file, av_err2str(ret)); diff --git a/libavfilter/vf_maskedclamp.c b/libavfilter/vf_maskedclamp.c index 16444e9de3d..52392c4c86f 100644 --- a/libavfilter/vf_maskedclamp.c +++ b/libavfilter/vf_maskedclamp.c @@ -26,6 +26,7 @@ #include "internal.h" #include "video.h" #include "framesync.h" +#include "maskedclamp.h" #define OFFSET(x) offsetof(MaskedClampContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM @@ -47,7 +48,7 @@ typedef struct MaskedClampContext { int depth; FFFrameSync fs; - int (*maskedclamp)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); + MaskedClampDSPContext dsp; } MaskedClampContext; static const AVOption maskedclamp_options[] = { @@ -74,6 +75,7 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA422P12, AV_PIX_FMT_YUVA444P12, 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, @@ -85,45 +87,7 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); } -static int process_frame(FFFrameSync *fs) -{ - AVFilterContext *ctx = fs->parent; - MaskedClampContext *s = fs->opaque; - AVFilterLink *outlink = ctx->outputs[0]; - AVFrame *out, *base, *dark, *bright; - int ret; - - if ((ret = ff_framesync_get_frame(&s->fs, 0, &base, 0)) < 0 || - (ret = ff_framesync_get_frame(&s->fs, 1, &dark, 0)) < 0 || - (ret = ff_framesync_get_frame(&s->fs, 2, &bright, 0)) < 0) - return ret; - - if (ctx->is_disabled) { - out = av_frame_clone(base); - 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, base); - - td.b = base; - td.o = dark; - td.m = bright; - td.d = out; - - ctx->internal->execute(ctx, s->maskedclamp, &td, NULL, FFMIN(s->height[0], - ff_filter_get_nb_threads(ctx))); - } - out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base); - - return ff_filter_frame(outlink, out); -} - -static int maskedclamp8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +static int maskedclamp_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { MaskedClampContext *s = ctx->priv; ThreadData *td = arg; @@ -144,7 +108,7 @@ static int maskedclamp8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) uint8_t *dst = td->d->data[p] + slice_start * dlinesize; const int undershoot = s->undershoot; const int overshoot = s->overshoot; - int x, y; + int y; if (!((1 << p) & s->planes)) { av_image_copy_plane(dst, dlinesize, bsrc, blinesize, @@ -153,14 +117,7 @@ static int maskedclamp8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) } for (y = slice_start; y < slice_end; y++) { - for (x = 0; x < w; x++) { - if (bsrc[x] < darksrc[x] - undershoot) - dst[x] = darksrc[x] - undershoot; - else if (bsrc[x] > brightsrc[x] + overshoot) - dst[x] = brightsrc[x] + overshoot; - else - dst[x] = bsrc[x]; - } + s->dsp.maskedclamp(bsrc, dst, darksrc, brightsrc, w, undershoot, overshoot); dst += dlinesize; bsrc += blinesize; @@ -172,55 +129,63 @@ static int maskedclamp8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) return 0; } -static int maskedclamp16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +static int process_frame(FFFrameSync *fs) { - MaskedClampContext *s = ctx->priv; - ThreadData *td = arg; - int p; + AVFilterContext *ctx = fs->parent; + MaskedClampContext *s = fs->opaque; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *out, *base, *dark, *bright; + int ret; - for (p = 0; p < s->nb_planes; p++) { - const ptrdiff_t blinesize = td->b->linesize[p] / 2; - const ptrdiff_t brightlinesize = td->m->linesize[p] / 2; - const ptrdiff_t darklinesize = td->o->linesize[p] / 2; - const ptrdiff_t dlinesize = td->d->linesize[p] / 2; - const int w = s->width[p]; - const int h = s->height[p]; - const int slice_start = (h * jobnr) / nb_jobs; - const int slice_end = (h * (jobnr+1)) / nb_jobs; - const uint16_t *bsrc = (const uint16_t *)td->b->data[p] + slice_start * blinesize; - const uint16_t *darksrc = (const uint16_t *)td->o->data[p] + slice_start * darklinesize; - const uint16_t *brightsrc = (const uint16_t *)td->m->data[p] + slice_start * brightlinesize; - uint16_t *dst = (uint16_t *)td->d->data[p] + slice_start * dlinesize; - const int undershoot = s->undershoot; - const int overshoot = s->overshoot; - int x, y; + if ((ret = ff_framesync_get_frame(&s->fs, 0, &base, 0)) < 0 || + (ret = ff_framesync_get_frame(&s->fs, 1, &dark, 0)) < 0 || + (ret = ff_framesync_get_frame(&s->fs, 2, &bright, 0)) < 0) + return ret; - if (!((1 << p) & s->planes)) { - av_image_copy_plane((uint8_t *)dst, dlinesize, (const uint8_t *)bsrc, blinesize, - s->linesize[p], slice_end - slice_start); - continue; - } + if (ctx->is_disabled) { + out = av_frame_clone(base); + if (!out) + return AVERROR(ENOMEM); + } else { + ThreadData td; - for (y = slice_start; y < slice_end; y++) { - for (x = 0; x < w; x++) { - if (bsrc[x] < darksrc[x] - undershoot) - dst[x] = darksrc[x] - undershoot; - else if (bsrc[x] > brightsrc[x] + overshoot) - dst[x] = brightsrc[x] + overshoot; - else - dst[x] = bsrc[x]; - } + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) + return AVERROR(ENOMEM); + av_frame_copy_props(out, base); - dst += dlinesize; - bsrc += blinesize; - darksrc += darklinesize; - brightsrc += brightlinesize; - } + td.b = base; + td.o = dark; + td.m = bright; + td.d = out; + + ctx->internal->execute(ctx, maskedclamp_slice, &td, NULL, FFMIN(s->height[0], + ff_filter_get_nb_threads(ctx))); } + out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base); - return 0; + return ff_filter_frame(outlink, out); +} + +#define MASKEDCLAMP(type, name) \ +static void maskedclamp##name(const uint8_t *bbsrc, uint8_t *ddst, \ + const uint8_t *ddarksrc, const uint8_t *bbrightsrc, \ + int w, int undershoot, int overshoot) \ +{ \ + const type *bsrc = (const type *)bbsrc; \ + const type *darksrc = (const type *)ddarksrc; \ + const type *brightsrc = (const type *)bbrightsrc; \ + type *dst = (type *)ddst; \ + \ + for (int x = 0; x < w; x++) { \ + dst[x] = FFMAX(bsrc[x], darksrc[x] - undershoot); \ + dst[x] = FFMIN(dst[x], brightsrc[x] + overshoot); \ + } \ } +MASKEDCLAMP(uint8_t, 8) +MASKEDCLAMP(uint16_t, 16) + static int config_input(AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; @@ -241,11 +206,16 @@ static int config_input(AVFilterLink *inlink) s->width[0] = s->width[3] = inlink->w; s->depth = desc->comp[0].depth; + s->undershoot = FFMIN(s->undershoot, (1 << s->depth) - 1); + s->overshoot = FFMIN(s->overshoot, (1 << s->depth) - 1); - if (desc->comp[0].depth == 8) - s->maskedclamp = maskedclamp8; + if (s->depth <= 8) + s->dsp.maskedclamp = maskedclamp8; else - s->maskedclamp = maskedclamp16; + s->dsp.maskedclamp = maskedclamp16; + + if (ARCH_X86) + ff_maskedclamp_init_x86(&s->dsp, s->depth); return 0; } diff --git a/libavfilter/vf_maskedmerge.c b/libavfilter/vf_maskedmerge.c index 0c531935efc..82f55e66a72 100644 --- a/libavfilter/vf_maskedmerge.c +++ b/libavfilter/vf_maskedmerge.c @@ -52,6 +52,7 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA422P12, AV_PIX_FMT_YUVA444P12, 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, diff --git a/libavfilter/vf_maskedminmax.c b/libavfilter/vf_maskedminmax.c new file mode 100644 index 00000000000..0685c8a16da --- /dev/null +++ b/libavfilter/vf_maskedminmax.c @@ -0,0 +1,360 @@ +/* + * 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/imgutils.h" +#include "libavutil/pixdesc.h" +#include "libavutil/opt.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" +#include "framesync.h" + +#define OFFSET(x) offsetof(MaskedMinMaxContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM + +typedef struct ThreadData { + AVFrame *src, *f1, *f2, *dst; +} ThreadData; + +typedef struct MaskedMinMaxContext { + const AVClass *class; + + int planes; + int maskedmin; + + int linesize[4]; + int planewidth[4], planeheight[4]; + int nb_planes; + int depth; + FFFrameSync fs; + + void (*maskedminmax)(const uint8_t *src, uint8_t *dst, const uint8_t *f1, const uint8_t *f2, int w); +} MaskedMinMaxContext; + +static const AVOption maskedminmax_options[] = { + { "planes", "set planes", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=0xF}, 0, 0xF, FLAGS }, + { NULL } +}; + +static av_cold int init(AVFilterContext *ctx) +{ + MaskedMinMaxContext *s = ctx->priv; + + s->maskedmin = !strcmp(ctx->filter->name, "maskedmin"); + + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, + AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, + AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, + 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_YUV440P12, + 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_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9, + AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, + AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, + 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_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_NONE + }; + + return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); +} + +static void maskedmin8(const uint8_t *src, uint8_t *dst, const uint8_t *f1, const uint8_t *f2, int w) +{ + for (int x = 0; x < w; x++) + dst[x] = FFABS(src[x] - f2[x]) < FFABS(src[x] - f1[x]) ? f2[x] : f1[x]; +} + +static void maskedmax8(const uint8_t *src, uint8_t *dst, const uint8_t *f1, const uint8_t *f2, int w) +{ + for (int x = 0; x < w; x++) + dst[x] = FFABS(src[x] - f2[x]) > FFABS(src[x] - f1[x]) ? f2[x] : f1[x]; +} + +static void maskedmin16(const uint8_t *ssrc, uint8_t *ddst, const uint8_t *ff1, const uint8_t *ff2, int w) +{ + const uint16_t *src = (const uint16_t *)ssrc; + const uint16_t *f1 = (const uint16_t *)ff1; + const uint16_t *f2 = (const uint16_t *)ff2; + uint16_t *dst = (uint16_t *)ddst; + + for (int x = 0; x < w; x++) + dst[x] = FFABS(src[x] - f2[x]) < FFABS(src[x] - f1[x]) ? f2[x] : f1[x]; +} + +static void maskedmax16(const uint8_t *ssrc, uint8_t *ddst, const uint8_t *ff1, const uint8_t *ff2, int w) +{ + const uint16_t *src = (const uint16_t *)ssrc; + const uint16_t *f1 = (const uint16_t *)ff1; + const uint16_t *f2 = (const uint16_t *)ff2; + uint16_t *dst = (uint16_t *)ddst; + + for (int x = 0; x < w; x++) + dst[x] = FFABS(src[x] - f2[x]) > FFABS(src[x] - f1[x]) ? f2[x] : f1[x]; +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + MaskedMinMaxContext *s = ctx->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + int vsub, hsub, ret; + + s->nb_planes = av_pix_fmt_count_planes(inlink->format); + + if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0) + return ret; + + hsub = desc->log2_chroma_w; + vsub = desc->log2_chroma_h; + s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, vsub); + s->planeheight[0] = s->planeheight[3] = inlink->h; + s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, hsub); + s->planewidth[0] = s->planewidth[3] = inlink->w; + + s->depth = desc->comp[0].depth; + + if (desc->comp[0].depth == 8) + s->maskedminmax = s->maskedmin ? maskedmin8 : maskedmax8; + else + s->maskedminmax = s->maskedmin ? maskedmin16 : maskedmax16; + + return 0; +} + +static int maskedminmax_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + MaskedMinMaxContext *s = ctx->priv; + ThreadData *td = arg; + + for (int p = 0; p < s->nb_planes; p++) { + const ptrdiff_t src_linesize = td->src->linesize[p]; + const ptrdiff_t f1_linesize = td->f1->linesize[p]; + const ptrdiff_t f2_linesize = td->f2->linesize[p]; + const ptrdiff_t dst_linesize = td->dst->linesize[p]; + const int w = s->planewidth[p]; + const int h = s->planeheight[p]; + const int slice_start = (h * jobnr) / nb_jobs; + const int slice_end = (h * (jobnr+1)) / nb_jobs; + const uint8_t *src = td->src->data[p] + slice_start * src_linesize; + const uint8_t *f1 = td->f1->data[p] + slice_start * f1_linesize; + const uint8_t *f2 = td->f2->data[p] + slice_start * f2_linesize; + uint8_t *dst = td->dst->data[p] + slice_start * dst_linesize; + + if (!((1 << p) & s->planes)) { + av_image_copy_plane(dst, dst_linesize, src, src_linesize, + s->linesize[p], slice_end - slice_start); + continue; + } + + for (int y = slice_start; y < slice_end; y++) { + s->maskedminmax(src, dst, f1, f2, w); + + dst += dst_linesize; + src += src_linesize; + f1 += f1_linesize; + f2 += f2_linesize; + } + } + + return 0; +} + +static int process_frame(FFFrameSync *fs) +{ + AVFilterContext *ctx = fs->parent; + MaskedMinMaxContext *s = fs->opaque; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *out, *src, *f1, *f2; + int ret; + + if ((ret = ff_framesync_get_frame(&s->fs, 0, &src, 0)) < 0 || + (ret = ff_framesync_get_frame(&s->fs, 1, &f1, 0)) < 0 || + (ret = ff_framesync_get_frame(&s->fs, 2, &f2, 0)) < 0) + return ret; + + if (ctx->is_disabled) { + out = av_frame_clone(src); + 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, src); + + td.src = src; + td.f1 = f1; + td.f2 = f2; + td.dst = out; + + ctx->internal->execute(ctx, maskedminmax_slice, &td, NULL, FFMIN(s->planeheight[0], + ff_filter_get_nb_threads(ctx))); + } + out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base); + + return ff_filter_frame(outlink, out); +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + MaskedMinMaxContext *s = ctx->priv; + AVFilterLink *source = ctx->inputs[0]; + AVFilterLink *f1 = ctx->inputs[1]; + AVFilterLink *f2 = ctx->inputs[2]; + FFFrameSyncIn *in; + int ret; + + if (source->format != f1->format || + source->format != f2->format) { + av_log(ctx, AV_LOG_ERROR, "inputs must be of same pixel format\n"); + return AVERROR(EINVAL); + } + if (source->w != f1->w || source->h != f1->h || + source->w != f2->w || source->h != f2->h) { + av_log(ctx, AV_LOG_ERROR, "First input link %s parameters " + "(size %dx%d) do not match the corresponding " + "second input link %s parameters (%dx%d) " + "and/or third input link %s parameters (size %dx%d)\n", + ctx->input_pads[0].name, source->w, source->h, + ctx->input_pads[1].name, f1->w, f1->h, + ctx->input_pads[2].name, f2->w, f2->h); + return AVERROR(EINVAL); + } + + outlink->w = source->w; + outlink->h = source->h; + outlink->sample_aspect_ratio = source->sample_aspect_ratio; + outlink->frame_rate = source->frame_rate; + + if ((ret = ff_framesync_init(&s->fs, ctx, 3)) < 0) + return ret; + + in = s->fs.in; + in[0].time_base = source->time_base; + in[1].time_base = f1->time_base; + in[2].time_base = f2->time_base; + in[0].sync = 1; + in[0].before = EXT_STOP; + in[0].after = EXT_INFINITY; + in[1].sync = 1; + in[1].before = EXT_STOP; + in[1].after = EXT_INFINITY; + in[2].sync = 1; + in[2].before = EXT_STOP; + in[2].after = EXT_INFINITY; + s->fs.opaque = s; + s->fs.on_event = process_frame; + + ret = ff_framesync_configure(&s->fs); + outlink->time_base = s->fs.time_base; + + return ret; +} + +static int activate(AVFilterContext *ctx) +{ + MaskedMinMaxContext *s = ctx->priv; + return ff_framesync_activate(&s->fs); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + MaskedMinMaxContext *s = ctx->priv; + + ff_framesync_uninit(&s->fs); +} + +static const AVFilterPad maskedminmax_inputs[] = { + { + .name = "source", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + }, + { + .name = "filter1", + .type = AVMEDIA_TYPE_VIDEO, + }, + { + .name = "filter2", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +static const AVFilterPad maskedminmax_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, + { NULL } +}; + +#define maskedmin_options maskedminmax_options +AVFILTER_DEFINE_CLASS(maskedmin); + +AVFilter ff_vf_maskedmin = { + .name = "maskedmin", + .description = NULL_IF_CONFIG_SMALL("Apply filtering with minimum difference of two streams."), + .priv_class = &maskedmin_class, + .priv_size = sizeof(MaskedMinMaxContext), + .init = init, + .uninit = uninit, + .activate = activate, + .query_formats = query_formats, + .inputs = maskedminmax_inputs, + .outputs = maskedminmax_outputs, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, +}; + +#define maskedmax_options maskedminmax_options +AVFILTER_DEFINE_CLASS(maskedmax); + +AVFilter ff_vf_maskedmax = { + .name = "maskedmax", + .description = NULL_IF_CONFIG_SMALL("Apply filtering with maximum difference of two streams."), + .priv_class = &maskedmax_class, + .priv_size = sizeof(MaskedMinMaxContext), + .init = init, + .uninit = uninit, + .activate = activate, + .query_formats = query_formats, + .inputs = maskedminmax_inputs, + .outputs = maskedminmax_outputs, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, +}; diff --git a/libavfilter/vf_maskedthreshold.c b/libavfilter/vf_maskedthreshold.c new file mode 100644 index 00000000000..d925819f6a7 --- /dev/null +++ b/libavfilter/vf_maskedthreshold.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2020 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/imgutils.h" +#include "libavutil/pixdesc.h" +#include "libavutil/opt.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" +#include "framesync.h" + +typedef struct MaskedThresholdContext { + const AVClass *class; + + int threshold; + int planes; + + int linesize[4]; + int planewidth[4], planeheight[4]; + int nb_planes; + int depth; + FFFrameSync fs; + + void (*maskedthreshold)(const uint8_t *src, const uint8_t *ref, uint8_t *dst, int threshold, int w); +} MaskedThresholdContext; + +#define OFFSET(x) offsetof(MaskedThresholdContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM + +typedef struct ThreadData { + AVFrame *src, *ref, *dst; +} ThreadData; + +static const AVOption maskedthreshold_options[] = { + { "threshold", "set threshold", OFFSET(threshold), AV_OPT_TYPE_INT, {.i64=1}, 0, UINT16_MAX, FLAGS }, + { "planes", "set planes", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=0xF}, 0, 0xF, FLAGS }, + { NULL } +}; + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, + AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, + AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, + 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_YUV440P12, + 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_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9, + AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, + AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, + 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_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_NONE + }; + + return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); +} + +static void threshold8(const uint8_t *src, const uint8_t *ref, uint8_t *dst, int threshold, int w) +{ + for (int x = 0; x < w; x++) + dst[x] = FFABS(src[x] - ref[x]) <= threshold ? src[x] : ref[x]; +} + +static void threshold16(const uint8_t *ssrc, const uint8_t *rref, uint8_t *ddst, int threshold, int w) +{ + const uint16_t *src = (const uint16_t *)ssrc; + const uint16_t *ref = (const uint16_t *)rref; + uint16_t *dst = (uint16_t *)ddst; + + for (int x = 0; x < w; x++) + dst[x] = FFABS(src[x] - ref[x]) <= threshold ? src[x] : ref[x]; +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + MaskedThresholdContext *s = ctx->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + int vsub, hsub, ret; + + s->nb_planes = av_pix_fmt_count_planes(inlink->format); + + if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0) + return ret; + + hsub = desc->log2_chroma_w; + vsub = desc->log2_chroma_h; + s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, vsub); + s->planeheight[0] = s->planeheight[3] = inlink->h; + s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, hsub); + s->planewidth[0] = s->planewidth[3] = inlink->w; + + s->depth = desc->comp[0].depth; + + if (desc->comp[0].depth == 8) + s->maskedthreshold = threshold8; + else + s->maskedthreshold = threshold16; + + return 0; +} + +static int threshold_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + MaskedThresholdContext *s = ctx->priv; + const int threshold = s->threshold; + ThreadData *td = arg; + + for (int p = 0; p < s->nb_planes; p++) { + const ptrdiff_t src_linesize = td->src->linesize[p]; + const ptrdiff_t ref_linesize = td->ref->linesize[p]; + const ptrdiff_t dst_linesize = td->dst->linesize[p]; + const int w = s->planewidth[p]; + const int h = s->planeheight[p]; + const int slice_start = (h * jobnr) / nb_jobs; + const int slice_end = (h * (jobnr+1)) / nb_jobs; + const uint8_t *src = td->src->data[p] + slice_start * src_linesize; + const uint8_t *ref = td->ref->data[p] + slice_start * ref_linesize; + uint8_t *dst = td->dst->data[p] + slice_start * dst_linesize; + + if (!((1 << p) & s->planes)) { + av_image_copy_plane(dst, dst_linesize, ref, ref_linesize, + s->linesize[p], slice_end - slice_start); + continue; + } + + for (int y = slice_start; y < slice_end; y++) { + s->maskedthreshold(src, ref, dst, threshold, w); + + dst += dst_linesize; + src += src_linesize; + ref += ref_linesize; + } + } + + return 0; +} + +static int process_frame(FFFrameSync *fs) +{ + AVFilterContext *ctx = fs->parent; + MaskedThresholdContext *s = fs->opaque; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *out, *src, *ref; + int ret; + + if ((ret = ff_framesync_get_frame(&s->fs, 0, &src, 0)) < 0 || + (ret = ff_framesync_get_frame(&s->fs, 1, &ref, 0)) < 0) + return ret; + + if (ctx->is_disabled) { + out = av_frame_clone(src); + 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, src); + + td.src = src; + td.ref = ref; + td.dst = out; + + ctx->internal->execute(ctx, threshold_slice, &td, NULL, FFMIN(s->planeheight[2], + ff_filter_get_nb_threads(ctx))); + } + out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base); + + return ff_filter_frame(outlink, out); +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + MaskedThresholdContext *s = ctx->priv; + AVFilterLink *source = ctx->inputs[0]; + AVFilterLink *ref = ctx->inputs[1]; + FFFrameSyncIn *in; + int ret; + + if (source->format != ref->format) { + av_log(ctx, AV_LOG_ERROR, "inputs must be of same pixel format\n"); + return AVERROR(EINVAL); + } + if (source->w != ref->w || source->h != ref->h) { + av_log(ctx, AV_LOG_ERROR, "First input link %s parameters " + "(size %dx%d) do not match the corresponding " + "second input link %s parameters (%dx%d)\n", + ctx->input_pads[0].name, source->w, source->h, + ctx->input_pads[1].name, ref->w, ref->h); + return AVERROR(EINVAL); + } + + outlink->w = source->w; + outlink->h = source->h; + outlink->sample_aspect_ratio = source->sample_aspect_ratio; + outlink->frame_rate = source->frame_rate; + + if ((ret = ff_framesync_init(&s->fs, ctx, 2)) < 0) + return ret; + + in = s->fs.in; + in[0].time_base = source->time_base; + in[1].time_base = ref->time_base; + in[0].sync = 1; + in[0].before = EXT_STOP; + in[0].after = EXT_INFINITY; + in[1].sync = 1; + in[1].before = EXT_STOP; + in[1].after = EXT_INFINITY; + s->fs.opaque = s; + s->fs.on_event = process_frame; + + ret = ff_framesync_configure(&s->fs); + outlink->time_base = s->fs.time_base; + + return ret; +} + +static int activate(AVFilterContext *ctx) +{ + MaskedThresholdContext *s = ctx->priv; + return ff_framesync_activate(&s->fs); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + MaskedThresholdContext *s = ctx->priv; + + ff_framesync_uninit(&s->fs); +} + +static const AVFilterPad maskedthreshold_inputs[] = { + { + .name = "source", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + }, + { + .name = "reference", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +static const AVFilterPad maskedthreshold_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(maskedthreshold); + +AVFilter ff_vf_maskedthreshold = { + .name = "maskedthreshold", + .description = NULL_IF_CONFIG_SMALL("Pick pixels comparing absolute difference of two streams with threshold."), + .priv_class = &maskedthreshold_class, + .priv_size = sizeof(MaskedThresholdContext), + .uninit = uninit, + .activate = activate, + .query_formats = query_formats, + .inputs = maskedthreshold_inputs, + .outputs = maskedthreshold_outputs, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, +}; diff --git a/libavfilter/vf_maskfun.c b/libavfilter/vf_maskfun.c index a8c6466d221..4ceb21b9431 100644 --- a/libavfilter/vf_maskfun.c +++ b/libavfilter/vf_maskfun.c @@ -248,6 +248,13 @@ static int config_input(AVFilterLink *inlink) return 0; } +static av_cold void uninit(AVFilterContext *ctx) +{ + MaskFunContext *s = ctx->priv; + + av_frame_free(&s->empty); +} + static const AVFilterPad maskfun_inputs[] = { { .name = "default", @@ -272,6 +279,7 @@ AVFilter ff_vf_maskfun = { .description = NULL_IF_CONFIG_SMALL("Create Mask."), .priv_size = sizeof(MaskFunContext), .query_formats = query_formats, + .uninit = uninit, .inputs = maskfun_inputs, .outputs = maskfun_outputs, .priv_class = &maskfun_class, diff --git a/libavfilter/vf_median.c b/libavfilter/vf_median.c new file mode 100644 index 00000000000..0189fabd07b --- /dev/null +++ b/libavfilter/vf_median.c @@ -0,0 +1,299 @@ +/* + * 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 + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + */ + +#include "libavutil/avassert.h" +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "median.h" +#include "video.h" + +#define DEPTH 8 +#include "median_template.c" + +#undef DEPTH +#define DEPTH 9 +#include "median_template.c" + +#undef DEPTH +#define DEPTH 10 +#include "median_template.c" + +#undef DEPTH +#define DEPTH 12 +#include "median_template.c" + +#undef DEPTH +#define DEPTH 14 +#include "median_template.c" + +#undef DEPTH +#define DEPTH 16 +#include "median_template.c" + +#define OFFSET(x) offsetof(MedianContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM + +static const AVOption median_options[] = { + { "radius", "set median radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64=1}, 1, 127, FLAGS }, + { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=0xF}, 0, 0xF, FLAGS }, + { "radiusV", "set median vertical radius", OFFSET(radiusV), AV_OPT_TYPE_INT, {.i64=0},0, 127, FLAGS }, + { "percentile", "set median percentile", OFFSET(percentile), AV_OPT_TYPE_FLOAT, {.dbl=.5}, 0., 1., FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(median); + +static void hadd(htype *dst, const htype *src, int bins) +{ + for (int i = 0; i < bins; i++) + dst[i] += src[i]; +} + +static void hsub(htype *dst, const htype *src, int bins) +{ + for (int i = 0; i < bins; i++) + dst[i] -= src[i]; +} + +static void hmuladd(htype *dst, const htype *src, int f, int bins) +{ + for (int i = 0; i < bins; i++) + dst[i] += f * src[i]; +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, + AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, + AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, + AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, AV_PIX_FMT_GBRP9, + 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_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12, + 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_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, + AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, + AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, + 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_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16, + AV_PIX_FMT_NONE + }; + + return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); +} + +static void check_params(MedianContext *s, AVFilterLink *inlink) +{ + for (int i = 0; i < s->nb_planes; i++) { + if (!(s->planes & (1 << i))) + continue; + + if (s->planewidth[i] < s->radius * 2 + 1) { + av_log(inlink->dst, AV_LOG_WARNING, "The %d plane width %d must be not less than %d, clipping radius.\n", i, s->planewidth[i], s->radius * 2 + 1); + s->radius = (s->planewidth[i] - 1) / 2; + } + + if (s->planeheight[i] < s->radiusV * 2 + 1) { + av_log(inlink->dst, AV_LOG_WARNING, "The %d plane height %d must be not less than %d, clipping radiusV.\n", i, s->planeheight[i], s->radiusV * 2 + 1); + s->radiusV = (s->planeheight[i] - 1) / 2; + } + } + + s->t = (2 * s->radius * s->radiusV + s->radiusV + s->radius) * 2.f * s->percentile; +} + +static int config_input(AVFilterLink *inlink) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + MedianContext *s = inlink->dst->priv; + + s->depth = desc->comp[0].depth; + s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); + s->planewidth[0] = s->planewidth[3] = inlink->w; + s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); + s->planeheight[0] = s->planeheight[3] = inlink->h; + + s->radiusV = !s->radiusV ? s->radius : s->radiusV; + s->nb_planes = av_pix_fmt_count_planes(inlink->format); + + check_params(s, inlink); + + s->nb_threads = FFMAX(1, FFMIN(s->planeheight[1] / (s->radiusV + 1), ff_filter_get_nb_threads(inlink->dst))); + s->bins = 1 << ((s->depth + 1) / 2); + s->fine_size = s->bins * s->bins * inlink->w; + s->coarse_size = s->bins * inlink->w; + s->coarse = av_calloc(s->nb_threads, sizeof(*s->coarse)); + s->fine = av_calloc(s->nb_threads, sizeof(*s->fine)); + if (!s->coarse || !s->fine) + return AVERROR(ENOMEM); + for (int i = 0; i < s->nb_threads; i++) { + s->coarse[i] = av_malloc_array(s->coarse_size, sizeof(**s->coarse)); + s->fine[i] = av_malloc_array(s->fine_size, sizeof(**s->fine)); + if (!s->coarse[i] || !s->fine[i]) + return AVERROR(ENOMEM); + } + + s->hadd = hadd; + s->hsub = hsub; + s->hmuladd = hmuladd; + + switch (s->depth) { + case 8: s->filter_plane = filter_plane_8; break; + case 9: s->filter_plane = filter_plane_9; break; + case 10: s->filter_plane = filter_plane_10; break; + case 12: s->filter_plane = filter_plane_12; break; + case 14: s->filter_plane = filter_plane_14; break; + case 16: s->filter_plane = filter_plane_16; break; + } + + return 0; +} + +typedef struct ThreadData { + AVFrame *in, *out; +} ThreadData; + +static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + MedianContext *s = ctx->priv; + ThreadData *td = arg; + AVFrame *in = td->in; + AVFrame *out = td->out; + + for (int plane = 0; plane < s->nb_planes; plane++) { + const int h = s->planeheight[plane]; + const int w = s->planewidth[plane]; + const int slice_h_start = (h * jobnr) / nb_jobs; + const int slice_h_end = (h * (jobnr+1)) / nb_jobs; + + if (!(s->planes & (1 << plane))) { + av_image_copy_plane(out->data[plane] + slice_h_start * out->linesize[plane], + out->linesize[plane], + in->data[plane] + slice_h_start * in->linesize[plane], + in->linesize[plane], + w * ((s->depth + 7) / 8), + slice_h_end - slice_h_start); + continue; + } + + s->filter_plane(ctx, in->data[plane], + in->linesize[plane], + out->data[plane] + slice_h_start * out->linesize[plane], + out->linesize[plane], w, h, + slice_h_start, slice_h_end, jobnr); + } + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + MedianContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + ThreadData td; + AVFrame *out; + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + + td.in = in; td.out = out; + ctx->internal->execute(ctx, filter_slice, &td, NULL, s->nb_threads); + + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + MedianContext *s = ctx->priv; + + for (int i = 0; i < s->nb_threads && s->coarse && s->fine; i++) { + av_freep(&s->coarse[i]); + av_freep(&s->fine[i]); + } + + av_freep(&s->coarse); + av_freep(&s->fine); +} + +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + MedianContext *s = ctx->priv; + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + if (!s->radiusV) + s->radiusV = s->radius; + check_params(s, ctx->inputs[0]); + + return 0; +} + +static const AVFilterPad median_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad median_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter ff_vf_median = { + .name = "median", + .description = NULL_IF_CONFIG_SMALL("Apply Median filter."), + .priv_size = sizeof(MedianContext), + .priv_class = &median_class, + .uninit = uninit, + .query_formats = query_formats, + .inputs = median_inputs, + .outputs = median_outputs, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = process_command, +}; diff --git a/libavfilter/vf_midequalizer.c b/libavfilter/vf_midequalizer.c index 38d2193b2c8..44c0bd751a1 100644 --- a/libavfilter/vf_midequalizer.c +++ b/libavfilter/vf_midequalizer.c @@ -74,7 +74,12 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, 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_YUVA422P12, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, + AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, + AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, + AV_PIX_FMT_GRAY16, AV_PIX_FMT_NONE }; diff --git a/libavfilter/vf_minterpolate.c b/libavfilter/vf_minterpolate.c index b0bb238ade7..c9ce80420d5 100644 --- a/libavfilter/vf_minterpolate.c +++ b/libavfilter/vf_minterpolate.c @@ -185,6 +185,7 @@ typedef struct MIContext { int64_t out_pts; int b_width, b_height, b_count; int log2_mb_size; + int bitdepth; int scd_method; int scene_changed; @@ -229,7 +230,7 @@ static const AVOption minterpolate_options[] = { { "scd", "scene change detection method", OFFSET(scd_method), AV_OPT_TYPE_INT, {.i64 = SCD_METHOD_FDIFF}, SCD_METHOD_NONE, SCD_METHOD_FDIFF, FLAGS, "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 = 5.0}, 0, 100.0, FLAGS }, + { "scd_threshold", "scene change threshold", OFFSET(scd_threshold), AV_OPT_TYPE_DOUBLE, {.dbl = 10.}, 0, 100.0, FLAGS }, { NULL } }; @@ -343,6 +344,7 @@ static int config_input(AVFilterLink *inlink) mi_ctx->log2_chroma_h = desc->log2_chroma_h; mi_ctx->log2_chroma_w = desc->log2_chroma_w; + mi_ctx->bitdepth = desc->comp[0].depth; mi_ctx->nb_planes = av_pix_fmt_count_planes(inlink->format); @@ -383,7 +385,7 @@ static int config_input(AVFilterLink *inlink) } if (mi_ctx->scd_method == SCD_METHOD_FDIFF) { - mi_ctx->sad = ff_scene_sad_get_fn(8); + mi_ctx->sad = ff_scene_sad_get_fn(mi_ctx->bitdepth == 8 ? 8 : 16); if (!mi_ctx->sad) return AVERROR(EINVAL); } @@ -836,7 +838,7 @@ static int detect_scene_change(MIContext *mi_ctx) uint64_t sad; mi_ctx->sad(p1, linesize1, p2, linesize2, me_ctx->width, me_ctx->height, &sad); emms_c(); - mafd = (double) sad / (me_ctx->height * me_ctx->width * 3); + mafd = (double) sad * 100.0 / (me_ctx->height * me_ctx->width) / (1 << mi_ctx->bitdepth); diff = fabs(mafd - mi_ctx->prev_mafd); ret = av_clipf(FFMIN(mafd, diff), 0, 100.0); mi_ctx->prev_mafd = mafd; @@ -1095,6 +1097,7 @@ static void interpolate(AVFilterLink *inlink, AVFrame *avf_out) } if (mi_ctx->scene_changed) { + av_log(ctx, AV_LOG_DEBUG, "scene changed, input pts %"PRId64"\n", mi_ctx->frames[1].avf->pts); /* duplicate frame */ av_frame_copy(avf_out, alpha > ALPHA_MAX / 2 ? mi_ctx->frames[2].avf : mi_ctx->frames[1].avf); return; diff --git a/libavfilter/vf_misc_vaapi.c b/libavfilter/vf_misc_vaapi.c index 54516d7e356..5814ff8c2ed 100644 --- a/libavfilter/vf_misc_vaapi.c +++ b/libavfilter/vf_misc_vaapi.c @@ -145,7 +145,7 @@ static int misc_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) err = av_frame_copy_props(output_frame, input_frame); if (err < 0) - return err; + goto fail; err = ff_vaapi_vpp_init_params(avctx, ¶ms, input_frame, output_frame); diff --git a/libavfilter/vf_mix.c b/libavfilter/vf_mix.c index b5a282ad4ce..bdb67d8e5ac 100644 --- a/libavfilter/vf_mix.c +++ b/libavfilter/vf_mix.c @@ -108,7 +108,10 @@ static av_cold int init(AVFilterContext *ctx) break; p = NULL; - av_sscanf(arg, "%f", &s->weights[i]); + if (av_sscanf(arg, "%f", &s->weights[i]) != 1) { + av_log(ctx, AV_LOG_ERROR, "Invalid syntax for weights[%d].\n", i); + return AVERROR(EINVAL); + } s->wfactor += s->weights[i]; last = i; } @@ -289,7 +292,7 @@ static av_cold void uninit(AVFilterContext *ctx) for (i = 0; i < ctx->nb_inputs; i++) av_freep(&ctx->input_pads[i].name); } else { - for (i = 0; i < s->nb_frames; i++) + for (i = 0; i < s->nb_frames && s->frames; i++) av_frame_free(&s->frames[i]); } av_freep(&s->frames); @@ -305,7 +308,7 @@ static int activate(AVFilterContext *ctx) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM static const AVOption mix_options[] = { - { "inputs", "set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64=2}, 2, INT_MAX, .flags = FLAGS }, + { "inputs", "set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64=2}, 2, INT16_MAX, .flags = FLAGS }, { "weights", "set weight for each input", OFFSET(weights_str), AV_OPT_TYPE_STRING, {.str="1 1"}, 0, 0, .flags = FLAGS }, { "scale", "set scale", OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, INT16_MAX, .flags = FLAGS }, { "duration", "how to determine end of stream", OFFSET(duration), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, .flags = FLAGS, "duration" }, diff --git a/libavfilter/vf_neighbor.c b/libavfilter/vf_neighbor.c index e50d4b4ed06..17a9b882653 100644 --- a/libavfilter/vf_neighbor.c +++ b/libavfilter/vf_neighbor.c @@ -64,6 +64,7 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA422P12, AV_PIX_FMT_YUVA444P12, 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, @@ -296,9 +297,11 @@ static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) src + (width - 2) * bpc, src + (width - 2) * bpc, src + (width - 2) * bpc + ph * stride, src + (width - 1) * bpc + ph * stride, src + (width - 2) * bpc + ph * stride}; - s->filter(dst, src, 1, threshold, coordinateslb, s->coordinates, s->max); - s->filter(dst + 1 * bpc, src + 1 * bpc, width - 2, threshold, coordinates, s->coordinates, s->max); - s->filter(dst + (width - 1) * bpc, src + (width - 1) * bpc, 1, threshold, coordinatesrb, s->coordinates, s->max); + s->filter(dst, src, 1, threshold, coordinateslb, s->coordinates, s->max); + if (width > 1) { + s->filter(dst + 1 * bpc, src + 1 * bpc, width - 2, threshold, coordinates, s->coordinates, s->max); + s->filter(dst + (width - 1) * bpc, src + (width - 1) * bpc, 1, threshold, coordinatesrb, s->coordinates, s->max); + } src += stride; dst += dstride; @@ -350,7 +353,7 @@ static const AVFilterPad neighbor_outputs[] = { }; #define OFFSET(x) offsetof(NContext, 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 #define DEFINE_NEIGHBOR_FILTER(name_, description_) \ AVFILTER_DEFINE_CLASS(name_); \ @@ -365,6 +368,7 @@ AVFilter ff_vf_##name_ = { \ .outputs = neighbor_outputs, \ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC| \ AVFILTER_FLAG_SLICE_THREADS, \ + .process_command = ff_filter_process_command, \ } #if CONFIG_EROSION_FILTER diff --git a/libavfilter/vf_nlmeans.c b/libavfilter/vf_nlmeans.c index dcb5a039533..06233b0dd46 100644 --- a/libavfilter/vf_nlmeans.c +++ b/libavfilter/vf_nlmeans.c @@ -419,7 +419,7 @@ static void weight_averages(uint8_t *dst, ptrdiff_t dst_linesize, // Also weight the centered pixel wa[x].total_weight += 1.f; wa[x].sum += 1.f * src[x]; - dst[x] = av_clip_uint8(wa[x].sum / wa[x].total_weight); + dst[x] = av_clip_uint8(wa[x].sum / wa[x].total_weight + 0.5f); } dst += dst_linesize; src += src_linesize; diff --git a/libavfilter/vf_nnedi.c b/libavfilter/vf_nnedi.c index b14aa64c046..9bad99e98a8 100644 --- a/libavfilter/vf_nnedi.c +++ b/libavfilter/vf_nnedi.c @@ -809,8 +809,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *src) ret = get_frame(ctx, 1); if (ret < 0) { av_frame_free(&s->dst); - av_frame_free(&s->src); av_frame_free(&s->second); + s->src = NULL; return ret; } dst = s->dst; diff --git a/libavfilter/vf_normalize.c b/libavfilter/vf_normalize.c index 48eea59e64c..e2e00b12349 100644 --- a/libavfilter/vf_normalize.c +++ b/libavfilter/vf_normalize.c @@ -73,6 +73,7 @@ */ #include "libavutil/imgutils.h" +#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" @@ -81,6 +82,17 @@ #include "internal.h" #include "video.h" +typedef struct NormalizeHistory { + uint16_t *history; // History entries. + uint64_t history_sum; // Sum of history entries. +} NormalizeHistory; + +typedef struct NormalizeLocal { + uint16_t in; // Original input byte value for this frame. + float smoothed; // Smoothed input value [0,255]. + float out; // Output value [0,255] +} NormalizeLocal; + typedef struct NormalizeContext { const AVClass *class; @@ -92,67 +104,202 @@ typedef struct NormalizeContext { float strength; uint8_t co[4]; // Offsets to R,G,B,A bytes respectively in each pixel + int depth; + int sblackpt[4]; + int swhitept[4]; int num_components; // Number of components in the pixel format int step; int history_len; // Number of frames to average; based on smoothing factor int frame_num; // Increments on each frame, starting from 0. // Per-extremum, per-channel history, for temporal smoothing. - struct { - uint8_t *history; // History entries. - uint32_t history_sum; // Sum of history entries. - } min[3], max[3]; // Min and max for each channel in {R,G,B}. - uint8_t *history_mem; // Single allocation for above history entries + NormalizeHistory min[3], max[3]; // Min and max for each channel in {R,G,B}. + uint16_t *history_mem; // Single allocation for above history entries + + uint16_t lut[3][65536]; // Lookup table + void (*find_min_max)(struct NormalizeContext *s, AVFrame *in, NormalizeLocal min[3], NormalizeLocal max[3]); + void (*process)(struct NormalizeContext *s, AVFrame *in, AVFrame *out); } NormalizeContext; #define OFFSET(x) offsetof(NormalizeContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define FLAGSR AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption normalize_options[] = { - { "blackpt", "output color to which darkest input color is mapped", OFFSET(blackpt), AV_OPT_TYPE_COLOR, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "whitept", "output color to which brightest input color is mapped", OFFSET(whitept), AV_OPT_TYPE_COLOR, { .str = "white" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "blackpt", "output color to which darkest input color is mapped", OFFSET(blackpt), AV_OPT_TYPE_COLOR, { .str = "black" }, 0, 0, FLAGSR }, + { "whitept", "output color to which brightest input color is mapped", OFFSET(whitept), AV_OPT_TYPE_COLOR, { .str = "white" }, 0, 0, FLAGSR }, { "smoothing", "amount of temporal smoothing of the input range, to reduce flicker", OFFSET(smoothing), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX/8, FLAGS }, - { "independence", "proportion of independent to linked channel normalization", OFFSET(independence), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, 1.0, FLAGS }, - { "strength", "strength of filter, from no effect to full normalization", OFFSET(strength), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, 1.0, FLAGS }, + { "independence", "proportion of independent to linked channel normalization", OFFSET(independence), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, 1.0, FLAGSR }, + { "strength", "strength of filter, from no effect to full normalization", OFFSET(strength), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, 0.0, 1.0, FLAGSR }, { NULL } }; AVFILTER_DEFINE_CLASS(normalize); +static void find_min_max(NormalizeContext *s, AVFrame *in, NormalizeLocal min[3], NormalizeLocal max[3]) +{ + for (int c = 0; c < 3; c++) + min[c].in = max[c].in = in->data[0][s->co[c]]; + for (int y = 0; y < in->height; y++) { + uint8_t *inp = in->data[0] + y * in->linesize[0]; + for (int x = 0; x < in->width; x++) { + for (int c = 0; c < 3; c++) { + min[c].in = FFMIN(min[c].in, inp[s->co[c]]); + max[c].in = FFMAX(max[c].in, inp[s->co[c]]); + } + inp += s->step; + } + } +} + +static void process(NormalizeContext *s, AVFrame *in, AVFrame *out) +{ + for (int y = 0; y < in->height; y++) { + uint8_t *inp = in->data[0] + y * in->linesize[0]; + uint8_t *outp = out->data[0] + y * out->linesize[0]; + for (int x = 0; x < in->width; x++) { + for (int c = 0; c < 3; c++) + outp[s->co[c]] = s->lut[c][inp[s->co[c]]]; + if (s->num_components == 4) + // Copy alpha as-is. + outp[s->co[3]] = inp[s->co[3]]; + inp += s->step; + outp += s->step; + } + } +} + +static void find_min_max_planar(NormalizeContext *s, AVFrame *in, NormalizeLocal min[3], NormalizeLocal max[3]) +{ + min[0].in = max[0].in = in->data[2][0]; + min[1].in = max[1].in = in->data[0][0]; + min[2].in = max[2].in = in->data[1][0]; + for (int y = 0; y < in->height; y++) { + uint8_t *inrp = in->data[2] + y * in->linesize[2]; + uint8_t *ingp = in->data[0] + y * in->linesize[0]; + uint8_t *inbp = in->data[1] + y * in->linesize[1]; + for (int x = 0; x < in->width; x++) { + min[0].in = FFMIN(min[0].in, inrp[x]); + max[0].in = FFMAX(max[0].in, inrp[x]); + min[1].in = FFMIN(min[1].in, ingp[x]); + max[1].in = FFMAX(max[1].in, ingp[x]); + min[2].in = FFMIN(min[2].in, inbp[x]); + max[2].in = FFMAX(max[2].in, inbp[x]); + } + } +} + +static void process_planar(NormalizeContext *s, AVFrame *in, AVFrame *out) +{ + for (int y = 0; y < in->height; y++) { + uint8_t *inrp = in->data[2] + y * in->linesize[2]; + uint8_t *ingp = in->data[0] + y * in->linesize[0]; + uint8_t *inbp = in->data[1] + y * in->linesize[1]; + uint8_t *inap = in->data[3] + y * in->linesize[3]; + uint8_t *outrp = out->data[2] + y * out->linesize[2]; + uint8_t *outgp = out->data[0] + y * out->linesize[0]; + uint8_t *outbp = out->data[1] + y * out->linesize[1]; + uint8_t *outap = out->data[3] + y * out->linesize[3]; + for (int x = 0; x < in->width; x++) { + outrp[x] = s->lut[0][inrp[x]]; + outgp[x] = s->lut[1][ingp[x]]; + outbp[x] = s->lut[2][inbp[x]]; + if (s->num_components == 4) + outap[x] = inap[x]; + } + } +} + +static void find_min_max_16(NormalizeContext *s, AVFrame *in, NormalizeLocal min[3], NormalizeLocal max[3]) +{ + for (int c = 0; c < 3; c++) + min[c].in = max[c].in = AV_RN16(in->data[0] + 2 * s->co[c]); + for (int y = 0; y < in->height; y++) { + uint16_t *inp = (uint16_t *)(in->data[0] + y * in->linesize[0]); + for (int x = 0; x < in->width; x++) { + for (int c = 0; c < 3; c++) { + min[c].in = FFMIN(min[c].in, inp[s->co[c]]); + max[c].in = FFMAX(max[c].in, inp[s->co[c]]); + } + inp += s->step; + } + } +} + +static void process_16(NormalizeContext *s, AVFrame *in, AVFrame *out) +{ + for (int y = 0; y < in->height; y++) { + uint16_t *inp = (uint16_t *)(in->data[0] + y * in->linesize[0]); + uint16_t *outp = (uint16_t *)(out->data[0] + y * out->linesize[0]); + for (int x = 0; x < in->width; x++) { + for (int c = 0; c < 3; c++) + outp[s->co[c]] = s->lut[c][inp[s->co[c]]]; + if (s->num_components == 4) + // Copy alpha as-is. + outp[s->co[3]] = inp[s->co[3]]; + inp += s->step; + outp += s->step; + } + } +} + +static void find_min_max_planar_16(NormalizeContext *s, AVFrame *in, NormalizeLocal min[3], NormalizeLocal max[3]) +{ + min[0].in = max[0].in = AV_RN16(in->data[2]); + min[1].in = max[1].in = AV_RN16(in->data[0]); + min[2].in = max[2].in = AV_RN16(in->data[1]); + for (int y = 0; y < in->height; y++) { + uint16_t *inrp = (uint16_t *)(in->data[2] + y * in->linesize[2]); + uint16_t *ingp = (uint16_t *)(in->data[0] + y * in->linesize[0]); + uint16_t *inbp = (uint16_t *)(in->data[1] + y * in->linesize[1]); + for (int x = 0; x < in->width; x++) { + min[0].in = FFMIN(min[0].in, inrp[x]); + max[0].in = FFMAX(max[0].in, inrp[x]); + min[1].in = FFMIN(min[1].in, ingp[x]); + max[1].in = FFMAX(max[1].in, ingp[x]); + min[2].in = FFMIN(min[2].in, inbp[x]); + max[2].in = FFMAX(max[2].in, inbp[x]); + } + } +} + +static void process_planar_16(NormalizeContext *s, AVFrame *in, AVFrame *out) +{ + for (int y = 0; y < in->height; y++) { + uint16_t *inrp = (uint16_t *)(in->data[2] + y * in->linesize[2]); + uint16_t *ingp = (uint16_t *)(in->data[0] + y * in->linesize[0]); + uint16_t *inbp = (uint16_t *)(in->data[1] + y * in->linesize[1]); + uint16_t *inap = (uint16_t *)(in->data[3] + y * in->linesize[3]); + uint16_t *outrp = (uint16_t *)(out->data[2] + y * out->linesize[2]); + uint16_t *outgp = (uint16_t *)(out->data[0] + y * out->linesize[0]); + uint16_t *outbp = (uint16_t *)(out->data[1] + y * out->linesize[1]); + uint16_t *outap = (uint16_t *)(out->data[3] + y * out->linesize[3]); + for (int x = 0; x < in->width; x++) { + outrp[x] = s->lut[0][inrp[x]]; + outgp[x] = s->lut[1][ingp[x]]; + outbp[x] = s->lut[2][inbp[x]]; + if (s->num_components == 4) + outap[x] = inap[x]; + } + } +} + // This function is the main guts of the filter. Normalizes the input frame // into the output frame. The frames are known to have the same dimensions // and pixel format. static void normalize(NormalizeContext *s, AVFrame *in, AVFrame *out) { // Per-extremum, per-channel local variables. - struct { - uint8_t in; // Original input byte value for this frame. - float smoothed; // Smoothed input value [0,255]. - float out; // Output value [0,255]. - } min[3], max[3]; // Min and max for each channel in {R,G,B}. + NormalizeLocal min[3], max[3]; // Min and max for each channel in {R,G,B}. float rgb_min_smoothed; // Min input range for linked normalization float rgb_max_smoothed; // Max input range for linked normalization - uint8_t lut[3][256]; // Lookup table - int x, y, c; + int c; // First, scan the input frame to find, for each channel, the minimum // (min.in) and maximum (max.in) values present in the channel. - for (c = 0; c < 3; c++) - min[c].in = max[c].in = in->data[0][s->co[c]]; - for (y = 0; y < in->height; y++) { - uint8_t *inp = in->data[0] + y * in->linesize[0]; - uint8_t *outp = out->data[0] + y * out->linesize[0]; - for (x = 0; x < in->width; x++) { - for (c = 0; c < 3; c++) { - min[c].in = FFMIN(min[c].in, inp[s->co[c]]); - max[c].in = FFMAX(max[c].in, inp[s->co[c]]); - } - inp += s->step; - outp += s->step; - } - } + s->find_min_max(s, in, min, max); // Next, for each channel, push min.in and max.in into their respective // histories, to determine the min.smoothed and max.smoothed for this frame. @@ -200,9 +347,9 @@ static void normalize(NormalizeContext *s, AVFrame *in, AVFrame *out) // Calculate the output range [min.out,max.out] as a ratio of the full- // strength output range [blackpt,whitept] and the original input range // [min.in,max.in], based on the user-specified filter strength. - min[c].out = (s->blackpt[c] * s->strength) + min[c].out = (s->sblackpt[c] * s->strength) + (min[c].in * (1.0f - s->strength)); - max[c].out = (s->whitept[c] * s->strength) + max[c].out = (s->swhitept[c] * s->strength) + (max[c].in * (1.0f - s->strength)); // Now, build a lookup table which linearly maps the adjusted input range @@ -213,7 +360,7 @@ static void normalize(NormalizeContext *s, AVFrame *in, AVFrame *out) if (min[c].smoothed == max[c].smoothed) { // There is no dynamic range to expand. No mapping for this channel. for (in_val = min[c].in; in_val <= max[c].in; in_val++) - lut[c][in_val] = min[c].out; + s->lut[c][in_val] = min[c].out; } else { // We must set lookup values for all values in the original input // range [min.in,max.in]. Since the original input range may be @@ -222,27 +369,14 @@ static void normalize(NormalizeContext *s, AVFrame *in, AVFrame *out) float scale = (max[c].out - min[c].out) / (max[c].smoothed - min[c].smoothed); for (in_val = min[c].in; in_val <= max[c].in; in_val++) { int out_val = (in_val - min[c].smoothed) * scale + min[c].out + 0.5f; - out_val = FFMAX(out_val, 0); - out_val = FFMIN(out_val, 255); - lut[c][in_val] = out_val; + out_val = av_clip_uintp2_c(out_val, s->depth); + s->lut[c][in_val] = out_val; } } } // Finally, process the pixels of the input frame using the lookup tables. - for (y = 0; y < in->height; y++) { - uint8_t *inp = in->data[0] + y * in->linesize[0]; - uint8_t *outp = out->data[0] + y * out->linesize[0]; - for (x = 0; x < in->width; x++) { - for (c = 0; c < 3; c++) - outp[s->co[c]] = lut[c][inp[s->co[c]]]; - if (s->num_components == 4) - // Copy alpha as-is. - outp[s->co[3]] = inp[s->co[3]]; - inp += s->step; - outp += s->step; - } - } + s->process(s, in, out); s->frame_num++; } @@ -267,6 +401,11 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_RGB0, AV_PIX_FMT_0BGR, AV_PIX_FMT_BGR0, + AV_PIX_FMT_RGB48, AV_PIX_FMT_BGR48, + AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64, + 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_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_NONE }; // According to filter_design.txt, using ff_set_common_formats() this way @@ -286,11 +425,13 @@ static int config_input(AVFilterLink *inlink) NormalizeContext *s = inlink->dst->priv; // Store offsets to R,G,B,A bytes respectively in each pixel const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); - int c; + int c, planar, scale; ff_fill_rgba_map(s->co, inlink->format); + s->depth = desc->comp[0].depth; + scale = 1 << (s->depth - 8); s->num_components = desc->nb_components; - s->step = av_get_padded_bits_per_pixel(desc) >> 3; + s->step = av_get_padded_bits_per_pixel(desc) >> (3 + (s->depth > 8)); // Convert smoothing value to history_len (a count of frames to average, // must be at least 1). Currently this is a direct assignment, but the // smoothing value was originally envisaged as a number of seconds. In @@ -300,14 +441,27 @@ static int config_input(AVFilterLink *inlink) // Allocate the history buffers -- there are 6 -- one for each extrema. // s->smoothing is limited to INT_MAX/8, so that (s->history_len * 6) // can't overflow on 32bit causing a too-small allocation. - s->history_mem = av_malloc(s->history_len * 6); + s->history_mem = av_malloc(s->history_len * 6 * sizeof(*s->history_mem)); if (s->history_mem == NULL) return AVERROR(ENOMEM); for (c = 0; c < 3; c++) { s->min[c].history = s->history_mem + (c*2) * s->history_len; s->max[c].history = s->history_mem + (c*2+1) * s->history_len; + s->sblackpt[c] = scale * s->blackpt[c] + (s->blackpt[c] >> (s->depth - 8)); + s->swhitept[c] = scale * s->whitept[c] + (s->whitept[c] >> (s->depth - 8)); } + + planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR; + + if (s->depth <= 8) { + s->find_min_max = planar ? find_min_max_planar : find_min_max; + s->process = planar? process_planar : process; + } else { + s->find_min_max = planar ? find_min_max_planar_16 : find_min_max_16; + s->process = planar? process_planar_16 : process_16; + } + return 0; } @@ -386,4 +540,5 @@ AVFilter ff_vf_normalize = { .inputs = inputs, .outputs = outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vf_overlay.c b/libavfilter/vf_overlay.c index 0a8f089c0d5..b5ab5fba5f6 100644 --- a/libavfilter/vf_overlay.c +++ b/libavfilter/vf_overlay.c @@ -991,8 +991,8 @@ static int activate(AVFilterContext *ctx) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM static const AVOption overlay_options[] = { - { "x", "set the x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "y", "set the y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "x", "set the x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, FLAGS }, + { "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" }, diff --git a/libavfilter/vf_overlay_cuda.c b/libavfilter/vf_overlay_cuda.c new file mode 100644 index 00000000000..2f0f860e504 --- /dev/null +++ b/libavfilter/vf_overlay_cuda.c @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2020 Yaroslav Pogrebnyak + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 + * Overlay one video on top of another using cuda hardware acceleration + */ + +#include "libavutil/log.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_cuda_internal.h" +#include "libavutil/cuda_check.h" + +#include "avfilter.h" +#include "framesync.h" +#include "internal.h" + +#define CHECK_CU(x) FF_CUDA_CHECK_DL(ctx, ctx->hwctx->internal->cuda_dl, x) +#define DIV_UP(a, b) ( ((a) + (b) - 1) / (b) ) + +#define BLOCK_X 32 +#define BLOCK_Y 16 + +static const enum AVPixelFormat supported_main_formats[] = { + AV_PIX_FMT_NV12, + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_NONE, +}; + +static const enum AVPixelFormat supported_overlay_formats[] = { + AV_PIX_FMT_NV12, + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVA420P, + AV_PIX_FMT_NONE, +}; + +/** + * OverlayCUDAContext + */ +typedef struct OverlayCUDAContext { + const AVClass *class; + + enum AVPixelFormat in_format_overlay; + enum AVPixelFormat in_format_main; + + AVCUDADeviceContext *hwctx; + + CUcontext cu_ctx; + CUmodule cu_module; + CUfunction cu_func; + CUstream cu_stream; + + FFFrameSync fs; + + int x_position; + int y_position; + +} OverlayCUDAContext; + +/** + * Helper to find out if provided format is supported by filter + */ +static int format_is_supported(const enum AVPixelFormat formats[], enum AVPixelFormat fmt) +{ + for (int i = 0; formats[i] != AV_PIX_FMT_NONE; i++) + if (formats[i] == fmt) + return 1; + return 0; +} + +/** + * Helper checks if we can process main and overlay pixel formats + */ +static int formats_match(const enum AVPixelFormat format_main, const enum AVPixelFormat format_overlay) { + switch(format_main) { + case AV_PIX_FMT_NV12: + return format_overlay == AV_PIX_FMT_NV12; + case AV_PIX_FMT_YUV420P: + return format_overlay == AV_PIX_FMT_YUV420P || + format_overlay == AV_PIX_FMT_YUVA420P; + default: + return 0; + } +} + +/** + * Call overlay kernell for a plane + */ +static int overlay_cuda_call_kernel( + OverlayCUDAContext *ctx, + int x_position, int y_position, + uint8_t* main_data, int main_linesize, + int main_width, int main_height, + uint8_t* overlay_data, int overlay_linesize, + int overlay_width, int overlay_height, + uint8_t* alpha_data, int alpha_linesize, + int alpha_adj_x, int alpha_adj_y) { + + CudaFunctions *cu = ctx->hwctx->internal->cuda_dl; + + void* kernel_args[] = { + &x_position, &y_position, + &main_data, &main_linesize, + &overlay_data, &overlay_linesize, + &overlay_width, &overlay_height, + &alpha_data, &alpha_linesize, + &alpha_adj_x, &alpha_adj_y, + }; + + return CHECK_CU(cu->cuLaunchKernel( + ctx->cu_func, + DIV_UP(main_width, BLOCK_X), DIV_UP(main_height, BLOCK_Y), 1, + BLOCK_X, BLOCK_Y, 1, + 0, ctx->cu_stream, kernel_args, NULL)); +} + +/** + * Perform blend overlay picture over main picture + */ +static int overlay_cuda_blend(FFFrameSync *fs) +{ + int ret; + + AVFilterContext *avctx = fs->parent; + OverlayCUDAContext *ctx = avctx->priv; + AVFilterLink *outlink = avctx->outputs[0]; + + CudaFunctions *cu = ctx->hwctx->internal->cuda_dl; + CUcontext dummy, cuda_ctx = ctx->hwctx->cuda_ctx; + + AVFrame *input_main, *input_overlay; + + ctx->cu_ctx = cuda_ctx; + + // read main and overlay frames from inputs + ret = ff_framesync_dualinput_get(fs, &input_main, &input_overlay); + if (ret < 0) + return ret; + + if (!input_main || !input_overlay) + return AVERROR_BUG; + + ret = av_frame_make_writable(input_main); + if (ret < 0) { + av_frame_free(&input_main); + return ret; + } + + // push cuda context + + ret = CHECK_CU(cu->cuCtxPushCurrent(cuda_ctx)); + if (ret < 0) { + av_frame_free(&input_main); + return ret; + } + + // overlay first plane + + overlay_cuda_call_kernel(ctx, + ctx->x_position, ctx->y_position, + input_main->data[0], input_main->linesize[0], + input_main->width, input_main->height, + input_overlay->data[0], input_overlay->linesize[0], + input_overlay->width, input_overlay->height, + input_overlay->data[3], input_overlay->linesize[3], 1, 1); + + // overlay rest planes depending on pixel format + + switch(ctx->in_format_overlay) { + case AV_PIX_FMT_NV12: + overlay_cuda_call_kernel(ctx, + ctx->x_position, ctx->y_position / 2, + input_main->data[1], input_main->linesize[1], + input_main->width, input_main->height / 2, + input_overlay->data[1], input_overlay->linesize[1], + input_overlay->width, input_overlay->height / 2, + 0, 0, 0, 0); + break; + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUVA420P: + overlay_cuda_call_kernel(ctx, + ctx->x_position / 2 , ctx->y_position / 2, + input_main->data[1], input_main->linesize[1], + input_main->width / 2, input_main->height / 2, + input_overlay->data[1], input_overlay->linesize[1], + input_overlay->width / 2, input_overlay->height / 2, + input_overlay->data[3], input_overlay->linesize[3], 2, 2); + + overlay_cuda_call_kernel(ctx, + ctx->x_position / 2 , ctx->y_position / 2, + input_main->data[2], input_main->linesize[2], + input_main->width / 2, input_main->height / 2, + input_overlay->data[2], input_overlay->linesize[2], + input_overlay->width / 2, input_overlay->height / 2, + input_overlay->data[3], input_overlay->linesize[3], 2, 2); + break; + default: + av_log(ctx, AV_LOG_ERROR, "Passed unsupported overlay pixel format\n"); + av_frame_free(&input_main); + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + return AVERROR_BUG; + } + + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + + return ff_filter_frame(outlink, input_main); +} + +/** + * Initialize overlay_cuda + */ +static av_cold int overlay_cuda_init(AVFilterContext *avctx) +{ + OverlayCUDAContext* ctx = avctx->priv; + ctx->fs.on_event = &overlay_cuda_blend; + + return 0; +} + +/** + * Uninitialize overlay_cuda + */ +static av_cold void overlay_cuda_uninit(AVFilterContext *avctx) +{ + OverlayCUDAContext* ctx = avctx->priv; + + ff_framesync_uninit(&ctx->fs); + + if (ctx->hwctx && ctx->cu_module) { + CUcontext dummy; + CudaFunctions *cu = ctx->hwctx->internal->cuda_dl; + CHECK_CU(cu->cuCtxPushCurrent(ctx->cu_ctx)); + CHECK_CU(cu->cuModuleUnload(ctx->cu_module)); + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + } +} + +/** + * Activate overlay_cuda + */ +static int overlay_cuda_activate(AVFilterContext *avctx) +{ + OverlayCUDAContext *ctx = avctx->priv; + + return ff_framesync_activate(&ctx->fs); +} + +/** + * Query formats + */ +static int overlay_cuda_query_formats(AVFilterContext *avctx) +{ + static const enum AVPixelFormat pixel_formats[] = { + AV_PIX_FMT_CUDA, AV_PIX_FMT_NONE, + }; + + AVFilterFormats *pix_fmts = ff_make_format_list(pixel_formats); + + return ff_set_common_formats(avctx, pix_fmts); +} + +/** + * Configure output + */ +static int overlay_cuda_config_output(AVFilterLink *outlink) +{ + + extern char vf_overlay_cuda_ptx[]; + + int err; + AVFilterContext* avctx = outlink->src; + OverlayCUDAContext* ctx = avctx->priv; + + AVFilterLink *inlink = avctx->inputs[0]; + AVHWFramesContext *frames_ctx = (AVHWFramesContext*)inlink->hw_frames_ctx->data; + + AVFilterLink *inlink_overlay = avctx->inputs[1]; + AVHWFramesContext *frames_ctx_overlay = (AVHWFramesContext*)inlink_overlay->hw_frames_ctx->data; + + CUcontext dummy, cuda_ctx; + CudaFunctions *cu; + + // check main input formats + + if (!frames_ctx) { + av_log(ctx, AV_LOG_ERROR, "No hw context provided on main input\n"); + return AVERROR(EINVAL); + } + + ctx->in_format_main = frames_ctx->sw_format; + if (!format_is_supported(supported_main_formats, ctx->in_format_main)) { + av_log(ctx, AV_LOG_ERROR, "Unsupported main input format: %s\n", + av_get_pix_fmt_name(ctx->in_format_main)); + return AVERROR(ENOSYS); + } + + // check overlay input formats + + if (!frames_ctx_overlay) { + av_log(ctx, AV_LOG_ERROR, "No hw context provided on overlay input\n"); + return AVERROR(EINVAL); + } + + ctx->in_format_overlay = frames_ctx_overlay->sw_format; + if (!format_is_supported(supported_overlay_formats, ctx->in_format_overlay)) { + av_log(ctx, AV_LOG_ERROR, "Unsupported overlay input format: %s\n", + av_get_pix_fmt_name(ctx->in_format_overlay)); + return AVERROR(ENOSYS); + } + + // check we can overlay pictures with those pixel formats + + if (!formats_match(ctx->in_format_main, ctx->in_format_overlay)) { + av_log(ctx, AV_LOG_ERROR, "Can't overlay %s on %s \n", + av_get_pix_fmt_name(ctx->in_format_overlay), av_get_pix_fmt_name(ctx->in_format_main)); + return AVERROR(EINVAL); + } + + // initialize + + ctx->hwctx = frames_ctx->device_ctx->hwctx; + cuda_ctx = ctx->hwctx->cuda_ctx; + ctx->fs.time_base = inlink->time_base; + + ctx->cu_stream = ctx->hwctx->stream; + + outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx); + + // load functions + + cu = ctx->hwctx->internal->cuda_dl; + + err = CHECK_CU(cu->cuCtxPushCurrent(cuda_ctx)); + if (err < 0) { + return err; + } + + err = CHECK_CU(cu->cuModuleLoadData(&ctx->cu_module, vf_overlay_cuda_ptx)); + if (err < 0) { + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + return err; + } + + err = CHECK_CU(cu->cuModuleGetFunction(&ctx->cu_func, ctx->cu_module, "Overlay_Cuda")); + if (err < 0) { + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + return err; + } + + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + + // init dual input + + err = ff_framesync_init_dualinput(&ctx->fs, avctx); + if (err < 0) { + return err; + } + + return ff_framesync_configure(&ctx->fs); +} + + +#define OFFSET(x) offsetof(OverlayCUDAContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) + +static const AVOption overlay_cuda_options[] = { + { "x", "Overlay x position", + OFFSET(x_position), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, .flags = FLAGS }, + { "y", "Overlay y position", + OFFSET(y_position), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, .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" }, + { "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 }, +}; + +FRAMESYNC_DEFINE_CLASS(overlay_cuda, OverlayCUDAContext, fs); + +static const AVFilterPad overlay_cuda_inputs[] = { + { + .name = "main", + .type = AVMEDIA_TYPE_VIDEO, + }, + { + .name = "overlay", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +static const AVFilterPad overlay_cuda_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &overlay_cuda_config_output, + }, + { NULL } +}; + +AVFilter ff_vf_overlay_cuda = { + .name = "overlay_cuda", + .description = NULL_IF_CONFIG_SMALL("Overlay one video on top of another using CUDA"), + .priv_size = sizeof(OverlayCUDAContext), + .priv_class = &overlay_cuda_class, + .init = &overlay_cuda_init, + .uninit = &overlay_cuda_uninit, + .activate = &overlay_cuda_activate, + .query_formats = &overlay_cuda_query_formats, + .inputs = overlay_cuda_inputs, + .outputs = overlay_cuda_outputs, + .preinit = overlay_cuda_framesync_preinit, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_overlay_cuda.cu b/libavfilter/vf_overlay_cuda.cu new file mode 100644 index 00000000000..43ec36c2ed1 --- /dev/null +++ b/libavfilter/vf_overlay_cuda.cu @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Yaroslav Pogrebnyak + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +extern "C" { + +__global__ void Overlay_Cuda( + int x_position, int y_position, + unsigned char* main, int main_linesize, + unsigned char* overlay, int overlay_linesize, + int overlay_w, int overlay_h, + unsigned char* overlay_alpha, int alpha_linesize, + int alpha_adj_x, int alpha_adj_y) +{ + int x = blockIdx.x * blockDim.x + threadIdx.x; + int y = blockIdx.y * blockDim.y + threadIdx.y; + + if (x >= overlay_w + x_position || + y >= overlay_h + y_position || + x < x_position || + y < y_position ) { + + return; + } + + int overlay_x = x - x_position; + int overlay_y = y - y_position; + + float alpha = 1.0; + if (alpha_linesize) { + alpha = overlay_alpha[alpha_adj_x * overlay_x + alpha_adj_y * overlay_y * alpha_linesize] / 255.0f; + } + + main[x + y*main_linesize] = alpha * overlay[overlay_x + overlay_y * overlay_linesize] + (1.0f - alpha) * main[x + y*main_linesize]; +} + +} + diff --git a/libavfilter/vf_overlay_vulkan.c b/libavfilter/vf_overlay_vulkan.c new file mode 100644 index 00000000000..60ca85fd085 --- /dev/null +++ b/libavfilter/vf_overlay_vulkan.c @@ -0,0 +1,490 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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.h" +#include "internal.h" +#include "framesync.h" + +#define CGROUPS (int [3]){ 32, 32, 1 } + +typedef struct OverlayVulkanContext { + VulkanFilterContext vkctx; + + int initialized; + VulkanPipeline *pl; + FFVkExecContext *exec; + FFFrameSync fs; + FFVkBuffer params_buf; + + /* Shader updators, must be in the main filter struct */ + VkDescriptorImageInfo main_images[3]; + VkDescriptorImageInfo overlay_images[3]; + VkDescriptorImageInfo output_images[3]; + VkDescriptorBufferInfo params_desc; + + int overlay_x; + int overlay_y; + int overlay_w; + int overlay_h; +} OverlayVulkanContext; + +static const char overlay_noalpha[] = { + C(0, void overlay_noalpha(int i, ivec2 pos) ) + C(0, { ) + C(1, if ((o_offset[i].x <= pos.x) && (o_offset[i].y <= pos.y) && + (pos.x < (o_offset[i].x + o_size[i].x)) && + (pos.y < (o_offset[i].y + o_size[i].y))) { ) + C(2, vec4 res = texture(overlay_img[i], pos - o_offset[i]); ) + C(2, imageStore(output_img[i], pos, res); ) + C(1, } else { ) + C(2, vec4 res = texture(main_img[i], pos); ) + C(2, imageStore(output_img[i], pos, res); ) + C(1, } ) + C(0, } ) +}; + +static const char overlay_alpha[] = { + C(0, void overlay_alpha_opaque(int i, ivec2 pos) ) + C(0, { ) + C(1, vec4 res = texture(main_img[i], pos); ) + C(1, if ((o_offset[i].x <= pos.x) && (o_offset[i].y <= pos.y) && + (pos.x < (o_offset[i].x + o_size[i].x)) && + (pos.y < (o_offset[i].y + o_size[i].y))) { ) + C(2, vec4 ovr = texture(overlay_img[i], pos - o_offset[i]); ) + C(2, res = ovr * ovr.a + res * (1.0f - ovr.a); ) + C(2, res.a = 1.0f; ) + C(2, imageStore(output_img[i], pos, res); ) + C(1, } ) + C(1, imageStore(output_img[i], pos, res); ) + C(0, } ) +}; + +static av_cold int init_filter(AVFilterContext *ctx) +{ + int err; + OverlayVulkanContext *s = ctx->priv; + VkSampler *sampler = ff_vk_init_sampler(ctx, 1, VK_FILTER_NEAREST); + if (!sampler) + return AVERROR_EXTERNAL; + + s->pl = ff_vk_create_pipeline(ctx); + if (!s->pl) + return AVERROR(ENOMEM); + + s->vkctx.queue_family_idx = s->vkctx.hwctx->queue_family_comp_index; + s->vkctx.queue_count = GET_QUEUE_COUNT(s->vkctx.hwctx, 0, 1, 0); + s->vkctx.cur_queue_idx = av_get_random_seed() % s->vkctx.queue_count; + + { /* Create the shader */ + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + const int ialpha = av_pix_fmt_desc_get(s->vkctx.input_format)->flags & AV_PIX_FMT_FLAG_ALPHA; + + VulkanDescriptorSetBinding 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, + .samplers = DUP_SAMPLER_ARRAY4(*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, + .samplers = DUP_SAMPLER_ARRAY4(*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, + }, + }; + + VulkanDescriptorSetBinding 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];", + }; + + SPIRVShader *shd = ff_vk_init_shader(ctx, s->pl, "overlay_compute", + VK_SHADER_STAGE_COMPUTE_BIT); + if (!shd) + return AVERROR(ENOMEM); + + ff_vk_set_compute_shader_sizes(ctx, shd, CGROUPS); + + RET(ff_vk_add_descriptor_set(ctx, s->pl, shd, desc_i, 3, 0)); /* set 0 */ + RET(ff_vk_add_descriptor_set(ctx, 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(ctx, shd, "main")); + } + + RET(ff_vk_init_pipeline_layout(ctx, s->pl)); + RET(ff_vk_init_compute_pipeline(ctx, 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(ctx, &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(ctx, &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(ctx, &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(ctx, s->pl, 1); + } + + /* Execution context */ + RET(ff_vk_create_exec_ctx(ctx, &s->exec)); + + 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; + 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; + + /* Update descriptors and init the exec context */ + ff_vk_start_exec_recording(avctx, s->exec); + cmd_buf = ff_vk_get_exec_buf(avctx, s->exec); + + for (int i = 0; i < planes; i++) { + RET(ff_vk_create_imageview(avctx, s->exec, &s->main_images[i].imageView, + main->img[i], + av_vkfmt_from_pixfmt(main_fc->sw_format)[i], + ff_comp_identity_map)); + + RET(ff_vk_create_imageview(avctx, s->exec, &s->overlay_images[i].imageView, + overlay->img[i], + av_vkfmt_from_pixfmt(overlay_fc->sw_format)[i], + ff_comp_identity_map)); + + RET(ff_vk_create_imageview(avctx, s->exec, &s->output_images[i].imageView, + out->img[i], + av_vkfmt_from_pixfmt(s->vkctx.output_format)[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(avctx, 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, + }, + }; + + vkCmdPipelineBarrier(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(avctx, s->exec, s->pl); + + vkCmdDispatch(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(avctx, s->exec, main_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + ff_vk_add_exec_dep(avctx, s->exec, overlay_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + ff_vk_add_exec_dep(avctx, s->exec, out_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + + err = ff_vk_submit_exec_queue(avctx, s->exec); + if (err) + return err; + + return err; + +fail: + ff_vk_discard_exec_deps(avctx, s->exec); + return err; +} + +static int overlay_vulkan_blend(FFFrameSync *fs) +{ + int err; + AVFilterContext *ctx = fs->parent; + OverlayVulkanContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *input_main, *input_overlay, *out; + + err = ff_framesync_get_frame(fs, 0, &input_main, 0); + if (err < 0) + goto fail; + err = ff_framesync_get_frame(fs, 1, &input_overlay, 0); + if (err < 0) + goto fail; + + if (!input_main || !input_overlay) + return 0; + + if (!s->initialized) { + AVHWFramesContext *main_fc = (AVHWFramesContext*)input_main->hw_frames_ctx->data; + AVHWFramesContext *overlay_fc = (AVHWFramesContext*)input_overlay->hw_frames_ctx->data; + if (main_fc->sw_format != overlay_fc->sw_format) { + av_log(ctx, AV_LOG_ERROR, "Mismatching sw formats!\n"); + return AVERROR(EINVAL); + } + + s->overlay_w = input_overlay->width; + s->overlay_h = input_overlay->height; + + RET(init_filter(ctx)); + } + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + err = AVERROR(ENOMEM); + goto fail; + } + + RET(process_frames(ctx, out, input_main, input_overlay)); + + err = av_frame_copy_props(out, input_main); + if (err < 0) + goto fail; + + return ff_filter_frame(outlink, out); + +fail: + av_frame_free(&out); + return err; +} + +static int overlay_vulkan_config_output(AVFilterLink *outlink) +{ + int err; + AVFilterContext *avctx = outlink->src; + OverlayVulkanContext *s = avctx->priv; + + err = ff_vk_filter_config_output(outlink); + if (err < 0) + return err; + + err = ff_framesync_init_dualinput(&s->fs, avctx); + if (err < 0) + return err; + + return ff_framesync_configure(&s->fs); +} + +static int overlay_vulkan_activate(AVFilterContext *avctx) +{ + OverlayVulkanContext *s = avctx->priv; + + return ff_framesync_activate(&s->fs); +} + +static av_cold int overlay_vulkan_init(AVFilterContext *avctx) +{ + OverlayVulkanContext *s = avctx->priv; + + s->fs.on_event = &overlay_vulkan_blend; + + return ff_vk_filter_init(avctx); +} + +static void overlay_vulkan_uninit(AVFilterContext *avctx) +{ + OverlayVulkanContext *s = avctx->priv; + + ff_vk_filter_uninit(avctx); + ff_framesync_uninit(&s->fs); + + ff_vk_free_buf(avctx, &s->params_buf); + + s->initialized = 0; +} + +#define OFFSET(x) offsetof(OverlayVulkanContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) +static const AVOption overlay_vulkan_options[] = { + { "x", "Set horizontal offset", OFFSET(overlay_x), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, .flags = FLAGS }, + { "y", "Set vertical offset", OFFSET(overlay_y), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, .flags = FLAGS }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(overlay_vulkan); + +static const AVFilterPad overlay_vulkan_inputs[] = { + { + .name = "main", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &ff_vk_filter_config_input, + }, + { + .name = "overlay", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &ff_vk_filter_config_input, + }, + { NULL } +}; + +static const AVFilterPad overlay_vulkan_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &overlay_vulkan_config_output, + }, + { NULL } +}; + +AVFilter ff_vf_overlay_vulkan = { + .name = "overlay_vulkan", + .description = NULL_IF_CONFIG_SMALL("Overlay a source on top of another"), + .priv_size = sizeof(OverlayVulkanContext), + .init = &overlay_vulkan_init, + .uninit = &overlay_vulkan_uninit, + .query_formats = &ff_vk_filter_query_formats, + .activate = &overlay_vulkan_activate, + .inputs = overlay_vulkan_inputs, + .outputs = overlay_vulkan_outputs, + .priv_class = &overlay_vulkan_class, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_pad.c b/libavfilter/vf_pad.c index ed155578e11..131f24807f9 100644 --- a/libavfilter/vf_pad.c +++ b/libavfilter/vf_pad.c @@ -178,14 +178,14 @@ static int config_input(AVFilterLink *inlink) if (s->y < 0 || s->y + inlink->h > s->h) s->y = var_values[VAR_Y] = (s->h - inlink->h) / 2; + 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); /* sanity check params */ - if (s->w < 0 || s->h < 0) { - av_log(ctx, AV_LOG_ERROR, "Negative values are not acceptable.\n"); + if (s->w < inlink->w || s->h < inlink->h) { + av_log(ctx, AV_LOG_ERROR, "Padded dimensions cannot be smaller than input dimensions.\n"); return AVERROR(EINVAL); } - 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); s->x = ff_draw_round_to_sub(&s->draw, 0, -1, s->x); s->y = ff_draw_round_to_sub(&s->draw, 1, -1, s->y); s->in_w = ff_draw_round_to_sub(&s->draw, 0, -1, inlink->w); @@ -210,7 +210,7 @@ static int config_input(AVFilterLink *inlink) return 0; eval_fail: - av_log(NULL, AV_LOG_ERROR, + av_log(ctx, AV_LOG_ERROR, "Error when evaluating the expression '%s'\n", expr); return ret; @@ -417,12 +417,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption pad_options[] = { - { "width", "set the pad area width expression", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "w", "set the pad area width expression", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "height", "set the pad area height expression", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "h", "set the pad area height expression", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "x", "set the x offset expression for the input image position", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "y", "set the y offset expression for the input image position", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "width", "set the pad area width expression", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, 0, 0, FLAGS }, + { "w", "set the pad area width expression", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, 0, 0, FLAGS }, + { "height", "set the pad area height expression", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, 0, 0, FLAGS }, + { "h", "set the pad area height expression", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, 0, 0, FLAGS }, + { "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" }, { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, diff --git a/libavfilter/vf_pad_opencl.c b/libavfilter/vf_pad_opencl.c new file mode 100644 index 00000000000..1129c40c604 --- /dev/null +++ b/libavfilter/vf_pad_opencl.c @@ -0,0 +1,397 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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/colorspace.h" +#include "libavutil/eval.h" +#include "libavutil/opt.h" +#include "libavutil/imgutils.h" +#include "avfilter.h" +#include "drawutils.h" +#include "formats.h" +#include "internal.h" +#include "opencl.h" +#include "opencl_source.h" +#include "video.h" + +static const char *const var_names[] = { + "in_w", "iw", + "in_h", "ih", + "out_w", "ow", + "out_h", "oh", + "x", + "y", + "a", + "sar", + "dar", + NULL +}; + +enum var_name { + VAR_IN_W, VAR_IW, + VAR_IN_H, VAR_IH, + VAR_OUT_W, VAR_OW, + VAR_OUT_H, VAR_OH, + VAR_X, + VAR_Y, + VAR_A, + VAR_SAR, + VAR_DAR, + VARS_NB +}; + +typedef struct PadOpenCLContext { + OpenCLFilterContext ocf; + int initialized; + int is_rgb; + int is_packed; + int hsub, vsub; + + char *w_expr; + char *h_expr; + char *x_expr; + char *y_expr; + AVRational aspect; + + cl_command_queue command_queue; + cl_kernel kernel_pad; + + int w, h; + int x, y; + uint8_t pad_rgba[4]; + uint8_t pad_color[4]; + cl_float4 pad_color_float; + cl_int2 pad_pos; +} PadOpenCLContext; + +static int pad_opencl_init(AVFilterContext *avctx, AVFrame *input_frame) +{ + PadOpenCLContext *ctx = avctx->priv; + AVHWFramesContext *input_frames_ctx = (AVHWFramesContext *)input_frame->hw_frames_ctx->data; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(input_frames_ctx->sw_format); + uint8_t rgba_map[4]; + cl_int cle; + int err; + + ff_fill_rgba_map(rgba_map, input_frames_ctx->sw_format); + ctx->is_rgb = !!(desc->flags & AV_PIX_FMT_FLAG_RGB); + ctx->is_packed = !(desc->flags & AV_PIX_FMT_FLAG_PLANAR); + ctx->hsub = desc->log2_chroma_w; + ctx->vsub = desc->log2_chroma_h; + + err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_pad, 1); + if (err < 0) + goto fail; + + ctx->command_queue = clCreateCommandQueue( + ctx->ocf.hwctx->context, + ctx->ocf.hwctx->device_id, + 0, + &cle + ); + + if (ctx->is_rgb) { + ctx->pad_color[rgba_map[0]] = ctx->pad_rgba[0]; + ctx->pad_color[rgba_map[1]] = ctx->pad_rgba[1]; + ctx->pad_color[rgba_map[2]] = ctx->pad_rgba[2]; + ctx->pad_color[rgba_map[3]] = ctx->pad_rgba[3]; + } else { + ctx->pad_color[0] = RGB_TO_Y_BT709(ctx->pad_rgba[0], ctx->pad_rgba[1], ctx->pad_rgba[2]); + ctx->pad_color[1] = RGB_TO_U_BT709(ctx->pad_rgba[0], ctx->pad_rgba[1], ctx->pad_rgba[2], 0); + ctx->pad_color[2] = RGB_TO_V_BT709(ctx->pad_rgba[0], ctx->pad_rgba[1], ctx->pad_rgba[2], 0); + ctx->pad_color[3] = ctx->pad_rgba[3]; + } + + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to create OpenCL command queue %d.\n", cle); + + ctx->kernel_pad = clCreateKernel(ctx->ocf.program, "pad", &cle); + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to create pad kernel: %d.\n", cle); + + for (int i = 0; i < 4; ++i) { + ctx->pad_color_float.s[i] = (float)ctx->pad_color[i] / 255.0; + } + + ctx->pad_pos.s[0] = ctx->x; + ctx->pad_pos.s[1] = ctx->y; + + ctx->initialized = 1; + return 0; + +fail: + if (ctx->command_queue) + clReleaseCommandQueue(ctx->command_queue); + if (ctx->kernel_pad) + clReleaseKernel(ctx->kernel_pad); + return err; +} + +static int filter_frame(AVFilterLink *link, AVFrame *input_frame) +{ + AVFilterContext *avctx = link->dst; + AVFilterLink *outlink = avctx->outputs[0]; + PadOpenCLContext *pad_ctx = avctx->priv; + AVFrame *output_frame = NULL; + int err; + cl_int cle; + size_t global_work[2]; + cl_mem src, dst; + + if (!input_frame->hw_frames_ctx) + return AVERROR(EINVAL); + + if (!pad_ctx->initialized) { + err = pad_opencl_init(avctx, input_frame); + if (err < 0) + goto fail; + } + + output_frame = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!output_frame) { + err = AVERROR(ENOMEM); + goto fail; + } + + for (int p = 0; p < FF_ARRAY_ELEMS(output_frame->data); p++) { + cl_float4 pad_color_float; + cl_int2 pad_pos; + + if (pad_ctx->is_packed) { + pad_color_float = pad_ctx->pad_color_float; + } else { + pad_color_float.s[0] = pad_ctx->pad_color_float.s[p]; + pad_color_float.s[1] = pad_ctx->pad_color_float.s[2]; + } + + if (p > 0 && p < 3) { + pad_pos.s[0] = pad_ctx->pad_pos.s[0] >> pad_ctx->hsub; + pad_pos.s[1] = pad_ctx->pad_pos.s[1] >> pad_ctx->vsub; + } else { + pad_pos.s[0] = pad_ctx->pad_pos.s[0]; + pad_pos.s[1] = pad_ctx->pad_pos.s[1]; + } + + src = (cl_mem)input_frame->data[p]; + dst = (cl_mem)output_frame->data[p]; + + if (!dst) + break; + + CL_SET_KERNEL_ARG(pad_ctx->kernel_pad, 0, cl_mem, &src); + CL_SET_KERNEL_ARG(pad_ctx->kernel_pad, 1, cl_mem, &dst); + CL_SET_KERNEL_ARG(pad_ctx->kernel_pad, 2, cl_float4, &pad_color_float); + CL_SET_KERNEL_ARG(pad_ctx->kernel_pad, 3, cl_int2, &pad_pos); + + err = ff_opencl_filter_work_size_from_image(avctx, global_work, output_frame, p, 16); + if (err < 0) + goto fail; + + cle = clEnqueueNDRangeKernel(pad_ctx->command_queue, pad_ctx->kernel_pad, 2, NULL, + global_work, NULL, 0, NULL, NULL); + + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to enqueue pad kernel: %d.\n", cle); + } + + // Run queued kernel + cle = clFinish(pad_ctx->command_queue); + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to finish command queue: %d.\n", cle); + + err = av_frame_copy_props(output_frame, input_frame); + if (err < 0) + goto fail; + + av_frame_free(&input_frame); + + return ff_filter_frame(outlink, output_frame); + +fail: + clFinish(pad_ctx->command_queue); + av_frame_free(&input_frame); + av_frame_free(&output_frame); + return err; +} + +static av_cold void pad_opencl_uninit(AVFilterContext *avctx) +{ + PadOpenCLContext *ctx = avctx->priv; + cl_int cle; + + if (ctx->kernel_pad) { + cle = clReleaseKernel(ctx->kernel_pad); + if (cle != CL_SUCCESS) + av_log(avctx, AV_LOG_ERROR, "Failed to release " + "kernel: %d.\n", cle); + } + + if (ctx->command_queue) { + cle = clReleaseCommandQueue(ctx->command_queue); + if (cle != CL_SUCCESS) + av_log(avctx, AV_LOG_ERROR, "Failed to release " + "command queue: %d.\n", cle); + } + + ff_opencl_filter_uninit(avctx); +} + +static int pad_opencl_config_output(AVFilterLink *outlink) +{ + AVFilterContext *avctx = outlink->src; + AVFilterLink *inlink = avctx->inputs[0]; + PadOpenCLContext *ctx = avctx->priv; + AVRational adjusted_aspect = ctx->aspect; + double var_values[VARS_NB], res; + int err, ret; + char *expr; + + var_values[VAR_IN_W] = var_values[VAR_IW] = inlink->w; + var_values[VAR_IN_H] = var_values[VAR_IH] = inlink->h; + var_values[VAR_OUT_W] = var_values[VAR_OW] = NAN; + var_values[VAR_OUT_H] = var_values[VAR_OH] = NAN; + var_values[VAR_A] = (double) inlink->w / inlink->h; + var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? + (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1; + var_values[VAR_DAR] = var_values[VAR_A] * var_values[VAR_SAR]; + + av_expr_parse_and_eval(&res, (expr = ctx->w_expr), + var_names, var_values, + NULL, NULL, NULL, NULL, NULL, 0, ctx); + ctx->w = var_values[VAR_OUT_W] = var_values[VAR_OW] = res; + if ((ret = av_expr_parse_and_eval(&res, (expr = ctx->h_expr), + var_names, var_values, + NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) + return ret; + ctx->h = var_values[VAR_OUT_H] = var_values[VAR_OH] = res; + if (!ctx->h) + var_values[VAR_OUT_H] = var_values[VAR_OH] = ctx->h = inlink->h; + + /* evaluate the width again, as it may depend on the evaluated output height */ + if ((ret = av_expr_parse_and_eval(&res, (expr = ctx->w_expr), + var_names, var_values, + NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) + return ret; + ctx->w = var_values[VAR_OUT_W] = var_values[VAR_OW] = res; + if (!ctx->w) + var_values[VAR_OUT_W] = var_values[VAR_OW] = ctx->w = inlink->w; + + if (adjusted_aspect.num && adjusted_aspect.den) { + adjusted_aspect = av_div_q(adjusted_aspect, inlink->sample_aspect_ratio); + if (ctx->h < av_rescale(ctx->w, adjusted_aspect.den, adjusted_aspect.num)) { + ctx->h = var_values[VAR_OUT_H] = var_values[VAR_OH] = av_rescale(ctx->w, adjusted_aspect.den, adjusted_aspect.num); + } else { + ctx->w = var_values[VAR_OUT_W] = var_values[VAR_OW] = av_rescale(ctx->h, adjusted_aspect.num, adjusted_aspect.den); + } + } + + /* evaluate x and y */ + av_expr_parse_and_eval(&res, (expr = ctx->x_expr), + var_names, var_values, + NULL, NULL, NULL, NULL, NULL, 0, ctx); + ctx->x = var_values[VAR_X] = res; + if ((ret = av_expr_parse_and_eval(&res, (expr = ctx->y_expr), + var_names, var_values, + NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) + return ret; + ctx->y = var_values[VAR_Y] = res; + /* evaluate x again, as it may depend on the evaluated y value */ + if ((ret = av_expr_parse_and_eval(&res, (expr = ctx->x_expr), + var_names, var_values, + NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) + return ret; + ctx->x = var_values[VAR_X] = res; + + if (ctx->x < 0 || ctx->x + inlink->w > ctx->w) + ctx->x = var_values[VAR_X] = (ctx->w - inlink->w) / 2; + if (ctx->y < 0 || ctx->y + inlink->h > ctx->h) + ctx->y = var_values[VAR_Y] = (ctx->h - inlink->h) / 2; + + /* sanity check params */ + if (ctx->w < inlink->w || ctx->h < inlink->h) { + av_log(ctx, AV_LOG_ERROR, "Padded dimensions cannot be smaller than input dimensions.\n"); + return AVERROR(EINVAL); + } + + if (ctx->w > avctx->inputs[0]->w) { + ctx->ocf.output_width = ctx->w; + } else { + ctx->ocf.output_width = avctx->inputs[0]->w; + } + + if (ctx->h > avctx->inputs[0]->h) { + ctx->ocf.output_height = ctx->h; + } else { + ctx->ocf.output_height = avctx->inputs[0]->h; + } + + if (ctx->x + avctx->inputs[0]->w > ctx->ocf.output_width || + ctx->y + avctx->inputs[0]->h > ctx->ocf.output_height) { + return AVERROR(EINVAL); + } + + err = ff_opencl_filter_config_output(outlink); + if (err < 0) + return err; + + return 0; +} + +static const AVFilterPad pad_opencl_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = &ff_opencl_filter_config_input, + }, + { NULL } +}; + +static const AVFilterPad pad_opencl_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &pad_opencl_config_output, + }, + { NULL } +}; + +#define OFFSET(x) offsetof(PadOpenCLContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM + +static const AVOption pad_opencl_options[] = { + { "width", "set the pad area width", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, 0, 0, FLAGS }, + { "w", "set the pad area width", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, 0, 0, FLAGS }, + { "height", "set the pad area height", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, 0, 0, FLAGS }, + { "h", "set the pad area height", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, 0, 0, FLAGS }, + { "x", "set the x offset for the input image position", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, INT16_MAX, FLAGS }, + { "y", "set the y offset for the input image position", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, INT16_MAX, FLAGS }, + { "color", "set the color of the padded area border", OFFSET(pad_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, 0, 0, FLAGS }, + { "aspect", "pad to fit an aspect instead of a resolution", OFFSET(aspect), AV_OPT_TYPE_RATIONAL, {.dbl = 0}, 0, INT16_MAX, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(pad_opencl); + +AVFilter ff_vf_pad_opencl = { + .name = "pad_opencl", + .description = NULL_IF_CONFIG_SMALL("Pad the input video."), + .priv_size = sizeof(PadOpenCLContext), + .priv_class = &pad_opencl_class, + .init = &ff_opencl_filter_init, + .uninit = &pad_opencl_uninit, + .query_formats = &ff_opencl_filter_query_formats, + .inputs = pad_opencl_inputs, + .outputs = pad_opencl_outputs, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE +}; diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c index 44323782d22..9b0a0e1b80b 100644 --- a/libavfilter/vf_palettegen.c +++ b/libavfilter/vf_palettegen.c @@ -83,7 +83,7 @@ typedef struct PaletteGenContext { 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}, 4, 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"}, CHAR_MIN, CHAR_MAX, 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" }, diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c index ed128813d65..b32ff817d03 100644 --- a/libavfilter/vf_paletteuse.c +++ b/libavfilter/vf_paletteuse.c @@ -122,7 +122,7 @@ static const AVOption paletteuse_options[] = { { "alpha_threshold", "set the alpha threshold for transparency", OFFSET(trans_thresh), AV_OPT_TYPE_INT, {.i64=128}, 0, 255, FLAGS }, /* following are the debug options, not part of the official API */ - { "debug_kdtree", "save Graphviz graph of the kdtree in specified file", OFFSET(dot_filename), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "debug_kdtree", "save Graphviz graph of the kdtree in specified file", OFFSET(dot_filename), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, { "color_search", "set reverse colormap color search method", OFFSET(color_search_method), AV_OPT_TYPE_INT, {.i64=COLOR_SEARCH_NNS_ITERATIVE}, 0, NB_COLOR_SEARCHES-1, FLAGS, "search" }, { "nns_iterative", "iterative search", 0, AV_OPT_TYPE_CONST, {.i64=COLOR_SEARCH_NNS_ITERATIVE}, INT_MIN, INT_MAX, FLAGS, "search" }, { "nns_recursive", "recursive search", 0, AV_OPT_TYPE_CONST, {.i64=COLOR_SEARCH_NNS_RECURSIVE}, INT_MIN, INT_MAX, FLAGS, "search" }, @@ -903,7 +903,6 @@ static int apply_palette(AVFilterLink *inlink, AVFrame *in, AVFrame **outf) AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) { - av_frame_free(&in); *outf = NULL; return AVERROR(ENOMEM); } @@ -913,13 +912,12 @@ static int apply_palette(AVFilterLink *inlink, AVFrame *in, AVFrame **outf) s->last_out, out, &x, &y, &w, &h); av_frame_unref(s->last_in); av_frame_unref(s->last_out); - if (av_frame_ref(s->last_in, in) < 0 || - av_frame_ref(s->last_out, out) < 0 || - av_frame_make_writable(s->last_in) < 0) { - av_frame_free(&in); + if ((ret = av_frame_ref(s->last_in, in)) < 0 || + (ret = av_frame_ref(s->last_out, out)) < 0 || + (ret = av_frame_make_writable(s->last_in)) < 0) { av_frame_free(&out); *outf = NULL; - return AVERROR(ENOMEM); + return ret; } ff_dlog(ctx, "%dx%d rect: (%d;%d) -> (%d,%d) [area:%dx%d]\n", @@ -934,7 +932,6 @@ static int apply_palette(AVFilterLink *inlink, AVFrame *in, AVFrame **outf) memcpy(out->data[1], s->palette, AVPALETTE_SIZE); if (s->calc_mean_err) debug_mean_error(s, in, out, inlink->frame_count_out); - av_frame_free(&in); *outf = out; return 0; } @@ -1023,20 +1020,17 @@ static int load_apply_palette(FFFrameSync *fs) if (ret < 0) return ret; if (!master || !second) { - ret = AVERROR_BUG; - goto error; + av_frame_free(&master); + return AVERROR_BUG; } if (!s->palette_loaded) { load_palette(s, second); } ret = apply_palette(inlink, master, &out); + av_frame_free(&master); if (ret < 0) - goto error; + return ret; return ff_filter_frame(ctx->outputs[0], out); - -error: - av_frame_free(&master); - return ret; } #define DEFINE_SET_FRAME(color_search, name, value) \ diff --git a/libavfilter/vf_phase.c b/libavfilter/vf_phase.c index fadeb6266dc..8536444db75 100644 --- a/libavfilter/vf_phase.c +++ b/libavfilter/vf_phase.c @@ -39,6 +39,29 @@ enum PhaseMode { AUTO_ANALYZE }; +#define DEPTH 8 +#include "phase_template.c" + +#undef DEPTH +#define DEPTH 9 +#include "phase_template.c" + +#undef DEPTH +#define DEPTH 10 +#include "phase_template.c" + +#undef DEPTH +#define DEPTH 12 +#include "phase_template.c" + +#undef DEPTH +#define DEPTH 14 +#include "phase_template.c" + +#undef DEPTH +#define DEPTH 16 +#include "phase_template.c" + typedef struct PhaseContext { const AVClass *class; int mode; ///format); int ret; + switch (desc->comp[0].depth) { + case 8: s->analyze_plane = analyze_plane_8; break; + case 9: s->analyze_plane = analyze_plane_9; break; + case 10: s->analyze_plane = analyze_plane_10; break; + case 12: s->analyze_plane = analyze_plane_12; break; + case 14: s->analyze_plane = analyze_plane_14; break; + case 16: s->analyze_plane = analyze_plane_16; break; + default: av_assert0(0); + }; + if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0) return ret; @@ -100,149 +158,6 @@ static int config_input(AVFilterLink *inlink) return 0; } -/* - * This macro interpolates the value of both fields at a point halfway - * between lines and takes the squared difference. In field resolution - * the point is a quarter pixel below a line in one field and a quarter - * pixel above a line in other. - * - * (The result is actually multiplied by 25) - */ -#define DIFF(a, as, b, bs) ((t) = ((*(a) - (b)[bs]) << 2) + (a)[(as) << 1] - (b)[-(bs)], (t) * (t)) - -/* - * Find which field combination has the smallest average squared difference - * between the fields. - */ -static enum PhaseMode analyze_plane(void *ctx, enum PhaseMode mode, AVFrame *old, AVFrame *new) -{ - double bdiff, tdiff, pdiff; - - if (mode == AUTO) { - mode = new->interlaced_frame ? new->top_field_first ? - TOP_FIRST : BOTTOM_FIRST : PROGRESSIVE; - } else if (mode == AUTO_ANALYZE) { - mode = new->interlaced_frame ? new->top_field_first ? - TOP_FIRST_ANALYZE : BOTTOM_FIRST_ANALYZE : FULL_ANALYZE; - } - - if (mode <= BOTTOM_FIRST) { - bdiff = pdiff = tdiff = 65536.0; - } else { - const int ns = new->linesize[0]; - const int os = old->linesize[0]; - const uint8_t *nptr = new->data[0]; - const uint8_t *optr = old->data[0]; - const int h = new->height; - const int w = new->width; - int bdif, tdif, pdif; - double scale; - - int top = 0, t; - const uint8_t *rend, *end = nptr + (h - 2) * ns; - - bdiff = pdiff = tdiff = 0.0; - - nptr += ns; - optr += os; - while (nptr < end) { - pdif = tdif = bdif = 0; - - switch (mode) { - case TOP_FIRST_ANALYZE: - if (top) { - for (rend = nptr + w; nptr < rend; nptr++, optr++) { - pdif += DIFF(nptr, ns, nptr, ns); - tdif += DIFF(nptr, ns, optr, os); - } - } else { - for (rend = nptr + w; nptr < rend; nptr++, optr++) { - pdif += DIFF(nptr, ns, nptr, ns); - tdif += DIFF(optr, os, nptr, ns); - } - } - break; - case BOTTOM_FIRST_ANALYZE: - if (top) { - for (rend = nptr + w; nptr < rend; nptr++, optr++) { - pdif += DIFF(nptr, ns, nptr, ns); - bdif += DIFF(optr, os, nptr, ns); - } - } else { - for (rend = nptr + w; nptr < rend; nptr++, optr++) { - pdif += DIFF(nptr, ns, nptr, ns); - bdif += DIFF(nptr, ns, optr, os); - } - } - break; - case ANALYZE: - if (top) { - for (rend = nptr + w; nptr < rend; nptr++, optr++) { - tdif += DIFF(nptr, ns, optr, os); - bdif += DIFF(optr, os, nptr, ns); - } - } else { - for (rend = nptr + w; nptr < rend; nptr++, optr++) { - bdif += DIFF(nptr, ns, optr, os); - tdif += DIFF(optr, os, nptr, ns); - } - } - break; - case FULL_ANALYZE: - if (top) { - for (rend = nptr + w; nptr < rend; nptr++, optr++) { - pdif += DIFF(nptr, ns, nptr, ns); - tdif += DIFF(nptr, ns, optr, os); - bdif += DIFF(optr, os, nptr, ns); - } - } else { - for (rend = nptr + w; nptr < rend; nptr++, optr++) { - pdif += DIFF(nptr, ns, nptr, ns); - bdif += DIFF(nptr, ns, optr, os); - tdif += DIFF(optr, os, nptr, ns); - } - } - break; - default: - av_assert0(0); - } - - pdiff += (double)pdif; - tdiff += (double)tdif; - bdiff += (double)bdif; - nptr += ns - w; - optr += os - w; - top ^= 1; - } - - scale = 1.0 / (w * (h - 3)) / 25.0; - pdiff *= scale; - tdiff *= scale; - bdiff *= scale; - - if (mode == TOP_FIRST_ANALYZE) { - bdiff = 65536.0; - } else if (mode == BOTTOM_FIRST_ANALYZE) { - tdiff = 65536.0; - } else if (mode == ANALYZE) { - pdiff = 65536.0; - } - - if (bdiff < pdiff && bdiff < tdiff) { - mode = BOTTOM_FIRST; - } else if (tdiff < pdiff && tdiff < bdiff) { - mode = TOP_FIRST; - } else { - mode = PROGRESSIVE; - } - } - - av_log(ctx, AV_LOG_DEBUG, "mode=%c tdiff=%f bdiff=%f pdiff=%f\n", - mode == BOTTOM_FIRST ? 'b' : mode == TOP_FIRST ? 't' : 'p', - tdiff, bdiff, pdiff); - return mode; -} - static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; @@ -272,7 +187,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->frame = in; mode = PROGRESSIVE; } else { - mode = analyze_plane(ctx, s->mode, s->frame, in); + mode = s->analyze_plane(ctx, s->mode, s->frame, in); } for (plane = 0; plane < s->nb_planes; plane++) { diff --git a/libavfilter/vf_photosensitivity.c b/libavfilter/vf_photosensitivity.c new file mode 100644 index 00000000000..e05c187eebb --- /dev/null +++ b/libavfilter/vf_photosensitivity.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2019 Vladimir Panteleev + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" + +#include "formats.h" +#include "internal.h" +#include "video.h" + +#define MAX_FRAMES 240 +#define GRID_SIZE 8 +#define NUM_CHANNELS 3 + +typedef struct PhotosensitivityFrame { + uint8_t grid[GRID_SIZE][GRID_SIZE][4]; +} PhotosensitivityFrame; + +typedef struct PhotosensitivityContext { + const AVClass *class; + + int nb_frames; + int skip; + float threshold_multiplier; + int bypass; + + int badness_threshold; + + /* Circular buffer */ + int history[MAX_FRAMES]; + int history_pos; + + PhotosensitivityFrame last_frame_e; + AVFrame *last_frame_av; +} PhotosensitivityContext; + +#define OFFSET(x) offsetof(PhotosensitivityContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption photosensitivity_options[] = { + { "frames", "set how many frames to use", OFFSET(nb_frames), AV_OPT_TYPE_INT, {.i64=30}, 2, MAX_FRAMES, FLAGS }, + { "f", "set how many frames to use", OFFSET(nb_frames), AV_OPT_TYPE_INT, {.i64=30}, 2, MAX_FRAMES, FLAGS }, + { "threshold", "set detection threshold factor (lower is stricter)", OFFSET(threshold_multiplier), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0.1, FLT_MAX, FLAGS }, + { "t", "set detection threshold factor (lower is stricter)", OFFSET(threshold_multiplier), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0.1, FLT_MAX, FLAGS }, + { "skip", "set pixels to skip when sampling frames", OFFSET(skip), AV_OPT_TYPE_INT, {.i64=1}, 1, 1024, FLAGS }, + { "bypass", "leave frames unchanged", OFFSET(bypass), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(photosensitivity); + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pixel_fmts[] = { + AV_PIX_FMT_RGB24, + AV_PIX_FMT_BGR24, + AV_PIX_FMT_NONE + }; + AVFilterFormats *formats = ff_make_format_list(pixel_fmts); + if (!formats) + return AVERROR(ENOMEM); + return ff_set_common_formats(ctx, formats); +} + +typedef struct ThreadData_convert_frame +{ + AVFrame *in; + PhotosensitivityFrame *out; + int skip; +} ThreadData_convert_frame; + +#define NUM_CELLS (GRID_SIZE * GRID_SIZE) + +static int convert_frame_partial(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + int cell, gx, gy, x0, x1, y0, y1, x, y, c, area; + int sum[NUM_CHANNELS]; + const uint8_t *p; + + ThreadData_convert_frame *td = arg; + + const int slice_start = (NUM_CELLS * jobnr) / nb_jobs; + const int slice_end = (NUM_CELLS * (jobnr+1)) / nb_jobs; + + int width = td->in->width, height = td->in->height, linesize = td->in->linesize[0], skip = td->skip; + const uint8_t *data = td->in->data[0]; + + for (cell = slice_start; cell < slice_end; cell++) { + gx = cell % GRID_SIZE; + gy = cell / GRID_SIZE; + + x0 = width * gx / GRID_SIZE; + x1 = width * (gx+1) / GRID_SIZE; + y0 = height * gy / GRID_SIZE; + y1 = height * (gy+1) / GRID_SIZE; + + for (c = 0; c < NUM_CHANNELS; c++) { + sum[c] = 0; + } + for (y = y0; y < y1; y += skip) { + p = data + y * linesize + x0 * NUM_CHANNELS; + for (x = x0; x < x1; x += skip) { + //av_log(NULL, AV_LOG_VERBOSE, "%d %d %d : (%d,%d) (%d,%d) -> %d,%d | *%d\n", c, gx, gy, x0, y0, x1, y1, x, y, (int)row); + sum[0] += p[0]; + sum[1] += p[1]; + sum[2] += p[2]; + p += NUM_CHANNELS * skip; + // TODO: variable size + } + } + + area = ((x1 - x0 + skip - 1) / skip) * ((y1 - y0 + skip - 1) / skip); + for (c = 0; c < NUM_CHANNELS; c++) { + if (area) + sum[c] /= area; + td->out->grid[gy][gx][c] = sum[c]; + } + } + return 0; +} + +static void convert_frame(AVFilterContext *ctx, AVFrame *in, PhotosensitivityFrame *out, int skip) +{ + ThreadData_convert_frame td; + td.in = in; + td.out = out; + td.skip = skip; + ctx->internal->execute(ctx, convert_frame_partial, &td, NULL, FFMIN(NUM_CELLS, ff_filter_get_nb_threads(ctx))); +} + +typedef struct ThreadData_blend_frame +{ + AVFrame *target; + AVFrame *source; + uint16_t s_mul; +} ThreadData_blend_frame; + +static int blend_frame_partial(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + int x, y; + uint8_t *t, *s; + + ThreadData_blend_frame *td = arg; + const uint16_t s_mul = td->s_mul; + const uint16_t t_mul = 0x100 - s_mul; + const int slice_start = (td->target->height * jobnr) / nb_jobs; + const int slice_end = (td->target->height * (jobnr+1)) / nb_jobs; + const int linesize = td->target->linesize[0]; + + for (y = slice_start; y < slice_end; y++) { + t = td->target->data[0] + y * td->target->linesize[0]; + s = td->source->data[0] + y * td->source->linesize[0]; + for (x = 0; x < linesize; x++) { + *t = (*t * t_mul + *s * s_mul) >> 8; + t++; s++; + } + } + return 0; +} + +static void blend_frame(AVFilterContext *ctx, AVFrame *target, AVFrame *source, float factor) +{ + ThreadData_blend_frame td; + td.target = target; + td.source = source; + td.s_mul = (uint16_t)(factor * 0x100); + ctx->internal->execute(ctx, blend_frame_partial, &td, NULL, FFMIN(ctx->outputs[0]->h, ff_filter_get_nb_threads(ctx))); +} + +static int get_badness(PhotosensitivityFrame *a, PhotosensitivityFrame *b) +{ + int badness, x, y, c; + badness = 0; + for (c = 0; c < NUM_CHANNELS; c++) { + for (y = 0; y < GRID_SIZE; y++) { + for (x = 0; x < GRID_SIZE; x++) { + badness += abs((int)a->grid[y][x][c] - (int)b->grid[y][x][c]); + //av_log(NULL, AV_LOG_VERBOSE, "%d - %d -> %d \n", a->grid[y][x], b->grid[y][x], badness); + //av_log(NULL, AV_LOG_VERBOSE, "%d -> %d \n", abs((int)a->grid[y][x] - (int)b->grid[y][x]), badness); + } + } + } + return badness; +} + +static int config_input(AVFilterLink *inlink) +{ + /* const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); */ + AVFilterContext *ctx = inlink->dst; + PhotosensitivityContext *s = ctx->priv; + + s->badness_threshold = (int)(GRID_SIZE * GRID_SIZE * 4 * 256 * s->nb_frames * s->threshold_multiplier / 128); + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + int this_badness, current_badness, fixed_badness, new_badness, i, res; + PhotosensitivityFrame ef; + AVFrame *src, *out; + int free_in = 0; + float factor; + AVDictionary **metadata; + + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + PhotosensitivityContext *s = ctx->priv; + + /* weighted moving average */ + current_badness = 0; + for (i = 1; i < s->nb_frames; i++) + current_badness += i * s->history[(s->history_pos + i) % s->nb_frames]; + current_badness /= s->nb_frames; + + convert_frame(ctx, in, &ef, s->skip); + this_badness = get_badness(&ef, &s->last_frame_e); + new_badness = current_badness + this_badness; + av_log(s, AV_LOG_VERBOSE, "badness: %6d -> %6d / %6d (%3d%% - %s)\n", + current_badness, new_badness, s->badness_threshold, + 100 * new_badness / s->badness_threshold, new_badness < s->badness_threshold ? "OK" : "EXCEEDED"); + + fixed_badness = new_badness; + if (new_badness < s->badness_threshold || !s->last_frame_av || s->bypass) { + factor = 1; /* for metadata */ + av_frame_free(&s->last_frame_av); + s->last_frame_av = src = in; + s->last_frame_e = ef; + s->history[s->history_pos] = this_badness; + } else { + factor = (float)(s->badness_threshold - current_badness) / (new_badness - current_badness); + if (factor <= 0) { + /* just duplicate the frame */ + s->history[s->history_pos] = 0; /* frame was duplicated, thus, delta is zero */ + } else { + res = av_frame_make_writable(s->last_frame_av); + if (res) { + av_frame_free(&in); + return res; + } + blend_frame(ctx, s->last_frame_av, in, factor); + + convert_frame(ctx, s->last_frame_av, &ef, s->skip); + this_badness = get_badness(&ef, &s->last_frame_e); + fixed_badness = current_badness + this_badness; + av_log(s, AV_LOG_VERBOSE, " fixed: %6d -> %6d / %6d (%3d%%) factor=%5.3f\n", + current_badness, fixed_badness, s->badness_threshold, + 100 * new_badness / s->badness_threshold, factor); + s->last_frame_e = ef; + s->history[s->history_pos] = this_badness; + } + src = s->last_frame_av; + free_in = 1; + } + s->history_pos = (s->history_pos + 1) % s->nb_frames; + + out = ff_get_video_buffer(outlink, in->width, in->height); + if (!out) { + if (free_in == 1) + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + metadata = &out->metadata; + if (metadata) { + char value[128]; + + snprintf(value, sizeof(value), "%f", (float)new_badness / s->badness_threshold); + av_dict_set(metadata, "lavfi.photosensitivity.badness", value, 0); + + snprintf(value, sizeof(value), "%f", (float)fixed_badness / s->badness_threshold); + av_dict_set(metadata, "lavfi.photosensitivity.fixed-badness", value, 0); + + snprintf(value, sizeof(value), "%f", (float)this_badness / s->badness_threshold); + av_dict_set(metadata, "lavfi.photosensitivity.frame-badness", value, 0); + + snprintf(value, sizeof(value), "%f", factor); + av_dict_set(metadata, "lavfi.photosensitivity.factor", value, 0); + } + av_frame_copy(out, src); + if (free_in == 1) + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + PhotosensitivityContext *s = ctx->priv; + + av_frame_free(&s->last_frame_av); +} + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = config_input, + }, + { NULL } +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter ff_vf_photosensitivity = { + .name = "photosensitivity", + .description = NULL_IF_CONFIG_SMALL("Filter out photosensitive epilepsy seizure-inducing flashes."), + .priv_size = sizeof(PhotosensitivityContext), + .priv_class = &photosensitivity_class, + .uninit = uninit, + .query_formats = query_formats, + .inputs = inputs, + .outputs = outputs, +}; diff --git a/libavfilter/vf_premultiply.c b/libavfilter/vf_premultiply.c index a9404b4eb3b..1fef4777de7 100644 --- a/libavfilter/vf_premultiply.c +++ b/libavfilter/vf_premultiply.c @@ -80,7 +80,7 @@ static int query_formats(AVFilterContext *ctx) static const enum AVPixelFormat alpha_pix_fmts[] = { AV_PIX_FMT_YUVA444P, - AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_NONE @@ -186,7 +186,7 @@ static void premultiply16yuv(const uint8_t *mmsrc, const uint8_t *aasrc, for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { - dst[x] = ((((msrc[x] - half) * (((asrc[x] >> 1) & 1) + asrc[x]))) >> shift) + half; + dst[x] = ((((msrc[x] - half) * (int64_t)(((asrc[x] >> 1) & 1) + asrc[x]))) >> shift) + half; } dst += dlinesize / 2; @@ -209,7 +209,7 @@ static void premultiply16offset(const uint8_t *mmsrc, const uint8_t *aasrc, for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { - dst[x] = ((((msrc[x] - offset) * (((asrc[x] >> 1) & 1) + asrc[x])) + half) >> shift) + offset; + dst[x] = ((((msrc[x] - offset) * (int64_t)(((asrc[x] >> 1) & 1) + asrc[x])) + half) >> shift) + offset; } dst += dlinesize / 2; @@ -639,6 +639,8 @@ static int activate(AVFilterContext *ctx) int ret, status; int64_t pts; + FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); + if ((ret = ff_inlink_consume_frame(ctx->inputs[0], &frame)) > 0) { ret = filter_frame(ctx, &out, frame, frame); av_frame_free(&frame); diff --git a/libavfilter/vf_procamp_vaapi.c b/libavfilter/vf_procamp_vaapi.c index c3e9866f227..7342048583e 100644 --- a/libavfilter/vf_procamp_vaapi.c +++ b/libavfilter/vf_procamp_vaapi.c @@ -150,7 +150,7 @@ static int procamp_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame err = av_frame_copy_props(output_frame, input_frame); if (err < 0) - return err; + goto fail; err = ff_vaapi_vpp_init_params(avctx, ¶ms, input_frame, output_frame); diff --git a/libavfilter/vf_psnr.c b/libavfilter/vf_psnr.c index 0675a17c5db..c9a673dcde7 100644 --- a/libavfilter/vf_psnr.c +++ b/libavfilter/vf_psnr.c @@ -350,6 +350,14 @@ static int config_output(AVFilterLink *outlink) if ((ret = ff_framesync_configure(&s->fs)) < 0) return ret; + outlink->time_base = s->fs.time_base; + + if (av_cmp_q(mainlink->time_base, outlink->time_base) || + av_cmp_q(ctx->inputs[1]->time_base, outlink->time_base)) + av_log(ctx, AV_LOG_WARNING, "not matching timebases found between first input: %d/%d and second input %d/%d, results may be incorrect!\n", + mainlink->time_base.num, mainlink->time_base.den, + ctx->inputs[1]->time_base.num, ctx->inputs[1]->time_base.den); + return 0; } diff --git a/libavfilter/vf_random.c b/libavfilter/vf_random.c index 373a7db053a..e73a224cd8c 100644 --- a/libavfilter/vf_random.c +++ b/libavfilter/vf_random.c @@ -97,8 +97,13 @@ static int request_frame(AVFilterLink *outlink) ret = ff_request_frame(ctx->inputs[0]); +next: if (ret == AVERROR_EOF && !ctx->is_disabled && s->nb_frames > 0) { AVFrame *out = s->frames[s->nb_frames - 1]; + if (!out) { + s->nb_frames--; + goto next; + } out->pts = s->pts[s->flush_idx++]; ret = ff_filter_frame(outlink, out); s->frames[s->nb_frames - 1] = NULL; @@ -108,6 +113,14 @@ static int request_frame(AVFilterLink *outlink) return ret; } +static av_cold void uninit(AVFilterContext *ctx) +{ + RandomContext *s = ctx->priv; + + for (int i = 0; i < s->nb_frames; i++) + av_frame_free(&s->frames[i]); +} + static const AVFilterPad random_inputs[] = { { .name = "default", @@ -132,6 +145,7 @@ AVFilter ff_vf_random = { .priv_size = sizeof(RandomContext), .priv_class = &random_class, .init = init, + .uninit = uninit, .inputs = random_inputs, .outputs = random_outputs, }; diff --git a/libavfilter/vf_readeia608.c b/libavfilter/vf_readeia608.c index 27a0c583216..2973847d408 100644 --- a/libavfilter/vf_readeia608.c +++ b/libavfilter/vf_readeia608.c @@ -36,41 +36,52 @@ #include "internal.h" #include "video.h" -#define FALL 0 -#define RISE 1 +#define LAG 25 +#define CLOCK_BITSIZE_MIN 0.2f +#define CLOCK_BITSIZE_MAX 1.5f +#define SYNC_BITSIZE_MIN 12.f +#define SYNC_BITSIZE_MAX 15.f + +typedef struct LineItem { + int input; + int output; + + float unfiltered; + float filtered; + float average; + float deviation; +} LineItem; + +typedef struct CodeItem { + uint8_t bit; + int size; +} CodeItem; typedef struct ReadEIA608Context { const AVClass *class; int start, end; - int min_range; - int max_peak_diff; - int max_period_diff; - int max_start_diff; int nb_found; int white; int black; - float mpd, mhd, msd, mac, spw, bhd, wth, bth; + float spw; int chp; int lp; - uint8_t *temp; + + uint64_t histogram[256]; + + CodeItem *code; + LineItem *line; } ReadEIA608Context; #define OFFSET(x) offsetof(ReadEIA608Context, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM static const AVOption readeia608_options[] = { - { "scan_min", "set from which line to scan for codes", OFFSET(start), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, - { "scan_max", "set to which line to scan for codes", OFFSET(end), AV_OPT_TYPE_INT, {.i64=29}, 0, INT_MAX, FLAGS }, - { "mac", "set minimal acceptable amplitude change for sync codes detection", OFFSET(mac), AV_OPT_TYPE_FLOAT, {.dbl=.2}, 0.001, 1, FLAGS }, - { "spw", "set ratio of width reserved for sync code detection", OFFSET(spw), AV_OPT_TYPE_FLOAT, {.dbl=.27}, 0.1, 0.7, FLAGS }, - { "mhd", "set max peaks height difference for sync code detection", OFFSET(mhd), AV_OPT_TYPE_FLOAT, {.dbl=.1}, 0, 0.5, FLAGS }, - { "mpd", "set max peaks period difference for sync code detection", OFFSET(mpd), AV_OPT_TYPE_FLOAT, {.dbl=.1}, 0, 0.5, FLAGS }, - { "msd", "set first two max start code bits differences", OFFSET(msd), AV_OPT_TYPE_FLOAT, {.dbl=.02}, 0, 0.5, FLAGS }, - { "bhd", "set min ratio of bits height compared to 3rd start code bit", OFFSET(bhd), AV_OPT_TYPE_FLOAT, {.dbl=.75}, 0.01, 1, FLAGS }, - { "th_w", "set white color threshold", OFFSET(wth), AV_OPT_TYPE_FLOAT, {.dbl=.35}, 0.1, 1, FLAGS }, - { "th_b", "set black color threshold", OFFSET(bth), AV_OPT_TYPE_FLOAT, {.dbl=.15}, 0, 0.5, FLAGS }, - { "chp", "check and apply parity bit", OFFSET(chp), AV_OPT_TYPE_BOOL, {.i64= 0}, 0, 1, FLAGS }, - { "lp", "lowpass line prior to processing", OFFSET(lp), AV_OPT_TYPE_BOOL, {.i64= 0}, 0, 1, FLAGS }, + { "scan_min", "set from which line to scan for codes", OFFSET(start), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, + { "scan_max", "set to which line to scan for codes", OFFSET(end), AV_OPT_TYPE_INT, {.i64=29}, 0, INT_MAX, FLAGS }, + { "spw", "set ratio of width reserved for sync code detection", OFFSET(spw), AV_OPT_TYPE_FLOAT, {.dbl=.27}, 0.1, 0.7, FLAGS }, + { "chp", "check and apply parity bit", OFFSET(chp), AV_OPT_TYPE_BOOL, {.i64= 0}, 0, 1, FLAGS }, + { "lp", "lowpass line prior to processing", OFFSET(lp), AV_OPT_TYPE_BOOL, {.i64= 1}, 0, 1, FLAGS }, { NULL } }; @@ -96,10 +107,9 @@ static int query_formats(AVFilterContext *ctx) static int config_input(AVFilterLink *inlink) { - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); AVFilterContext *ctx = inlink->dst; ReadEIA608Context *s = ctx->priv; - int depth = desc->comp[0].depth; + int size = inlink->w + LAG; if (s->end >= inlink->h) { av_log(ctx, AV_LOG_WARNING, "Last line to scan too large, clipping.\n"); @@ -111,124 +121,243 @@ static int config_input(AVFilterLink *inlink) return AVERROR(EINVAL); } - s->min_range = s->mac * ((1 << depth) - 1); - s->max_peak_diff = s->mhd * ((1 << depth) - 1); - s->max_period_diff = s->mpd * ((1 << depth) - 1); - s->max_start_diff = s->msd * ((1 << depth) - 1); - s->white = s->wth * ((1 << depth) - 1); - s->black = s->bth * ((1 << depth) - 1); - s->temp = av_calloc(inlink->w, sizeof(*s->temp)); - if (!s->temp) + s->line = av_calloc(size, sizeof(*s->line)); + s->code = av_calloc(size, sizeof(*s->code)); + if (!s->line || !s->code) return AVERROR(ENOMEM); return 0; } -static void extract_line(AVFilterContext *ctx, AVFilterLink *inlink, AVFrame *in, int line) +static void build_histogram(ReadEIA608Context *s, const LineItem *line, int len) { - ReadEIA608Context *s = ctx->priv; - int max = 0, min = INT_MAX; - int i, ch, range = 0; - const uint8_t *src; - uint16_t clock[8][2] = { { 0 } }; - const int sync_width = s->spw * in->width; - int last = 0, peaks = 0, max_peak_diff = 0, dir = RISE; - const int width_per_bit = (in->width - sync_width) / 19; - uint8_t byte[2] = { 0 }; - int s1, s2, s3, parity; + memset(s->histogram, 0, sizeof(s->histogram)); - src = &in->data[0][line * in->linesize[0]]; + for (int i = LAG; i < len + LAG; i++) + s->histogram[line[i].input]++; +} - if (s->lp) { - uint8_t *dst = s->temp; - int w = inlink->w - 1; +static void find_black_and_white(ReadEIA608Context *s) +{ + int start = 0, end = 0, middle; + int black = 0, white = 0; + int cnt; + + for (int i = 0; i < 256; i++) { + if (s->histogram[i]) { + start = i; + break; + } + } - for (i = 0; i < inlink->w; i++) { - int a = FFMAX(i - 3, 0); - int b = FFMAX(i - 2, 0); - int c = FFMAX(i - 1, 0); - int d = FFMIN(i + 3, w); - int e = FFMIN(i + 2, w); - int f = FFMIN(i + 1, w); + for (int i = 255; i >= 0; i--) { + if (s->histogram[i]) { + end = i; + break; + } + } - dst[i] = (src[a] + src[b] + src[c] + src[i] + src[d] + src[e] + src[f] + 6) / 7; + middle = start + (end - start) / 2; + + cnt = 0; + for (int i = start; i <= middle; i++) { + if (s->histogram[i] > cnt) { + cnt = s->histogram[i]; + black = i; } + } + + cnt = 0; + for (int i = end; i >= middle; i--) { + if (s->histogram[i] > cnt) { + cnt = s->histogram[i]; + white = i; + } + } - src = s->temp; + s->black = black; + s->white = white; +} + +static float meanf(const LineItem *line, int len) +{ + float sum = 0.0, mean = 0.0; + + for (int i = 0; i < len; i++) + sum += line[i].filtered; + + mean = sum / len; + + return mean; +} + +static float stddevf(const LineItem *line, int len) +{ + float m = meanf(line, len); + float standard_deviation = 0.f; + + for (int i = 0; i < len; i++) + standard_deviation += (line[i].filtered - m) * (line[i].filtered - m); + + return sqrtf(standard_deviation / (len - 1)); +} + +static void thresholding(ReadEIA608Context *s, LineItem *line, + int lag, float threshold, float influence, int len) +{ + for (int i = lag; i < len + lag; i++) { + line[i].unfiltered = line[i].input / 255.f; + line[i].filtered = line[i].unfiltered; } - for (i = 0; i < sync_width; i++) { - max = FFMAX(max, src[i]); - min = FFMIN(min, src[i]); + for (int i = 0; i < lag; i++) { + line[i].unfiltered = meanf(line, len * s->spw); + line[i].filtered = line[i].unfiltered; } - range = max - min; - if (range < s->min_range) - return; + line[lag - 1].average = meanf(line, lag); + line[lag - 1].deviation = stddevf(line, lag); - for (i = 0; i < sync_width; i++) { - int Y = src[i]; - - if (dir == RISE) { - if (Y < last) { - dir = FALL; - if (last >= s->white) { - clock[peaks][0] = last; - clock[peaks][1] = i; - peaks++; - if (peaks > 7) - break; - } - } - } else if (dir == FALL) { - if (Y > last && last <= s->black) { - dir = RISE; + for (int i = lag; i < len + lag; i++) { + if (fabsf(line[i].unfiltered - line[i-1].average) > threshold * line[i-1].deviation) { + if (line[i].unfiltered > line[i-1].average) { + line[i].output = 255; + } else { + line[i].output = 0; } + + line[i].filtered = influence * line[i].unfiltered + (1.f - influence) * line[i-1].filtered; + } else { + int distance_from_black, distance_from_white; + + distance_from_black = FFABS(line[i].input - s->black); + distance_from_white = FFABS(line[i].input - s->white); + + line[i].output = distance_from_black <= distance_from_white ? 0 : 255; } - last = Y; + + line[i].average = meanf(line + i - lag, lag); + line[i].deviation = stddevf(line + i - lag, lag); } +} - if (peaks != 7) { - av_log(ctx, AV_LOG_DEBUG, "peaks: %d != 7\n", peaks); - return; +static int periods(const LineItem *line, CodeItem *code, int len) +{ + int hold = line[LAG].output, cnt = 0; + int last = LAG; + + memset(code, 0, len * sizeof(*code)); + + for (int i = LAG + 1; i < len + LAG; i++) { + if (line[i].output != hold) { + code[cnt].size = i - last; + code[cnt].bit = hold; + hold = line[i].output; + last = i; + cnt++; + } } - for (i = 1; i < 7; i++) - max_peak_diff = FFMAX(max_peak_diff, FFABS(clock[i][0] - clock[i-1][0])); + code[cnt].size = LAG + len - last; + code[cnt].bit = hold; - if (max_peak_diff > s->max_peak_diff) { - av_log(ctx, AV_LOG_DEBUG, "mhd: %d > %d\n", max_peak_diff, s->max_peak_diff); - return; + return cnt + 1; +} + +static void dump_code(AVFilterContext *ctx, int len, int item) +{ + ReadEIA608Context *s = ctx->priv; + + av_log(ctx, AV_LOG_DEBUG, "%d:", item); + for (int i = 0; i < len; i++) { + av_log(ctx, AV_LOG_DEBUG, " %03d", s->code[i].size); } + av_log(ctx, AV_LOG_DEBUG, "\n"); +} + +static void extract_line(AVFilterContext *ctx, AVFrame *in, int w, int nb_line) +{ + ReadEIA608Context *s = ctx->priv; + LineItem *line = s->line; + int i, j, ch, len; + const uint8_t *src; + uint8_t byte[2] = { 0 }; + uint8_t codes[19] = { 0 }; + float bit_size = 0.f; + int parity; + + memset(line, 0, (w + LAG) * sizeof(*line)); + + src = &in->data[0][nb_line * in->linesize[0]]; + if (s->lp) { + for (i = 0; i < w; i++) { + int a = FFMAX(i - 3, 0); + int b = FFMAX(i - 2, 0); + int c = FFMAX(i - 1, 0); + int d = FFMIN(i + 3, w-1); + int e = FFMIN(i + 2, w-1); + int f = FFMIN(i + 1, w-1); - max = 0; min = INT_MAX; - for (i = 1; i < 7; i++) { - max = FFMAX(max, FFABS(clock[i][1] - clock[i-1][1])); - min = FFMIN(min, FFABS(clock[i][1] - clock[i-1][1])); + line[LAG + i].input = (src[a] + src[b] + src[c] + src[i] + src[d] + src[e] + src[f] + 6) / 7; + } + } else { + for (i = 0; i < w; i++) { + line[LAG + i].input = src[i]; + } } - range = max - min; - if (range > s->max_period_diff) { - av_log(ctx, AV_LOG_DEBUG, "mpd: %d > %d\n", range, s->max_period_diff); + build_histogram(s, line, w); + find_black_and_white(s); + if (s->white - s->black < 5) + return; + + thresholding(s, line, LAG, 1, 0, w); + len = periods(line, s->code, w); + dump_code(ctx, len, nb_line); + if (len < 15 || + s->code[14].bit != 0 || + w / (float)s->code[14].size < SYNC_BITSIZE_MIN || + w / (float)s->code[14].size > SYNC_BITSIZE_MAX) { return; } - s1 = src[sync_width + width_per_bit * 0 + width_per_bit / 2]; - s2 = src[sync_width + width_per_bit * 1 + width_per_bit / 2]; - s3 = src[sync_width + width_per_bit * 2 + width_per_bit / 2]; + for (i = 14; i < len; i++) { + bit_size += s->code[i].size; + } + + bit_size /= 19.f; + for (i = 1; i < 14; i++) { + if (s->code[i].size / bit_size > CLOCK_BITSIZE_MAX || + s->code[i].size / bit_size < CLOCK_BITSIZE_MIN) { + return; + } + } - if (FFABS(s1 - s2) > s->max_start_diff || s1 > s->black || s2 > s->black || s3 < s->white) { - av_log(ctx, AV_LOG_DEBUG, "msd: %d > %d\n", FFABS(s1 - s2), s->max_start_diff); + if (s->code[15].size / bit_size < 0.45f) { return; } + for (j = 0, i = 14; i < len; i++) { + int run, bit; + + run = lrintf(s->code[i].size / bit_size); + bit = s->code[i].bit; + + for (int k = 0; j < 19 && k < run; k++) { + codes[j++] = bit; + } + + if (j >= 19) + break; + } + for (ch = 0; ch < 2; ch++) { for (parity = 0, i = 0; i < 8; i++) { - int b = src[sync_width + width_per_bit * (i + 3 + 8 * ch) + width_per_bit / 2]; + int b = codes[3 + ch * 8 + i]; - if (b - s1 > (s3 - s1) * s->bhd) { - b = 1; + if (b == 255) { parity++; + b = 1; } else { b = 0; } @@ -237,7 +366,7 @@ static void extract_line(AVFilterContext *ctx, AVFilterLink *inlink, AVFrame *in if (s->chp) { if (!(parity & 1)) { - byte[ch] = 0; + byte[ch] = 0x7F; } } } @@ -245,12 +374,16 @@ static void extract_line(AVFilterContext *ctx, AVFilterLink *inlink, AVFrame *in { uint8_t key[128], value[128]; + //snprintf(key, sizeof(key), "lavfi.readeia608.%d.bits", s->nb_found); + //snprintf(value, sizeof(value), "0b%d%d%d%d%d%d%d%d 0b%d%d%d%d%d%d%d%d", codes[3]==255,codes[4]==255,codes[5]==255,codes[6]==255,codes[7]==255,codes[8]==255,codes[9]==255,codes[10]==255,codes[11]==255,codes[12]==255,codes[13]==255,codes[14]==255,codes[15]==255,codes[16]==255,codes[17]==255,codes[18]==255); + //av_dict_set(&in->metadata, key, value, 0); + snprintf(key, sizeof(key), "lavfi.readeia608.%d.cc", s->nb_found); snprintf(value, sizeof(value), "0x%02X%02X", byte[0], byte[1]); av_dict_set(&in->metadata, key, value, 0); snprintf(key, sizeof(key), "lavfi.readeia608.%d.line", s->nb_found); - snprintf(value, sizeof(value), "%d", line); + snprintf(value, sizeof(value), "%d", nb_line); av_dict_set(&in->metadata, key, value, 0); } @@ -266,7 +399,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->nb_found = 0; for (i = s->start; i <= s->end; i++) - extract_line(ctx, inlink, in, i); + extract_line(ctx, in, inlink->w, i); return ff_filter_frame(outlink, in); } @@ -275,7 +408,8 @@ static av_cold void uninit(AVFilterContext *ctx) { ReadEIA608Context *s = ctx->priv; - av_freep(&s->temp); + av_freep(&s->code); + av_freep(&s->line); } static const AVFilterPad readeia608_inputs[] = { diff --git a/libavfilter/vf_remap.c b/libavfilter/vf_remap.c index b1c3e432428..6d5d75225b8 100644 --- a/libavfilter/vf_remap.c +++ b/libavfilter/vf_remap.c @@ -36,10 +36,12 @@ * Target_frame[y][x] = Source_frame[ ymap[y][x] ][ [xmap[y][x] ]; */ +#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" @@ -52,6 +54,8 @@ typedef struct RemapContext { int nb_planes; int nb_components; int step; + uint8_t fill_rgba[4]; + int fill_color[4]; FFFrameSync fs; @@ -65,6 +69,7 @@ static const AVOption remap_options[] = { { "format", "set output format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "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 }, { NULL } }; @@ -89,7 +94,7 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV444P16, - AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16, 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, @@ -140,7 +145,8 @@ static int query_formats(AVFilterContext *ctx) static int remap_planar##bits##_##name##_slice(AVFilterContext *ctx, void *arg, \ int jobnr, int nb_jobs) \ { \ - const ThreadData *td = (ThreadData*)arg; \ + RemapContext *s = ctx->priv; \ + const ThreadData *td = arg; \ const AVFrame *in = td->in; \ const AVFrame *xin = td->xin; \ const AVFrame *yin = td->yin; \ @@ -158,13 +164,14 @@ static int remap_planar##bits##_##name##_slice(AVFilterContext *ctx, void *arg, const int slinesize = in->linesize[plane] / div; \ const uint16_t *xmap = (const uint16_t *)xin->data[0] + slice_start * xlinesize; \ const uint16_t *ymap = (const uint16_t *)yin->data[0] + slice_start * ylinesize; \ + const int color = s->fill_color[plane]; \ \ for (y = slice_start; y < slice_end; y++) { \ for (x = 0; x < out->width; x++) { \ if (ymap[x] < in->height && xmap[x] < in->width) { \ dst[x] = src[ymap[x] * slinesize + xmap[x]]; \ } else { \ - dst[x] = 0; \ + dst[x] = color; \ } \ } \ dst += dlinesize; \ @@ -189,7 +196,8 @@ DEFINE_REMAP_PLANAR_FUNC(nearest, 16, 2) static int remap_packed##bits##_##name##_slice(AVFilterContext *ctx, void *arg, \ int jobnr, int nb_jobs) \ { \ - const ThreadData *td = (ThreadData*)arg; \ + RemapContext *s = ctx->priv; \ + const ThreadData *td = arg; \ const AVFrame *in = td->in; \ const AVFrame *xin = td->xin; \ const AVFrame *yin = td->yin; \ @@ -213,7 +221,7 @@ static int remap_packed##bits##_##name##_slice(AVFilterContext *ctx, void *arg, if (ymap[x] < in->height && xmap[x] < in->width) { \ dst[x * step + c] = src[ymap[x] * slinesize + xmap[x] * step + c]; \ } else { \ - dst[x * step + c] = 0; \ + dst[x * step + c] = s->fill_color[c]; \ } \ } \ } \ @@ -233,11 +241,28 @@ static int config_input(AVFilterLink *inlink) AVFilterContext *ctx = inlink->dst; RemapContext *s = ctx->priv; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + int depth = desc->comp[0].depth; + int is_rgb = !!(desc->flags & AV_PIX_FMT_FLAG_RGB); + int factor = 1 << (depth - 8); + uint8_t rgba_map[4]; + ff_fill_rgba_map(rgba_map, inlink->format); s->nb_planes = av_pix_fmt_count_planes(inlink->format); s->nb_components = desc->nb_components; - if (desc->comp[0].depth == 8) { + if (is_rgb) { + s->fill_color[rgba_map[0]] = s->fill_rgba[0] * factor; + s->fill_color[rgba_map[1]] = s->fill_rgba[1] * factor; + s->fill_color[rgba_map[2]] = s->fill_rgba[2] * factor; + s->fill_color[rgba_map[3]] = s->fill_rgba[3] * factor; + } else { + s->fill_color[0] = RGB_TO_Y_BT709(s->fill_rgba[0], s->fill_rgba[1], s->fill_rgba[2]) * factor; + s->fill_color[1] = RGB_TO_U_BT709(s->fill_rgba[0], s->fill_rgba[1], s->fill_rgba[2], 0) * factor; + s->fill_color[2] = RGB_TO_V_BT709(s->fill_rgba[0], s->fill_rgba[1], s->fill_rgba[2], 0) * factor; + s->fill_color[3] = s->fill_rgba[3] * factor; + } + + if (depth == 8) { if (s->nb_planes > 1 || s->nb_components == 1) { s->remap_slice = remap_planar8_nearest_slice; } else { diff --git a/libavfilter/vf_rotate.c b/libavfilter/vf_rotate.c index 371ff7f722b..02f56c6ebc3 100644 --- a/libavfilter/vf_rotate.c +++ b/libavfilter/vf_rotate.c @@ -94,16 +94,17 @@ typedef struct ThreadData { #define OFFSET(x) offsetof(RotContext, 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 rotate_options[] = { - { "angle", "set angle (in radians)", OFFSET(angle_expr_str), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, - { "a", "set angle (in radians)", OFFSET(angle_expr_str), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, - { "out_w", "set output width expression", OFFSET(outw_expr_str), AV_OPT_TYPE_STRING, {.str="iw"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, - { "ow", "set output width expression", OFFSET(outw_expr_str), AV_OPT_TYPE_STRING, {.str="iw"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, - { "out_h", "set output height expression", OFFSET(outh_expr_str), AV_OPT_TYPE_STRING, {.str="ih"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, - { "oh", "set output height expression", OFFSET(outh_expr_str), AV_OPT_TYPE_STRING, {.str="ih"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, - { "fillcolor", "set background fill color", OFFSET(fillcolor_str), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, - { "c", "set background fill color", OFFSET(fillcolor_str), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, + { "angle", "set angle (in radians)", OFFSET(angle_expr_str), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, .flags=TFLAGS }, + { "a", "set angle (in radians)", OFFSET(angle_expr_str), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, .flags=TFLAGS }, + { "out_w", "set output width expression", OFFSET(outw_expr_str), AV_OPT_TYPE_STRING, {.str="iw"}, 0, 0, .flags=FLAGS }, + { "ow", "set output width expression", OFFSET(outw_expr_str), AV_OPT_TYPE_STRING, {.str="iw"}, 0, 0, .flags=FLAGS }, + { "out_h", "set output height expression", OFFSET(outh_expr_str), AV_OPT_TYPE_STRING, {.str="ih"}, 0, 0, .flags=FLAGS }, + { "oh", "set output height expression", OFFSET(outh_expr_str), AV_OPT_TYPE_STRING, {.str="ih"}, 0, 0, .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 }, { "bilinear", "use bilinear interpolation", OFFSET(use_bilinear), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, .flags=FLAGS }, { NULL } }; diff --git a/libavfilter/vf_scale.c b/libavfilter/vf_scale.c index 7aebf56ad8a..0348f19d33e 100644 --- a/libavfilter/vf_scale.c +++ b/libavfilter/vf_scale.c @@ -29,9 +29,10 @@ #include "avfilter.h" #include "formats.h" #include "internal.h" -#include "scale.h" +#include "scale_eval.h" #include "video.h" #include "libavutil/avstring.h" +#include "libavutil/eval.h" #include "libavutil/internal.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" @@ -41,6 +42,62 @@ #include "libavutil/avassert.h" #include "libswscale/swscale.h" +static const char *const var_names[] = { + "in_w", "iw", + "in_h", "ih", + "out_w", "ow", + "out_h", "oh", + "a", + "sar", + "dar", + "hsub", + "vsub", + "ohsub", + "ovsub", + "n", + "t", + "pos", + "main_w", + "main_h", + "main_a", + "main_sar", + "main_dar", "mdar", + "main_hsub", + "main_vsub", + "main_n", + "main_t", + "main_pos", + NULL +}; + +enum var_name { + VAR_IN_W, VAR_IW, + VAR_IN_H, VAR_IH, + VAR_OUT_W, VAR_OW, + VAR_OUT_H, VAR_OH, + VAR_A, + VAR_SAR, + VAR_DAR, + VAR_HSUB, + VAR_VSUB, + VAR_OHSUB, + VAR_OVSUB, + VAR_N, + VAR_T, + VAR_POS, + VAR_S2R_MAIN_W, + VAR_S2R_MAIN_H, + VAR_S2R_MAIN_A, + VAR_S2R_MAIN_SAR, + VAR_S2R_MAIN_DAR, VAR_S2R_MDAR, + VAR_S2R_MAIN_HSUB, + VAR_S2R_MAIN_VSUB, + VAR_S2R_MAIN_N, + VAR_S2R_MAIN_T, + VAR_S2R_MAIN_POS, + VARS_NB +}; + enum EvalMode { EVAL_MODE_INIT, EVAL_MODE_FRAME, @@ -72,6 +129,10 @@ typedef struct ScaleContext { char *w_expr; ///< width expression string char *h_expr; ///< height expression string + AVExpr *w_pexpr; + AVExpr *h_pexpr; + double var_values[VARS_NB]; + char *flags_str; char *in_color_matrix; @@ -86,6 +147,7 @@ typedef struct ScaleContext { int in_v_chr_pos; int force_original_aspect_ratio; + int force_divisible_by; int nb_slices; @@ -95,6 +157,119 @@ typedef struct ScaleContext { AVFilter ff_vf_scale2ref; +static int config_props(AVFilterLink *outlink); + +static int check_exprs(AVFilterContext *ctx) +{ + ScaleContext *scale = ctx->priv; + unsigned vars_w[VARS_NB] = { 0 }, vars_h[VARS_NB] = { 0 }; + + if (!scale->w_pexpr && !scale->h_pexpr) + return AVERROR(EINVAL); + + if (scale->w_pexpr) + av_expr_count_vars(scale->w_pexpr, vars_w, VARS_NB); + if (scale->h_pexpr) + av_expr_count_vars(scale->h_pexpr, vars_h, VARS_NB); + + if (vars_w[VAR_OUT_W] || vars_w[VAR_OW]) { + av_log(ctx, AV_LOG_ERROR, "Width expression cannot be self-referencing: '%s'.\n", scale->w_expr); + return AVERROR(EINVAL); + } + + if (vars_h[VAR_OUT_H] || vars_h[VAR_OH]) { + av_log(ctx, AV_LOG_ERROR, "Height expression cannot be self-referencing: '%s'.\n", scale->h_expr); + return AVERROR(EINVAL); + } + + if ((vars_w[VAR_OUT_H] || vars_w[VAR_OH]) && + (vars_h[VAR_OUT_W] || vars_h[VAR_OW])) { + av_log(ctx, AV_LOG_WARNING, "Circular references detected for width '%s' and height '%s' - possibly invalid.\n", scale->w_expr, scale->h_expr); + } + + if (ctx->filter != &ff_vf_scale2ref && + (vars_w[VAR_S2R_MAIN_W] || vars_h[VAR_S2R_MAIN_W] || + vars_w[VAR_S2R_MAIN_H] || vars_h[VAR_S2R_MAIN_H] || + vars_w[VAR_S2R_MAIN_A] || vars_h[VAR_S2R_MAIN_A] || + vars_w[VAR_S2R_MAIN_SAR] || vars_h[VAR_S2R_MAIN_SAR] || + 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_HSUB] || vars_h[VAR_S2R_MAIN_HSUB] || + vars_w[VAR_S2R_MAIN_VSUB] || vars_h[VAR_S2R_MAIN_VSUB] || + 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 scale2ref variables are not valid in scale filter.\n"); + return AVERROR(EINVAL); + } + + if (scale->eval_mode == EVAL_MODE_INIT && + (vars_w[VAR_N] || vars_h[VAR_N] || + vars_w[VAR_T] || vars_h[VAR_T] || + vars_w[VAR_POS] || vars_h[VAR_POS] || + 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"); + return AVERROR(EINVAL); + } + + return 0; +} + +static int scale_parse_expr(AVFilterContext *ctx, char *str_expr, AVExpr **pexpr_ptr, const char *var, const char *args) +{ + ScaleContext *scale = ctx->priv; + int ret, is_inited = 0; + char *old_str_expr = NULL; + AVExpr *old_pexpr = NULL; + + if (str_expr) { + old_str_expr = av_strdup(str_expr); + if (!old_str_expr) + return AVERROR(ENOMEM); + av_opt_set(scale, var, args, 0); + } + + if (*pexpr_ptr) { + old_pexpr = *pexpr_ptr; + *pexpr_ptr = NULL; + is_inited = 1; + } + + ret = av_expr_parse(pexpr_ptr, args, var_names, + NULL, NULL, NULL, NULL, 0, ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Cannot parse expression for %s: '%s'\n", var, args); + goto revert; + } + + ret = check_exprs(ctx); + if (ret < 0) + goto revert; + + if (is_inited && (ret = config_props(ctx->outputs[0])) < 0) + goto revert; + + av_expr_free(old_pexpr); + old_pexpr = NULL; + av_freep(&old_str_expr); + + return 0; + +revert: + av_expr_free(*pexpr_ptr); + *pexpr_ptr = NULL; + if (old_str_expr) { + av_opt_set(scale, var, old_str_expr, 0); + av_free(old_str_expr); + } + if (old_pexpr) + *pexpr_ptr = old_pexpr; + + return ret; +} + static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts) { ScaleContext *scale = ctx->priv; @@ -126,6 +301,14 @@ static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts) if (!scale->h_expr) av_opt_set(scale, "h", "ih", 0); + ret = scale_parse_expr(ctx, NULL, &scale->w_pexpr, "width", scale->w_expr); + if (ret < 0) + return ret; + + ret = scale_parse_expr(ctx, NULL, &scale->h_pexpr, "height", scale->h_expr); + if (ret < 0) + return ret; + 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); @@ -148,6 +331,9 @@ static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts) static av_cold void uninit(AVFilterContext *ctx) { ScaleContext *scale = ctx->priv; + av_expr_free(scale->w_pexpr); + av_expr_free(scale->h_pexpr); + scale->w_pexpr = scale->h_pexpr = NULL; sws_freeContext(scale->sws); sws_freeContext(scale->isws[0]); sws_freeContext(scale->isws[1]); @@ -217,6 +403,81 @@ static const int *parse_yuv_type(const char *s, enum AVColorSpace colorspace) return sws_getCoefficients(colorspace); } +static int scale_eval_dimensions(AVFilterContext *ctx) +{ + ScaleContext *scale = ctx->priv; + const char scale2ref = ctx->filter == &ff_vf_scale2ref; + const AVFilterLink *inlink = scale2ref ? ctx->inputs[1] : ctx->inputs[0]; + const AVFilterLink *outlink = ctx->outputs[0]; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + const AVPixFmtDescriptor *out_desc = av_pix_fmt_desc_get(outlink->format); + char *expr; + int eval_w, eval_h; + int ret; + double res; + const AVPixFmtDescriptor *main_desc; + const AVFilterLink *main_link; + + if (scale2ref) { + main_link = ctx->inputs[0]; + main_desc = av_pix_fmt_desc_get(main_link->format); + } + + scale->var_values[VAR_IN_W] = scale->var_values[VAR_IW] = inlink->w; + scale->var_values[VAR_IN_H] = scale->var_values[VAR_IH] = inlink->h; + scale->var_values[VAR_OUT_W] = scale->var_values[VAR_OW] = NAN; + scale->var_values[VAR_OUT_H] = scale->var_values[VAR_OH] = NAN; + scale->var_values[VAR_A] = (double) inlink->w / inlink->h; + scale->var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? + (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1; + scale->var_values[VAR_DAR] = scale->var_values[VAR_A] * scale->var_values[VAR_SAR]; + scale->var_values[VAR_HSUB] = 1 << desc->log2_chroma_w; + scale->var_values[VAR_VSUB] = 1 << desc->log2_chroma_h; + scale->var_values[VAR_OHSUB] = 1 << out_desc->log2_chroma_w; + scale->var_values[VAR_OVSUB] = 1 << out_desc->log2_chroma_h; + + if (scale2ref) { + scale->var_values[VAR_S2R_MAIN_W] = main_link->w; + scale->var_values[VAR_S2R_MAIN_H] = main_link->h; + scale->var_values[VAR_S2R_MAIN_A] = (double) main_link->w / main_link->h; + scale->var_values[VAR_S2R_MAIN_SAR] = main_link->sample_aspect_ratio.num ? + (double) main_link->sample_aspect_ratio.num / main_link->sample_aspect_ratio.den : 1; + scale->var_values[VAR_S2R_MAIN_DAR] = scale->var_values[VAR_S2R_MDAR] = + scale->var_values[VAR_S2R_MAIN_A] * scale->var_values[VAR_S2R_MAIN_SAR]; + scale->var_values[VAR_S2R_MAIN_HSUB] = 1 << main_desc->log2_chroma_w; + scale->var_values[VAR_S2R_MAIN_VSUB] = 1 << main_desc->log2_chroma_h; + } + + res = av_expr_eval(scale->w_pexpr, scale->var_values, NULL); + eval_w = scale->var_values[VAR_OUT_W] = scale->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res; + + res = av_expr_eval(scale->h_pexpr, scale->var_values, NULL); + if (isnan(res)) { + expr = scale->h_expr; + ret = AVERROR(EINVAL); + goto fail; + } + eval_h = scale->var_values[VAR_OUT_H] = scale->var_values[VAR_OH] = (int) res == 0 ? inlink->h : (int) res; + + res = av_expr_eval(scale->w_pexpr, scale->var_values, NULL); + if (isnan(res)) { + expr = scale->w_expr; + ret = AVERROR(EINVAL); + goto fail; + } + eval_w = scale->var_values[VAR_OUT_W] = scale->var_values[VAR_OW] = (int) res == 0 ? inlink->w : (int) res; + + scale->w = eval_w; + scale->h = eval_h; + + return 0; + +fail: + av_log(ctx, AV_LOG_ERROR, + "Error when evaluating the expression '%s'.\n", expr); + return ret; +} + static int config_props(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; @@ -227,37 +488,23 @@ static int config_props(AVFilterLink *outlink) enum AVPixelFormat outfmt = outlink->format; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); ScaleContext *scale = ctx->priv; - int w, h; int ret; - if ((ret = ff_scale_eval_dimensions(ctx, - scale->w_expr, scale->h_expr, - inlink, outlink, - &w, &h)) < 0) + if ((ret = scale_eval_dimensions(ctx)) < 0) goto fail; - /* Note that force_original_aspect_ratio may overwrite the previous set - * dimensions so that it is not divisible by the set factors anymore. */ - if (scale->force_original_aspect_ratio) { - int tmp_w = av_rescale(h, inlink->w, inlink->h); - int tmp_h = av_rescale(w, inlink->h, inlink->w); + ff_scale_adjust_dimensions(inlink, &scale->w, &scale->h, + scale->force_original_aspect_ratio, + scale->force_divisible_by); - if (scale->force_original_aspect_ratio == 1) { - w = FFMIN(tmp_w, w); - h = FFMIN(tmp_h, h); - } else { - w = FFMAX(tmp_w, w); - h = FFMAX(tmp_h, h); - } - } - - if (w > INT_MAX || h > INT_MAX || - (h * inlink->w) > INT_MAX || - (w * inlink->h) > INT_MAX) + if (scale->w > INT_MAX || + scale->h > INT_MAX || + (scale->h * inlink->w) > INT_MAX || + (scale->w * inlink->h) > INT_MAX) av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n"); - outlink->w = w; - outlink->h = h; + outlink->w = scale->w; + outlink->h = scale->h; /* TODO: make algorithm configurable */ @@ -384,45 +631,83 @@ static int scale_slice(AVFilterLink *link, AVFrame *out_buf, AVFrame *cur_pic, s int in_stride[4],out_stride[4]; int i; - for(i=0; i<4; i++){ + for (i=0; i<4; i++) { int vsub= ((i+1)&2) ? scale->vsub : 0; in_stride[i] = cur_pic->linesize[i] * mul; out_stride[i] = out_buf->linesize[i] * mul; in[i] = cur_pic->data[i] + ((y>>vsub)+field) * cur_pic->linesize[i]; out[i] = out_buf->data[i] + field * out_buf->linesize[i]; } - if(scale->input_is_pal) + if (scale->input_is_pal) in[1] = cur_pic->data[1]; - if(scale->output_is_pal) + if (scale->output_is_pal) out[1] = out_buf->data[1]; return sws_scale(sws, in, in_stride, y/mul, h, out,out_stride); } -static int filter_frame(AVFilterLink *link, AVFrame *in) +#define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts) * av_q2d(tb)) + +static int scale_frame(AVFilterLink *link, AVFrame *in, AVFrame **frame_out) { - ScaleContext *scale = link->dst->priv; - AVFilterLink *outlink = link->dst->outputs[0]; + AVFilterContext *ctx = link->dst; + ScaleContext *scale = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; AVFrame *out; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format); char buf[32]; int in_range; + int frame_changed; + *frame_out = NULL; if (in->colorspace == AVCOL_SPC_YCGCO) av_log(link->dst, AV_LOG_WARNING, "Detected unsupported YCgCo colorspace.\n"); - if( in->width != link->w - || 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) { + frame_changed = in->width != link->w || + 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 (scale->eval_mode == EVAL_MODE_FRAME || frame_changed) { int ret; + unsigned vars_w[VARS_NB] = { 0 }, vars_h[VARS_NB] = { 0 }; + + av_expr_count_vars(scale->w_pexpr, vars_w, VARS_NB); + av_expr_count_vars(scale->h_pexpr, vars_h, VARS_NB); + + 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]) && + scale->w && scale->h) + goto scale; if (scale->eval_mode == EVAL_MODE_INIT) { snprintf(buf, sizeof(buf)-1, "%d", outlink->w); av_opt_set(scale, "w", buf, 0); snprintf(buf, sizeof(buf)-1, "%d", outlink->h); av_opt_set(scale, "h", buf, 0); + + ret = scale_parse_expr(ctx, NULL, &scale->w_pexpr, "width", scale->w_expr); + if (ret < 0) + return ret; + + ret = scale_parse_expr(ctx, NULL, &scale->h_pexpr, "height", scale->h_expr); + if (ret < 0) + return ret; + } + + 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); + scale->var_values[VAR_S2R_MAIN_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; + } else { + scale->var_values[VAR_N] = link->frame_count_out; + scale->var_values[VAR_T] = TS2T(in->pts, link->time_base); + scale->var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; } link->dst->inputs[0]->format = in->format; @@ -432,13 +717,15 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) 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; - if ((ret = config_props(outlink)) < 0) return ret; } - if (!scale->sws) - return ff_filter_frame(outlink, in); +scale: + if (!scale->sws) { + *frame_out = in; + return 0; + } scale->hsub = desc->log2_chroma_w; scale->vsub = desc->log2_chroma_h; @@ -448,12 +735,13 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) av_frame_free(&in); return AVERROR(ENOMEM); } + *frame_out = out; av_frame_copy_props(out, in); out->width = outlink->w; out->height = outlink->h; - if(scale->output_is_pal) + 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; @@ -504,10 +792,10 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) (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->interlaced_frame)) { scale_slice(link, out, in, scale->isws[0], 0, (link->h+1)/2, 2, 0); scale_slice(link, out, in, scale->isws[1], 0, link->h /2, 2, 1); - }else if (scale->nb_slices) { + } else if (scale->nb_slices) { int i, slice_h, slice_start, slice_end = 0; const int nb_slices = FFMIN(scale->nb_slices, link->h); for (i = 0; i < nb_slices; i++) { @@ -516,17 +804,55 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) slice_h = slice_end - slice_start; scale_slice(link, out, in, scale->sws, slice_start, slice_h, 1, 0); } - }else{ + } else { scale_slice(link, out, in, scale->sws, 0, link->h, 1, 0); } av_frame_free(&in); - return ff_filter_frame(outlink, out); + return 0; +} + +static int filter_frame(AVFilterLink *link, AVFrame *in) +{ + AVFilterContext *ctx = link->dst; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *out; + int ret; + + ret = scale_frame(link, in, &out); + if (out) + return ff_filter_frame(outlink, out); + + return ret; } static int filter_frame_ref(AVFilterLink *link, AVFrame *in) { + ScaleContext *scale = link->dst->priv; AVFilterLink *outlink = link->dst->outputs[1]; + int frame_changed; + + frame_changed = in->width != link->w || + 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 (frame_changed) { + link->format = in->format; + link->w = in->width; + link->h = in->height; + link->sample_aspect_ratio.num = in->sample_aspect_ratio.num; + link->sample_aspect_ratio.den = in->sample_aspect_ratio.den; + + config_props_ref(outlink); + } + + 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); + scale->var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; + } return ff_filter_frame(outlink, in); } @@ -535,23 +861,24 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar char *res, int res_len, int flags) { ScaleContext *scale = ctx->priv; - int ret; + char *str_expr; + AVExpr **pexpr_ptr; + int ret, w, h; - if ( !strcmp(cmd, "width") || !strcmp(cmd, "w") - || !strcmp(cmd, "height") || !strcmp(cmd, "h")) { + w = !strcmp(cmd, "width") || !strcmp(cmd, "w"); + h = !strcmp(cmd, "height") || !strcmp(cmd, "h"); - int old_w = scale->w; - int old_h = scale->h; - AVFilterLink *outlink = ctx->outputs[0]; + if (w || h) { + str_expr = w ? scale->w_expr : scale->h_expr; + pexpr_ptr = w ? &scale->w_pexpr : &scale->h_pexpr; - av_opt_set(scale, cmd, args, 0); - if ((ret = config_props(outlink)) < 0) { - scale->w = old_w; - scale->h = old_h; - } + ret = scale_parse_expr(ctx, str_expr, pexpr_ptr, cmd, args); } else ret = AVERROR(ENOSYS); + if (ret < 0) + av_log(ctx, AV_LOG_ERROR, "Failed to process command. Continuing with existing parameters.\n"); + return ret; } @@ -562,12 +889,13 @@ static const AVClass *child_class_next(const AVClass *prev) #define OFFSET(x) offsetof(ScaleContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define TFLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption scale_options[] = { - { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, - { "width", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, - { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, - { "height","Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, + { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, .flags = TFLAGS }, + { "width", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, .flags = TFLAGS }, + { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, .flags = TFLAGS }, + { "height","Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, .flags = TFLAGS }, { "flags", "Flags to pass to libswscale", OFFSET(flags_str), AV_OPT_TYPE_STRING, { .str = "bilinear" }, .flags = FLAGS }, { "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 }, @@ -600,6 +928,7 @@ static const AVOption scale_options[] = { { "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_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 = SWS_PARAM_DEFAULT }, INT_MIN, INT_MAX, FLAGS }, { "param1", "Scaler param 1", OFFSET(param[1]), AV_OPT_TYPE_DOUBLE, { .dbl = SWS_PARAM_DEFAULT }, INT_MIN, INT_MAX, FLAGS }, { "nb_slices", "set the number of slices (debug purpose only)", OFFSET(nb_slices), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, diff --git a/libavfilter/vf_scale_cuda.c b/libavfilter/vf_scale_cuda.c index 0a73ea14228..1ffb73f8318 100644 --- a/libavfilter/vf_scale_cuda.c +++ b/libavfilter/vf_scale_cuda.c @@ -35,7 +35,7 @@ #include "avfilter.h" #include "formats.h" #include "internal.h" -#include "scale.h" +#include "scale_eval.h" #include "video.h" static const enum AVPixelFormat supported_formats[] = { @@ -82,6 +82,9 @@ typedef struct CUDAScaleContext { char *w_expr; ///< width expression string char *h_expr; ///< height expression string + int force_original_aspect_ratio; + int force_divisible_by; + CUcontext cu_ctx; CUmodule cu_module; CUfunction cu_func_uchar; @@ -305,6 +308,9 @@ static av_cold int cudascale_config_props(AVFilterLink *outlink) &w, &h)) < 0) goto fail; + ff_scale_adjust_dimensions(inlink, &w, &h, + s->force_original_aspect_ratio, s->force_divisible_by); + if (((int64_t)h * inlink->w) > INT_MAX || ((int64_t)w * inlink->h) > INT_MAX) av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n"); @@ -536,6 +542,11 @@ static int cudascale_filter_frame(AVFilterLink *link, AVFrame *in) 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 }, + { "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_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_npp.c b/libavfilter/vf_scale_npp.c index a3e085764a8..502ecfda946 100644 --- a/libavfilter/vf_scale_npp.c +++ b/libavfilter/vf_scale_npp.c @@ -37,7 +37,7 @@ #include "avfilter.h" #include "formats.h" #include "internal.h" -#include "scale.h" +#include "scale_eval.h" #include "video.h" #define CHECK_CU(x) FF_CUDA_CHECK_DL(ctx, device_hwctx->internal->cuda_dl, x) @@ -98,6 +98,9 @@ typedef struct NPPScaleContext { char *h_expr; ///< height expression string char *format_str; + int force_original_aspect_ratio; + int force_divisible_by; + int interp_algo; } NPPScaleContext; @@ -347,6 +350,9 @@ static int nppscale_config_props(AVFilterLink *outlink) &w, &h)) < 0) goto fail; + ff_scale_adjust_dimensions(inlink, &w, &h, + s->force_original_aspect_ratio, s->force_divisible_by); + if (((int64_t)h * inlink->w) > INT_MAX || ((int64_t)w * inlink->h) > INT_MAX) av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n"); @@ -552,6 +558,11 @@ static const AVOption options[] = { { "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" }, + { "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_qsv.c b/libavfilter/vf_scale_qsv.c index 499534e30d5..5064dcbb602 100644 --- a/libavfilter/vf_scale_qsv.c +++ b/libavfilter/vf_scale_qsv.c @@ -201,8 +201,8 @@ static int init_out_pool(AVFilterContext *ctx, out_frames_hwctx = out_frames_ctx->hwctx; out_frames_ctx->format = AV_PIX_FMT_QSV; - out_frames_ctx->width = FFALIGN(out_width, 32); - out_frames_ctx->height = FFALIGN(out_height, 32); + out_frames_ctx->width = FFALIGN(out_width, 16); + out_frames_ctx->height = FFALIGN(out_height, 16); out_frames_ctx->sw_format = out_format; out_frames_ctx->initial_pool_size = 4; @@ -410,7 +410,7 @@ static int init_out_session(AVFilterContext *ctx) s->scale_conf.Header.BufferSz = sizeof(mfxExtVPPScaling); s->scale_conf.ScalingMode = s->mode; s->ext_buffers[s->num_ext_buf++] = (mfxExtBuffer*)&s->scale_conf; - av_log(ctx, AV_LOG_VERBOSE, "Scaling mode: %"PRIu16"\n", s->mode); + av_log(ctx, AV_LOG_VERBOSE, "Scaling mode: %d\n", s->mode); #endif par.ExtParam = s->ext_buffers; @@ -541,7 +541,7 @@ static int qsvscale_config_props(AVFilterLink *outlink) return 0; fail: - av_log(NULL, AV_LOG_ERROR, + av_log(ctx, AV_LOG_ERROR, "Error when evaluating the expression '%s'\n", expr); return ret; } @@ -629,7 +629,7 @@ static const AVOption options[] = { }; static const AVClass qsvscale_class = { - .class_name = "qsvscale", + .class_name = "scale_qsv", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT, diff --git a/libavfilter/vf_scale_vaapi.c b/libavfilter/vf_scale_vaapi.c index c32395ac09a..b9a5eeff65d 100644 --- a/libavfilter/vf_scale_vaapi.c +++ b/libavfilter/vf_scale_vaapi.c @@ -26,7 +26,7 @@ #include "avfilter.h" #include "formats.h" #include "internal.h" -#include "scale.h" +#include "scale_eval.h" #include "video.h" #include "vaapi_vpp.h" @@ -40,6 +40,9 @@ typedef struct ScaleVAAPIContext { char *w_expr; // width expression string char *h_expr; // height expression string + int force_original_aspect_ratio; + int force_divisible_by; + char *colour_primaries_string; char *colour_transfer_string; char *colour_matrix_string; @@ -81,6 +84,9 @@ static int scale_vaapi_config_output(AVFilterLink *outlink) &vpp_ctx->output_width, &vpp_ctx->output_height)) < 0) return err; + ff_scale_adjust_dimensions(inlink, &vpp_ctx->output_width, &vpp_ctx->output_height, + ctx->force_original_aspect_ratio, ctx->force_divisible_by); + err = ff_vaapi_vpp_config_output(outlink); if (err < 0) return err; @@ -119,7 +125,7 @@ static int scale_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) err = av_frame_copy_props(output_frame, input_frame); if (err < 0) - return err; + goto fail; if (ctx->colour_primaries != AVCOL_PRI_UNSPECIFIED) output_frame->color_primaries = ctx->colour_primaries; @@ -247,6 +253,11 @@ 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_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_vulkan.c b/libavfilter/vf_scale_vulkan.c new file mode 100644 index 00000000000..35ea2e1e5d2 --- /dev/null +++ b/libavfilter/vf_scale_vulkan.c @@ -0,0 +1,538 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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.h" +#include "scale_eval.h" +#include "internal.h" +#include "colorspace.h" + +#define CGROUPS (int [3]){ 32, 32, 1 } + +enum ScalerFunc { + F_BILINEAR = 0, + F_NEAREST, + + F_NB, +}; + +typedef struct ScaleVulkanContext { + VulkanFilterContext vkctx; + + int initialized; + FFVkExecContext *exec; + VulkanPipeline *pl; + FFVkBuffer params_buf; + + /* Shader updators, must be in the main filter struct */ + VkDescriptorImageInfo input_images[3]; + VkDescriptorImageInfo output_images[3]; + VkDescriptorBufferInfo params_desc; + + enum ScalerFunc scaler; + char *out_format_string; + enum AVColorRange out_range; + char *w_expr; + char *h_expr; +} ScaleVulkanContext; + +static const char scale_bilinear[] = { + C(0, vec4 scale_bilinear(int idx, ivec2 pos, vec2 crop_range, vec2 crop_off)) + C(0, { ) + C(1, vec2 npos = (vec2(pos) + 0.5f) / imageSize(output_img[idx]); ) + C(1, npos *= crop_range; /* Reduce the range */ ) + C(1, npos += crop_off; /* Offset the start */ ) + C(1, return texture(input_img[idx], npos); ) + C(0, } ) +}; + +static const char rgb2yuv[] = { + C(0, vec4 rgb2yuv(vec4 src, int fullrange) ) + C(0, { ) + C(1, src *= yuv_matrix; ) + C(1, if (fullrange == 1) { ) + C(2, src += vec4(0.0, 0.5, 0.5, 0.0); ) + C(1, } else { ) + C(2, src *= vec4(219.0 / 255.0, 224.0 / 255.0, 224.0 / 255.0, 1.0); ) + C(2, src += vec4(16.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0, 0.0); ) + C(1, } ) + C(1, return src; ) + C(0, } ) +}; + +static const char write_nv12[] = { + C(0, void write_nv12(vec4 src, ivec2 pos) ) + C(0, { ) + C(1, imageStore(output_img[0], pos, vec4(src.r, 0.0, 0.0, 0.0)); ) + C(1, pos /= ivec2(2); ) + C(1, imageStore(output_img[1], pos, vec4(src.g, src.b, 0.0, 0.0)); ) + C(0, } ) +}; + +static const char write_420[] = { + C(0, void write_420(vec4 src, ivec2 pos) ) + C(0, { ) + C(1, imageStore(output_img[0], pos, vec4(src.r, 0.0, 0.0, 0.0)); ) + C(1, pos /= ivec2(2); ) + C(1, imageStore(output_img[1], pos, vec4(src.g, 0.0, 0.0, 0.0)); ) + C(1, imageStore(output_img[2], pos, vec4(src.b, 0.0, 0.0, 0.0)); ) + C(0, } ) +}; + +static const char write_444[] = { + C(0, void write_444(vec4 src, ivec2 pos) ) + C(0, { ) + C(1, imageStore(output_img[0], pos, vec4(src.r, 0.0, 0.0, 0.0)); ) + C(1, imageStore(output_img[1], pos, vec4(src.g, 0.0, 0.0, 0.0)); ) + C(1, imageStore(output_img[2], pos, vec4(src.b, 0.0, 0.0, 0.0)); ) + C(0, } ) +}; + +static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) +{ + int err; + VkSampler *sampler; + VkFilter sampler_mode; + ScaleVulkanContext *s = ctx->priv; + + int crop_x = in->crop_left; + int crop_y = in->crop_top; + int crop_w = in->width - (in->crop_left + in->crop_right); + int crop_h = in->height - (in->crop_top + in->crop_bottom); + + s->vkctx.queue_family_idx = s->vkctx.hwctx->queue_family_comp_index; + s->vkctx.queue_count = GET_QUEUE_COUNT(s->vkctx.hwctx, 0, 1, 0); + s->vkctx.cur_queue_idx = av_get_random_seed() % s->vkctx.queue_count; + + switch (s->scaler) { + case F_NEAREST: + sampler_mode = VK_FILTER_NEAREST; + break; + case F_BILINEAR: + sampler_mode = VK_FILTER_LINEAR; + break; + }; + + /* Create a sampler */ + sampler = ff_vk_init_sampler(ctx, 0, sampler_mode); + if (!sampler) + return AVERROR_EXTERNAL; + + s->pl = ff_vk_create_pipeline(ctx); + if (!s->pl) + return AVERROR(ENOMEM); + + { /* Create the shader */ + VulkanDescriptorSetBinding desc_i[2] = { + { + .name = "input_img", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = av_pix_fmt_count_planes(s->vkctx.input_format), + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .updater = s->input_images, + .samplers = DUP_SAMPLER_ARRAY4(*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, + }, + }; + + VulkanDescriptorSetBinding 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;", + }; + + SPIRVShader *shd = ff_vk_init_shader(ctx, s->pl, "scale_compute", + VK_SHADER_STAGE_COMPUTE_BIT); + if (!shd) + return AVERROR(ENOMEM); + + ff_vk_set_compute_shader_sizes(ctx, shd, CGROUPS); + + RET(ff_vk_add_descriptor_set(ctx, s->pl, shd, desc_i, 2, 0)); /* set 0 */ + RET(ff_vk_add_descriptor_set(ctx, s->pl, shd, &desc_b, 1, 0)); /* set 0 */ + + GLSLD( scale_bilinear ); + + if (s->vkctx.output_format != s->vkctx.input_format) { + GLSLD( rgb2yuv ); + } + + 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; + } + + 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); + } + } + + GLSLC(0, } ); + + RET(ff_vk_compile_shader(ctx, shd, "main")); + } + + RET(ff_vk_init_pipeline_layout(ctx, s->pl)); + RET(ff_vk_init_compute_pipeline(ctx, s->pl)); + + if (s->vkctx.output_format != s->vkctx.input_format) { + const struct LumaCoefficients *lcoeffs; + double tmp_mat[3][3]; + + struct { + float yuv_matrix[4][4]; + } *par; + + lcoeffs = ff_get_luma_coefficients(in->colorspace); + if (!lcoeffs) { + av_log(ctx, AV_LOG_ERROR, "Unsupported colorspace\n"); + return AVERROR(EINVAL); + } + + err = ff_vk_create_buf(ctx, &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(ctx, &s->params_buf, (uint8_t **)&par, 1, 0); + if (err) + return err; + + 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; + + err = ff_vk_unmap_buffers(ctx, &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(ctx, s->pl, 1); + } + + /* Execution context */ + RET(ff_vk_create_exec_ctx(ctx, &s->exec)); + + s->initialized = 1; + + return 0; + +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; + 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; + + /* Update descriptors and init the exec context */ + ff_vk_start_exec_recording(avctx, s->exec); + cmd_buf = ff_vk_get_exec_buf(avctx, s->exec); + + for (int i = 0; i < av_pix_fmt_count_planes(s->vkctx.input_format); i++) { + RET(ff_vk_create_imageview(avctx, s->exec, &s->input_images[i].imageView, + in->img[i], + av_vkfmt_from_pixfmt(s->vkctx.input_format)[i], + ff_comp_identity_map)); + + s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + + for (int i = 0; i < av_pix_fmt_count_planes(s->vkctx.output_format); i++) { + RET(ff_vk_create_imageview(avctx, s->exec, &s->output_images[i].imageView, + out->img[i], + av_vkfmt_from_pixfmt(s->vkctx.output_format)[i], + ff_comp_identity_map)); + + s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + } + + ff_vk_update_descriptor_set(avctx, s->pl, 0); + + for (int i = 0; i < av_pix_fmt_count_planes(s->vkctx.input_format); 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; + } + + vkCmdPipelineBarrier(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(avctx, s->exec, s->pl); + + vkCmdDispatch(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(avctx, s->exec, in_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + ff_vk_add_exec_dep(avctx, s->exec, out_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + + err = ff_vk_submit_exec_queue(avctx, s->exec); + if (err) + return err; + + return err; + +fail: + ff_vk_discard_exec_deps(avctx, s->exec); + return err; +} + +static int scale_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) +{ + int err; + AVFilterContext *ctx = link->dst; + ScaleVulkanContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + + AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + err = AVERROR(ENOMEM); + goto fail; + } + + if (!s->initialized) + RET(init_filter(ctx, in)); + + RET(process_frames(ctx, out, in)); + + err = av_frame_copy_props(out, in); + if (err < 0) + goto fail; + + if (s->out_range != AVCOL_RANGE_UNSPECIFIED) + out->color_range = s->out_range; + if (s->vkctx.output_format != s->vkctx.input_format) + out->chroma_location = AVCHROMA_LOC_TOPLEFT; + + av_frame_free(&in); + + return ff_filter_frame(outlink, out); + +fail: + av_frame_free(&in); + av_frame_free(&out); + return err; +} + +static int scale_vulkan_config_output(AVFilterLink *outlink) +{ + int err; + AVFilterContext *avctx = outlink->src; + ScaleVulkanContext *s = avctx->priv; + AVFilterLink *inlink = outlink->src->inputs[0]; + + err = ff_scale_eval_dimensions(s, s->w_expr, s->h_expr, inlink, outlink, + &s->vkctx.output_width, + &s->vkctx.output_height); + if (err < 0) + return err; + + if (s->out_format_string) { + s->vkctx.output_format = av_get_pix_fmt(s->out_format_string); + if (s->vkctx.output_format == AV_PIX_FMT_NONE) { + av_log(avctx, AV_LOG_ERROR, "Invalid output format.\n"); + return AVERROR(EINVAL); + } + } else { + s->vkctx.output_format = s->vkctx.input_format; + } + + if (s->vkctx.output_format != s->vkctx.input_format) { + if (!ff_vk_mt_is_np_rgb(s->vkctx.input_format)) { + av_log(avctx, AV_LOG_ERROR, "Unsupported input format for conversion\n"); + return AVERROR(EINVAL); + } + if (s->vkctx.output_format != AV_PIX_FMT_NV12 && + s->vkctx.output_format != AV_PIX_FMT_YUV420P && + s->vkctx.output_format != AV_PIX_FMT_YUV444P) { + av_log(avctx, AV_LOG_ERROR, "Unsupported output format\n"); + return AVERROR(EINVAL); + } + } else if (s->out_range != AVCOL_RANGE_UNSPECIFIED) { + av_log(avctx, AV_LOG_ERROR, "Cannot change range without converting format\n"); + return AVERROR(EINVAL); + } + + err = ff_vk_filter_config_output(outlink); + if (err < 0) + return err; + + return 0; +} + +static void scale_vulkan_uninit(AVFilterContext *avctx) +{ + ScaleVulkanContext *s = avctx->priv; + + ff_vk_filter_uninit(avctx); + ff_vk_free_buf(avctx, &s->params_buf); + + s->initialized = 0; +} + +#define OFFSET(x) offsetof(ScaleVulkanContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) +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" }, + { "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" }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(scale_vulkan); + +static const AVFilterPad scale_vulkan_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = &scale_vulkan_filter_frame, + .config_props = &ff_vk_filter_config_input, + }, + { NULL } +}; + +static const AVFilterPad scale_vulkan_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &scale_vulkan_config_output, + }, + { NULL } +}; + +AVFilter ff_vf_scale_vulkan = { + .name = "scale_vulkan", + .description = NULL_IF_CONFIG_SMALL("Scale Vulkan frames"), + .priv_size = sizeof(ScaleVulkanContext), + .init = &ff_vk_filter_init, + .uninit = &scale_vulkan_uninit, + .query_formats = &ff_vk_filter_query_formats, + .inputs = scale_vulkan_inputs, + .outputs = scale_vulkan_outputs, + .priv_class = &scale_vulkan_class, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_scdet.c b/libavfilter/vf_scdet.c new file mode 100644 index 00000000000..b91d91f1e1b --- /dev/null +++ b/libavfilter/vf_scdet.c @@ -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 + * video scene change detection filter + */ + +#include "libavutil/avassert.h" +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "libavutil/timestamp.h" + +#include "avfilter.h" +#include "filters.h" +#include "scene_sad.h" + +typedef struct SCDetContext { + const AVClass *class; + + ptrdiff_t width[4]; + ptrdiff_t height[4]; + int nb_planes; + int bitdepth; + ff_scene_sad_fn sad; + double prev_mafd; + double scene_score; + AVFrame *prev_picref; + double threshold; + int sc_pass; +} SCDetContext; + +#define OFFSET(x) offsetof(SCDetContext, x) +#define V AV_OPT_FLAG_VIDEO_PARAM +#define F AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption scdet_options[] = { + { "threshold", "set scene change detect threshold", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl = 10.}, 0, 100., V|F }, + { "t", "set scene change detect threshold", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl = 10.}, 0, 100., V|F }, + { "sc_pass", "Set the flag to pass scene change frames", OFFSET(sc_pass), AV_OPT_TYPE_BOOL, {.dbl = 0 }, 0, 1, V|F }, + { "s", "Set the flag to pass scene change frames", OFFSET(sc_pass), AV_OPT_TYPE_BOOL, {.dbl = 0 }, 0, 1, V|F }, + {NULL} +}; + +AVFILTER_DEFINE_CLASS(scdet); + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, AV_PIX_FMT_RGBA, + AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA, AV_PIX_FMT_GRAY8, + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P, + AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUVJ440P, + AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P, + AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV420P12, + AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV422P12, + AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_NONE + }; + + AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); + if (!fmts_list) + return AVERROR(ENOMEM); + return ff_set_common_formats(ctx, fmts_list); +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + SCDetContext *s = ctx->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + int is_yuv = !(desc->flags & AV_PIX_FMT_FLAG_RGB) && + (desc->flags & AV_PIX_FMT_FLAG_PLANAR) && + desc->nb_components >= 3; + + s->bitdepth = desc->comp[0].depth; + s->nb_planes = is_yuv ? 1 : av_pix_fmt_count_planes(inlink->format); + + for (int plane = 0; plane < 4; plane++) { + ptrdiff_t line_size = av_image_get_linesize(inlink->format, inlink->w, plane); + s->width[plane] = line_size >> (s->bitdepth > 8); + s->height[plane] = inlink->h >> ((plane == 1 || plane == 2) ? desc->log2_chroma_h : 0); + } + + s->sad = ff_scene_sad_get_fn(s->bitdepth == 8 ? 8 : 16); + if (!s->sad) + return AVERROR(EINVAL); + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + SCDetContext *s = ctx->priv; + + av_frame_free(&s->prev_picref); +} + +static double get_scene_score(AVFilterContext *ctx, AVFrame *frame) +{ + double ret = 0; + SCDetContext *s = ctx->priv; + AVFrame *prev_picref = s->prev_picref; + + if (prev_picref && frame->height == prev_picref->height + && frame->width == prev_picref->width) { + uint64_t sad = 0; + double mafd, diff; + uint64_t count = 0; + + for (int plane = 0; plane < s->nb_planes; plane++) { + uint64_t plane_sad; + s->sad(prev_picref->data[plane], prev_picref->linesize[plane], + frame->data[plane], frame->linesize[plane], + s->width[plane], s->height[plane], &plane_sad); + sad += plane_sad; + 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.); + s->prev_mafd = mafd; + av_frame_free(&prev_picref); + } + s->prev_picref = av_frame_clone(frame); + return ret; +} + +static int set_meta(SCDetContext *s, AVFrame *frame, const char *key, const char *value) +{ + return av_dict_set(&frame->metadata, key, value, 0); +} + +static int activate(AVFilterContext *ctx) +{ + int ret; + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + SCDetContext *s = ctx->priv; + AVFrame *frame; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + ret = ff_inlink_consume_frame(inlink, &frame); + if (ret < 0) + return ret; + + if (frame) { + char buf[64]; + s->scene_score = get_scene_score(ctx, frame); + snprintf(buf, sizeof(buf), "%0.3f", s->prev_mafd); + set_meta(s, frame, "lavfi.scd.mafd", buf); + snprintf(buf, sizeof(buf), "%0.3f", s->scene_score); + set_meta(s, frame, "lavfi.scd.score", buf); + + if (s->scene_score > s->threshold) { + av_log(s, AV_LOG_INFO, "lavfi.scd.score: %.3f, lavfi.scd.time: %s\n", + s->scene_score, av_ts2timestr(frame->pts, &inlink->time_base)); + set_meta(s, frame, "lavfi.scd.time", + av_ts2timestr(frame->pts, &inlink->time_base)); + } + if (s->sc_pass) { + if (s->scene_score > s->threshold) + return ff_filter_frame(outlink, frame); + else { + av_frame_free(&frame); + } + } else + return ff_filter_frame(outlink, frame); + } + + FF_FILTER_FORWARD_STATUS(inlink, outlink); + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; +} + +static const AVFilterPad scdet_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + }, + { NULL } +}; + +static const AVFilterPad scdet_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter ff_vf_scdet = { + .name = "scdet", + .description = NULL_IF_CONFIG_SMALL("Detect video scene change"), + .priv_size = sizeof(SCDetContext), + .priv_class = &scdet_class, + .uninit = uninit, + .query_formats = query_formats, + .inputs = scdet_inputs, + .outputs = scdet_outputs, + .activate = activate, +}; diff --git a/libavfilter/vf_scroll.c b/libavfilter/vf_scroll.c new file mode 100644 index 00000000000..bb5907b1c07 --- /dev/null +++ b/libavfilter/vf_scroll.c @@ -0,0 +1,224 @@ +/* + * 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/common.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" + +typedef struct ScrollContext { + const AVClass *class; + + float h_speed, v_speed; + float h_pos, v_pos; + float h_ipos, v_ipos; + + int pos_h[4], pos_v[4]; + + const AVPixFmtDescriptor *desc; + int nb_planes; + int bytes; + + int planewidth[4]; + int planeheight[4]; +} ScrollContext; + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, + AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, + AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, + 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_YUV440P12, + 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_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9, + AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, + AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, + 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_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_NONE + }; + + AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); + if (!fmts_list) + return AVERROR(ENOMEM); + return ff_set_common_formats(ctx, fmts_list); +} + +typedef struct ThreadData { + AVFrame *in, *out; +} ThreadData; + +static int scroll_slice(AVFilterContext *ctx, void *arg, int jobnr, + int nb_jobs) +{ + ScrollContext *s = ctx->priv; + ThreadData *td = arg; + AVFrame *in = td->in; + AVFrame *out = td->out; + + for (int p = 0; p < s->nb_planes; p++) { + const uint8_t *src = in->data[p]; + const int h = s->planeheight[p]; + const int w = s->planewidth[p] * s->bytes; + const int slice_start = (h * jobnr) / nb_jobs; + const int slice_end = (h * (jobnr+1)) / nb_jobs; + uint8_t *dst = out->data[p] + slice_start * out->linesize[p]; + + for (int y = slice_start; y < slice_end; y++) { + int yy = (y + s->pos_v[p]) % h; + const uint8_t *ssrc = src + yy * in->linesize[p]; + + if (s->pos_h[p] < w) + memcpy(dst, ssrc + s->pos_h[p], w - s->pos_h[p]); + if (s->pos_h[p] > 0) + memcpy(dst + w - s->pos_h[p], ssrc, s->pos_h[p]); + + dst += out->linesize[p]; + } + } + + return 0; +} + +static void scroll(AVFilterContext *ctx, AVFrame *in, AVFrame *out) +{ + ScrollContext *s = ctx->priv; + ThreadData td; + int h_pos, v_pos; + + s->h_pos = fmodf(s->h_pos, in->width); + s->v_pos = fmodf(s->v_pos, in->height); + + h_pos = s->h_pos; + v_pos = s->v_pos; + + if (h_pos < 0) + h_pos += in->width; + if (v_pos < 0) + v_pos += in->height; + + s->pos_v[1] = s->pos_v[2] = AV_CEIL_RSHIFT(v_pos, s->desc->log2_chroma_h); + s->pos_v[0] = s->pos_v[3] = v_pos; + s->pos_h[1] = s->pos_h[2] = AV_CEIL_RSHIFT(h_pos, s->desc->log2_chroma_w) * s->bytes; + s->pos_h[0] = s->pos_h[3] = h_pos * s->bytes; + + td.in = in; td.out = out; + ctx->internal->execute(ctx, scroll_slice, &td, NULL, + FFMIN(out->height, ff_filter_get_nb_threads(ctx))); + + s->h_pos += s->h_speed * in->width; + s->v_pos += s->v_speed * in->height; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *out; + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + + scroll(ctx, in, out); + + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + ScrollContext *s = ctx->priv; + + s->desc = av_pix_fmt_desc_get(inlink->format); + s->nb_planes = s->desc->nb_components; + s->bytes = (s->desc->comp[0].depth + 7) >> 3; + + s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h); + s->planeheight[0] = s->planeheight[3] = inlink->h; + s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, s->desc->log2_chroma_w); + s->planewidth[0] = s->planewidth[3] = inlink->w; + + s->h_pos = (1.f - s->h_ipos) * inlink->w; + s->v_pos = (1.f - s->v_ipos) * inlink->h; + + return 0; +} + +#define OFFSET(x) offsetof(ScrollContext, 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 scroll_options[] = { + { "horizontal", "set the horizontal scrolling speed", OFFSET(h_speed), AV_OPT_TYPE_FLOAT, {.dbl=0.}, -1., 1., VFT }, + { "h", "set the horizontal scrolling speed", OFFSET(h_speed), AV_OPT_TYPE_FLOAT, {.dbl=0.}, -1., 1., VFT }, + { "vertical", "set the vertical scrolling speed", OFFSET(v_speed), AV_OPT_TYPE_FLOAT, {.dbl=0.}, -1., 1., VFT }, + { "v", "set the vertical scrolling speed", OFFSET(v_speed), AV_OPT_TYPE_FLOAT, {.dbl=0.}, -1., 1., VFT }, + { "hpos", "set initial horizontal position", OFFSET(h_ipos), AV_OPT_TYPE_FLOAT, {.dbl=0.}, 0, 1., FLAGS }, + { "vpos", "set initial vertical position", OFFSET(v_ipos), AV_OPT_TYPE_FLOAT, {.dbl=0.}, 0, 1., FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(scroll); + +static const AVFilterPad scroll_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad scroll_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter ff_vf_scroll = { + .name = "scroll", + .description = NULL_IF_CONFIG_SMALL("Scroll input video."), + .priv_size = sizeof(ScrollContext), + .priv_class = &scroll_class, + .query_formats = query_formats, + .inputs = scroll_inputs, + .outputs = scroll_outputs, + .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 b99f31bef2c..d8f339d5403 100644 --- a/libavfilter/vf_selectivecolor.c +++ b/libavfilter/vf_selectivecolor.c @@ -93,7 +93,7 @@ typedef struct SelectiveColorContext { #define OFFSET(x) offsetof(SelectiveColorContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM #define RANGE_OPTION(color_name, range) \ - { color_name"s", "adjust "color_name" regions", OFFSET(opt_cmyk_adjust[range]), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS } + { 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" }, diff --git a/libavfilter/vf_setfield.c b/libavfilter/vf_setfield.c deleted file mode 100644 index f4dc33d7e59..00000000000 --- a/libavfilter/vf_setfield.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2012 Stefano Sabatini - * - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General 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 - * set field order - */ - -#include "libavutil/opt.h" -#include "avfilter.h" -#include "internal.h" -#include "video.h" - -enum SetFieldMode { - MODE_AUTO = -1, - MODE_BFF, - MODE_TFF, - MODE_PROG, -}; - -typedef struct SetFieldContext { - const AVClass *class; - int mode; ///< SetFieldMode -} SetFieldContext; - -#define OFFSET(x) offsetof(SetFieldContext, x) -#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM - -static const AVOption setfield_options[] = { - {"mode", "select interlace mode", OFFSET(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"}, - {NULL} -}; - -AVFILTER_DEFINE_CLASS(setfield); - -static int filter_frame(AVFilterLink *inlink, AVFrame *picref) -{ - SetFieldContext *setfield = inlink->dst->priv; - - if (setfield->mode == MODE_PROG) { - picref->interlaced_frame = 0; - } else if (setfield->mode != MODE_AUTO) { - picref->interlaced_frame = 1; - picref->top_field_first = setfield->mode; - } - return ff_filter_frame(inlink->dst->outputs[0], picref); -} - -static const AVFilterPad setfield_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = filter_frame, - }, - { NULL } -}; - -static const AVFilterPad setfield_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, - { NULL } -}; - -AVFilter ff_vf_setfield = { - .name = "setfield", - .description = NULL_IF_CONFIG_SMALL("Force field for the output video frame."), - .priv_size = sizeof(SetFieldContext), - .priv_class = &setfield_class, - .inputs = setfield_inputs, - .outputs = setfield_outputs, -}; diff --git a/libavfilter/vf_setparams.c b/libavfilter/vf_setparams.c index fe298e5a06e..689097fac0b 100644 --- a/libavfilter/vf_setparams.c +++ b/libavfilter/vf_setparams.c @@ -75,6 +75,7 @@ static const AVOption setparams_options[] = { {"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"}, diff --git a/libavfilter/vf_showinfo.c b/libavfilter/vf_showinfo.c index f6f8f497787..5d4aee4169b 100644 --- a/libavfilter/vf_showinfo.c +++ b/libavfilter/vf_showinfo.c @@ -24,6 +24,7 @@ #include +#include "libavutil/bswap.h" #include "libavutil/adler32.h" #include "libavutil/display.h" #include "libavutil/imgutils.h" @@ -34,6 +35,8 @@ #include "libavutil/stereo3d.h" #include "libavutil/timestamp.h" #include "libavutil/timecode.h" +#include "libavutil/mastering_display_metadata.h" +#include "libavutil/video_enc_params.h" #include "avfilter.h" #include "internal.h" @@ -61,7 +64,7 @@ static void dump_spherical(AVFilterContext *ctx, AVFrame *frame, AVFrameSideData av_log(ctx, AV_LOG_INFO, "spherical information: "); if (sd->size < sizeof(*spherical)) { - av_log(ctx, AV_LOG_INFO, "invalid data"); + av_log(ctx, AV_LOG_ERROR, "invalid data"); return; } @@ -99,7 +102,7 @@ static void dump_stereo3d(AVFilterContext *ctx, AVFrameSideData *sd) av_log(ctx, AV_LOG_INFO, "stereoscopic information: "); if (sd->size < sizeof(*stereo)) { - av_log(ctx, AV_LOG_INFO, "invalid data"); + av_log(ctx, AV_LOG_ERROR, "invalid data"); return; } @@ -120,7 +123,7 @@ static void dump_roi(AVFilterContext *ctx, AVFrameSideData *sd) roi = (const AVRegionOfInterest *)sd->data; roi_size = roi->self_size; if (!roi_size || sd->size % roi_size != 0) { - av_log(ctx, AV_LOG_ERROR, "Invalid AVRegionOfInterest.self_size.\n"); + av_log(ctx, AV_LOG_ERROR, "Invalid AVRegionOfInterest.self_size."); return; } nb_rois = sd->size / roi_size; @@ -133,6 +136,60 @@ static void dump_roi(AVFilterContext *ctx, AVFrameSideData *sd) } } +static void dump_mastering_display(AVFilterContext *ctx, AVFrameSideData *sd) +{ + AVMasteringDisplayMetadata *mastering_display; + + av_log(ctx, AV_LOG_INFO, "mastering display: "); + if (sd->size < sizeof(*mastering_display)) { + av_log(ctx, AV_LOG_ERROR, "invalid data"); + return; + } + + mastering_display = (AVMasteringDisplayMetadata *)sd->data; + + av_log(ctx, AV_LOG_INFO, "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", + mastering_display->has_primaries, mastering_display->has_luminance, + av_q2d(mastering_display->display_primaries[0][0]), + av_q2d(mastering_display->display_primaries[0][1]), + av_q2d(mastering_display->display_primaries[1][0]), + av_q2d(mastering_display->display_primaries[1][1]), + av_q2d(mastering_display->display_primaries[2][0]), + av_q2d(mastering_display->display_primaries[2][1]), + av_q2d(mastering_display->white_point[0]), av_q2d(mastering_display->white_point[1]), + av_q2d(mastering_display->min_luminance), av_q2d(mastering_display->max_luminance)); +} + +static void dump_content_light_metadata(AVFilterContext *ctx, AVFrameSideData *sd) +{ + AVContentLightMetadata* metadata = (AVContentLightMetadata*)sd->data; + + av_log(ctx, AV_LOG_INFO, "Content Light Level information: " + "MaxCLL=%d, MaxFALL=%d", + metadata->MaxCLL, metadata->MaxFALL); +} + +static void dump_video_enc_params(AVFilterContext *ctx, AVFrameSideData *sd) +{ + AVVideoEncParams *par = (AVVideoEncParams*)sd->data; + int plane, acdc; + + av_log(ctx, AV_LOG_INFO, "video encoding parameters: type %d; ", par->type); + if (par->qp) + av_log(ctx, AV_LOG_INFO, "qp=%d; ", par->qp); + for (plane = 0; plane < FF_ARRAY_ELEMS(par->delta_qp); plane++) + for (acdc = 0; acdc < FF_ARRAY_ELEMS(par->delta_qp[plane]); acdc++) { + int delta_qp = par->delta_qp[plane][acdc]; + if (delta_qp) + av_log(ctx, AV_LOG_INFO, "delta_qp[%d][%d]=%d; ", + plane, acdc, delta_qp); + } + if (par->nb_blocks) + av_log(ctx, AV_LOG_INFO, "%u blocks; ", par->nb_blocks); +} + static void dump_color_property(AVFilterContext *ctx, AVFrame *frame) { const char *color_range_str = av_color_range_name(frame->color_range); @@ -166,7 +223,7 @@ static void dump_color_property(AVFilterContext *ctx, AVFrame *frame) av_log(ctx, AV_LOG_INFO, "\n"); } -static void update_sample_stats(const uint8_t *src, int len, int64_t *sum, int64_t *sum2) +static void update_sample_stats_8(const uint8_t *src, int len, int64_t *sum, int64_t *sum2) { int i; @@ -176,6 +233,30 @@ static void update_sample_stats(const uint8_t *src, int len, int64_t *sum, int64 } } +static void update_sample_stats_16(int be, const uint8_t *src, int len, int64_t *sum, int64_t *sum2) +{ + const uint16_t *src1 = (const uint16_t *)src; + int i; + + for (i = 0; i < len / 2; i++) { + if ((HAVE_BIGENDIAN && !be) || (!HAVE_BIGENDIAN && be)) { + *sum += av_bswap16(src1[i]); + *sum2 += (uint32_t)av_bswap16(src1[i]) * (uint32_t)av_bswap16(src1[i]); + } else { + *sum += src1[i]; + *sum2 += (uint32_t)src1[i] * (uint32_t)src1[i]; + } + } +} + +static void update_sample_stats(int depth, int be, const uint8_t *src, int len, int64_t *sum, int64_t *sum2) +{ + if (depth <= 8) + update_sample_stats_8(src, len, sum, sum2); + else + update_sample_stats_16(be, src, len, sum, sum2); +} + static int filter_frame(AVFilterLink *inlink, AVFrame *frame) { AVFilterContext *ctx = inlink->dst; @@ -184,12 +265,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) uint32_t plane_checksum[4] = {0}, checksum = 0; int64_t sum[4] = {0}, sum2[4] = {0}; int32_t pixelcount[4] = {0}; + int bitdepth = desc->comp[0].depth; + int be = desc->flags & AV_PIX_FMT_FLAG_BE; int i, plane, vsub = desc->log2_chroma_h; for (plane = 0; plane < 4 && s->calculate_checksums && frame->data[plane] && frame->linesize[plane]; plane++) { uint8_t *data = frame->data[plane]; int h = plane == 1 || plane == 2 ? AV_CEIL_RSHIFT(inlink->h, vsub) : inlink->h; int linesize = av_image_get_linesize(frame->format, frame->width, plane); + int width = linesize >> (bitdepth > 8); if (linesize < 0) return linesize; @@ -198,8 +282,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) plane_checksum[plane] = av_adler32_update(plane_checksum[plane], data, linesize); checksum = av_adler32_update(checksum, data, linesize); - update_sample_stats(data, linesize, sum+plane, sum2+plane); - pixelcount[plane] += linesize; + update_sample_stats(bitdepth, be, data, linesize, sum+plane, sum2+plane); + pixelcount[plane] += width; data += frame->linesize[plane]; } } @@ -254,10 +338,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) break; case AV_FRAME_DATA_S12M_TIMECODE: { uint32_t *tc = (uint32_t*)sd->data; - for (int j = 1; j <= tc[0]; j++) { + int m = FFMIN(tc[0],3); + if (sd->size != 16) { + av_log(ctx, AV_LOG_ERROR, "invalid data"); + break; + } + for (int j = 1; j <= m; j++) { char tcbuf[AV_TIMECODE_STR_SIZE]; av_timecode_make_smpte_tc_string(tcbuf, tc[j], 0); - av_log(ctx, AV_LOG_INFO, "timecode - %s%s", tcbuf, j != tc[0] ? ", " : ""); + av_log(ctx, AV_LOG_INFO, "timecode - %s%s", tcbuf, j != m ? ", " : ""); } break; } @@ -271,6 +360,21 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) case AV_FRAME_DATA_REGIONS_OF_INTEREST: dump_roi(ctx, sd); break; + case AV_FRAME_DATA_MASTERING_DISPLAY_METADATA: + dump_mastering_display(ctx, sd); + break; + case AV_FRAME_DATA_CONTENT_LIGHT_LEVEL: + dump_content_light_metadata(ctx, sd); + break; + case AV_FRAME_DATA_GOP_TIMECODE: { + char tcbuf[AV_TIMECODE_STR_SIZE]; + av_timecode_make_mpeg_tc_string(tcbuf, *(int64_t *)(sd->data)); + av_log(ctx, AV_LOG_INFO, "GOP timecode - %s", tcbuf); + break; + } + case AV_FRAME_DATA_VIDEO_ENC_PARAMS: + dump_video_enc_params(ctx, sd); + break; default: av_log(ctx, AV_LOG_WARNING, "unknown side data type %d (%d bytes)", sd->type, sd->size); diff --git a/libavfilter/vf_showpalette.c b/libavfilter/vf_showpalette.c index d886ab87a77..5b0772bc0bb 100644 --- a/libavfilter/vf_showpalette.c +++ b/libavfilter/vf_showpalette.c @@ -96,7 +96,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) int ret; AVFrame *out; AVFilterContext *ctx = inlink->dst; - const ShowPaletteContext *s= ctx->priv; + const ShowPaletteContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; out = ff_get_video_buffer(outlink, outlink->w, outlink->h); @@ -136,5 +136,4 @@ AVFilter ff_vf_showpalette = { .inputs = showpalette_inputs, .outputs = showpalette_outputs, .priv_class = &showpalette_class, - .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_shuffleframes.c b/libavfilter/vf_shuffleframes.c index 8e595111b80..1f780c7d49c 100644 --- a/libavfilter/vf_shuffleframes.c +++ b/libavfilter/vf_shuffleframes.c @@ -69,7 +69,7 @@ static av_cold int init(AVFilterContext *ctx) } if (s->map[n] < -1 || s->map[n] >= nb_items) { - av_log(ctx, AV_LOG_ERROR, "Index out of range.\n"); + av_log(ctx, AV_LOG_ERROR, "Index %d out of range: [-1, %d].\n", s->map[n], nb_items - 1); av_free(mapping); return AVERROR(EINVAL); } diff --git a/libavfilter/vf_shuffleplanes.c b/libavfilter/vf_shuffleplanes.c index 5d1302db048..6c718893ce5 100644 --- a/libavfilter/vf_shuffleplanes.c +++ b/libavfilter/vf_shuffleplanes.c @@ -40,41 +40,50 @@ typedef struct ShufflePlanesContext { int copy; } ShufflePlanesContext; +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats = NULL; + ShufflePlanesContext *s = ctx->priv; + int fmt, ret, i; + + for (fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); + int planes = av_pix_fmt_count_planes(fmt); + + if (!(desc->flags & AV_PIX_FMT_FLAG_PAL) && + !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { + for (i = 0; i < 4; i++) { + if (s->map[i] >= planes) + break; + + if ((desc->log2_chroma_h || desc->log2_chroma_w) && + (i == 1 || i == 2) != (s->map[i] == 1 || s->map[i] == 2)) + break; + } + + if (i != 4) + continue; + if ((ret = ff_add_format(&formats, fmt)) < 0) { + ff_formats_unref(&formats); + return ret; + } + } + } + + return ff_set_common_formats(ctx, formats); +} + static av_cold int shuffleplanes_config_input(AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; ShufflePlanesContext *s = ctx->priv; - const AVPixFmtDescriptor *desc; int used[4] = { 0 }; int i; s->copy = 0; s->planes = av_pix_fmt_count_planes(inlink->format); - desc = av_pix_fmt_desc_get(inlink->format); for (i = 0; i < s->planes; i++) { - if (s->map[i] >= s->planes) { - av_log(ctx, AV_LOG_ERROR, - "Non-existing input plane #%d mapped to output plane #%d.\n", - s->map[i], i); - return AVERROR(EINVAL); - } - - if ((desc->log2_chroma_h || desc->log2_chroma_w) && - (i == 1 || i == 2) != (s->map[i] == 1 || s->map[i] == 2)) { - av_log(ctx, AV_LOG_ERROR, - "Cannot map between a subsampled chroma plane and a luma " - "or alpha plane.\n"); - return AVERROR(EINVAL); - } - - if ((desc->flags & AV_PIX_FMT_FLAG_PAL || - desc->flags & FF_PSEUDOPAL) && - (i == 1) != (s->map[i] == 1)) { - av_log(ctx, AV_LOG_ERROR, - "Cannot map between a palette plane and a data plane.\n"); - return AVERROR(EINVAL); - } if (used[s->map[i]]) s->copy = 1; used[s->map[i]]++; @@ -127,10 +136,10 @@ static int shuffleplanes_filter_frame(AVFilterLink *inlink, AVFrame *frame) #define OFFSET(x) offsetof(ShufflePlanesContext, x) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) static const AVOption shuffleplanes_options[] = { - { "map0", "Index of the input plane to be used as the first output plane ", OFFSET(map[0]), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 4, FLAGS }, - { "map1", "Index of the input plane to be used as the second output plane ", OFFSET(map[1]), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 4, FLAGS }, - { "map2", "Index of the input plane to be used as the third output plane ", OFFSET(map[2]), AV_OPT_TYPE_INT, { .i64 = 2 }, 0, 4, FLAGS }, - { "map3", "Index of the input plane to be used as the fourth output plane ", OFFSET(map[3]), AV_OPT_TYPE_INT, { .i64 = 3 }, 0, 4, FLAGS }, + { "map0", "Index of the input plane to be used as the first output plane ", OFFSET(map[0]), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 3, FLAGS }, + { "map1", "Index of the input plane to be used as the second output plane ", OFFSET(map[1]), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 3, FLAGS }, + { "map2", "Index of the input plane to be used as the third output plane ", OFFSET(map[2]), AV_OPT_TYPE_INT, { .i64 = 2 }, 0, 3, FLAGS }, + { "map3", "Index of the input plane to be used as the fourth output plane ", OFFSET(map[3]), AV_OPT_TYPE_INT, { .i64 = 3 }, 0, 3, FLAGS }, { NULL }, }; @@ -142,7 +151,6 @@ static const AVFilterPad shuffleplanes_inputs[] = { .type = AVMEDIA_TYPE_VIDEO, .config_props = shuffleplanes_config_input, .filter_frame = shuffleplanes_filter_frame, - .get_video_buffer = ff_null_get_video_buffer, }, { NULL }, }; @@ -160,6 +168,7 @@ AVFilter ff_vf_shuffleplanes = { .description = NULL_IF_CONFIG_SMALL("Shuffle video planes."), .priv_size = sizeof(ShufflePlanesContext), .priv_class = &shuffleplanes_class, + .query_formats = query_formats, .inputs = shuffleplanes_inputs, .outputs = shuffleplanes_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_signalstats.c b/libavfilter/vf_signalstats.c index 2b8c0de4c4d..1331327bb33 100644 --- a/libavfilter/vf_signalstats.c +++ b/libavfilter/vf_signalstats.c @@ -50,6 +50,7 @@ typedef struct SignalstatsContext { int nb_jobs; int *jobs_rets; + int maxsize; // history stats array size int *histy, *histu, *histv, *histsat; AVFrame *frame_sat; @@ -149,7 +150,7 @@ static AVFrame *alloc_frame(enum AVPixelFormat pixfmt, int w, int h) frame->width = w; frame->height = h; - if (av_frame_get_buffer(frame, 32) < 0) { + if (av_frame_get_buffer(frame, 0) < 0) { av_frame_free(&frame); return NULL; } @@ -157,7 +158,7 @@ static AVFrame *alloc_frame(enum AVPixelFormat pixfmt, int w, int h) return frame; } -static int config_props(AVFilterLink *outlink) +static int config_output(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; SignalstatsContext *s = ctx->priv; @@ -166,15 +167,14 @@ static int config_props(AVFilterLink *outlink) s->hsub = desc->log2_chroma_w; s->vsub = desc->log2_chroma_h; s->depth = desc->comp[0].depth; - if (s->depth > 8) { - s->histy = av_malloc_array(1 << s->depth, sizeof(*s->histy)); - s->histu = av_malloc_array(1 << s->depth, sizeof(*s->histu)); - s->histv = av_malloc_array(1 << s->depth, sizeof(*s->histv)); - s->histsat = av_malloc_array(1 << s->depth, sizeof(*s->histsat)); - - if (!s->histy || !s->histu || !s->histv || !s->histsat) - return AVERROR(ENOMEM); - } + s->maxsize = 1 << s->depth; + s->histy = av_malloc_array(s->maxsize, sizeof(*s->histy)); + s->histu = av_malloc_array(s->maxsize, sizeof(*s->histu)); + s->histv = av_malloc_array(s->maxsize, sizeof(*s->histv)); + s->histsat = av_malloc_array(s->maxsize, sizeof(*s->histsat)); + + if (!s->histy || !s->histu || !s->histv || !s->histsat) + return AVERROR(ENOMEM); outlink->w = inlink->w; outlink->h = inlink->h; @@ -462,8 +462,6 @@ static const struct { {NULL} }; -#define DEPTH 256 - static int compute_sat_hue_metrics8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { int i, j; @@ -491,7 +489,7 @@ static int compute_sat_hue_metrics8(AVFilterContext *ctx, void *arg, int jobnr, const int yuvu = p_u[i]; const int yuvv = p_v[i]; p_sat[i] = hypot(yuvu - 128, yuvv - 128); // int or round? - ((int16_t*)p_hue)[i] = floor((180 / M_PI) * atan2f(yuvu-128, yuvv-128) + 180); + ((int16_t*)p_hue)[i] = fmod(floor((180 / M_PI) * atan2f(yuvu-128, yuvv-128) + 180), 360.); } p_u += lsz_u; p_v += lsz_v; @@ -530,7 +528,7 @@ static int compute_sat_hue_metrics16(AVFilterContext *ctx, void *arg, int jobnr, const int yuvu = p_u[i]; const int yuvv = p_v[i]; p_sat[i] = hypot(yuvu - mid, yuvv - mid); // int or round? - ((int16_t*)p_hue)[i] = floor((180 / M_PI) * atan2f(yuvu-mid, yuvv-mid) + 180); + ((int16_t*)p_hue)[i] = fmod(floor((180 / M_PI) * atan2f(yuvu-mid, yuvv-mid) + 180), 360.); } p_u += lsz_u; p_v += lsz_v; @@ -557,11 +555,11 @@ static int filter_frame8(AVFilterLink *link, AVFrame *in) pw = 0, cpw = 0; // prev int fil; char metabuf[128]; - unsigned int histy[DEPTH] = {0}, - histu[DEPTH] = {0}, - histv[DEPTH] = {0}, + unsigned int *histy = s->histy, + *histu = s->histu, + *histv = s->histv, histhue[360] = {0}, - histsat[DEPTH] = {0}; // limited to 8 bit data. + *histsat = s->histsat; int miny = -1, minu = -1, minv = -1; int maxy = -1, maxu = -1, maxv = -1; int lowy = -1, lowu = -1, lowv = -1; @@ -605,6 +603,7 @@ static int filter_frame8(AVFilterLink *link, AVFrame *in) NULL, FFMIN(s->chromah, ff_filter_get_nb_threads(ctx))); // Calculate luma histogram and difference with previous frame or field. + memset(s->histy, 0, s->maxsize * sizeof(*s->histy)); for (j = 0; j < link->h; j++) { for (i = 0; i < link->w; i++) { const int yuv = in->data[0][w + i]; @@ -618,6 +617,9 @@ static int filter_frame8(AVFilterLink *link, AVFrame *in) } // Calculate chroma histogram and difference with previous frame or field. + memset(s->histu, 0, s->maxsize * sizeof(*s->histu)); + memset(s->histv, 0, s->maxsize * sizeof(*s->histv)); + memset(s->histsat, 0, s->maxsize * sizeof(*s->histsat)); for (j = 0; j < s->chromah; j++) { for (i = 0; i < s->chromaw; i++) { const int yuvu = in->data[1][cw+i]; @@ -662,7 +664,7 @@ static int filter_frame8(AVFilterLink *link, AVFrame *in) chighp = lrint(s->cfs * 90 / 100.); accy = accu = accv = accsat = 0; - for (fil = 0; fil < DEPTH; fil++) { + for (fil = 0; fil < s->maxsize; fil++) { if (miny < 0 && histy[fil]) miny = fil; if (minu < 0 && histu[fil]) minu = fil; if (minv < 0 && histv[fil]) minv = fil; @@ -823,7 +825,7 @@ static int filter_frame16(AVFilterLink *link, AVFrame *in) NULL, FFMIN(s->chromah, ff_filter_get_nb_threads(ctx))); // Calculate luma histogram and difference with previous frame or field. - memset(s->histy, 0, (1 << s->depth) * sizeof(*s->histy)); + memset(s->histy, 0, s->maxsize * sizeof(*s->histy)); for (j = 0; j < link->h; j++) { for (i = 0; i < link->w; i++) { const int yuv = AV_RN16(in->data[0] + w + i * 2); @@ -837,9 +839,9 @@ static int filter_frame16(AVFilterLink *link, AVFrame *in) } // Calculate chroma histogram and difference with previous frame or field. - memset(s->histu, 0, (1 << s->depth) * sizeof(*s->histu)); - memset(s->histv, 0, (1 << s->depth) * sizeof(*s->histv)); - memset(s->histsat, 0, (1 << s->depth) * sizeof(*s->histsat)); + memset(s->histu, 0, s->maxsize * sizeof(*s->histu)); + memset(s->histv, 0, s->maxsize * sizeof(*s->histv)); + memset(s->histsat, 0, s->maxsize * sizeof(*s->histsat)); for (j = 0; j < s->chromah; j++) { for (i = 0; i < s->chromaw; i++) { const int yuvu = AV_RN16(in->data[1] + cw + i * 2); @@ -884,7 +886,7 @@ static int filter_frame16(AVFilterLink *link, AVFrame *in) chighp = lrint(s->cfs * 90 / 100.); accy = accu = accv = accsat = 0; - for (fil = 0; fil < 1 << s->depth; fil++) { + for (fil = 0; fil < s->maxsize; fil++) { if (miny < 0 && histy[fil]) miny = fil; if (minu < 0 && histu[fil]) minu = fil; if (minv < 0 && histv[fil]) minv = fil; @@ -1004,7 +1006,7 @@ static const AVFilterPad signalstats_inputs[] = { static const AVFilterPad signalstats_outputs[] = { { .name = "default", - .config_props = config_props, + .config_props = config_output, .type = AVMEDIA_TYPE_VIDEO, }, { NULL } diff --git a/libavfilter/vf_spp.c b/libavfilter/vf_spp.c index fe579cedb12..a83b1195c06 100644 --- a/libavfilter/vf_spp.c +++ b/libavfilter/vf_spp.c @@ -57,8 +57,9 @@ static void *child_next(void *obj, void *prev) #define OFFSET(x) offsetof(SPPContext, 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 spp_options[] = { - { "quality", "set quality", OFFSET(log2_count), AV_OPT_TYPE_INT, {.i64 = 3}, 0, MAX_LEVEL, FLAGS }, + { "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" }, @@ -222,10 +223,14 @@ static inline void add_block(uint16_t *dst, int linesize, const int16_t block[64 int y; for (y = 0; y < 8; y++) { - *(uint32_t *)&dst[0 + y*linesize] += *(uint32_t *)&block[0 + y*8]; - *(uint32_t *)&dst[2 + y*linesize] += *(uint32_t *)&block[2 + y*8]; - *(uint32_t *)&dst[4 + y*linesize] += *(uint32_t *)&block[4 + y*8]; - *(uint32_t *)&dst[6 + y*linesize] += *(uint32_t *)&block[6 + y*8]; + dst[0 + y*linesize] += block[0 + y*8]; + dst[1 + y*linesize] += block[1 + y*8]; + dst[2 + y*linesize] += block[2 + y*8]; + dst[3 + y*linesize] += block[3 + y*8]; + dst[4 + y*linesize] += block[4 + y*8]; + dst[5 + y*linesize] += block[5 + y*8]; + dst[6 + y*linesize] += block[6 + y*8]; + dst[7 + y*linesize] += block[7 + y*8]; } } @@ -278,7 +283,7 @@ static void filter(SPPContext *p, uint8_t *dst, uint8_t *src, const int x1 = x + offset[i + count - 1][0]; const int y1 = y + offset[i + count - 1][1]; const int index = x1 + y1*linesize; - p->dct->get_pixels(block, p->src + sample_bytes*index, sample_bytes*linesize); + p->dct->get_pixels_unaligned(block, p->src + sample_bytes*index, sample_bytes*linesize); p->dct->fdct(block); p->requantize(block2, block, qp, p->dct->idct_permutation); p->dct->idct(block2); @@ -444,7 +449,7 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar { SPPContext *s = ctx->priv; - if (!strcmp(cmd, "level")) { + if (!strcmp(cmd, "level") || !strcmp(cmd, "quality")) { if (!strcmp(args, "max")) s->log2_count = MAX_LEVEL; else @@ -459,9 +464,8 @@ static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts) SPPContext *s = ctx->priv; int ret; - s->avctx = avcodec_alloc_context3(NULL); s->dct = avcodec_dct_alloc(); - if (!s->avctx || !s->dct) + if (!s->dct) return AVERROR(ENOMEM); if (opts) { @@ -488,10 +492,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&s->temp); av_freep(&s->src); - if (s->avctx) { - avcodec_close(s->avctx); - av_freep(&s->avctx); - } av_freep(&s->dct); av_freep(&s->non_b_qp_table); } diff --git a/libavfilter/vf_spp.h b/libavfilter/vf_spp.h index c03073a4e17..879ed40f03c 100644 --- a/libavfilter/vf_spp.h +++ b/libavfilter/vf_spp.h @@ -22,7 +22,6 @@ #ifndef AVFILTER_SPP_H #define AVFILTER_SPP_H -#include "libavcodec/avcodec.h" #include "libavcodec/avdct.h" #include "avfilter.h" @@ -38,7 +37,6 @@ typedef struct SPPContext { int temp_linesize; uint8_t *src; uint16_t *temp; - AVCodecContext *avctx; AVDCT *dct; int8_t *non_b_qp_table; int non_b_qp_alloc_size; diff --git a/libavfilter/vf_sr.c b/libavfilter/vf_sr.c index 0433246e265..f000eda186a 100644 --- a/libavfilter/vf_sr.c +++ b/libavfilter/vf_sr.c @@ -41,7 +41,7 @@ typedef struct SRContext { DNNBackendType backend_type; DNNModule *dnn_module; DNNModel *model; - DNNInputData input; + DNNData input; DNNData output; int scale_factor; struct SwsContext *sws_contexts[3]; @@ -176,40 +176,12 @@ static int config_props(AVFilterLink *inlink) sr_context->sws_slice_h = inlink->h; } else { if (inlink->format != AV_PIX_FMT_GRAY8){ - sws_src_h = sr_context->input.height; - sws_src_w = sr_context->input.width; - sws_dst_h = sr_context->output.height; - sws_dst_w = sr_context->output.width; - - switch (inlink->format){ - case AV_PIX_FMT_YUV420P: - sws_src_h = AV_CEIL_RSHIFT(sws_src_h, 1); - sws_src_w = AV_CEIL_RSHIFT(sws_src_w, 1); - sws_dst_h = AV_CEIL_RSHIFT(sws_dst_h, 1); - sws_dst_w = AV_CEIL_RSHIFT(sws_dst_w, 1); - break; - case AV_PIX_FMT_YUV422P: - sws_src_w = AV_CEIL_RSHIFT(sws_src_w, 1); - sws_dst_w = AV_CEIL_RSHIFT(sws_dst_w, 1); - break; - case AV_PIX_FMT_YUV444P: - break; - case AV_PIX_FMT_YUV410P: - sws_src_h = AV_CEIL_RSHIFT(sws_src_h, 2); - sws_src_w = AV_CEIL_RSHIFT(sws_src_w, 2); - sws_dst_h = AV_CEIL_RSHIFT(sws_dst_h, 2); - sws_dst_w = AV_CEIL_RSHIFT(sws_dst_w, 2); - break; - case AV_PIX_FMT_YUV411P: - sws_src_w = AV_CEIL_RSHIFT(sws_src_w, 2); - sws_dst_w = AV_CEIL_RSHIFT(sws_dst_w, 2); - break; - default: - av_log(context, AV_LOG_ERROR, - "could not create SwsContext for scaling for given input pixel format: %s\n", - av_get_pix_fmt_name(inlink->format)); - return AVERROR(EIO); - } + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + sws_src_h = AV_CEIL_RSHIFT(sr_context->input.height, desc->log2_chroma_h); + sws_src_w = AV_CEIL_RSHIFT(sr_context->input.width, desc->log2_chroma_w); + sws_dst_h = AV_CEIL_RSHIFT(sr_context->output.height, desc->log2_chroma_h); + sws_dst_w = AV_CEIL_RSHIFT(sr_context->output.width, desc->log2_chroma_w); + sr_context->sws_contexts[0] = sws_getContext(sws_src_w, sws_src_h, AV_PIX_FMT_GRAY8, sws_dst_w, sws_dst_h, AV_PIX_FMT_GRAY8, SWS_BICUBIC, NULL, NULL, NULL); @@ -317,5 +289,4 @@ AVFilter ff_vf_sr = { .inputs = sr_inputs, .outputs = sr_outputs, .priv_class = &sr_class, - .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_ssim.c b/libavfilter/vf_ssim.c index 4c957f41a3c..a32fada220f 100644 --- a/libavfilter/vf_ssim.c +++ b/libavfilter/vf_ssim.c @@ -55,13 +55,13 @@ typedef struct SSIMContext { uint64_t nb_frames; double ssim[4], ssim_total; char comps[4]; - float coefs[4]; + double coefs[4]; uint8_t rgba_map[4]; int planewidth[4]; int planeheight[4]; int *temp; int is_rgb; - float (*ssim_plane)(SSIMDSPContext *dsp, + double (*ssim_plane)(SSIMDSPContext *dsp, uint8_t *main, int main_stride, uint8_t *ref, int ref_stride, int width, int height, void *temp, @@ -206,9 +206,9 @@ static float ssim_endn_16bit(const int64_t (*sum0)[4], const int64_t (*sum1)[4], return ssim; } -static float ssim_endn_8bit(const int (*sum0)[4], const int (*sum1)[4], int width) +static double ssim_endn_8bit(const int (*sum0)[4], const int (*sum1)[4], int width) { - float ssim = 0.0; + double ssim = 0.0; int i; for (i = 0; i < width; i++) @@ -221,14 +221,14 @@ static float ssim_endn_8bit(const int (*sum0)[4], const int (*sum1)[4], int widt #define SUM_LEN(w) (((w) >> 2) + 3) -static float ssim_plane_16bit(SSIMDSPContext *dsp, +static double ssim_plane_16bit(SSIMDSPContext *dsp, uint8_t *main, int main_stride, uint8_t *ref, int ref_stride, int width, int height, void *temp, int max) { int z = 0, y; - float ssim = 0.0; + double ssim = 0.0; int64_t (*sum0)[4] = temp; int64_t (*sum1)[4] = sum0 + SUM_LEN(width); @@ -249,14 +249,14 @@ static float ssim_plane_16bit(SSIMDSPContext *dsp, return ssim / ((height - 1) * (width - 1)); } -static float ssim_plane(SSIMDSPContext *dsp, +static double ssim_plane(SSIMDSPContext *dsp, uint8_t *main, int main_stride, uint8_t *ref, int ref_stride, int width, int height, void *temp, int max) { int z = 0, y; - float ssim = 0.0; + double ssim = 0.0; int (*sum0)[4] = temp; int (*sum1)[4] = sum0 + SUM_LEN(width); @@ -279,7 +279,7 @@ static float ssim_plane(SSIMDSPContext *dsp, static double ssim_db(double ssim, double weight) { - return 10 * log10(weight / (weight - ssim)); + return (fabs(weight - ssim) > 1e-9) ? 10.0 * log10(weight / (weight - ssim)) : INFINITY; } static int do_ssim(FFFrameSync *fs) @@ -288,7 +288,7 @@ static int do_ssim(FFFrameSync *fs) SSIMContext *s = ctx->priv; AVFrame *master, *ref; AVDictionary **metadata; - float c[4], ssimv = 0.0; + double c[4] = { 0 }, ssimv = 0.0; int ret, i; ret = ff_framesync_dualinput_get(fs, &master, &ref); @@ -443,6 +443,14 @@ static int config_output(AVFilterLink *outlink) if ((ret = ff_framesync_configure(&s->fs)) < 0) return ret; + outlink->time_base = s->fs.time_base; + + if (av_cmp_q(mainlink->time_base, outlink->time_base) || + av_cmp_q(ctx->inputs[1]->time_base, outlink->time_base)) + av_log(ctx, AV_LOG_WARNING, "not matching timebases found between first input: %d/%d and second input %d/%d, results may be incorrect!\n", + mainlink->time_base.num, mainlink->time_base.den, + ctx->inputs[1]->time_base.num, ctx->inputs[1]->time_base.den); + return 0; } diff --git a/libavfilter/vf_stack.c b/libavfilter/vf_stack.c index 4d254e00136..35b7177e834 100644 --- a/libavfilter/vf_stack.c +++ b/libavfilter/vf_stack.c @@ -21,9 +21,11 @@ #include "libavutil/avstring.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" +#include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "drawutils.h" #include "formats.h" #include "internal.h" #include "framesync.h" @@ -44,6 +46,12 @@ typedef struct StackContext { int is_vertical; int is_horizontal; int nb_planes; + uint8_t fillcolor[4]; + char *fillcolor_str; + int fillcolor_enable; + + FFDrawContext draw; + FFDrawColor color; StackItem *items; AVFrame **frames; @@ -53,8 +61,13 @@ typedef struct StackContext { static int query_formats(AVFilterContext *ctx) { AVFilterFormats *pix_fmts = NULL; + StackContext *s = ctx->priv; int fmt, ret; + if (s->fillcolor_enable) { + return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); + } + for (fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); if (!(desc->flags & AV_PIX_FMT_FLAG_PAL || @@ -82,7 +95,17 @@ static av_cold int init(AVFilterContext *ctx) if (!s->frames) return AVERROR(ENOMEM); + s->items = av_calloc(s->nb_inputs, sizeof(*s->items)); + if (!s->items) + return AVERROR(ENOMEM); + if (!strcmp(ctx->filter->name, "xstack")) { + if (strcmp(s->fillcolor_str, "none") && + av_parse_color(s->fillcolor, s->fillcolor_str, -1, ctx) >= 0) { + s->fillcolor_enable = 1; + } else { + s->fillcolor_enable = 0; + } if (!s->layout) { if (s->nb_inputs == 2) { s->layout = av_strdup("0_0|w0_0"); @@ -93,10 +116,6 @@ static av_cold int init(AVFilterContext *ctx) return AVERROR(EINVAL); } } - - s->items = av_calloc(s->nb_inputs, sizeof(*s->items)); - if (!s->items) - return AVERROR(ENOMEM); } for (i = 0; i < s->nb_inputs; i++) { @@ -116,6 +135,29 @@ static av_cold int init(AVFilterContext *ctx) return 0; } +static int process_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs) +{ + StackContext *s = ctx->priv; + AVFrame *out = arg; + AVFrame **in = s->frames; + const int start = (s->nb_inputs * job ) / nb_jobs; + const int end = (s->nb_inputs * (job+1)) / nb_jobs; + + for (int i = start; i < end; i++) { + StackItem *item = &s->items[i]; + + for (int p = 0; p < s->nb_planes; p++) { + av_image_copy_plane(out->data[p] + out->linesize[p] * item->y[p] + item->x[p], + out->linesize[p], + in[i]->data[p], + in[i]->linesize[p], + item->linesize[p], item->height[p]); + } + } + + return 0; +} + static int process_frame(FFFrameSync *fs) { AVFilterContext *ctx = fs->parent; @@ -123,7 +165,7 @@ static int process_frame(FFFrameSync *fs) StackContext *s = fs->opaque; AVFrame **in = s->frames; AVFrame *out; - int i, p, ret, offset[4] = { 0 }; + int i, ret; for (i = 0; i < s->nb_inputs; i++) { if ((ret = ff_framesync_get_frame(&s->fs, i, &in[i], 0)) < 0) @@ -136,47 +178,11 @@ static int process_frame(FFFrameSync *fs) out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base); out->sample_aspect_ratio = outlink->sample_aspect_ratio; - for (i = 0; i < s->nb_inputs; i++) { - AVFilterLink *inlink = ctx->inputs[i]; - int linesize[4]; - int height[4]; - - if (s->is_horizontal || s->is_vertical) { - if ((ret = av_image_fill_linesizes(linesize, inlink->format, inlink->w)) < 0) { - av_frame_free(&out); - return ret; - } - - height[1] = height[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h); - height[0] = height[3] = inlink->h; - } - - for (p = 0; p < s->nb_planes; p++) { - if (s->is_vertical) { - av_image_copy_plane(out->data[p] + offset[p] * out->linesize[p], - out->linesize[p], - in[i]->data[p], - in[i]->linesize[p], - linesize[p], height[p]); - offset[p] += height[p]; - } else if (s->is_horizontal) { - av_image_copy_plane(out->data[p] + offset[p], - out->linesize[p], - in[i]->data[p], - in[i]->linesize[p], - linesize[p], height[p]); - offset[p] += linesize[p]; - } else { - StackItem *item = &s->items[i]; + if (s->fillcolor_enable) + ff_fill_rectangle(&s->draw, &s->color, out->data, out->linesize, + 0, 0, outlink->w, outlink->h); - av_image_copy_plane(out->data[p] + out->linesize[p] * item->y[p] + item->x[p], - out->linesize[p], - in[i]->data[p], - in[i]->linesize[p], - item->linesize[p], item->height[p]); - } - } - } + ctx->internal->execute(ctx, process_slice, out, NULL, FFMIN(s->nb_inputs, ff_filter_get_nb_threads(ctx))); return ff_filter_frame(outlink, out); } @@ -197,20 +203,53 @@ static int config_output(AVFilterLink *outlink) return AVERROR_BUG; if (s->is_vertical) { - for (i = 1; i < s->nb_inputs; i++) { + for (i = 0; i < s->nb_inputs; i++) { + AVFilterLink *inlink = ctx->inputs[i]; + StackItem *item = &s->items[i]; + if (ctx->inputs[i]->w != width) { av_log(ctx, AV_LOG_ERROR, "Input %d width %d does not match input %d width %d.\n", i, ctx->inputs[i]->w, 0, width); return AVERROR(EINVAL); } - height += ctx->inputs[i]->h; + + if ((ret = av_image_fill_linesizes(item->linesize, inlink->format, inlink->w)) < 0) { + return ret; + } + + item->height[1] = item->height[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h); + item->height[0] = item->height[3] = inlink->h; + + if (i) { + item->y[1] = item->y[2] = AV_CEIL_RSHIFT(height, s->desc->log2_chroma_h); + item->y[0] = item->y[3] = height; + + height += ctx->inputs[i]->h; + } } } else if (s->is_horizontal) { - for (i = 1; i < s->nb_inputs; i++) { + for (i = 0; i < s->nb_inputs; i++) { + AVFilterLink *inlink = ctx->inputs[i]; + StackItem *item = &s->items[i]; + if (ctx->inputs[i]->h != height) { av_log(ctx, AV_LOG_ERROR, "Input %d height %d does not match input %d height %d.\n", i, ctx->inputs[i]->h, 0, height); return AVERROR(EINVAL); } - width += ctx->inputs[i]->w; + + if ((ret = av_image_fill_linesizes(item->linesize, inlink->format, inlink->w)) < 0) { + return ret; + } + + item->height[1] = item->height[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h); + item->height[0] = item->height[3] = inlink->h; + + if (i) { + if ((ret = av_image_fill_linesizes(item->x, inlink->format, width)) < 0) { + return ret; + } + + width += ctx->inputs[i]->w; + } } } else { char *arg, *p = s->layout, *saveptr = NULL; @@ -218,6 +257,11 @@ static int config_output(AVFilterLink *outlink) char *arg3, *p3, *saveptr3 = NULL; int inw, inh, size; + if (s->fillcolor_enable) { + ff_draw_init(&s->draw, ctx->inputs[0]->format, 0); + ff_draw_color(&s->draw, &s->color, s->fillcolor); + } + for (i = 0; i < s->nb_inputs; i++) { AVFilterLink *inlink = ctx->inputs[i]; StackItem *item = &s->items[i]; @@ -294,6 +338,17 @@ static int config_output(AVFilterLink *outlink) outlink->frame_rate = frame_rate; outlink->sample_aspect_ratio = sar; + for (i = 1; i < s->nb_inputs; i++) { + AVFilterLink *inlink = ctx->inputs[i]; + if (outlink->frame_rate.num != inlink->frame_rate.num || + outlink->frame_rate.den != inlink->frame_rate.den) { + av_log(ctx, AV_LOG_VERBOSE, + "Video inputs have different frame rates, output will be VFR\n"); + outlink->frame_rate = av_make_q(1, 0); + break; + } + } + if ((ret = ff_framesync_init(&s->fs, ctx, s->nb_inputs)) < 0) return ret; @@ -367,7 +422,7 @@ AVFilter ff_vf_hstack = { .init = init, .uninit = uninit, .activate = activate, - .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, + .flags = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_SLICE_THREADS, }; #endif /* CONFIG_HSTACK_FILTER */ @@ -387,7 +442,7 @@ AVFilter ff_vf_vstack = { .init = init, .uninit = uninit, .activate = activate, - .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, + .flags = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_SLICE_THREADS, }; #endif /* CONFIG_VSTACK_FILTER */ @@ -398,6 +453,7 @@ static const AVOption xstack_options[] = { { "inputs", "set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64=2}, 2, INT_MAX, .flags = FLAGS }, { "layout", "set custom layout", OFFSET(layout), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, .flags = FLAGS }, { "shortest", "force termination when the shortest input terminates", OFFSET(shortest), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, .flags = FLAGS }, + { "fill", "set the color for unused pixels", OFFSET(fillcolor_str), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = FLAGS }, { NULL }, }; @@ -413,7 +469,7 @@ AVFilter ff_vf_xstack = { .init = init, .uninit = uninit, .activate = activate, - .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, + .flags = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_SLICE_THREADS, }; #endif /* CONFIG_XSTACK_FILTER */ diff --git a/libavfilter/vf_stereo3d.c b/libavfilter/vf_stereo3d.c index 8b22f880ca6..ff17b07c3d5 100644 --- a/libavfilter/vf_stereo3d.c +++ b/libavfilter/vf_stereo3d.c @@ -100,9 +100,9 @@ static const int ana_coeff[][3][6] = { { 0, 0, 0, 0, 65536, 0}, { 0, 0, 0, 0, 0, 65536}}, [ANAGLYPH_RC_DUBOIS] = - {{29891, 32800, 11559, -2849, -5763, -102}, - {-2627, -2479, -1033, 24804, 48080, -1209}, - { -997, -1350, -358, -4729, -7403, 80373}}, + {{29884, 32768, 11534, -2818, -5767, -131}, + {-2621, -2490, -1049, 24773, 48103, -1180}, + { -983, -1376, -328, -4719, -7406, 80347}}, [ANAGLYPH_GM_GRAY] = {{ 0, 0, 0, 19595, 38470, 7471}, {19595, 38470, 7471, 0, 0, 0}, @@ -132,9 +132,9 @@ static const int ana_coeff[][3][6] = { { 0, 0, 0, 0, 65536, 0}, { 0, 0, 65536, 0, 0, 0}}, [ANAGLYPH_YB_DUBOIS] = - {{65535,-12650,18451, -987, -7590, -1049}, - {-1604, 56032, 4196, 370, 3826, -1049}, - {-2345,-10676, 1358, 5801, 11416, 56217}}, + {{69599,-13435,19595, -1048, -8061, -1114}, + {-1704, 59507, 4456, 393, 4063, -1114}, + {-2490,-11338, 1442, 6160, 12124, 59703}}, }; typedef struct Stereo3DContext { @@ -160,9 +160,13 @@ typedef struct Stereo3DContext { 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" }, @@ -175,9 +179,13 @@ static const AVOption stereo3d_options[] = { { "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" }, @@ -551,8 +559,6 @@ static int config_output(AVFilterLink *outlink) break; case CHECKERBOARD_LR: case CHECKERBOARD_RL: - s->out.width = s->width * 2; - break; case INTERLEAVE_COLS_LR: case INTERLEAVE_COLS_RL: s->out.width = s->width * 2; @@ -666,7 +672,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) AVFilterContext *ctx = inlink->dst; Stereo3DContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; - AVFrame *out, *oleft, *oright, *ileft, *iright; + AVFrame *out = NULL, *oleft, *oright, *ileft, *iright; int out_off_left[4], out_off_right[4]; int i, ret; @@ -1076,6 +1082,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) av_frame_free(&s->prev); av_frame_free(&inpicref); } + av_assert0(out); out->sample_aspect_ratio = s->aspect; return ff_filter_frame(outlink, out); } diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c index a7b02461f2d..a3b4029af49 100644 --- a/libavfilter/vf_subtitles.c +++ b/libavfilter/vf_subtitles.c @@ -66,10 +66,10 @@ typedef struct AssContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM #define COMMON_OPTIONS \ - {"filename", "set the filename of file to read", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, \ - {"f", "set the filename of file to read", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, \ - {"original_size", "set the size of the original video (used to scale fonts)", OFFSET(original_w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, \ - {"fontsdir", "set the directory containing the fonts to read", OFFSET(fontsdir), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS }, \ + {"filename", "set the filename of file to read", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, \ + {"f", "set the filename of file to read", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, \ + {"original_size", "set the size of the original video (used to scale fonts)", OFFSET(original_w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, FLAGS }, \ + {"fontsdir", "set the directory containing the fonts to read", OFFSET(fontsdir), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, \ {"alpha", "enable processing of alpha channel", OFFSET(alpha), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, FLAGS }, \ /* libass supports a log level ranging from 0 to 7 */ @@ -263,10 +263,10 @@ AVFilter ff_vf_ass = { static const AVOption subtitles_options[] = { COMMON_OPTIONS - {"charenc", "set input character encoding", OFFSET(charenc), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS}, + {"charenc", "set input character encoding", OFFSET(charenc), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS}, {"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}, CHAR_MIN, CHAR_MAX, FLAGS}, + {"force_style", "force subtitle style", OFFSET(force_style), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS}, {NULL}, }; diff --git a/libavfilter/vf_swaprect.c b/libavfilter/vf_swaprect.c index f1fab1e36d0..cf9c298f2f4 100644 --- a/libavfilter/vf_swaprect.c +++ b/libavfilter/vf_swaprect.c @@ -99,7 +99,7 @@ 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); - var_values[VAR_POS] = in->pkt_pos ? NAN : in->pkt_pos; + var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; ret = av_expr_parse_and_eval(&dw, s->w, var_names, &var_values[0], diff --git a/libavfilter/vf_telecine.c b/libavfilter/vf_telecine.c index 62599a7a3a6..741b19a4f19 100644 --- a/libavfilter/vf_telecine.c +++ b/libavfilter/vf_telecine.c @@ -207,6 +207,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) s->stride[i], (s->planeheight[i] - !s->first_field + 1) / 2); } + s->frame[nout]->interlaced_frame = 1; + s->frame[nout]->top_field_first = !s->first_field; nout++; len--; s->occupied = 0; @@ -220,6 +222,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) inpicref->data[i], inpicref->linesize[i], s->stride[i], s->planeheight[i]); + s->frame[nout]->interlaced_frame = inpicref->interlaced_frame; + s->frame[nout]->top_field_first = inpicref->top_field_first; nout++; len -= 2; } @@ -236,6 +240,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; if (!frame) { av_frame_free(&inpicref); @@ -243,6 +249,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) } av_frame_copy_props(frame, inpicref); + frame->interlaced_frame = interlaced; + frame->top_field_first = tff; 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 0effdc91e9a..ac04615bdc8 100644 --- a/libavfilter/vf_thumbnail.c +++ b/libavfilter/vf_thumbnail.c @@ -162,7 +162,7 @@ static av_cold void uninit(AVFilterContext *ctx) { int i; ThumbContext *s = ctx->priv; - for (i = 0; i < s->n_frames && s->frames[i].buf; i++) + for (i = 0; i < s->n_frames && s->frames && s->frames[i].buf; i++) av_frame_free(&s->frames[i].buf); av_freep(&s->frames); } @@ -234,4 +234,5 @@ AVFilter ff_vf_thumbnail = { .inputs = thumbnail_inputs, .outputs = thumbnail_outputs, .priv_class = &thumbnail_class, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_tile.c b/libavfilter/vf_tile.c index 439689a14d4..6278f72abb3 100644 --- a/libavfilter/vf_tile.c +++ b/libavfilter/vf_tile.c @@ -262,6 +262,7 @@ static av_cold void uninit(AVFilterContext *ctx) { TileContext *tile = ctx->priv; + av_frame_free(&tile->out_ref); av_frame_free(&tile->prev_out_ref); } diff --git a/libavfilter/vf_tinterlace.c b/libavfilter/vf_tinterlace.c index fc5d11e0537..a77753775c9 100644 --- a/libavfilter/vf_tinterlace.c +++ b/libavfilter/vf_tinterlace.c @@ -53,6 +53,7 @@ static const AVOption tinterlace_options[] = { {"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" }, {NULL} }; @@ -63,10 +64,10 @@ static const AVOption interlace_options[] = { { "scan", "scanning mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_TFF}, 0, 1, FLAGS, "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(flags), AV_OPT_TYPE_FLAGS, {.i64 = TINTERLACE_FLAG_VLPF}, 0, 2, FLAGS, "flags" }, - { "off", "disable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, FLAGS, "flags" }, - { "linear", "linear vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags" }, - { "complex", "complex vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, "flags" }, + { "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" }, { NULL } }; @@ -439,6 +440,16 @@ 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) { + 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 + ret = ff_filter_frame(outlink, out); + return ret; + } tff = tinterlace->mode == MODE_INTERLEAVE_TOP; out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) @@ -518,6 +529,12 @@ static int init_interlace(AVFilterContext *ctx) if (tinterlace->mode <= MODE_BFF) tinterlace->mode += MODE_INTERLEAVE_TOP; + tinterlace->flags |= TINTERLACE_FLAG_BYPASS_IL; + if (tinterlace->lowpass == VLPF_LIN) + tinterlace->flags |= TINTERLACE_FLAG_VLPF; + if (tinterlace->lowpass == VLPF_CMP) + tinterlace->flags |= TINTERLACE_FLAG_CVLPF; + return 0; } diff --git a/libavfilter/vf_tonemap_opencl.c b/libavfilter/vf_tonemap_opencl.c index 315ead49d40..b880228727e 100644 --- a/libavfilter/vf_tonemap_opencl.c +++ b/libavfilter/vf_tonemap_opencl.c @@ -542,7 +542,7 @@ static const AVFilterPad tonemap_opencl_outputs[] = { AVFilter ff_vf_tonemap_opencl = { .name = "tonemap_opencl", - .description = NULL_IF_CONFIG_SMALL("perform HDR to SDR conversion with tonemapping"), + .description = NULL_IF_CONFIG_SMALL("Perform HDR to SDR conversion with tonemapping."), .priv_size = sizeof(TonemapOpenCLContext), .priv_class = &tonemap_opencl_class, .init = &ff_opencl_filter_init, diff --git a/libavfilter/vf_tonemap_vaapi.c b/libavfilter/vf_tonemap_vaapi.c new file mode 100644 index 00000000000..2f41b904243 --- /dev/null +++ b/libavfilter/vf_tonemap_vaapi.c @@ -0,0 +1,419 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 "libavutil/mem.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "libavutil/mastering_display_metadata.h" + +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "vaapi_vpp.h" + +typedef struct HDRVAAPIContext { + VAAPIVPPContext vpp_ctx; // must be the first field + + char *output_format_string; + + char *color_primaries_string; + char *color_transfer_string; + char *color_matrix_string; + + enum AVColorPrimaries color_primaries; + enum AVColorTransferCharacteristic color_transfer; + enum AVColorSpace color_matrix; + + VAHdrMetaDataHDR10 in_metadata; + + AVFrameSideData *src_display; + AVFrameSideData *src_light; +} HDRVAAPIContext; + +static int tonemap_vaapi_save_metadata(AVFilterContext *avctx, AVFrame *input_frame) +{ + HDRVAAPIContext *ctx = avctx->priv; + AVMasteringDisplayMetadata *hdr_meta; + AVContentLightMetadata *light_meta; + + if (input_frame->color_trc != AVCOL_TRC_SMPTE2084) { + av_log(avctx, AV_LOG_WARNING, "Only support HDR10 as input for vaapi tone-mapping\n"); + } + + ctx->src_display = av_frame_get_side_data(input_frame, + AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + if (ctx->src_display) { + hdr_meta = (AVMasteringDisplayMetadata *)ctx->src_display->data; + if (!hdr_meta) { + av_log(avctx, AV_LOG_ERROR, "No mastering display data\n"); + return AVERROR(EINVAL); + } + + if (hdr_meta->has_luminance) { + const int luma_den = 10000; + ctx->in_metadata.max_display_mastering_luminance = + lrint(luma_den * av_q2d(hdr_meta->max_luminance)); + ctx->in_metadata.min_display_mastering_luminance = + FFMIN(lrint(luma_den * av_q2d(hdr_meta->min_luminance)), + ctx->in_metadata.max_display_mastering_luminance); + + av_log(avctx, AV_LOG_DEBUG, + "Mastering Display Metadata(in luminance):\n"); + av_log(avctx, AV_LOG_DEBUG, + "min_luminance=%u, max_luminance=%u\n", + ctx->in_metadata.min_display_mastering_luminance, + ctx->in_metadata.max_display_mastering_luminance); + } + + if (hdr_meta->has_primaries) { + int i; + const int mapping[3] = {1, 2, 0}; //green, blue, red + const int chroma_den = 50000; + + for (i = 0; i < 3; i++) { + const int j = mapping[i]; + ctx->in_metadata.display_primaries_x[i] = + FFMIN(lrint(chroma_den * + av_q2d(hdr_meta->display_primaries[j][0])), + chroma_den); + ctx->in_metadata.display_primaries_y[i] = + FFMIN(lrint(chroma_den * + av_q2d(hdr_meta->display_primaries[j][1])), + chroma_den); + } + + ctx->in_metadata.white_point_x = + FFMIN(lrint(chroma_den * av_q2d(hdr_meta->white_point[0])), + chroma_den); + ctx->in_metadata.white_point_y = + FFMIN(lrint(chroma_den * av_q2d(hdr_meta->white_point[1])), + chroma_den); + + av_log(avctx, AV_LOG_DEBUG, + "Mastering Display Metadata(in primaries):\n"); + av_log(avctx, AV_LOG_DEBUG, + "G(%u,%u) B(%u,%u) R(%u,%u) WP(%u,%u)\n", + ctx->in_metadata.display_primaries_x[0], + ctx->in_metadata.display_primaries_y[0], + ctx->in_metadata.display_primaries_x[1], + ctx->in_metadata.display_primaries_y[1], + ctx->in_metadata.display_primaries_x[2], + ctx->in_metadata.display_primaries_y[2], + ctx->in_metadata.white_point_x, + ctx->in_metadata.white_point_y); + } + } else { + av_log(avctx, AV_LOG_ERROR, "No mastering display data from input\n"); + return AVERROR(EINVAL); + } + + ctx->src_light = av_frame_get_side_data(input_frame, + AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + if (ctx->src_light) { + light_meta = (AVContentLightMetadata *)ctx->src_light->data; + if (!light_meta) { + av_log(avctx, AV_LOG_ERROR, "No light metadata\n"); + return AVERROR(EINVAL); + } + + ctx->in_metadata.max_content_light_level = light_meta->MaxCLL; + ctx->in_metadata.max_pic_average_light_level = light_meta->MaxFALL; + + av_log(avctx, AV_LOG_DEBUG, + "Mastering Content Light Level (in):\n"); + av_log(avctx, AV_LOG_DEBUG, + "MaxCLL(%u) MaxFALL(%u)\n", + ctx->in_metadata.max_content_light_level, + ctx->in_metadata.max_pic_average_light_level); + } else { + av_log(avctx, AV_LOG_DEBUG, "No content light level from input\n"); + } + return 0; +} + +static int tonemap_vaapi_set_filter_params(AVFilterContext *avctx, AVFrame *input_frame) +{ + VAAPIVPPContext *vpp_ctx = avctx->priv; + HDRVAAPIContext *ctx = avctx->priv; + VAStatus vas; + VAProcFilterParameterBufferHDRToneMapping *hdrtm_param; + + vas = vaMapBuffer(vpp_ctx->hwctx->display, vpp_ctx->filter_buffers[0], + (void**)&hdrtm_param); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to map " + "buffer (%d): %d (%s).\n", + vpp_ctx->filter_buffers[0], vas, vaErrorStr(vas)); + return AVERROR(EIO); + } + + memcpy(hdrtm_param->data.metadata, &ctx->in_metadata, sizeof(VAHdrMetaDataHDR10)); + + vas = vaUnmapBuffer(vpp_ctx->hwctx->display, vpp_ctx->filter_buffers[0]); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to unmap output buffers: " + "%d (%s).\n", vas, vaErrorStr(vas)); + return AVERROR(EIO); + } + + return 0; +} + +static int tonemap_vaapi_build_filter_params(AVFilterContext *avctx) +{ + VAAPIVPPContext *vpp_ctx = avctx->priv; + HDRVAAPIContext *ctx = avctx->priv; + VAStatus vas; + VAProcFilterParameterBufferHDRToneMapping hdrtm_param; + VAProcFilterCapHighDynamicRange hdr_cap[VAProcHighDynamicRangeMetadataTypeCount]; + int num_query_caps; + int i; + + memset(&hdrtm_param, 0, sizeof(hdrtm_param)); + memset(&ctx->in_metadata, 0, sizeof(ctx->in_metadata)); + + num_query_caps = VAProcHighDynamicRangeMetadataTypeCount; + vas = vaQueryVideoProcFilterCaps(vpp_ctx->hwctx->display, + vpp_ctx->va_context, + VAProcFilterHighDynamicRangeToneMapping, + &hdr_cap, &num_query_caps); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to query HDR caps " + "context: %d (%s).\n", vas, vaErrorStr(vas)); + return AVERROR(EIO); + } + + for (i = 0; i < num_query_caps; i++) { + if (hdr_cap[i].metadata_type != VAProcHighDynamicRangeMetadataNone) + break; + } + + if (i >= num_query_caps) { + av_log(avctx, AV_LOG_ERROR, "VAAPI driver doesn't support HDR\n"); + return AVERROR(EINVAL); + } + + for (i = 0; i < num_query_caps; i++) { + if (VA_TONE_MAPPING_HDR_TO_SDR & hdr_cap[i].caps_flag) + break; + } + + if (i >= num_query_caps) { + av_log(avctx, AV_LOG_ERROR, + "VAAPI driver doesn't support HDR to SDR\n"); + return AVERROR(EINVAL); + } + + hdrtm_param.type = VAProcFilterHighDynamicRangeToneMapping; + hdrtm_param.data.metadata_type = VAProcHighDynamicRangeMetadataHDR10; + hdrtm_param.data.metadata = &ctx->in_metadata; + hdrtm_param.data.metadata_size = sizeof(VAHdrMetaDataHDR10); + + return ff_vaapi_vpp_make_param_buffers(avctx, + VAProcFilterParameterBufferType, + &hdrtm_param, sizeof(hdrtm_param), 1); +} + +static int tonemap_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) +{ + AVFilterContext *avctx = inlink->dst; + AVFilterLink *outlink = avctx->outputs[0]; + VAAPIVPPContext *vpp_ctx = avctx->priv; + HDRVAAPIContext *ctx = avctx->priv; + AVFrame *output_frame = NULL; + VASurfaceID input_surface, output_surface; + + VAProcPipelineParameterBuffer params; + int err; + + av_log(avctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n", + av_get_pix_fmt_name(input_frame->format), + input_frame->width, input_frame->height, input_frame->pts); + + if (vpp_ctx->va_context == VA_INVALID_ID){ + av_frame_free(&input_frame); + return AVERROR(EINVAL); + } + + err = tonemap_vaapi_save_metadata(avctx, input_frame); + if (err < 0) + goto fail; + + err = tonemap_vaapi_set_filter_params(avctx, input_frame); + if (err < 0) + goto fail; + + input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3]; + av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for tonemap vpp input.\n", + input_surface); + + output_frame = ff_get_video_buffer(outlink, vpp_ctx->output_width, + vpp_ctx->output_height); + if (!output_frame) { + err = AVERROR(ENOMEM); + goto fail; + } + + output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3]; + av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for tonemap vpp output.\n", + output_surface); + memset(¶ms, 0, sizeof(params)); + + err = av_frame_copy_props(output_frame, input_frame); + if (err < 0) + goto fail; + + if (ctx->color_primaries != AVCOL_PRI_UNSPECIFIED) + output_frame->color_primaries = ctx->color_primaries; + + if (ctx->color_transfer != AVCOL_TRC_UNSPECIFIED) + output_frame->color_trc = ctx->color_transfer; + else + output_frame->color_trc = AVCOL_TRC_BT709; + + if (ctx->color_matrix != AVCOL_SPC_UNSPECIFIED) + output_frame->colorspace = ctx->color_matrix; + + err = ff_vaapi_vpp_init_params(avctx, ¶ms, + input_frame, output_frame); + if (err < 0) + goto fail; + + err = ff_vaapi_vpp_render_picture(avctx, ¶ms, output_frame); + if (err < 0) + goto fail; + + av_frame_free(&input_frame); + + av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n", + av_get_pix_fmt_name(output_frame->format), + output_frame->width, output_frame->height, output_frame->pts); + + return ff_filter_frame(outlink, output_frame); + +fail: + av_frame_free(&input_frame); + av_frame_free(&output_frame); + return err; +} + +static av_cold int tonemap_vaapi_init(AVFilterContext *avctx) +{ + VAAPIVPPContext *vpp_ctx = avctx->priv; + HDRVAAPIContext *ctx = avctx->priv; + + ff_vaapi_vpp_ctx_init(avctx); + vpp_ctx->build_filter_params = tonemap_vaapi_build_filter_params; + vpp_ctx->pipeline_uninit = ff_vaapi_vpp_pipeline_uninit; + + 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"); + } + +#define STRING_OPTION(var_name, func_name, default_value) do { \ + if (ctx->var_name ## _string) { \ + int var = av_ ## func_name ## _from_name(ctx->var_name ## _string); \ + if (var < 0) { \ + av_log(avctx, AV_LOG_ERROR, "Invalid %s.\n", #var_name); \ + return AVERROR(EINVAL); \ + } \ + ctx->var_name = var; \ + } else { \ + ctx->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); + + return 0; +} + +#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" }, + { "matrix", "Output color matrix coefficient set", + OFFSET(color_matrix_string), AV_OPT_TYPE_STRING, + { .str = NULL }, .flags = FLAGS, "matrix" }, + { "m", "Output color matrix coefficient set", + OFFSET(color_matrix_string), AV_OPT_TYPE_STRING, + { .str = NULL }, .flags = FLAGS, "matrix" }, + { "primaries", "Output color primaries set", + OFFSET(color_primaries_string), AV_OPT_TYPE_STRING, + { .str = NULL }, .flags = FLAGS, "primaries" }, + { "p", "Output color primaries set", + OFFSET(color_primaries_string), AV_OPT_TYPE_STRING, + { .str = NULL }, .flags = FLAGS, "primaries" }, + { "transfer", "Output color transfer characteristics set", + OFFSET(color_transfer_string), AV_OPT_TYPE_STRING, + { .str = NULL }, .flags = FLAGS, "transfer" }, + { "t", "Output color transfer characteristics set", + OFFSET(color_transfer_string), AV_OPT_TYPE_STRING, + { .str = NULL }, .flags = FLAGS, "transfer" }, + { NULL } +}; + + +AVFILTER_DEFINE_CLASS(tonemap_vaapi); + +static const AVFilterPad tonemap_vaapi_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = &tonemap_vaapi_filter_frame, + .config_props = &ff_vaapi_vpp_config_input, + }, + { NULL } +}; + +static const AVFilterPad tonemap_vaapi_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &ff_vaapi_vpp_config_output, + }, + { NULL } +}; + +AVFilter ff_vf_tonemap_vaapi = { + .name = "tonemap_vaapi", + .description = NULL_IF_CONFIG_SMALL("VAAPI VPP for tone-mapping"), + .priv_size = sizeof(HDRVAAPIContext), + .init = &tonemap_vaapi_init, + .uninit = &ff_vaapi_vpp_ctx_uninit, + .query_formats = &ff_vaapi_vpp_query_formats, + .inputs = tonemap_vaapi_inputs, + .outputs = tonemap_vaapi_outputs, + .priv_class = &tonemap_vaapi_class, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_tpad.c b/libavfilter/vf_tpad.c index 86e063090bf..0cd65d3e7b9 100644 --- a/libavfilter/vf_tpad.c +++ b/libavfilter/vf_tpad.c @@ -181,7 +181,7 @@ static int config_input(AVFilterLink *inlink) return 0; } -static void uninit(AVFilterContext *ctx) +static av_cold void uninit(AVFilterContext *ctx) { TPadContext *s = ctx->priv; diff --git a/libavfilter/vf_transpose.c b/libavfilter/vf_transpose.c index dd54947bd96..cb49c4a301d 100644 --- a/libavfilter/vf_transpose.c +++ b/libavfilter/vf_transpose.c @@ -40,14 +40,6 @@ #include "video.h" #include "transpose.h" -typedef struct TransVtable { - void (*transpose_8x8)(uint8_t *src, ptrdiff_t src_linesize, - uint8_t *dst, ptrdiff_t dst_linesize); - void (*transpose_block)(uint8_t *src, ptrdiff_t src_linesize, - uint8_t *dst, ptrdiff_t dst_linesize, - int w, int h); -} TransVtable; - typedef struct TransContext { const AVClass *class; int hsub, vsub; @@ -243,6 +235,14 @@ static int config_props_output(AVFilterLink *outlink) } } + if (ARCH_X86) { + for (int i = 0; i < 4; i++) { + TransVtable *v = &s->vtables[i]; + + ff_transpose_init_x86(v, s->pixsteps[i]); + } + } + av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d dir:%d -> w:%d h:%d rotation:%s vflip:%d\n", inlink->w, inlink->h, s->dir, outlink->w, outlink->h, diff --git a/libavfilter/vf_transpose_vaapi.c b/libavfilter/vf_transpose_vaapi.c index 69dbdd70176..a4c654266de 100644 --- a/libavfilter/vf_transpose_vaapi.c +++ b/libavfilter/vf_transpose_vaapi.c @@ -145,7 +145,7 @@ static int transpose_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_fra err = av_frame_copy_props(output_frame, input_frame); if (err < 0) - return err; + goto fail; err = ff_vaapi_vpp_init_params(avctx, ¶ms, input_frame, output_frame); diff --git a/libavfilter/vf_unsharp.c b/libavfilter/vf_unsharp.c index af05833a5d4..7b430b650d4 100644 --- a/libavfilter/vf_unsharp.c +++ b/libavfilter/vf_unsharp.c @@ -218,7 +218,7 @@ static int init_filter_param(AVFilterContext *ctx, UnsharpFilterParam *fp, const effect, effect_type, fp->msize_x, fp->msize_y, fp->amount / 65535.0); fp->sr = av_malloc_array((MAX_MATRIX_SIZE - 1) * s->nb_threads, sizeof(uint32_t)); - fp->sc = av_malloc_array(2 * fp->steps_y * s->nb_threads, sizeof(uint32_t **)); + fp->sc = av_mallocz_array(2 * fp->steps_y * s->nb_threads, sizeof(uint32_t *)); if (!fp->sr || !fp->sc) return AVERROR(ENOMEM); @@ -230,10 +230,10 @@ static int init_filter_param(AVFilterContext *ctx, UnsharpFilterParam *fp, const return 0; } -static int config_props(AVFilterLink *link) +static int config_input(AVFilterLink *inlink) { - UnsharpContext *s = link->dst->priv; - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format); + UnsharpContext *s = inlink->dst->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); int ret; s->hsub = desc->log2_chroma_w; @@ -241,13 +241,13 @@ static int config_props(AVFilterLink *link) // ensure (height / nb_threads) > 4 * steps_y, // so that we don't have too much overlap between two threads - s->nb_threads = FFMIN(ff_filter_get_nb_threads(link->dst), - link->h / (4 * s->luma.steps_y)); + s->nb_threads = FFMIN(ff_filter_get_nb_threads(inlink->dst), + inlink->h / (4 * s->luma.steps_y)); - ret = init_filter_param(link->dst, &s->luma, "luma", link->w); + ret = init_filter_param(inlink->dst, &s->luma, "luma", inlink->w); if (ret < 0) return ret; - ret = init_filter_param(link->dst, &s->chroma, "chroma", AV_CEIL_RSHIFT(link->w, s->hsub)); + ret = init_filter_param(inlink->dst, &s->chroma, "chroma", AV_CEIL_RSHIFT(inlink->w, s->hsub)); if (ret < 0) return ret; @@ -258,9 +258,11 @@ static void free_filter_param(UnsharpFilterParam *fp, int nb_threads) { int z; - for (z = 0; z < 2 * fp->steps_y * nb_threads; z++) - av_freep(&fp->sc[z]); - av_freep(&fp->sc); + if (fp->sc) { + for (z = 0; z < 2 * fp->steps_y * nb_threads; z++) + av_freep(&fp->sc[z]); + av_freep(&fp->sc); + } av_freep(&fp->sr); } @@ -325,7 +327,7 @@ static const AVFilterPad avfilter_vf_unsharp_inputs[] = { .name = "default", .type = AVMEDIA_TYPE_VIDEO, .filter_frame = filter_frame, - .config_props = config_props, + .config_props = config_input, }, { NULL } }; diff --git a/libavfilter/vf_untile.c b/libavfilter/vf_untile.c new file mode 100644 index 00000000000..9a2eb249010 --- /dev/null +++ b/libavfilter/vf_untile.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2020 Nicolas George + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "avfilter.h" +#include "formats.h" +#include "filters.h" + +typedef struct UntileContext { + const AVClass *class; + unsigned w, h; + unsigned current; + unsigned nb_frames; + AVFrame *frame; + const AVPixFmtDescriptor *desc; + int64_t dpts, pts; + int max_step[4]; +} UntileContext; + +#define OFFSET(x) offsetof(UntileContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption untile_options[] = { + { "layout", "set grid size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, + {.str = "6x5"}, 0, 0, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(untile); + +static av_cold int init(AVFilterContext *ctx) +{ + UntileContext *s = ctx->priv; + + if (s->w > UINT_MAX / s->h) { + av_log(ctx, AV_LOG_ERROR, "Tile size %ux%u is insane.\n", + s->w, s->h); + return AVERROR(EINVAL); + } + s->nb_frames = s->w * s->h; + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats = NULL; + int ret; + + ret = ff_formats_pixdesc_filter(&formats, 0, + AV_PIX_FMT_FLAG_HWACCEL | + AV_PIX_FMT_FLAG_BITSTREAM | + FF_PIX_FMT_FLAG_SW_FLAT_SUB); + if (ret < 0) + return ret; + return ff_set_common_formats(ctx, formats); +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + UntileContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + AVRational dt; + + s->desc = av_pix_fmt_desc_get(outlink->format); + if (inlink->w % (s->w << s->desc->log2_chroma_w) || + inlink->h % (s->h << s->desc->log2_chroma_h)) { + av_log(ctx, AV_LOG_ERROR, + "Input resolution %ux%u not multiple of layout %ux%u.\n", + inlink->w, inlink->h, s->w, s->h); + return AVERROR(EINVAL); + } + outlink->w = inlink->w / s->w; + outlink->h = inlink->h / s->h; + outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; + outlink->frame_rate = av_mul_q(inlink->frame_rate, av_make_q(s->nb_frames, 1)); + if (outlink->frame_rate.num) + dt = av_inv_q(outlink->frame_rate); + else + dt = av_mul_q(inlink->time_base, av_make_q(1, s->nb_frames)); + outlink->time_base = av_gcd_q(inlink->time_base, dt, AV_TIME_BASE / 2, AV_TIME_BASE_Q); + s->dpts = av_rescale_q(1, dt, outlink->time_base); + av_log(ctx, AV_LOG_VERBOSE, "frame interval: %"PRId64"*%d/%d\n", + s->dpts, dt.num, dt.den); + av_image_fill_max_pixsteps(s->max_step, NULL, s->desc); + return 0; +} + +static int activate(AVFilterContext *ctx) +{ + UntileContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *out; + int i, x, y, ret; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + if (!s->frame) { + ret = ff_inlink_consume_frame(inlink, &s->frame); + if (ret < 0) + return ret; + if (ret) + s->pts = av_rescale_q(s->frame->pts, inlink->time_base, outlink->time_base); + } + if (s->frame) { + if (s->current == s->nb_frames - 1) { + out = s->frame; + s->frame = NULL; + } else { + out = av_frame_clone(s->frame); + if (!out) + return AVERROR(ENOMEM); + } + x = outlink->w * (s->current % s->w); + y = outlink->h * (s->current / s->w); + out->width = outlink->w; + out->height = outlink->h; + out->data[0] += y * out->linesize[0]; + out->data[0] += x * s->max_step[0]; + if (!(s->desc->flags & AV_PIX_FMT_FLAG_PAL || s->desc->flags & FF_PSEUDOPAL)) { + for (i = 1; i < 3; i ++) { + if (out->data[i]) { + out->data[i] += (y >> s->desc->log2_chroma_w) * out->linesize[i]; + out->data[i] += (x >> s->desc->log2_chroma_h) * s->max_step[i]; + } + } + } + if (out->data[3]) { + out->data[3] += y * out->linesize[3]; + out->data[3] += x * s->max_step[3]; + } + out->pts = s->pts; + s->pts += s->dpts; + if (++s->current == s->nb_frames) + s->current = 0; + return ff_filter_frame(outlink, out); + } + FF_FILTER_FORWARD_STATUS(inlink, outlink); + FF_FILTER_FORWARD_WANTED(outlink, inlink); + return FFERROR_NOT_READY; + +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + UntileContext *s = ctx->priv; + + av_frame_free(&s->frame); +} + +static const AVFilterPad untile_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +static const AVFilterPad untile_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, + { NULL } +}; + +AVFilter ff_vf_untile = { + .name = "untile", + .description = NULL_IF_CONFIG_SMALL("Untile a frame into a sequence of frames."), + .init = init, + .uninit = uninit, + .query_formats = query_formats, + .activate = activate, + .priv_size = sizeof(UntileContext), + .inputs = untile_inputs, + .outputs = untile_outputs, + .priv_class = &untile_class, +}; diff --git a/libavfilter/vf_v360.c b/libavfilter/vf_v360.c new file mode 100644 index 00000000000..e9457d94000 --- /dev/null +++ b/libavfilter/vf_v360.c @@ -0,0 +1,4322 @@ +/* + * Copyright (c) 2019 Eugene Lyapustin + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 + * 360 video conversion filter. + * Principle of operation: + * + * (for each pixel in output frame) + * 1) Calculate OpenGL-like coordinates (x, y, z) for pixel position (i, j) + * 2) Apply 360 operations (rotation, mirror) to (x, y, z) + * 3) Calculate pixel position (u, v) in input frame + * 4) Calculate interpolation window and weight for each pixel + * + * (for each frame) + * 5) Remap input frame to output frame using precalculated data + */ + +#include + +#include "libavutil/avassert.h" +#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" +#include "libavutil/opt.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" +#include "v360.h" + +typedef struct ThreadData { + AVFrame *in; + AVFrame *out; +} ThreadData; + +#define OFFSET(x) offsetof(V360Context, 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 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" }, + { "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" }, + { "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" }, + { "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=90.f}, 0.00001f, 360.f,TFLAGS, "h_fov"}, + { "v_fov", "output vertical field of view", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl=45.f}, 0.00001f, 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=90.f}, 0.00001f, 360.f,TFLAGS, "ih_fov"}, + { "iv_fov", "input vertical field of view", OFFSET(iv_fov), AV_OPT_TYPE_FLOAT, {.dbl=45.f}, 0.00001f, 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"}, + {"alpha_mask", "build mask in alpha plane", OFFSET(alpha), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "alpha"}, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(v360); + +static int query_formats(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + static const enum AVPixelFormat pix_fmts[] = { + // YUVA444 + AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA444P9, + AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, + AV_PIX_FMT_YUVA444P16, + + // YUVA422 + AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA422P9, + AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, + AV_PIX_FMT_YUVA422P16, + + // YUVA420 + AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA420P9, + AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16, + + // YUVJ + AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, + AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUVJ411P, + + // YUV444 + AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P9, + AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV444P16, + + // YUV440 + AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV440P10, + AV_PIX_FMT_YUV440P12, + + // YUV422 + AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P9, + AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV422P12, + AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV422P16, + + // YUV420 + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P9, + AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV420P12, + AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV420P16, + + // YUV411 + AV_PIX_FMT_YUV411P, + + // YUV410 + AV_PIX_FMT_YUV410P, + + // GBR + AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, + AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12, + AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16, + + // GBRA + AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, + AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, + + // GRAY + 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_NONE + }; + static const enum AVPixelFormat alpha_pix_fmts[] = { + AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA444P9, + AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, + AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA422P9, + AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, + AV_PIX_FMT_YUVA422P16, + AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA420P9, + AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, + AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, + AV_PIX_FMT_NONE + }; + + AVFilterFormats *fmts_list = ff_make_format_list(s->alpha ? alpha_pix_fmts : pix_fmts); + if (!fmts_list) + return AVERROR(ENOMEM); + return ff_set_common_formats(ctx, fmts_list); +} + +#define DEFINE_REMAP1_LINE(bits, div) \ +static void remap1_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *const src, \ + ptrdiff_t in_linesize, \ + const int16_t *const u, const int16_t *const v, \ + const int16_t *const ker) \ +{ \ + const uint##bits##_t *const s = (const uint##bits##_t *const)src; \ + uint##bits##_t *d = (uint##bits##_t *)dst; \ + \ + in_linesize /= div; \ + \ + for (int x = 0; x < width; x++) \ + d[x] = s[v[x] * in_linesize + u[x]]; \ +} + +DEFINE_REMAP1_LINE( 8, 1) +DEFINE_REMAP1_LINE(16, 2) + +/** + * Generate remapping function with a given window size and pixel depth. + * + * @param ws size of interpolation window + * @param bits number of bits per pixel + */ +#define DEFINE_REMAP(ws, bits) \ +static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \ +{ \ + ThreadData *td = arg; \ + const V360Context *s = ctx->priv; \ + const AVFrame *in = td->in; \ + AVFrame *out = td->out; \ + \ + for (int stereo = 0; stereo < 1 + s->out_stereo > STEREO_2D; stereo++) { \ + for (int plane = 0; plane < s->nb_planes; plane++) { \ + const unsigned map = s->map[plane]; \ + const int in_linesize = in->linesize[plane]; \ + const int out_linesize = out->linesize[plane]; \ + const int uv_linesize = s->uv_linesize[plane]; \ + const int in_offset_w = stereo ? s->in_offset_w[plane] : 0; \ + const int in_offset_h = stereo ? s->in_offset_h[plane] : 0; \ + const int out_offset_w = stereo ? s->out_offset_w[plane] : 0; \ + const int out_offset_h = stereo ? s->out_offset_h[plane] : 0; \ + const uint8_t *const src = in->data[plane] + \ + in_offset_h * in_linesize + in_offset_w * (bits >> 3); \ + uint8_t *dst = out->data[plane] + out_offset_h * out_linesize + out_offset_w * (bits >> 3); \ + const uint8_t *mask = plane == 3 ? s->mask : NULL; \ + const int width = s->pr_width[plane]; \ + const int height = s->pr_height[plane]; \ + \ + const int slice_start = (height * jobnr ) / nb_jobs; \ + const int slice_end = (height * (jobnr + 1)) / nb_jobs; \ + \ + for (int y = slice_start; y < slice_end && !mask; y++) { \ + const int16_t *const u = s->u[map] + y * uv_linesize * ws * ws; \ + const int16_t *const v = s->v[map] + y * uv_linesize * ws * ws; \ + const int16_t *const ker = s->ker[map] + y * uv_linesize * ws * ws; \ + \ + s->remap_line(dst + y * out_linesize, width, src, in_linesize, u, v, ker); \ + } \ + \ + for (int y = slice_start; y < slice_end && mask; y++) { \ + memcpy(dst + y * out_linesize, mask + y * width * (bits >> 3), width * (bits >> 3)); \ + } \ + } \ + } \ + \ + return 0; \ +} + +DEFINE_REMAP(1, 8) +DEFINE_REMAP(2, 8) +DEFINE_REMAP(3, 8) +DEFINE_REMAP(4, 8) +DEFINE_REMAP(1, 16) +DEFINE_REMAP(2, 16) +DEFINE_REMAP(3, 16) +DEFINE_REMAP(4, 16) + +#define DEFINE_REMAP_LINE(ws, bits, div) \ +static void remap##ws##_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *const src, \ + ptrdiff_t in_linesize, \ + const int16_t *const u, const int16_t *const v, \ + const int16_t *const ker) \ +{ \ + const uint##bits##_t *const s = (const uint##bits##_t *const)src; \ + uint##bits##_t *d = (uint##bits##_t *)dst; \ + \ + in_linesize /= div; \ + \ + for (int x = 0; x < width; x++) { \ + const int16_t *const uu = u + x * ws * ws; \ + const int16_t *const vv = v + x * ws * ws; \ + const int16_t *const kker = ker + x * ws * ws; \ + int tmp = 0; \ + \ + for (int i = 0; i < ws; i++) { \ + for (int j = 0; j < ws; j++) { \ + tmp += kker[i * ws + j] * s[vv[i * ws + j] * in_linesize + uu[i * ws + j]]; \ + } \ + } \ + \ + d[x] = av_clip_uint##bits(tmp >> 14); \ + } \ +} + +DEFINE_REMAP_LINE(2, 8, 1) +DEFINE_REMAP_LINE(3, 8, 1) +DEFINE_REMAP_LINE(4, 8, 1) +DEFINE_REMAP_LINE(2, 16, 2) +DEFINE_REMAP_LINE(3, 16, 2) +DEFINE_REMAP_LINE(4, 16, 2) + +void ff_v360_init(V360Context *s, int depth) +{ + switch (s->interp) { + case NEAREST: + s->remap_line = depth <= 8 ? remap1_8bit_line_c : remap1_16bit_line_c; + break; + case BILINEAR: + s->remap_line = depth <= 8 ? remap2_8bit_line_c : remap2_16bit_line_c; + break; + case LAGRANGE9: + s->remap_line = depth <= 8 ? remap3_8bit_line_c : remap3_16bit_line_c; + break; + case BICUBIC: + case LANCZOS: + case SPLINE16: + case GAUSSIAN: + s->remap_line = depth <= 8 ? remap4_8bit_line_c : remap4_16bit_line_c; + break; + } + + if (ARCH_X86) + ff_v360_init_x86(s, depth); +} + +/** + * Save nearest pixel coordinates for remapping. + * + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + * @param rmap calculated 4x4 window + * @param u u remap data + * @param v v remap data + * @param ker ker remap data + */ +static void nearest_kernel(float du, float dv, const XYRemap *rmap, + int16_t *u, int16_t *v, int16_t *ker) +{ + const int i = lrintf(dv) + 1; + const int j = lrintf(du) + 1; + + u[0] = rmap->u[i][j]; + v[0] = rmap->v[i][j]; +} + +/** + * Calculate kernel for bilinear interpolation. + * + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + * @param rmap calculated 4x4 window + * @param u u remap data + * @param v v remap data + * @param ker ker remap data + */ +static void bilinear_kernel(float du, float dv, const XYRemap *rmap, + int16_t *u, int16_t *v, int16_t *ker) +{ + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + u[i * 2 + j] = rmap->u[i + 1][j + 1]; + v[i * 2 + j] = rmap->v[i + 1][j + 1]; + } + } + + ker[0] = lrintf((1.f - du) * (1.f - dv) * 16385.f); + ker[1] = lrintf( du * (1.f - dv) * 16385.f); + ker[2] = lrintf((1.f - du) * dv * 16385.f); + ker[3] = lrintf( du * dv * 16385.f); +} + +/** + * Calculate 1-dimensional lagrange coefficients. + * + * @param t relative coordinate + * @param coeffs coefficients + */ +static inline void calculate_lagrange_coeffs(float t, float *coeffs) +{ + coeffs[0] = (t - 1.f) * (t - 2.f) * 0.5f; + coeffs[1] = -t * (t - 2.f); + coeffs[2] = t * (t - 1.f) * 0.5f; +} + +/** + * Calculate kernel for lagrange interpolation. + * + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + * @param rmap calculated 4x4 window + * @param u u remap data + * @param v v remap data + * @param ker ker remap data + */ +static void lagrange_kernel(float du, float dv, const XYRemap *rmap, + int16_t *u, int16_t *v, int16_t *ker) +{ + float du_coeffs[3]; + float dv_coeffs[3]; + + calculate_lagrange_coeffs(du, du_coeffs); + calculate_lagrange_coeffs(dv, dv_coeffs); + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + u[i * 3 + j] = rmap->u[i + 1][j + 1]; + v[i * 3 + j] = rmap->v[i + 1][j + 1]; + ker[i * 3 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f); + } + } +} + +/** + * Calculate 1-dimensional cubic coefficients. + * + * @param t relative coordinate + * @param coeffs coefficients + */ +static inline void calculate_bicubic_coeffs(float t, float *coeffs) +{ + const float tt = t * t; + const float ttt = t * t * t; + + coeffs[0] = - t / 3.f + tt / 2.f - ttt / 6.f; + coeffs[1] = 1.f - t / 2.f - tt + ttt / 2.f; + coeffs[2] = t + tt / 2.f - ttt / 2.f; + coeffs[3] = - t / 6.f + ttt / 6.f; +} + +/** + * Calculate kernel for bicubic interpolation. + * + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + * @param rmap calculated 4x4 window + * @param u u remap data + * @param v v remap data + * @param ker ker remap data + */ +static void bicubic_kernel(float du, float dv, const XYRemap *rmap, + int16_t *u, int16_t *v, int16_t *ker) +{ + float du_coeffs[4]; + float dv_coeffs[4]; + + calculate_bicubic_coeffs(du, du_coeffs); + calculate_bicubic_coeffs(dv, dv_coeffs); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + u[i * 4 + j] = rmap->u[i][j]; + v[i * 4 + j] = rmap->v[i][j]; + ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f); + } + } +} + +/** + * Calculate 1-dimensional lanczos coefficients. + * + * @param t relative coordinate + * @param coeffs coefficients + */ +static inline void calculate_lanczos_coeffs(float t, float *coeffs) +{ + float sum = 0.f; + + for (int i = 0; i < 4; i++) { + const float x = M_PI * (t - i + 1); + if (x == 0.f) { + coeffs[i] = 1.f; + } else { + coeffs[i] = sinf(x) * sinf(x / 2.f) / (x * x / 2.f); + } + sum += coeffs[i]; + } + + for (int i = 0; i < 4; i++) { + coeffs[i] /= sum; + } +} + +/** + * Calculate kernel for lanczos interpolation. + * + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + * @param rmap calculated 4x4 window + * @param u u remap data + * @param v v remap data + * @param ker ker remap data + */ +static void lanczos_kernel(float du, float dv, const XYRemap *rmap, + int16_t *u, int16_t *v, int16_t *ker) +{ + float du_coeffs[4]; + float dv_coeffs[4]; + + calculate_lanczos_coeffs(du, du_coeffs); + calculate_lanczos_coeffs(dv, dv_coeffs); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + u[i * 4 + j] = rmap->u[i][j]; + v[i * 4 + j] = rmap->v[i][j]; + ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f); + } + } +} + +/** + * Calculate 1-dimensional spline16 coefficients. + * + * @param t relative coordinate + * @param coeffs coefficients + */ +static void calculate_spline16_coeffs(float t, float *coeffs) +{ + coeffs[0] = ((-1.f / 3.f * t + 0.8f) * t - 7.f / 15.f) * t; + coeffs[1] = ((t - 9.f / 5.f) * t - 0.2f) * t + 1.f; + coeffs[2] = ((6.f / 5.f - t) * t + 0.8f) * t; + coeffs[3] = ((1.f / 3.f * t - 0.2f) * t - 2.f / 15.f) * t; +} + +/** + * Calculate kernel for spline16 interpolation. + * + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + * @param rmap calculated 4x4 window + * @param u u remap data + * @param v v remap data + * @param ker ker remap data + */ +static void spline16_kernel(float du, float dv, const XYRemap *rmap, + int16_t *u, int16_t *v, int16_t *ker) +{ + float du_coeffs[4]; + float dv_coeffs[4]; + + calculate_spline16_coeffs(du, du_coeffs); + calculate_spline16_coeffs(dv, dv_coeffs); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + u[i * 4 + j] = rmap->u[i][j]; + v[i * 4 + j] = rmap->v[i][j]; + ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f); + } + } +} + +/** + * Calculate 1-dimensional gaussian coefficients. + * + * @param t relative coordinate + * @param coeffs coefficients + */ +static void calculate_gaussian_coeffs(float t, float *coeffs) +{ + float sum = 0.f; + + for (int i = 0; i < 4; i++) { + const float x = t - (i - 1); + if (x == 0.f) { + coeffs[i] = 1.f; + } else { + coeffs[i] = expf(-2.f * x * x) * expf(-x * x / 2.f); + } + sum += coeffs[i]; + } + + for (int i = 0; i < 4; i++) { + coeffs[i] /= sum; + } +} + +/** + * Calculate kernel for gaussian interpolation. + * + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + * @param rmap calculated 4x4 window + * @param u u remap data + * @param v v remap data + * @param ker ker remap data + */ +static void gaussian_kernel(float du, float dv, const XYRemap *rmap, + int16_t *u, int16_t *v, int16_t *ker) +{ + float du_coeffs[4]; + float dv_coeffs[4]; + + calculate_gaussian_coeffs(du, du_coeffs); + calculate_gaussian_coeffs(dv, dv_coeffs); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + u[i * 4 + j] = rmap->u[i][j]; + v[i * 4 + j] = rmap->v[i][j]; + ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f); + } + } +} + +/** + * Modulo operation with only positive remainders. + * + * @param a dividend + * @param b divisor + * + * @return positive remainder of (a / b) + */ +static inline int mod(int a, int b) +{ + const int res = a % b; + if (res < 0) { + return res + b; + } else { + return res; + } +} + +/** + * Reflect y operation. + * + * @param y input vertical position + * @param h input height + */ +static inline int reflecty(int y, int h) +{ + if (y < 0) { + return -y; + } else if (y >= h) { + return 2 * h - 1 - y; + } + + return y; +} + +/** + * Reflect x operation for equirect. + * + * @param x input horizontal position + * @param y input vertical position + * @param w input width + * @param h input height + */ +static inline int ereflectx(int x, int y, int w, int h) +{ + if (y < 0 || y >= h) + x += w / 2; + + return mod(x, w); +} + +/** + * Reflect x operation. + * + * @param x input horizontal position + * @param y input vertical position + * @param w input width + * @param h input height + */ +static inline int reflectx(int x, int y, int w, int h) +{ + if (y < 0 || y >= h) + return w - 1 - x; + + return mod(x, w); +} + +/** + * Convert char to corresponding direction. + * Used for cubemap options. + */ +static int get_direction(char c) +{ + switch (c) { + case 'r': + return RIGHT; + case 'l': + return LEFT; + case 'u': + return UP; + case 'd': + return DOWN; + case 'f': + return FRONT; + case 'b': + return BACK; + default: + return -1; + } +} + +/** + * Convert char to corresponding rotation angle. + * Used for cubemap options. + */ +static int get_rotation(char c) +{ + switch (c) { + case '0': + return ROT_0; + case '1': + return ROT_90; + case '2': + return ROT_180; + case '3': + return ROT_270; + default: + return -1; + } +} + +/** + * Convert char to corresponding rotation order. + */ +static int get_rorder(char c) +{ + switch (c) { + case 'Y': + case 'y': + return YAW; + case 'P': + case 'p': + return PITCH; + case 'R': + case 'r': + return ROLL; + default: + return -1; + } +} + +/** + * Prepare data for processing cubemap input format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_cube_in(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + for (int face = 0; face < NB_FACES; face++) { + const char c = s->in_forder[face]; + int direction; + + if (c == '\0') { + av_log(ctx, AV_LOG_ERROR, + "Incomplete in_forder option. Direction for all 6 faces should be specified.\n"); + return AVERROR(EINVAL); + } + + direction = get_direction(c); + if (direction == -1) { + av_log(ctx, AV_LOG_ERROR, + "Incorrect direction symbol '%c' in in_forder option.\n", c); + return AVERROR(EINVAL); + } + + s->in_cubemap_face_order[direction] = face; + } + + for (int face = 0; face < NB_FACES; face++) { + const char c = s->in_frot[face]; + int rotation; + + if (c == '\0') { + av_log(ctx, AV_LOG_ERROR, + "Incomplete in_frot option. Rotation for all 6 faces should be specified.\n"); + return AVERROR(EINVAL); + } + + rotation = get_rotation(c); + if (rotation == -1) { + av_log(ctx, AV_LOG_ERROR, + "Incorrect rotation symbol '%c' in in_frot option.\n", c); + return AVERROR(EINVAL); + } + + s->in_cubemap_face_rotation[face] = rotation; + } + + return 0; +} + +/** + * Prepare data for processing cubemap output format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_cube_out(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + for (int face = 0; face < NB_FACES; face++) { + const char c = s->out_forder[face]; + int direction; + + if (c == '\0') { + av_log(ctx, AV_LOG_ERROR, + "Incomplete out_forder option. Direction for all 6 faces should be specified.\n"); + return AVERROR(EINVAL); + } + + direction = get_direction(c); + if (direction == -1) { + av_log(ctx, AV_LOG_ERROR, + "Incorrect direction symbol '%c' in out_forder option.\n", c); + return AVERROR(EINVAL); + } + + s->out_cubemap_direction_order[face] = direction; + } + + for (int face = 0; face < NB_FACES; face++) { + const char c = s->out_frot[face]; + int rotation; + + if (c == '\0') { + av_log(ctx, AV_LOG_ERROR, + "Incomplete out_frot option. Rotation for all 6 faces should be specified.\n"); + return AVERROR(EINVAL); + } + + rotation = get_rotation(c); + if (rotation == -1) { + av_log(ctx, AV_LOG_ERROR, + "Incorrect rotation symbol '%c' in out_frot option.\n", c); + return AVERROR(EINVAL); + } + + s->out_cubemap_face_rotation[face] = rotation; + } + + return 0; +} + +static inline void rotate_cube_face(float *uf, float *vf, int rotation) +{ + float tmp; + + switch (rotation) { + case ROT_0: + break; + case ROT_90: + tmp = *uf; + *uf = -*vf; + *vf = tmp; + break; + case ROT_180: + *uf = -*uf; + *vf = -*vf; + break; + case ROT_270: + tmp = -*uf; + *uf = *vf; + *vf = tmp; + break; + default: + av_assert0(0); + } +} + +static inline void rotate_cube_face_inverse(float *uf, float *vf, int rotation) +{ + float tmp; + + switch (rotation) { + case ROT_0: + break; + case ROT_90: + tmp = -*uf; + *uf = *vf; + *vf = tmp; + break; + case ROT_180: + *uf = -*uf; + *vf = -*vf; + break; + case ROT_270: + tmp = *uf; + *uf = -*vf; + *vf = tmp; + break; + default: + av_assert0(0); + } +} + +/** + * Normalize vector. + * + * @param vec vector + */ +static void normalize_vector(float *vec) +{ + const float norm = sqrtf(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]); + + vec[0] /= norm; + vec[1] /= norm; + vec[2] /= norm; +} + +/** + * Calculate 3D coordinates on sphere for corresponding cubemap position. + * Common operation for every cubemap. + * + * @param s filter private context + * @param uf horizontal cubemap coordinate [0, 1) + * @param vf vertical cubemap coordinate [0, 1) + * @param face face of cubemap + * @param vec coordinates on sphere + * @param scalew scale for uf + * @param scaleh scale for vf + */ +static void cube_to_xyz(const V360Context *s, + float uf, float vf, int face, + float *vec, float scalew, float scaleh) +{ + const int direction = s->out_cubemap_direction_order[face]; + float l_x, l_y, l_z; + + uf /= scalew; + vf /= scaleh; + + rotate_cube_face_inverse(&uf, &vf, s->out_cubemap_face_rotation[face]); + + switch (direction) { + case RIGHT: + l_x = 1.f; + l_y = vf; + l_z = -uf; + break; + case LEFT: + l_x = -1.f; + l_y = vf; + l_z = uf; + break; + case UP: + l_x = uf; + l_y = -1.f; + l_z = vf; + break; + case DOWN: + l_x = uf; + l_y = 1.f; + l_z = -vf; + break; + case FRONT: + l_x = uf; + l_y = vf; + l_z = 1.f; + break; + case BACK: + l_x = -uf; + l_y = vf; + l_z = -1.f; + break; + default: + av_assert0(0); + } + + vec[0] = l_x; + vec[1] = l_y; + vec[2] = l_z; + + normalize_vector(vec); +} + +/** + * Calculate cubemap position for corresponding 3D coordinates on sphere. + * Common operation for every cubemap. + * + * @param s filter private context + * @param vec coordinated on sphere + * @param uf horizontal cubemap coordinate [0, 1) + * @param vf vertical cubemap coordinate [0, 1) + * @param direction direction of view + */ +static void xyz_to_cube(const V360Context *s, + const float *vec, + float *uf, float *vf, int *direction) +{ + const float phi = atan2f(vec[0], vec[2]); + const float theta = asinf(vec[1]); + float phi_norm, theta_threshold; + int face; + + if (phi >= -M_PI_4 && phi < M_PI_4) { + *direction = FRONT; + phi_norm = phi; + } else if (phi >= -(M_PI_2 + M_PI_4) && phi < -M_PI_4) { + *direction = LEFT; + phi_norm = phi + M_PI_2; + } else if (phi >= M_PI_4 && phi < M_PI_2 + M_PI_4) { + *direction = RIGHT; + phi_norm = phi - M_PI_2; + } else { + *direction = BACK; + phi_norm = phi + ((phi > 0.f) ? -M_PI : M_PI); + } + + theta_threshold = atanf(cosf(phi_norm)); + if (theta > theta_threshold) { + *direction = DOWN; + } else if (theta < -theta_threshold) { + *direction = UP; + } + + switch (*direction) { + case RIGHT: + *uf = -vec[2] / vec[0]; + *vf = vec[1] / vec[0]; + break; + case LEFT: + *uf = -vec[2] / vec[0]; + *vf = -vec[1] / vec[0]; + break; + case UP: + *uf = -vec[0] / vec[1]; + *vf = -vec[2] / vec[1]; + break; + case DOWN: + *uf = vec[0] / vec[1]; + *vf = -vec[2] / vec[1]; + break; + case FRONT: + *uf = vec[0] / vec[2]; + *vf = vec[1] / vec[2]; + break; + case BACK: + *uf = vec[0] / vec[2]; + *vf = -vec[1] / vec[2]; + break; + default: + av_assert0(0); + } + + face = s->in_cubemap_face_order[*direction]; + rotate_cube_face(uf, vf, s->in_cubemap_face_rotation[face]); + + (*uf) *= s->input_mirror_modifier[0]; + (*vf) *= s->input_mirror_modifier[1]; +} + +/** + * Find position on another cube face in case of overflow/underflow. + * Used for calculation of interpolation window. + * + * @param s filter private context + * @param uf horizontal cubemap coordinate + * @param vf vertical cubemap coordinate + * @param direction direction of view + * @param new_uf new horizontal cubemap coordinate + * @param new_vf new vertical cubemap coordinate + * @param face face position on cubemap + */ +static void process_cube_coordinates(const V360Context *s, + float uf, float vf, int direction, + float *new_uf, float *new_vf, int *face) +{ + /* + * Cubemap orientation + * + * width + * <-------> + * +-------+ + * | | U + * | up | h -------> + * +-------+-------+-------+-------+ ^ e | + * | | | | | | i V | + * | left | front | right | back | | g | + * +-------+-------+-------+-------+ v h v + * | | t + * | down | + * +-------+ + */ + + *face = s->in_cubemap_face_order[direction]; + rotate_cube_face_inverse(&uf, &vf, s->in_cubemap_face_rotation[*face]); + + if ((uf < -1.f || uf >= 1.f) && (vf < -1.f || vf >= 1.f)) { + // There are no pixels to use in this case + *new_uf = uf; + *new_vf = vf; + } else if (uf < -1.f) { + uf += 2.f; + switch (direction) { + case RIGHT: + direction = FRONT; + *new_uf = uf; + *new_vf = vf; + break; + case LEFT: + direction = BACK; + *new_uf = uf; + *new_vf = vf; + break; + case UP: + direction = LEFT; + *new_uf = vf; + *new_vf = -uf; + break; + case DOWN: + direction = LEFT; + *new_uf = -vf; + *new_vf = uf; + break; + case FRONT: + direction = LEFT; + *new_uf = uf; + *new_vf = vf; + break; + case BACK: + direction = RIGHT; + *new_uf = uf; + *new_vf = vf; + break; + default: + av_assert0(0); + } + } else if (uf >= 1.f) { + uf -= 2.f; + switch (direction) { + case RIGHT: + direction = BACK; + *new_uf = uf; + *new_vf = vf; + break; + case LEFT: + direction = FRONT; + *new_uf = uf; + *new_vf = vf; + break; + case UP: + direction = RIGHT; + *new_uf = -vf; + *new_vf = uf; + break; + case DOWN: + direction = RIGHT; + *new_uf = vf; + *new_vf = -uf; + break; + case FRONT: + direction = RIGHT; + *new_uf = uf; + *new_vf = vf; + break; + case BACK: + direction = LEFT; + *new_uf = uf; + *new_vf = vf; + break; + default: + av_assert0(0); + } + } else if (vf < -1.f) { + vf += 2.f; + switch (direction) { + case RIGHT: + direction = UP; + *new_uf = vf; + *new_vf = -uf; + break; + case LEFT: + direction = UP; + *new_uf = -vf; + *new_vf = uf; + break; + case UP: + direction = BACK; + *new_uf = -uf; + *new_vf = -vf; + break; + case DOWN: + direction = FRONT; + *new_uf = uf; + *new_vf = vf; + break; + case FRONT: + direction = UP; + *new_uf = uf; + *new_vf = vf; + break; + case BACK: + direction = UP; + *new_uf = -uf; + *new_vf = -vf; + break; + default: + av_assert0(0); + } + } else if (vf >= 1.f) { + vf -= 2.f; + switch (direction) { + case RIGHT: + direction = DOWN; + *new_uf = -vf; + *new_vf = uf; + break; + case LEFT: + direction = DOWN; + *new_uf = vf; + *new_vf = -uf; + break; + case UP: + direction = FRONT; + *new_uf = uf; + *new_vf = vf; + break; + case DOWN: + direction = BACK; + *new_uf = -uf; + *new_vf = -vf; + break; + case FRONT: + direction = DOWN; + *new_uf = uf; + *new_vf = vf; + break; + case BACK: + direction = DOWN; + *new_uf = -uf; + *new_vf = -vf; + break; + default: + av_assert0(0); + } + } else { + // Inside cube face + *new_uf = uf; + *new_vf = vf; + } + + *face = s->in_cubemap_face_order[direction]; + rotate_cube_face(new_uf, new_vf, s->in_cubemap_face_rotation[*face]); +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in cubemap3x2 format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int cube3x2_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width / 3.f) : 1.f - s->out_pad; + const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 2.f) : 1.f - s->out_pad; + + const float ew = width / 3.f; + const float eh = height / 2.f; + + const int u_face = floorf(i / ew); + const int v_face = floorf(j / eh); + const int face = u_face + 3 * v_face; + + const int u_shift = ceilf(ew * u_face); + const int v_shift = ceilf(eh * v_face); + const int ewi = ceilf(ew * (u_face + 1)) - u_shift; + const int ehi = ceilf(eh * (v_face + 1)) - v_shift; + + const float uf = 2.f * (i - u_shift + 0.5f) / ewi - 1.f; + const float vf = 2.f * (j - v_shift + 0.5f) / ehi - 1.f; + + cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh); + + return 1; +} + +/** + * Calculate frame position in cubemap3x2 format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_cube3x2(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width / 3.f) : 1.f - s->in_pad; + const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 2.f) : 1.f - s->in_pad; + const float ew = width / 3.f; + const float eh = height / 2.f; + float uf, vf; + int ui, vi; + int ewi, ehi; + int direction, face; + int u_face, v_face; + + xyz_to_cube(s, vec, &uf, &vf, &direction); + + uf *= scalew; + vf *= scaleh; + + face = s->in_cubemap_face_order[direction]; + u_face = face % 3; + v_face = face / 3; + ewi = ceilf(ew * (u_face + 1)) - ceilf(ew * u_face); + ehi = ceilf(eh * (v_face + 1)) - ceilf(eh * v_face); + + uf = 0.5f * ewi * (uf + 1.f) - 0.5f; + vf = 0.5f * ehi * (vf + 1.f) - 0.5f; + + ui = floorf(uf); + vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + int new_ui = ui + j - 1; + int new_vi = vi + i - 1; + int u_shift, v_shift; + int new_ewi, new_ehi; + + if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) { + face = s->in_cubemap_face_order[direction]; + + u_face = face % 3; + v_face = face / 3; + u_shift = ceilf(ew * u_face); + v_shift = ceilf(eh * v_face); + } else { + uf = 2.f * new_ui / ewi - 1.f; + vf = 2.f * new_vi / ehi - 1.f; + + uf /= scalew; + vf /= scaleh; + + process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face); + + uf *= scalew; + vf *= scaleh; + + u_face = face % 3; + v_face = face / 3; + u_shift = ceilf(ew * u_face); + v_shift = ceilf(eh * v_face); + new_ewi = ceilf(ew * (u_face + 1)) - u_shift; + new_ehi = ceilf(eh * (v_face + 1)) - v_shift; + + new_ui = av_clip(lrintf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1); + new_vi = av_clip(lrintf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1); + } + + us[i][j] = u_shift + new_ui; + vs[i][j] = v_shift + new_vi; + } + } + + return 1; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in cubemap1x6 format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int cube1x6_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float scalew = s->fout_pad > 0 ? 1.f - (float)(s->fout_pad) / width : 1.f - s->out_pad; + const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 6.f) : 1.f - s->out_pad; + + const float ew = width; + const float eh = height / 6.f; + + const int face = floorf(j / eh); + + const int v_shift = ceilf(eh * face); + const int ehi = ceilf(eh * (face + 1)) - v_shift; + + const float uf = 2.f * (i + 0.5f) / ew - 1.f; + const float vf = 2.f * (j - v_shift + 0.5f) / ehi - 1.f; + + cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh); + + return 1; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in cubemap6x1 format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int cube6x1_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width / 6.f) : 1.f - s->out_pad; + const float scaleh = s->fout_pad > 0 ? 1.f - (float)(s->fout_pad) / height : 1.f - s->out_pad; + + const float ew = width / 6.f; + const float eh = height; + + const int face = floorf(i / ew); + + const int u_shift = ceilf(ew * face); + const int ewi = ceilf(ew * (face + 1)) - u_shift; + + const float uf = 2.f * (i - u_shift + 0.5f) / ewi - 1.f; + const float vf = 2.f * (j + 0.5f) / eh - 1.f; + + cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh); + + return 1; +} + +/** + * Calculate frame position in cubemap1x6 format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_cube1x6(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float scalew = s->fin_pad > 0 ? 1.f - (float)(s->fin_pad) / width : 1.f - s->in_pad; + const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 6.f) : 1.f - s->in_pad; + const float eh = height / 6.f; + const int ewi = width; + float uf, vf; + int ui, vi; + int ehi; + int direction, face; + + xyz_to_cube(s, vec, &uf, &vf, &direction); + + uf *= scalew; + vf *= scaleh; + + face = s->in_cubemap_face_order[direction]; + ehi = ceilf(eh * (face + 1)) - ceilf(eh * face); + + uf = 0.5f * ewi * (uf + 1.f) - 0.5f; + vf = 0.5f * ehi * (vf + 1.f) - 0.5f; + + ui = floorf(uf); + vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + int new_ui = ui + j - 1; + int new_vi = vi + i - 1; + int v_shift; + int new_ehi; + + if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) { + face = s->in_cubemap_face_order[direction]; + + v_shift = ceilf(eh * face); + } else { + uf = 2.f * new_ui / ewi - 1.f; + vf = 2.f * new_vi / ehi - 1.f; + + uf /= scalew; + vf /= scaleh; + + process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face); + + uf *= scalew; + vf *= scaleh; + + v_shift = ceilf(eh * face); + new_ehi = ceilf(eh * (face + 1)) - v_shift; + + new_ui = av_clip(lrintf(0.5f * ewi * (uf + 1.f)), 0, ewi - 1); + new_vi = av_clip(lrintf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1); + } + + us[i][j] = new_ui; + vs[i][j] = v_shift + new_vi; + } + } + + return 1; +} + +/** + * Calculate frame position in cubemap6x1 format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_cube6x1(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width / 6.f) : 1.f - s->in_pad; + const float scaleh = s->fin_pad > 0 ? 1.f - (float)(s->fin_pad) / height : 1.f - s->in_pad; + const float ew = width / 6.f; + const int ehi = height; + float uf, vf; + int ui, vi; + int ewi; + int direction, face; + + xyz_to_cube(s, vec, &uf, &vf, &direction); + + uf *= scalew; + vf *= scaleh; + + face = s->in_cubemap_face_order[direction]; + ewi = ceilf(ew * (face + 1)) - ceilf(ew * face); + + uf = 0.5f * ewi * (uf + 1.f) - 0.5f; + vf = 0.5f * ehi * (vf + 1.f) - 0.5f; + + ui = floorf(uf); + vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + int new_ui = ui + j - 1; + int new_vi = vi + i - 1; + int u_shift; + int new_ewi; + + if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) { + face = s->in_cubemap_face_order[direction]; + + u_shift = ceilf(ew * face); + } else { + uf = 2.f * new_ui / ewi - 1.f; + vf = 2.f * new_vi / ehi - 1.f; + + uf /= scalew; + vf /= scaleh; + + process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face); + + uf *= scalew; + vf *= scaleh; + + u_shift = ceilf(ew * face); + new_ewi = ceilf(ew * (face + 1)) - u_shift; + + new_ui = av_clip(lrintf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1); + new_vi = av_clip(lrintf(0.5f * ehi * (vf + 1.f)), 0, ehi - 1); + } + + us[i][j] = u_shift + new_ui; + vs[i][j] = new_vi; + } + } + + return 1; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in equirectangular format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int equirect_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float phi = ((2.f * i + 0.5f) / width - 1.f) * M_PI; + const float theta = ((2.f * j + 0.5f) / height - 1.f) * M_PI_2; + + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + vec[0] = cos_theta * sin_phi; + vec[1] = sin_theta; + vec[2] = cos_theta * cos_phi; + + return 1; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in half equirectangular format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int hequirect_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float phi = ((2.f * i + 0.5f) / width - 1.f) * M_PI_2; + const float theta = ((2.f * j + 0.5f) / height - 1.f) * M_PI_2; + + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + vec[0] = cos_theta * sin_phi; + vec[1] = sin_theta; + vec[2] = cos_theta * cos_phi; + + return 1; +} + +/** + * Prepare data for processing stereographic output format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_stereographic_out(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->flat_range[0] = tanf(FFMIN(s->h_fov, 359.f) * M_PI / 720.f); + s->flat_range[1] = tanf(FFMIN(s->v_fov, 359.f) * M_PI / 720.f); + + return 0; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in stereographic format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int stereographic_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float x = ((2.f * i + 1.f) / width - 1.f) * s->flat_range[0]; + const float y = ((2.f * j + 1.f) / height - 1.f) * s->flat_range[1]; + const float r = hypotf(x, y); + const float theta = atanf(r) * 2.f; + const float sin_theta = sinf(theta); + + vec[0] = x / r * sin_theta; + vec[1] = y / r * sin_theta; + vec[2] = cosf(theta); + + normalize_vector(vec); + + return 1; +} + +/** + * Prepare data for processing stereographic input format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_stereographic_in(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->iflat_range[0] = tanf(FFMIN(s->ih_fov, 359.f) * M_PI / 720.f); + s->iflat_range[1] = tanf(FFMIN(s->iv_fov, 359.f) * M_PI / 720.f); + + return 0; +} + +/** + * Calculate frame position in stereographic format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_stereographic(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float theta = acosf(vec[2]); + const float r = tanf(theta * 0.5f); + const float c = r / hypotf(vec[0], vec[1]); + const float x = vec[0] * c / s->iflat_range[0] * s->input_mirror_modifier[0]; + const float y = vec[1] * c / s->iflat_range[1] * s->input_mirror_modifier[1]; + + const float uf = (x + 1.f) * width / 2.f; + const float vf = (y + 1.f) * height / 2.f; + + const int ui = floorf(uf); + const int vi = floorf(vf); + + const int visible = isfinite(x) && isfinite(y) && vi >= 0 && vi < height && ui >= 0 && ui < width; + + *du = visible ? uf - ui : 0.f; + *dv = visible ? vf - vi : 0.f; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0; + vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0; + } + } + + return visible; +} + +/** + * Calculate frame position in equirectangular format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_equirect(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float phi = atan2f(vec[0], vec[2]) * s->input_mirror_modifier[0]; + const float theta = asinf(vec[1]) * s->input_mirror_modifier[1]; + + const float uf = (phi / M_PI + 1.f) * width / 2.f; + const float vf = (theta / M_PI_2 + 1.f) * height / 2.f; + + const int ui = floorf(uf); + const int vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = ereflectx(ui + j - 1, vi + i - 1, width, height); + vs[i][j] = reflecty(vi + i - 1, height); + } + } + + return 1; +} + +/** + * Calculate frame position in half equirectangular format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_hequirect(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float phi = atan2f(vec[0], vec[2]) * s->input_mirror_modifier[0]; + const float theta = asinf(vec[1]) * s->input_mirror_modifier[1]; + + const float uf = (phi / M_PI_2 + 1.f) * width / 2.f; + const float vf = (theta / M_PI_2 + 1.f) * height / 2.f; + + const int ui = floorf(uf); + const int vi = floorf(vf); + + const int visible = phi >= -M_PI_2 && phi <= M_PI_2; + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = av_clip(ui + j - 1, 0, width - 1); + vs[i][j] = av_clip(vi + i - 1, 0, height - 1); + } + } + + return visible; +} + +/** + * Prepare data for processing flat input format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_flat_in(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->iflat_range[0] = tanf(0.5f * s->ih_fov * M_PI / 180.f); + s->iflat_range[1] = tanf(0.5f * s->iv_fov * M_PI / 180.f); + + return 0; +} + +/** + * Calculate frame position in flat format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_flat(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float theta = acosf(vec[2]); + const float r = tanf(theta); + const float rr = fabsf(r) < 1e+6f ? r : hypotf(width, height); + const float zf = vec[2]; + const float h = hypotf(vec[0], vec[1]); + const float c = h <= 1e-6f ? 1.f : rr / h; + float uf = vec[0] * c / s->iflat_range[0] * s->input_mirror_modifier[0]; + float vf = vec[1] * c / s->iflat_range[1] * s->input_mirror_modifier[1]; + int visible, ui, vi; + + uf = zf >= 0.f ? (uf + 1.f) * width / 2.f : 0.f; + vf = zf >= 0.f ? (vf + 1.f) * height / 2.f : 0.f; + + ui = floorf(uf); + vi = floorf(vf); + + visible = vi >= 0 && vi < height && ui >= 0 && ui < width && zf >= 0.f; + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0; + vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0; + } + } + + return visible; +} + +/** + * Calculate frame position in mercator format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_mercator(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float phi = atan2f(vec[0], vec[2]) * s->input_mirror_modifier[0]; + const float theta = vec[1] * s->input_mirror_modifier[1]; + + const float uf = (phi / M_PI + 1.f) * width / 2.f; + const float vf = (av_clipf(logf((1.f + theta) / (1.f - theta)) / (2.f * M_PI), -1.f, 1.f) + 1.f) * height / 2.f; + + const int ui = floorf(uf); + const int vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = av_clip(ui + j - 1, 0, width - 1); + vs[i][j] = av_clip(vi + i - 1, 0, height - 1); + } + } + + return 1; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in mercator format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int mercator_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float phi = ((2.f * i + 1.f) / width - 1.f) * M_PI + M_PI_2; + const float y = ((2.f * j + 1.f) / height - 1.f) * M_PI; + const float div = expf(2.f * y) + 1.f; + + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + const float sin_theta = 2.f * expf(y) / div; + const float cos_theta = (expf(2.f * y) - 1.f) / div; + + vec[0] = -sin_theta * cos_phi; + vec[1] = cos_theta; + vec[2] = sin_theta * sin_phi; + + return 1; +} + +/** + * Calculate frame position in ball format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_ball(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float l = hypotf(vec[0], vec[1]); + const float r = sqrtf(1.f - vec[2]) / M_SQRT2; + + const float uf = (1.f + r * vec[0] * s->input_mirror_modifier[0] / (l > 0.f ? l : 1.f)) * width * 0.5f; + const float vf = (1.f + r * vec[1] * s->input_mirror_modifier[1] / (l > 0.f ? l : 1.f)) * height * 0.5f; + + const int ui = floorf(uf); + const int vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = av_clip(ui + j - 1, 0, width - 1); + vs[i][j] = av_clip(vi + i - 1, 0, height - 1); + } + } + + return 1; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in ball format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int ball_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float x = (2.f * i + 1.f) / width - 1.f; + const float y = (2.f * j + 1.f) / height - 1.f; + const float l = hypotf(x, y); + + if (l <= 1.f) { + const float z = 2.f * l * sqrtf(1.f - l * l); + + vec[0] = z * x / (l > 0.f ? l : 1.f); + vec[1] = z * y / (l > 0.f ? l : 1.f); + vec[2] = 1.f - 2.f * l * l; + } else { + vec[0] = 0.f; + vec[1] = 1.f; + vec[2] = 0.f; + return 0; + } + + return 1; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in hammer format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int hammer_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float x = ((2.f * i + 1.f) / width - 1.f); + const float y = ((2.f * j + 1.f) / height - 1.f); + + const float xx = x * x; + const float yy = y * y; + + const float z = sqrtf(1.f - xx * 0.5f - yy * 0.5f); + + const float a = M_SQRT2 * x * z; + const float b = 2.f * z * z - 1.f; + + const float aa = a * a; + const float bb = b * b; + + const float w = sqrtf(1.f - 2.f * yy * z * z); + + vec[0] = w * 2.f * a * b / (aa + bb); + vec[1] = M_SQRT2 * y * z; + vec[2] = w * (bb - aa) / (aa + bb); + + normalize_vector(vec); + + return 1; +} + +/** + * Calculate frame position in hammer format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_hammer(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float theta = atan2f(vec[0], vec[2]) * s->input_mirror_modifier[0]; + + const float z = sqrtf(1.f + sqrtf(1.f - vec[1] * vec[1]) * cosf(theta * 0.5f)); + const float x = sqrtf(1.f - vec[1] * vec[1]) * sinf(theta * 0.5f) / z; + const float y = vec[1] / z * s->input_mirror_modifier[1]; + + const float uf = (x + 1.f) * width / 2.f; + const float vf = (y + 1.f) * height / 2.f; + + const int ui = floorf(uf); + const int vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = av_clip(ui + j - 1, 0, width - 1); + vs[i][j] = av_clip(vi + i - 1, 0, height - 1); + } + } + + return 1; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in sinusoidal format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int sinusoidal_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float theta = ((2.f * j + 1.f) / height - 1.f) * M_PI_2; + const float phi = ((2.f * i + 1.f) / width - 1.f) * M_PI / cosf(theta); + + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + vec[0] = cos_theta * sin_phi; + vec[1] = sin_theta; + vec[2] = cos_theta * cos_phi; + + normalize_vector(vec); + + return 1; +} + +/** + * Calculate frame position in sinusoidal format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_sinusoidal(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float theta = asinf(vec[1]) * s->input_mirror_modifier[1]; + const float phi = atan2f(vec[0], vec[2]) * s->input_mirror_modifier[0] * cosf(theta); + + const float uf = (phi / M_PI + 1.f) * width / 2.f; + const float vf = (theta / M_PI_2 + 1.f) * height / 2.f; + + const int ui = floorf(uf); + const int vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = av_clip(ui + j - 1, 0, width - 1); + vs[i][j] = av_clip(vi + i - 1, 0, height - 1); + } + } + + return 1; +} + +/** + * Prepare data for processing equi-angular cubemap input format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_eac_in(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + if (s->ih_flip && s->iv_flip) { + s->in_cubemap_face_order[RIGHT] = BOTTOM_LEFT; + s->in_cubemap_face_order[LEFT] = BOTTOM_RIGHT; + s->in_cubemap_face_order[UP] = TOP_LEFT; + s->in_cubemap_face_order[DOWN] = TOP_RIGHT; + s->in_cubemap_face_order[FRONT] = BOTTOM_MIDDLE; + s->in_cubemap_face_order[BACK] = TOP_MIDDLE; + } else if (s->ih_flip) { + s->in_cubemap_face_order[RIGHT] = TOP_LEFT; + s->in_cubemap_face_order[LEFT] = TOP_RIGHT; + s->in_cubemap_face_order[UP] = BOTTOM_LEFT; + s->in_cubemap_face_order[DOWN] = BOTTOM_RIGHT; + s->in_cubemap_face_order[FRONT] = TOP_MIDDLE; + s->in_cubemap_face_order[BACK] = BOTTOM_MIDDLE; + } else if (s->iv_flip) { + s->in_cubemap_face_order[RIGHT] = BOTTOM_RIGHT; + s->in_cubemap_face_order[LEFT] = BOTTOM_LEFT; + s->in_cubemap_face_order[UP] = TOP_RIGHT; + s->in_cubemap_face_order[DOWN] = TOP_LEFT; + s->in_cubemap_face_order[FRONT] = BOTTOM_MIDDLE; + s->in_cubemap_face_order[BACK] = TOP_MIDDLE; + } else { + s->in_cubemap_face_order[RIGHT] = TOP_RIGHT; + s->in_cubemap_face_order[LEFT] = TOP_LEFT; + s->in_cubemap_face_order[UP] = BOTTOM_RIGHT; + s->in_cubemap_face_order[DOWN] = BOTTOM_LEFT; + s->in_cubemap_face_order[FRONT] = TOP_MIDDLE; + s->in_cubemap_face_order[BACK] = BOTTOM_MIDDLE; + } + + if (s->iv_flip) { + s->in_cubemap_face_rotation[TOP_LEFT] = ROT_270; + s->in_cubemap_face_rotation[TOP_MIDDLE] = ROT_90; + s->in_cubemap_face_rotation[TOP_RIGHT] = ROT_270; + s->in_cubemap_face_rotation[BOTTOM_LEFT] = ROT_0; + s->in_cubemap_face_rotation[BOTTOM_MIDDLE] = ROT_0; + s->in_cubemap_face_rotation[BOTTOM_RIGHT] = ROT_0; + } else { + s->in_cubemap_face_rotation[TOP_LEFT] = ROT_0; + s->in_cubemap_face_rotation[TOP_MIDDLE] = ROT_0; + s->in_cubemap_face_rotation[TOP_RIGHT] = ROT_0; + s->in_cubemap_face_rotation[BOTTOM_LEFT] = ROT_270; + s->in_cubemap_face_rotation[BOTTOM_MIDDLE] = ROT_90; + s->in_cubemap_face_rotation[BOTTOM_RIGHT] = ROT_270; + } + + return 0; +} + +/** + * Prepare data for processing equi-angular cubemap output format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_eac_out(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->out_cubemap_direction_order[TOP_LEFT] = LEFT; + s->out_cubemap_direction_order[TOP_MIDDLE] = FRONT; + s->out_cubemap_direction_order[TOP_RIGHT] = RIGHT; + s->out_cubemap_direction_order[BOTTOM_LEFT] = DOWN; + s->out_cubemap_direction_order[BOTTOM_MIDDLE] = BACK; + s->out_cubemap_direction_order[BOTTOM_RIGHT] = UP; + + s->out_cubemap_face_rotation[TOP_LEFT] = ROT_0; + s->out_cubemap_face_rotation[TOP_MIDDLE] = ROT_0; + s->out_cubemap_face_rotation[TOP_RIGHT] = ROT_0; + s->out_cubemap_face_rotation[BOTTOM_LEFT] = ROT_270; + s->out_cubemap_face_rotation[BOTTOM_MIDDLE] = ROT_90; + s->out_cubemap_face_rotation[BOTTOM_RIGHT] = ROT_270; + + return 0; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in equi-angular cubemap format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int eac_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float pixel_pad = 2; + const float u_pad = pixel_pad / width; + const float v_pad = pixel_pad / height; + + int u_face, v_face, face; + + float l_x, l_y, l_z; + + float uf = (i + 0.5f) / width; + float vf = (j + 0.5f) / height; + + // EAC has 2-pixel padding on faces except between faces on the same row + // Padding pixels seems not to be stretched with tangent as regular pixels + // Formulas below approximate original padding as close as I could get experimentally + + // Horizontal padding + uf = 3.f * (uf - u_pad) / (1.f - 2.f * u_pad); + if (uf < 0.f) { + u_face = 0; + uf -= 0.5f; + } else if (uf >= 3.f) { + u_face = 2; + uf -= 2.5f; + } else { + u_face = floorf(uf); + uf = fmodf(uf, 1.f) - 0.5f; + } + + // Vertical padding + v_face = floorf(vf * 2.f); + vf = (vf - v_pad - 0.5f * v_face) / (0.5f - 2.f * v_pad) - 0.5f; + + if (uf >= -0.5f && uf < 0.5f) { + uf = tanf(M_PI_2 * uf); + } else { + uf = 2.f * uf; + } + if (vf >= -0.5f && vf < 0.5f) { + vf = tanf(M_PI_2 * vf); + } else { + vf = 2.f * vf; + } + + face = u_face + 3 * v_face; + + switch (face) { + case TOP_LEFT: + l_x = -1.f; + l_y = vf; + l_z = uf; + break; + case TOP_MIDDLE: + l_x = uf; + l_y = vf; + l_z = 1.f; + break; + case TOP_RIGHT: + l_x = 1.f; + l_y = vf; + l_z = -uf; + break; + case BOTTOM_LEFT: + l_x = -vf; + l_y = 1.f; + l_z = -uf; + break; + case BOTTOM_MIDDLE: + l_x = -vf; + l_y = -uf; + l_z = -1.f; + break; + case BOTTOM_RIGHT: + l_x = -vf; + l_y = -1.f; + l_z = uf; + break; + default: + av_assert0(0); + } + + vec[0] = l_x; + vec[1] = l_y; + vec[2] = l_z; + + normalize_vector(vec); + + return 1; +} + +/** + * Calculate frame position in equi-angular cubemap format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_eac(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float pixel_pad = 2; + const float u_pad = pixel_pad / width; + const float v_pad = pixel_pad / height; + + float uf, vf; + int ui, vi; + int direction, face; + int u_face, v_face; + + xyz_to_cube(s, vec, &uf, &vf, &direction); + + face = s->in_cubemap_face_order[direction]; + u_face = face % 3; + v_face = face / 3; + + uf = M_2_PI * atanf(uf) + 0.5f; + vf = M_2_PI * atanf(vf) + 0.5f; + + // These formulas are inversed from eac_to_xyz ones + uf = (uf + u_face) * (1.f - 2.f * u_pad) / 3.f + u_pad; + vf = vf * (0.5f - 2.f * v_pad) + v_pad + 0.5f * v_face; + + uf *= width; + vf *= height; + + uf -= 0.5f; + vf -= 0.5f; + + ui = floorf(uf); + vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = av_clip(ui + j - 1, 0, width - 1); + vs[i][j] = av_clip(vi + i - 1, 0, height - 1); + } + } + + return 1; +} + +/** + * Prepare data for processing flat output format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_flat_out(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->flat_range[0] = tanf(0.5f * s->h_fov * M_PI / 180.f); + s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f); + + return 0; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in flat format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int flat_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float l_x = s->flat_range[0] * ((2.f * i + 0.5f) / width - 1.f); + const float l_y = s->flat_range[1] * ((2.f * j + 0.5f) / height - 1.f); + + vec[0] = l_x; + vec[1] = l_y; + vec[2] = 1.f; + + normalize_vector(vec); + + return 1; +} + +/** + * Prepare data for processing fisheye output format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_fisheye_out(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->flat_range[0] = s->h_fov / 180.f; + s->flat_range[1] = s->v_fov / 180.f; + + return 0; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in fisheye format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int fisheye_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float uf = s->flat_range[0] * ((2.f * i) / width - 1.f); + const float vf = s->flat_range[1] * ((2.f * j + 1.f) / height - 1.f); + + const float phi = atan2f(vf, uf); + const float theta = M_PI_2 * (1.f - hypotf(uf, vf)); + + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + vec[0] = cos_theta * cos_phi; + vec[1] = cos_theta * sin_phi; + vec[2] = sin_theta; + + normalize_vector(vec); + + return 1; +} + +/** + * Prepare data for processing fisheye input format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_fisheye_in(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->iflat_range[0] = s->ih_fov / 180.f; + s->iflat_range[1] = s->iv_fov / 180.f; + + return 0; +} + +/** + * Calculate frame position in fisheye format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_fisheye(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float h = hypotf(vec[0], vec[1]); + const float lh = h > 0.f ? h : 1.f; + const float phi = atan2f(h, vec[2]) / M_PI; + + float uf = vec[0] / lh * phi * s->input_mirror_modifier[0] / s->iflat_range[0]; + float vf = vec[1] / lh * phi * s->input_mirror_modifier[1] / s->iflat_range[1]; + + const int visible = hypotf(uf, vf) <= 0.5f; + int ui, vi; + + uf = (uf + 0.5f) * width; + vf = (vf + 0.5f) * height; + + ui = floorf(uf); + vi = floorf(vf); + + *du = visible ? uf - ui : 0.f; + *dv = visible ? vf - vi : 0.f; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0; + vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0; + } + } + + return visible; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in pannini format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int pannini_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float uf = ((2.f * i + 1.f) / width - 1.f); + const float vf = ((2.f * j + 1.f) / height - 1.f); + + const float d = s->h_fov; + const float k = uf * uf / ((d + 1.f) * (d + 1.f)); + const float dscr = k * k * d * d - (k + 1.f) * (k * d * d - 1.f); + const float clon = (-k * d + sqrtf(dscr)) / (k + 1.f); + const float S = (d + 1.f) / (d + clon); + const float lon = atan2f(uf, S * clon); + const float lat = atan2f(vf, S); + + vec[0] = sinf(lon) * cosf(lat); + vec[1] = sinf(lat); + vec[2] = cosf(lon) * cosf(lat); + + normalize_vector(vec); + + return 1; +} + +/** + * Calculate frame position in pannini format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_pannini(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float phi = atan2f(vec[0], vec[2]) * s->input_mirror_modifier[0]; + const float theta = asinf(vec[1]) * s->input_mirror_modifier[1]; + + const float d = s->ih_fov; + const float S = (d + 1.f) / (d + cosf(phi)); + + const float x = S * sinf(phi); + const float y = S * tanf(theta); + + const float uf = (x + 1.f) * width / 2.f; + const float vf = (y + 1.f) * height / 2.f; + + const int ui = floorf(uf); + const int vi = floorf(vf); + + const int visible = vi >= 0 && vi < height && ui >= 0 && ui < width && vec[2] >= 0.f; + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0; + vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0; + } + } + + return visible; +} + +/** + * Prepare data for processing cylindrical output format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_cylindrical_out(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->flat_range[0] = M_PI * s->h_fov / 360.f; + s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f); + + return 0; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in cylindrical format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int cylindrical_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float uf = s->flat_range[0] * ((2.f * i + 1.f) / width - 1.f); + const float vf = s->flat_range[1] * ((2.f * j + 1.f) / height - 1.f); + + const float phi = uf; + const float theta = atanf(vf); + + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + vec[0] = cos_theta * sin_phi; + vec[1] = sin_theta; + vec[2] = cos_theta * cos_phi; + + normalize_vector(vec); + + return 1; +} + +/** + * Prepare data for processing cylindrical input format. + * + * @param ctx filter context + * + * @return error code + */ +static int prepare_cylindrical_in(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + s->iflat_range[0] = M_PI * s->ih_fov / 360.f; + s->iflat_range[1] = tanf(0.5f * s->iv_fov * M_PI / 180.f); + + return 0; +} + +/** + * Calculate frame position in cylindrical format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_cylindrical(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float phi = atan2f(vec[0], vec[2]) * s->input_mirror_modifier[0] / s->iflat_range[0]; + const float theta = asinf(vec[1]) * s->input_mirror_modifier[1]; + + const float uf = (phi + 1.f) * (width - 1) / 2.f; + const float vf = (tanf(theta) / s->iflat_range[1] + 1.f) * height / 2.f; + + const int ui = floorf(uf); + const int vi = floorf(vf); + + const int visible = vi >= 0 && vi < height && ui >= 0 && ui < width && + theta <= M_PI * s->iv_fov / 180.f && + theta >= -M_PI * s->iv_fov / 180.f; + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0; + vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0; + } + } + + return visible; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in perspective format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int perspective_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float uf = ((2.f * i + 1.f) / width - 1.f); + const float vf = ((2.f * j + 1.f) / height - 1.f); + const float rh = hypotf(uf, vf); + const float sinzz = 1.f - rh * rh; + const float h = 1.f + s->v_fov; + const float sinz = (h - sqrtf(sinzz)) / (h / rh + rh / h); + const float sinz2 = sinz * sinz; + + if (sinz2 <= 1.f) { + const float cosz = sqrtf(1.f - sinz2); + + const float theta = asinf(cosz); + const float phi = atan2f(uf, vf); + + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + vec[0] = cos_theta * sin_phi; + vec[1] = sin_theta; + vec[2] = cos_theta * cos_phi; + } else { + vec[0] = 0.f; + vec[1] = 1.f; + vec[2] = 0.f; + return 0; + } + + normalize_vector(vec); + return 1; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in tetrahedron format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int tetrahedron_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float uf = (float)i / width; + const float vf = (float)j / height; + + vec[0] = uf < 0.5f ? uf * 4.f - 1.f : 3.f - uf * 4.f; + vec[1] = 1.f - vf * 2.f; + vec[2] = 2.f * fabsf(1.f - fabsf(1.f - uf * 2.f + vf)) - 1.f; + + normalize_vector(vec); + + return 1; +} + +/** + * Calculate frame position in tetrahedron format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_tetrahedron(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float d0 = vec[0] * 1.f + vec[1] * 1.f + vec[2] *-1.f; + const float d1 = vec[0] *-1.f + vec[1] *-1.f + vec[2] *-1.f; + const float d2 = vec[0] * 1.f + vec[1] *-1.f + vec[2] * 1.f; + const float d3 = vec[0] *-1.f + vec[1] * 1.f + vec[2] * 1.f; + const float d = FFMAX(d0, FFMAX3(d1, d2, d3)); + + float uf, vf, x, y, z; + int ui, vi; + + x = vec[0] / d; + y = vec[1] / d; + z = -vec[2] / d; + + vf = 0.5f - y * 0.5f * s->input_mirror_modifier[1]; + + if ((x + y >= 0.f && y + z >= 0.f && -z - x <= 0.f) || + (x + y <= 0.f && -y + z >= 0.f && z - x >= 0.f)) { + uf = 0.25f * x * s->input_mirror_modifier[0] + 0.25f; + } else { + uf = 0.75f - 0.25f * x * s->input_mirror_modifier[0]; + } + + uf *= width; + vf *= height; + + ui = floorf(uf); + vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = reflectx(ui + j - 1, vi + i - 1, width, height); + vs[i][j] = reflecty(vi + i - 1, height); + } + } + + return 1; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int dfisheye_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float ew = width / 2.f; + const float eh = height; + + const int ei = i >= ew ? i - ew : i; + const float m = i >= ew ? 1.f : -1.f; + + const float uf = s->flat_range[0] * ((2.f * ei) / ew - 1.f); + const float vf = s->flat_range[1] * ((2.f * j + 1.f) / eh - 1.f); + + const float h = hypotf(uf, vf); + const float lh = h > 0.f ? h : 1.f; + const float theta = m * M_PI_2 * (1.f - h); + + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + vec[0] = cos_theta * m * uf / lh; + vec[1] = cos_theta * vf / lh; + vec[2] = sin_theta; + + normalize_vector(vec); + + return 1; +} + +/** + * Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_dfisheye(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float ew = width / 2.f; + const float eh = height; + + const float h = hypotf(vec[0], vec[1]); + const float lh = h > 0.f ? h : 1.f; + const float theta = acosf(fabsf(vec[2])) / M_PI; + + float uf = (theta * (vec[0] / lh) * s->input_mirror_modifier[0] / s->iflat_range[0] + 0.5f) * ew; + float vf = (theta * (vec[1] / lh) * s->input_mirror_modifier[1] / s->iflat_range[1] + 0.5f) * eh; + + int ui, vi; + int u_shift; + + if (vec[2] >= 0.f) { + u_shift = ceilf(ew); + } else { + u_shift = 0; + uf = ew - uf; + } + + ui = floorf(uf); + vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = av_clip(u_shift + ui + j - 1, 0, width - 1); + vs[i][j] = av_clip( vi + i - 1, 0, height - 1); + } + } + + return 1; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in barrel facebook's format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int barrel_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float scale = 0.99f; + float l_x, l_y, l_z; + + if (i < 4 * width / 5) { + const float theta_range = M_PI_4; + + const int ew = 4 * width / 5; + const int eh = height; + + const float phi = ((2.f * i) / ew - 1.f) * M_PI / scale; + const float theta = ((2.f * j) / eh - 1.f) * theta_range / scale; + + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + l_x = cos_theta * sin_phi; + l_y = sin_theta; + l_z = cos_theta * cos_phi; + } else { + const int ew = width / 5; + const int eh = height / 2; + + float uf, vf; + + if (j < eh) { // UP + uf = 2.f * (i - 4 * ew) / ew - 1.f; + vf = 2.f * (j ) / eh - 1.f; + + uf /= scale; + vf /= scale; + + l_x = uf; + l_y = -1.f; + l_z = vf; + } else { // DOWN + uf = 2.f * (i - 4 * ew) / ew - 1.f; + vf = 2.f * (j - eh) / eh - 1.f; + + uf /= scale; + vf /= scale; + + l_x = uf; + l_y = 1.f; + l_z = -vf; + } + } + + vec[0] = l_x; + vec[1] = l_y; + vec[2] = l_z; + + normalize_vector(vec); + + return 1; +} + +/** + * Calculate frame position in barrel facebook's format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_barrel(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float scale = 0.99f; + + const float phi = atan2f(vec[0], vec[2]) * s->input_mirror_modifier[0]; + const float theta = asinf(vec[1]) * s->input_mirror_modifier[1]; + const float theta_range = M_PI_4; + + int ew, eh; + int u_shift, v_shift; + float uf, vf; + int ui, vi; + + if (theta > -theta_range && theta < theta_range) { + ew = 4 * width / 5; + eh = height; + + u_shift = s->ih_flip ? width / 5 : 0; + v_shift = 0; + + uf = (phi / M_PI * scale + 1.f) * ew / 2.f; + vf = (theta / theta_range * scale + 1.f) * eh / 2.f; + } else { + ew = width / 5; + eh = height / 2; + + u_shift = s->ih_flip ? 0 : 4 * ew; + + if (theta < 0.f) { // UP + uf = -vec[0] / vec[1]; + vf = -vec[2] / vec[1]; + v_shift = 0; + } else { // DOWN + uf = vec[0] / vec[1]; + vf = -vec[2] / vec[1]; + v_shift = eh; + } + + uf *= s->input_mirror_modifier[0] * s->input_mirror_modifier[1]; + vf *= s->input_mirror_modifier[1]; + + uf = 0.5f * ew * (uf * scale + 1.f); + vf = 0.5f * eh * (vf * scale + 1.f); + } + + ui = floorf(uf); + vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = u_shift + av_clip(ui + j - 1, 0, ew - 1); + vs[i][j] = v_shift + av_clip(vi + i - 1, 0, eh - 1); + } + } + + return 1; +} + +/** + * Calculate frame position in barrel split facebook's format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_barrelsplit(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + const float phi = atan2f(vec[0], vec[2]) * s->input_mirror_modifier[0]; + const float theta = asinf(vec[1]) * s->input_mirror_modifier[1]; + + const float theta_range = M_PI_4; + + int ew, eh; + int u_shift, v_shift; + float uf, vf; + int ui, vi; + + if (theta >= -theta_range && theta <= theta_range) { + const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width * 2.f / 3.f) : 1.f - s->in_pad; + const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 2.f) : 1.f - s->in_pad; + + ew = width / 3 * 2; + eh = height / 2; + + u_shift = s->ih_flip ? width / 3 : 0; + v_shift = phi >= M_PI_2 || phi < -M_PI_2 ? eh : 0; + + uf = fmodf(phi, M_PI_2) / M_PI_2; + vf = theta / M_PI_4; + + if (v_shift) + uf = uf >= 0.f ? fmodf(uf - 1.f, 1.f) : fmodf(uf + 1.f, 1.f); + + uf = (uf * scalew + 1.f) * width / 3.f; + vf = (vf * scaleh + 1.f) * height / 4.f; + } else { + const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width / 3.f) : 1.f - s->in_pad; + const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 4.f) : 1.f - s->in_pad; + int v_offset = 0; + + ew = width / 3; + eh = height / 4; + + u_shift = s->ih_flip ? 0 : 2 * ew; + + if (theta <= 0.f && theta >= -M_PI_2 && + phi <= M_PI_2 && phi >= -M_PI_2) { + uf = -vec[0] / vec[1]; + vf = -vec[2] / vec[1]; + v_shift = 0; + v_offset = -eh; + } else if (theta >= 0.f && theta <= M_PI_2 && + phi <= M_PI_2 && phi >= -M_PI_2) { + uf = vec[0] / vec[1]; + vf = -vec[2] / vec[1]; + v_shift = height * 0.25f; + } else if (theta <= 0.f && theta >= -M_PI_2) { + uf = vec[0] / vec[1]; + vf = vec[2] / vec[1]; + v_shift = height * 0.5f; + v_offset = -eh; + } else { + uf = -vec[0] / vec[1]; + vf = vec[2] / vec[1]; + v_shift = height * 0.75f; + } + + uf *= s->input_mirror_modifier[0] * s->input_mirror_modifier[1]; + vf *= s->input_mirror_modifier[1]; + + uf = 0.5f * width / 3.f * (uf * scalew + 1.f); + vf = height * 0.25f * (vf * scaleh + 1.f) + v_offset; + } + + ui = floorf(uf); + vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = u_shift + av_clip(ui + j - 1, 0, ew - 1); + vs[i][j] = v_shift + av_clip(vi + i - 1, 0, eh - 1); + } + } + + return 1; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in barrel split facebook's format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int barrelsplit_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float x = (i + 0.5f) / width; + const float y = (j + 0.5f) / height; + float l_x, l_y, l_z; + + if (x < 2.f / 3.f) { + const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width * 2.f / 3.f) : 1.f - s->out_pad; + const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 2.f) : 1.f - s->out_pad; + + const float back = floorf(y * 2.f); + + const float phi = ((3.f / 2.f * x - 0.5f) / scalew - back) * M_PI; + const float theta = (y - 0.25f - 0.5f * back) / scaleh * M_PI; + + const float sin_phi = sinf(phi); + const float cos_phi = cosf(phi); + const float sin_theta = sinf(theta); + const float cos_theta = cosf(theta); + + l_x = cos_theta * sin_phi; + l_y = sin_theta; + l_z = cos_theta * cos_phi; + } else { + const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width / 3.f) : 1.f - s->out_pad; + const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 4.f) : 1.f - s->out_pad; + + const int face = floorf(y * 4.f); + float uf, vf; + + uf = x * 3.f - 2.f; + + switch (face) { + case 0: + vf = y * 2.f; + uf = 1.f - uf; + vf = 0.5f - vf; + + l_x = (0.5f - uf) / scalew; + l_y = -0.5f; + l_z = (0.5f - vf) / scaleh; + break; + case 1: + vf = y * 2.f; + uf = 1.f - uf; + vf = 1.f - (vf - 0.5f); + + l_x = (0.5f - uf) / scalew; + l_y = 0.5f; + l_z = (-0.5f + vf) / scaleh; + break; + case 2: + vf = y * 2.f - 0.5f; + vf = 1.f - (1.f - vf); + + l_x = (0.5f - uf) / scalew; + l_y = -0.5f; + l_z = (0.5f - vf) / scaleh; + break; + case 3: + vf = y * 2.f - 1.5f; + + l_x = (0.5f - uf) / scalew; + l_y = 0.5f; + l_z = (-0.5f + vf) / scaleh; + break; + } + } + + vec[0] = l_x; + vec[1] = l_y; + vec[2] = l_z; + + normalize_vector(vec); + + return 1; +} + +/** + * Calculate 3D coordinates on sphere for corresponding frame position in tspyramid format. + * + * @param s filter private context + * @param i horizontal position on frame [0, width) + * @param j vertical position on frame [0, height) + * @param width frame width + * @param height frame height + * @param vec coordinates on sphere + */ +static int tspyramid_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float x = (i + 0.5f) / width; + const float y = (j + 0.5f) / height; + + if (x < 0.5f) { + vec[0] = x * 4.f - 1.f; + vec[1] = (y * 2.f - 1.f); + vec[2] = 1.f; + } else if (x >= 0.6875f && x < 0.8125f && + y >= 0.375f && y < 0.625f) { + vec[0] = -(x - 0.6875f) * 16.f + 1.f; + vec[1] = (y - 0.375f) * 8.f - 1.f; + vec[2] = -1.f; + } else if (0.5f <= x && x < 0.6875f && + ((0.f <= y && y < 0.375f && y >= 2.f * (x - 0.5f)) || + (0.375f <= y && y < 0.625f) || + (0.625f <= y && y < 1.f && y <= 2.f * (1.f - x)))) { + vec[0] = 1.f; + vec[1] = 2.f * (y - 2.f * x + 1.f) / (3.f - 4.f * x) - 1.f; + vec[2] = -2.f * (x - 0.5f) / 0.1875f + 1.f; + } else if (0.8125f <= x && x < 1.f && + ((0.f <= y && y < 0.375f && x >= (1.f - y / 2.f)) || + (0.375f <= y && y < 0.625f) || + (0.625f <= y && y < 1.f && y <= (2.f * x - 1.f)))) { + vec[0] = -1.f; + vec[1] = 2.f * (y + 2.f * x - 2.f) / (4.f * x - 3.f) - 1.f; + vec[2] = 2.f * (x - 0.8125f) / 0.1875f - 1.f; + } else if (0.f <= y && y < 0.375f && + ((0.5f <= x && x < 0.8125f && y < 2.f * (x - 0.5f)) || + (0.6875f <= x && x < 0.8125f) || + (0.8125f <= x && x < 1.f && x < (1.f - y / 2.f)))) { + vec[0] = 2.f * (1.f - x - 0.5f * y) / (0.5f - y) - 1.f; + vec[1] = -1.f; + vec[2] = 2.f * (0.375f - y) / 0.375f - 1.f; + } else { + vec[0] = 2.f * (0.5f - x + 0.5f * y) / (y - 0.5f) - 1.f; + vec[1] = 1.f; + vec[2] = -2.f * (1.f - y) / 0.375f + 1.f; + } + + normalize_vector(vec); + + return 1; +} + +/** + * Calculate frame position in tspyramid format for corresponding 3D coordinates on sphere. + * + * @param s filter private context + * @param vec coordinates on sphere + * @param width frame width + * @param height frame height + * @param us horizontal coordinates for interpolation window + * @param vs vertical coordinates for interpolation window + * @param du horizontal relative coordinate + * @param dv vertical relative coordinate + */ +static int xyz_to_tspyramid(const V360Context *s, + const float *vec, int width, int height, + int16_t us[4][4], int16_t vs[4][4], float *du, float *dv) +{ + float uf, vf; + int ui, vi; + int face; + + xyz_to_cube(s, vec, &uf, &vf, &face); + + uf = (uf + 1.f) * 0.5f; + vf = (vf + 1.f) * 0.5f; + + switch (face) { + case UP: + uf = 0.1875f * vf - 0.375f * uf * vf - 0.125f * uf + 0.8125f; + vf = 0.375f - 0.375f * vf; + break; + case FRONT: + uf = 0.5f * uf; + break; + case DOWN: + uf = 1.f - 0.1875f * vf - 0.5f * uf + 0.375f * uf * vf; + vf = 1.f - 0.375f * vf; + break; + case LEFT: + vf = 0.25f * vf + 0.75f * uf * vf - 0.375f * uf + 0.375f; + uf = 0.1875f * uf + 0.8125f; + break; + case RIGHT: + vf = 0.375f * uf - 0.75f * uf * vf + vf; + uf = 0.1875f * uf + 0.5f; + break; + case BACK: + uf = 0.125f * uf + 0.6875f; + vf = 0.25f * vf + 0.375f; + break; + } + + uf *= width; + vf *= height; + + ui = floorf(uf); + vi = floorf(vf); + + *du = uf - ui; + *dv = vf - vi; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + us[i][j] = reflectx(ui + j - 1, vi + i - 1, width, height); + vs[i][j] = reflecty(vi + i - 1, height); + } + } + + return 1; +} + +static void multiply_matrix(float c[3][3], const float a[3][3], const float b[3][3]) +{ + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + float sum = 0.f; + + for (int k = 0; k < 3; k++) + sum += a[i][k] * b[k][j]; + + c[i][j] = sum; + } + } +} + +/** + * Calculate rotation matrix for yaw/pitch/roll angles. + */ +static inline void calculate_rotation_matrix(float yaw, float pitch, float roll, + float rot_mat[3][3], + const int rotation_order[3]) +{ + const float yaw_rad = yaw * M_PI / 180.f; + const float pitch_rad = pitch * M_PI / 180.f; + const float roll_rad = roll * M_PI / 180.f; + + const float sin_yaw = sinf(yaw_rad); + const float cos_yaw = cosf(yaw_rad); + const float sin_pitch = sinf(pitch_rad); + const float cos_pitch = cosf(pitch_rad); + const float sin_roll = sinf(roll_rad); + const float cos_roll = cosf(roll_rad); + + float m[3][3][3]; + float temp[3][3]; + + m[0][0][0] = cos_yaw; m[0][0][1] = 0; m[0][0][2] = sin_yaw; + m[0][1][0] = 0; m[0][1][1] = 1; m[0][1][2] = 0; + m[0][2][0] = -sin_yaw; m[0][2][1] = 0; m[0][2][2] = cos_yaw; + + m[1][0][0] = 1; m[1][0][1] = 0; m[1][0][2] = 0; + m[1][1][0] = 0; m[1][1][1] = cos_pitch; m[1][1][2] = -sin_pitch; + m[1][2][0] = 0; m[1][2][1] = sin_pitch; m[1][2][2] = cos_pitch; + + m[2][0][0] = cos_roll; m[2][0][1] = -sin_roll; m[2][0][2] = 0; + m[2][1][0] = sin_roll; m[2][1][1] = cos_roll; m[2][1][2] = 0; + m[2][2][0] = 0; m[2][2][1] = 0; m[2][2][2] = 1; + + multiply_matrix(temp, m[rotation_order[0]], m[rotation_order[1]]); + multiply_matrix(rot_mat, temp, m[rotation_order[2]]); +} + +/** + * Rotate vector with given rotation matrix. + * + * @param rot_mat rotation matrix + * @param vec vector + */ +static inline void rotate(const float rot_mat[3][3], + float *vec) +{ + const float x_tmp = vec[0] * rot_mat[0][0] + vec[1] * rot_mat[0][1] + vec[2] * rot_mat[0][2]; + const float y_tmp = vec[0] * rot_mat[1][0] + vec[1] * rot_mat[1][1] + vec[2] * rot_mat[1][2]; + const float z_tmp = vec[0] * rot_mat[2][0] + vec[1] * rot_mat[2][1] + vec[2] * rot_mat[2][2]; + + vec[0] = x_tmp; + vec[1] = y_tmp; + vec[2] = z_tmp; +} + +static inline void set_mirror_modifier(int h_flip, int v_flip, int d_flip, + float *modifier) +{ + modifier[0] = h_flip ? -1.f : 1.f; + modifier[1] = v_flip ? -1.f : 1.f; + modifier[2] = d_flip ? -1.f : 1.f; +} + +static inline void mirror(const float *modifier, float *vec) +{ + vec[0] *= modifier[0]; + vec[1] *= modifier[1]; + vec[2] *= modifier[2]; +} + +static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int sizeof_mask, int p) +{ + if (!s->u[p]) + s->u[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv); + if (!s->v[p]) + s->v[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv); + if (!s->u[p] || !s->v[p]) + return AVERROR(ENOMEM); + if (sizeof_ker) { + if (!s->ker[p]) + s->ker[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_ker); + if (!s->ker[p]) + return AVERROR(ENOMEM); + } + + if (sizeof_mask && !p) { + if (!s->mask) + s->mask = av_calloc(s->pr_width[p] * s->pr_height[p], sizeof_mask); + if (!s->mask) + return AVERROR(ENOMEM); + } + + return 0; +} + +static void fov_from_dfov(int format, float d_fov, float w, float h, float *h_fov, float *v_fov) +{ + switch (format) { + case STEREOGRAPHIC: + { + const float d = 0.5f * hypotf(w, h); + const float l = d / (tanf(d_fov * M_PI / 720.f)); + + *h_fov = 2.f * atan2f(w * 0.5f, l) * 360.f / M_PI; + *v_fov = 2.f * atan2f(h * 0.5f, l) * 360.f / M_PI; + } + break; + case DUAL_FISHEYE: + { + const float d = 0.5f * hypotf(w * 0.5f, h); + + *h_fov = d / w * 2.f * d_fov; + *v_fov = d / h * d_fov; + } + break; + case FISHEYE: + { + const float d = 0.5f * hypotf(w, h); + + *h_fov = d / w * d_fov; + *v_fov = d / h * d_fov; + } + break; + case FLAT: + default: + { + const float da = tanf(0.5f * FFMIN(d_fov, 359.f) * M_PI / 180.f); + const float d = hypotf(w, h); + + *h_fov = atan2f(da * w, d) * 360.f / M_PI; + *v_fov = atan2f(da * h, d) * 360.f / M_PI; + + if (*h_fov < 0.f) + *h_fov += 360.f; + if (*v_fov < 0.f) + *v_fov += 360.f; + } + break; + } +} + +static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc) +{ + outw[1] = outw[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w); + outw[0] = outw[3] = w; + outh[1] = outh[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h); + outh[0] = outh[3] = h; +} + +// Calculate remap data +static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + V360Context *s = ctx->priv; + + for (int p = 0; p < s->nb_allocated; p++) { + const int max_value = s->max_value; + const int width = s->pr_width[p]; + const int uv_linesize = s->uv_linesize[p]; + const int height = s->pr_height[p]; + const int in_width = s->inplanewidth[p]; + const int in_height = s->inplaneheight[p]; + const int slice_start = (height * jobnr ) / nb_jobs; + const int slice_end = (height * (jobnr + 1)) / nb_jobs; + float du, dv; + float vec[3]; + XYRemap rmap; + + for (int j = slice_start; j < slice_end; j++) { + for (int i = 0; i < width; i++) { + int16_t *u = s->u[p] + (j * uv_linesize + i) * s->elements; + int16_t *v = s->v[p] + (j * uv_linesize + i) * s->elements; + int16_t *ker = s->ker[p] + (j * uv_linesize + i) * s->elements; + uint8_t *mask8 = p ? NULL : s->mask + (j * s->pr_width[0] + i); + uint16_t *mask16 = p ? NULL : (uint16_t *)s->mask + (j * s->pr_width[0] + i); + int in_mask, out_mask; + + if (s->out_transpose) + out_mask = s->out_transform(s, j, i, height, width, vec); + else + out_mask = s->out_transform(s, i, j, width, height, vec); + av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2])); + rotate(s->rot_mat, vec); + av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2])); + normalize_vector(vec); + mirror(s->output_mirror_modifier, vec); + if (s->in_transpose) + in_mask = s->in_transform(s, vec, in_height, in_width, rmap.v, rmap.u, &du, &dv); + else + in_mask = s->in_transform(s, vec, in_width, in_height, rmap.u, rmap.v, &du, &dv); + av_assert1(!isnan(du) && !isnan(dv)); + s->calculate_kernel(du, dv, &rmap, u, v, ker); + + if (!p && s->mask) { + if (s->mask_size == 1) { + mask8[0] = 255 * (out_mask & in_mask); + } else { + mask16[0] = max_value * (out_mask & in_mask); + } + } + } + } + } + + return 0; +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AVFilterLink *inlink = ctx->inputs[0]; + V360Context *s = ctx->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + const int depth = desc->comp[0].depth; + const int sizeof_mask = s->mask_size = (depth + 7) >> 3; + int sizeof_uv; + int sizeof_ker; + int err; + int h, w; + int in_offset_h, in_offset_w; + int out_offset_h, out_offset_w; + float hf, wf; + int (*prepare_out)(AVFilterContext *ctx); + int have_alpha; + + s->max_value = (1 << depth) - 1; + s->input_mirror_modifier[0] = s->ih_flip ? -1.f : 1.f; + s->input_mirror_modifier[1] = s->iv_flip ? -1.f : 1.f; + + switch (s->interp) { + case NEAREST: + s->calculate_kernel = nearest_kernel; + s->remap_slice = depth <= 8 ? remap1_8bit_slice : remap1_16bit_slice; + s->elements = 1; + sizeof_uv = sizeof(int16_t) * s->elements; + sizeof_ker = 0; + break; + case BILINEAR: + s->calculate_kernel = bilinear_kernel; + s->remap_slice = depth <= 8 ? remap2_8bit_slice : remap2_16bit_slice; + s->elements = 2 * 2; + sizeof_uv = sizeof(int16_t) * s->elements; + sizeof_ker = sizeof(int16_t) * s->elements; + break; + case LAGRANGE9: + s->calculate_kernel = lagrange_kernel; + s->remap_slice = depth <= 8 ? remap3_8bit_slice : remap3_16bit_slice; + s->elements = 3 * 3; + sizeof_uv = sizeof(int16_t) * s->elements; + sizeof_ker = sizeof(int16_t) * s->elements; + break; + case BICUBIC: + s->calculate_kernel = bicubic_kernel; + s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice; + s->elements = 4 * 4; + sizeof_uv = sizeof(int16_t) * s->elements; + sizeof_ker = sizeof(int16_t) * s->elements; + break; + case LANCZOS: + s->calculate_kernel = lanczos_kernel; + s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice; + s->elements = 4 * 4; + sizeof_uv = sizeof(int16_t) * s->elements; + sizeof_ker = sizeof(int16_t) * s->elements; + break; + case SPLINE16: + s->calculate_kernel = spline16_kernel; + s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice; + s->elements = 4 * 4; + sizeof_uv = sizeof(int16_t) * s->elements; + sizeof_ker = sizeof(int16_t) * s->elements; + break; + case GAUSSIAN: + s->calculate_kernel = gaussian_kernel; + s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice; + s->elements = 4 * 4; + sizeof_uv = sizeof(int16_t) * s->elements; + sizeof_ker = sizeof(int16_t) * s->elements; + break; + default: + av_assert0(0); + } + + ff_v360_init(s, depth); + + for (int order = 0; order < NB_RORDERS; order++) { + const char c = s->rorder[order]; + int rorder; + + if (c == '\0') { + av_log(ctx, AV_LOG_WARNING, + "Incomplete rorder option. Direction for all 3 rotation orders should be specified. Switching to default rorder.\n"); + s->rotation_order[0] = YAW; + s->rotation_order[1] = PITCH; + s->rotation_order[2] = ROLL; + break; + } + + rorder = get_rorder(c); + if (rorder == -1) { + av_log(ctx, AV_LOG_WARNING, + "Incorrect rotation order symbol '%c' in rorder option. Switching to default rorder.\n", c); + s->rotation_order[0] = YAW; + s->rotation_order[1] = PITCH; + s->rotation_order[2] = ROLL; + break; + } + + s->rotation_order[order] = rorder; + } + + switch (s->in_stereo) { + case STEREO_2D: + w = inlink->w; + h = inlink->h; + in_offset_w = in_offset_h = 0; + break; + case STEREO_SBS: + w = inlink->w / 2; + h = inlink->h; + in_offset_w = w; + in_offset_h = 0; + break; + case STEREO_TB: + w = inlink->w; + h = inlink->h / 2; + in_offset_w = 0; + in_offset_h = h; + break; + default: + av_assert0(0); + } + + set_dimensions(s->inplanewidth, s->inplaneheight, w, h, desc); + set_dimensions(s->in_offset_w, s->in_offset_h, in_offset_w, in_offset_h, desc); + + s->in_width = s->inplanewidth[0]; + s->in_height = s->inplaneheight[0]; + + if (s->id_fov > 0.f) + fov_from_dfov(s->in, s->id_fov, w, h, &s->ih_fov, &s->iv_fov); + + if (s->in_transpose) + FFSWAP(int, s->in_width, s->in_height); + + switch (s->in) { + case EQUIRECTANGULAR: + s->in_transform = xyz_to_equirect; + err = 0; + wf = w; + hf = h; + break; + case CUBEMAP_3_2: + s->in_transform = xyz_to_cube3x2; + err = prepare_cube_in(ctx); + wf = w / 3.f * 4.f; + hf = h; + break; + case CUBEMAP_1_6: + s->in_transform = xyz_to_cube1x6; + err = prepare_cube_in(ctx); + wf = w * 4.f; + hf = h / 3.f; + break; + case CUBEMAP_6_1: + s->in_transform = xyz_to_cube6x1; + err = prepare_cube_in(ctx); + wf = w / 3.f * 2.f; + hf = h * 2.f; + break; + case EQUIANGULAR: + s->in_transform = xyz_to_eac; + err = prepare_eac_in(ctx); + wf = w; + hf = h / 9.f * 8.f; + break; + case FLAT: + s->in_transform = xyz_to_flat; + err = prepare_flat_in(ctx); + wf = w; + hf = h; + break; + case PERSPECTIVE: + av_log(ctx, AV_LOG_ERROR, "Supplied format is not accepted as input.\n"); + return AVERROR(EINVAL); + case DUAL_FISHEYE: + s->in_transform = xyz_to_dfisheye; + err = prepare_fisheye_in(ctx); + wf = w; + hf = h; + break; + case BARREL: + s->in_transform = xyz_to_barrel; + err = 0; + wf = w / 5.f * 4.f; + hf = h; + break; + case STEREOGRAPHIC: + s->in_transform = xyz_to_stereographic; + err = prepare_stereographic_in(ctx); + wf = w; + hf = h / 2.f; + break; + case MERCATOR: + s->in_transform = xyz_to_mercator; + err = 0; + wf = w; + hf = h / 2.f; + break; + case BALL: + s->in_transform = xyz_to_ball; + err = 0; + wf = w; + hf = h / 2.f; + break; + case HAMMER: + s->in_transform = xyz_to_hammer; + err = 0; + wf = w; + hf = h; + break; + case SINUSOIDAL: + s->in_transform = xyz_to_sinusoidal; + err = 0; + wf = w; + hf = h; + break; + case FISHEYE: + s->in_transform = xyz_to_fisheye; + err = prepare_fisheye_in(ctx); + wf = w * 2; + hf = h; + break; + case PANNINI: + s->in_transform = xyz_to_pannini; + err = 0; + wf = w; + hf = h; + break; + case CYLINDRICAL: + s->in_transform = xyz_to_cylindrical; + err = prepare_cylindrical_in(ctx); + wf = w; + hf = h * 2.f; + break; + case TETRAHEDRON: + s->in_transform = xyz_to_tetrahedron; + err = 0; + wf = w; + hf = h; + break; + case BARREL_SPLIT: + s->in_transform = xyz_to_barrelsplit; + err = 0; + wf = w * 4.f / 3.f; + hf = h; + break; + case TSPYRAMID: + s->in_transform = xyz_to_tspyramid; + err = 0; + wf = w; + hf = h; + break; + case HEQUIRECTANGULAR: + s->in_transform = xyz_to_hequirect; + err = 0; + wf = w * 2.f; + hf = h; + break; + default: + av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n"); + return AVERROR_BUG; + } + + if (err != 0) { + return err; + } + + switch (s->out) { + case EQUIRECTANGULAR: + s->out_transform = equirect_to_xyz; + prepare_out = NULL; + w = lrintf(wf); + h = lrintf(hf); + break; + case CUBEMAP_3_2: + s->out_transform = cube3x2_to_xyz; + prepare_out = prepare_cube_out; + w = lrintf(wf / 4.f * 3.f); + h = lrintf(hf); + break; + case CUBEMAP_1_6: + s->out_transform = cube1x6_to_xyz; + prepare_out = prepare_cube_out; + w = lrintf(wf / 4.f); + h = lrintf(hf * 3.f); + break; + case CUBEMAP_6_1: + s->out_transform = cube6x1_to_xyz; + prepare_out = prepare_cube_out; + w = lrintf(wf / 2.f * 3.f); + h = lrintf(hf / 2.f); + break; + case EQUIANGULAR: + s->out_transform = eac_to_xyz; + prepare_out = prepare_eac_out; + w = lrintf(wf); + h = lrintf(hf / 8.f * 9.f); + break; + case FLAT: + s->out_transform = flat_to_xyz; + prepare_out = prepare_flat_out; + w = lrintf(wf); + h = lrintf(hf); + break; + case DUAL_FISHEYE: + s->out_transform = dfisheye_to_xyz; + prepare_out = prepare_fisheye_out; + w = lrintf(wf); + h = lrintf(hf); + break; + case BARREL: + s->out_transform = barrel_to_xyz; + prepare_out = NULL; + w = lrintf(wf / 4.f * 5.f); + h = lrintf(hf); + break; + case STEREOGRAPHIC: + s->out_transform = stereographic_to_xyz; + prepare_out = prepare_stereographic_out; + w = lrintf(wf); + h = lrintf(hf * 2.f); + break; + case MERCATOR: + s->out_transform = mercator_to_xyz; + prepare_out = NULL; + w = lrintf(wf); + h = lrintf(hf * 2.f); + break; + case BALL: + s->out_transform = ball_to_xyz; + prepare_out = NULL; + w = lrintf(wf); + h = lrintf(hf * 2.f); + break; + case HAMMER: + s->out_transform = hammer_to_xyz; + prepare_out = NULL; + w = lrintf(wf); + h = lrintf(hf); + break; + case SINUSOIDAL: + s->out_transform = sinusoidal_to_xyz; + prepare_out = NULL; + w = lrintf(wf); + h = lrintf(hf); + break; + case FISHEYE: + s->out_transform = fisheye_to_xyz; + prepare_out = prepare_fisheye_out; + w = lrintf(wf * 0.5f); + h = lrintf(hf); + break; + case PANNINI: + s->out_transform = pannini_to_xyz; + prepare_out = NULL; + w = lrintf(wf); + h = lrintf(hf); + break; + case CYLINDRICAL: + s->out_transform = cylindrical_to_xyz; + prepare_out = prepare_cylindrical_out; + w = lrintf(wf); + h = lrintf(hf * 0.5f); + break; + case PERSPECTIVE: + s->out_transform = perspective_to_xyz; + prepare_out = NULL; + w = lrintf(wf / 2.f); + h = lrintf(hf); + break; + case TETRAHEDRON: + s->out_transform = tetrahedron_to_xyz; + prepare_out = NULL; + w = lrintf(wf); + h = lrintf(hf); + break; + case BARREL_SPLIT: + s->out_transform = barrelsplit_to_xyz; + prepare_out = NULL; + w = lrintf(wf / 4.f * 3.f); + h = lrintf(hf); + break; + case TSPYRAMID: + s->out_transform = tspyramid_to_xyz; + prepare_out = NULL; + w = lrintf(wf); + h = lrintf(hf); + break; + case HEQUIRECTANGULAR: + s->out_transform = hequirect_to_xyz; + prepare_out = NULL; + w = lrintf(wf / 2.f); + h = lrintf(hf); + break; + default: + av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n"); + return AVERROR_BUG; + } + + // Override resolution with user values if specified + if (s->width > 0 && s->height <= 0 && s->h_fov > 0.f && s->v_fov > 0.f && + s->out == FLAT && s->d_fov == 0.f) { + w = s->width; + h = w / tanf(s->h_fov * M_PI / 360.f) * tanf(s->v_fov * M_PI / 360.f); + } else if (s->width <= 0 && s->height > 0 && s->h_fov > 0.f && s->v_fov > 0.f && + s->out == FLAT && s->d_fov == 0.f) { + h = s->height; + w = h / tanf(s->v_fov * M_PI / 360.f) * tanf(s->h_fov * M_PI / 360.f); + } else if (s->width > 0 && s->height > 0) { + w = s->width; + h = s->height; + } else if (s->width > 0 || s->height > 0) { + av_log(ctx, AV_LOG_ERROR, "Both width and height values should be specified.\n"); + return AVERROR(EINVAL); + } else { + if (s->out_transpose) + FFSWAP(int, w, h); + + if (s->in_transpose) + FFSWAP(int, w, h); + } + + s->width = w; + s->height = h; + + if (s->d_fov > 0.f) + fov_from_dfov(s->out, s->d_fov, w, h, &s->h_fov, &s->v_fov); + + if (prepare_out) { + err = prepare_out(ctx); + if (err != 0) + return err; + } + + set_dimensions(s->pr_width, s->pr_height, w, h, desc); + + switch (s->out_stereo) { + case STEREO_2D: + out_offset_w = out_offset_h = 0; + break; + case STEREO_SBS: + out_offset_w = w; + out_offset_h = 0; + w *= 2; + break; + case STEREO_TB: + out_offset_w = 0; + out_offset_h = h; + h *= 2; + break; + default: + av_assert0(0); + } + + set_dimensions(s->out_offset_w, s->out_offset_h, out_offset_w, out_offset_h, desc); + set_dimensions(s->planewidth, s->planeheight, w, h, desc); + + for (int i = 0; i < 4; i++) + s->uv_linesize[i] = FFALIGN(s->pr_width[i], 8); + + outlink->h = h; + outlink->w = w; + + s->nb_planes = av_pix_fmt_count_planes(inlink->format); + have_alpha = !!(desc->flags & AV_PIX_FMT_FLAG_ALPHA); + + if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) { + s->nb_allocated = 1; + s->map[0] = s->map[1] = s->map[2] = s->map[3] = 0; + } else { + s->nb_allocated = 2; + s->map[0] = s->map[3] = 0; + s->map[1] = s->map[2] = 1; + } + + for (int i = 0; i < s->nb_allocated; i++) + allocate_plane(s, sizeof_uv, sizeof_ker, sizeof_mask * have_alpha * s->alpha, i); + + calculate_rotation_matrix(s->yaw, s->pitch, s->roll, s->rot_mat, s->rotation_order); + set_mirror_modifier(s->h_flip, s->v_flip, s->d_flip, s->output_mirror_modifier); + + ctx->internal->execute(ctx, v360_slice, NULL, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + V360Context *s = ctx->priv; + AVFrame *out; + ThreadData td; + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + + td.in = in; + td.out = out; + + ctx->internal->execute(ctx, s->remap_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); + + av_frame_free(&in); + return ff_filter_frame(outlink, out); +} + +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + int ret; + + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; + + return config_output(ctx->outputs[0]); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + V360Context *s = ctx->priv; + + for (int p = 0; p < s->nb_allocated; p++) { + av_freep(&s->u[p]); + av_freep(&s->v[p]); + av_freep(&s->ker[p]); + } + av_freep(&s->mask); +} + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, + { NULL } +}; + +AVFilter ff_vf_v360 = { + .name = "v360", + .description = NULL_IF_CONFIG_SMALL("Convert 360 projection of video."), + .priv_size = sizeof(V360Context), + .uninit = uninit, + .query_formats = query_formats, + .inputs = inputs, + .outputs = outputs, + .priv_class = &v360_class, + .flags = AVFILTER_FLAG_SLICE_THREADS, + .process_command = process_command, +}; diff --git a/libavfilter/vf_vaguedenoiser.c b/libavfilter/vf_vaguedenoiser.c index b323ce54796..49b338ff91f 100644 --- a/libavfilter/vf_vaguedenoiser.c +++ b/libavfilter/vf_vaguedenoiser.c @@ -38,6 +38,7 @@ typedef struct VagueDenoiserContext { float threshold; float percent; int method; + int type; int nsteps; int planes; @@ -60,7 +61,7 @@ typedef struct VagueDenoiserContext { void (*thresholding)(float *block, const int width, const int height, const int stride, const float threshold, - const float percent, const int nsteps); + const float percent); } VagueDenoiserContext; #define OFFSET(x) offsetof(VagueDenoiserContext, x) @@ -74,6 +75,9 @@ static const AVOption vaguedenoiser_options[] = { { "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" }, { NULL } }; @@ -104,8 +108,8 @@ static const float synthesis_high[9] = { static int query_formats(AVFilterContext *ctx) { static const enum AVPixelFormat pix_fmts[] = { - AV_PIX_FMT_GRAY8, - AV_PIX_FMT_GRAY16, + 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_YUV410P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, @@ -121,6 +125,11 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, + AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA422P16, + AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_NONE }; AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); @@ -328,7 +337,7 @@ static void invert_step(const float *input, float *output, float *temp, const in static void hard_thresholding(float *block, const int width, const int height, const int stride, const float threshold, - const float percent, const int unused) + const float percent) { const float frac = 1.f - percent * 0.01f; int y, x; @@ -343,22 +352,14 @@ static void hard_thresholding(float *block, const int width, const int height, } static void soft_thresholding(float *block, const int width, const int height, const int stride, - const float threshold, const float percent, const int nsteps) + const float threshold, const float percent) { const float frac = 1.f - percent * 0.01f; const float shift = threshold * 0.01f * percent; - int w = width; - int h = height; - int y, x, l; - - for (l = 0; l < nsteps; l++) { - w = (w + 1) >> 1; - h = (h + 1) >> 1; - } + int y, x; for (y = 0; y < height; y++) { - const int x0 = (y < h) ? w : 0; - for (x = x0; x < width; x++) { + for (x = 0; x < width; x++) { const float temp = FFABS(block[x]); if (temp <= threshold) block[x] *= frac; @@ -371,7 +372,7 @@ static void soft_thresholding(float *block, const int width, const int height, c static void qian_thresholding(float *block, const int width, const int height, const int stride, const float threshold, - const float percent, const int unused) + const float percent) { const float percent01 = percent * 0.01f; const float tr2 = threshold * threshold * percent01; @@ -392,6 +393,23 @@ static void qian_thresholding(float *block, const int width, const int height, } } +static float bayes_threshold(float *block, const int width, const int height, + const int stride, const float threshold) +{ + float mean = 0.f; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + mean += block[x] * block[x]; + } + block += stride; + } + + mean /= width * height; + + return threshold * threshold / (FFMAX(sqrtf(mean - threshold), FLT_EPSILON)); +} + static void filter(VagueDenoiserContext *s, AVFrame *in, AVFrame *out) { int p, y, x, i, j; @@ -455,7 +473,28 @@ static void filter(VagueDenoiserContext *s, AVFrame *in, AVFrame *out) v_low_size0 = (v_low_size0 + 1) >> 1; } - s->thresholding(s->block, width, height, width, s->threshold, s->percent, s->nsteps); + if (s->type == 0) { + s->thresholding(s->block, width, height, width, s->threshold, s->percent); + } else { + for (int n = 0; n < s->nsteps; n++) { + float threshold; + float *block; + + if (n == s->nsteps - 1) { + threshold = bayes_threshold(s->block, s->hlowsize[p][n], s->vlowsize[p][n], width, s->threshold); + s->thresholding(s->block, s->hlowsize[p][n], s->vlowsize[p][n], width, threshold, s->percent); + } + block = s->block + s->hlowsize[p][n]; + threshold = bayes_threshold(block, s->hhighsize[p][n], s->vlowsize[p][n], width, s->threshold); + s->thresholding(block, s->hhighsize[p][n], s->vlowsize[p][n], width, threshold, s->percent); + block = s->block + s->vlowsize[p][n] * width; + threshold = bayes_threshold(block, s->hlowsize[p][n], s->vhighsize[p][n], width, s->threshold); + s->thresholding(block, s->hlowsize[p][n], s->vhighsize[p][n], width, threshold, s->percent); + block = s->block + s->hlowsize[p][n] + s->vlowsize[p][n] * width; + threshold = bayes_threshold(block, s->hhighsize[p][n], s->vhighsize[p][n], width, s->threshold); + s->thresholding(block, s->hhighsize[p][n], s->vhighsize[p][n], width, threshold, s->percent); + } + } while (nsteps_invert--) { const int idx = s->vlowsize[p][nsteps_invert] + s->vhighsize[p][nsteps_invert]; diff --git a/libavfilter/vf_vectorscope.c b/libavfilter/vf_vectorscope.c index e3e00797d0c..38af8780424 100644 --- a/libavfilter/vf_vectorscope.c +++ b/libavfilter/vf_vectorscope.c @@ -29,8 +29,16 @@ #include "internal.h" #include "video.h" +enum GraticuleType { + GRAT_NONE, + GRAT_GREEN, + GRAT_COLOR, + GRAT_INVERT, + NB_GRATICULES +}; + enum VectorscopeMode { - GRAY, + TINT, COLOR, COLOR2, COLOR3, @@ -45,6 +53,7 @@ typedef struct VectorscopeContext { int intensity; float fintensity; uint16_t bg_color[4]; + float ftint[2]; int planewidth[4]; int planeheight[4]; int hsub, vsub; @@ -59,6 +68,7 @@ typedef struct VectorscopeContext { float bgopacity; float lthreshold; float hthreshold; + int tint[2]; int tmin; int tmax; int flags; @@ -79,7 +89,8 @@ typedef struct VectorscopeContext { 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=GRAY}, 0, 0, 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" }, @@ -95,11 +106,12 @@ static const AVOption vectorscope_options[] = { { "instant", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "envelope" }, { "peak", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "envelope" }, { "peak+instant", 0, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "envelope" }, - { "graticule", "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "graticule"}, - { "g", "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "graticule"}, - { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "graticule" }, - { "green", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "graticule" }, - { "color", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "graticule" }, + { "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" }, { "opacity", "set graticule opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGS}, { "o", "set graticule opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGS}, { "flags", "set graticule flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=4}, 0, 7, FLAGS, "flags"}, @@ -118,6 +130,10 @@ static const AVOption vectorscope_options[] = { { "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" }, + { "tint0", "set 1st tint", OFFSET(ftint[0]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS}, + { "t0", "set 1st tint", OFFSET(ftint[0]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS}, + { "tint1", "set 2nd tint", OFFSET(ftint[1]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS}, + { "t1", "set 2nd tint", OFFSET(ftint[1]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS}, { NULL } }; @@ -139,7 +155,7 @@ static const enum AVPixelFormat out_yuv10_pix_fmts[] = { }; static const enum AVPixelFormat out_yuv12_pix_fmts[] = { - AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_NONE }; @@ -167,7 +183,7 @@ static const enum AVPixelFormat in1_pix_fmts[] = { AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, - AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, @@ -188,6 +204,7 @@ static const enum AVPixelFormat in2_pix_fmts[] = { 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_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12, + AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_NONE }; @@ -417,6 +434,8 @@ static void vectorscope16(VectorscopeContext *s, AVFrame *in, AVFrame *out, int uint16_t *dpx = dst[px]; uint16_t *dpy = dst[py]; uint16_t *dpd = dst[pd]; + uint16_t *dp1 = dst[1]; + uint16_t *dp2 = dst[2]; const int max = s->size - 1; const int mid = s->size / 2; const int tmin = s->tmin; @@ -433,42 +452,21 @@ static void vectorscope16(VectorscopeContext *s, AVFrame *in, AVFrame *out, int switch (s->mode) { case COLOR: case COLOR5: - case GRAY: - if (s->is_yuv) { - for (i = 0; i < h; i++) { - const int iwx = i * slinesizex; - const int iwy = i * slinesizey; - const int iwd = i * slinesized; - for (j = 0; j < w; j++) { - const int x = FFMIN(spx[iwx + j], max); - const int y = FFMIN(spy[iwy + j], max); - const int z = spd[iwd + j]; - const int pos = y * dlinesize + x; - - if (z < tmin || z > tmax) - continue; - - dpd[pos] = FFMIN(dpd[pos] + intensity, max); - } - } - } else { - for (i = 0; i < h; i++) { - const int iwx = i * slinesizex; - const int iwy = i * slinesizey; - const int iwd = i * slinesized; - for (j = 0; j < w; j++) { - const int x = FFMIN(spx[iwx + j], max); - const int y = FFMIN(spy[iwy + j], max); - const int z = spd[iwd + j]; - const int pos = y * dlinesize + x; + case TINT: + for (i = 0; i < h; i++) { + const int iwx = i * slinesizex; + const int iwy = i * slinesizey; + const int iwd = i * slinesized; + for (j = 0; j < w; j++) { + const int x = FFMIN(spx[iwx + j], max); + const int y = FFMIN(spy[iwy + j], max); + const int z = spd[iwd + j]; + const int pos = y * dlinesize + x; - if (z < tmin || z > tmax) - continue; + if (z < tmin || z > tmax) + continue; - dst[0][pos] = FFMIN(dst[0][pos] + intensity, max); - dst[1][pos] = FFMIN(dst[1][pos] + intensity, max); - dst[2][pos] = FFMIN(dst[2][pos] + intensity, max); - } + dpd[pos] = FFMIN(dpd[pos] + intensity, max); } } break; @@ -572,7 +570,28 @@ static void vectorscope16(VectorscopeContext *s, AVFrame *in, AVFrame *out, int } } - if (s->mode == COLOR) { + if (s->mode == TINT && s->is_yuv && + (s->tint[0] != mid || s->tint[1] != mid)) { + for (i = 0; i < out->height; i++) { + for (j = 0; j < out->width; j++) { + const int pos = i * dlinesize + j; + if (dpd[pos]) { + dp1[pos] = s->tint[0]; + dp2[pos] = s->tint[1]; + } + } + } + } else if (s->mode == TINT && !s->is_yuv) { + for (i = 0; i < out->height; i++) { + for (j = 0; j < out->width; j++) { + const int pos = i * dlinesize + j; + if (dpd[pos]) { + dpx[pos] = av_clip(dpd[pos] + dpd[pos] * s->ftint[0], 0, max); + dpy[pos] = av_clip(dpd[pos] + dpd[pos] * s->ftint[1], 0, max); + } + } + } + } else if (s->mode == COLOR) { for (i = 0; i < out->height; i++) { for (j = 0; j < out->width; j++) { if (!dpd[i * dlinesize + j]) { @@ -615,6 +634,8 @@ static void vectorscope8(VectorscopeContext *s, AVFrame *in, AVFrame *out, int p uint8_t *dpx = dst[px]; uint8_t *dpy = dst[py]; uint8_t *dpd = dst[pd]; + uint8_t *dp1 = dst[1]; + uint8_t *dp2 = dst[2]; const int tmin = s->tmin; const int tmax = s->tmax; int i, j, k; @@ -627,42 +648,21 @@ static void vectorscope8(VectorscopeContext *s, AVFrame *in, AVFrame *out, int p switch (s->mode) { case COLOR5: case COLOR: - case GRAY: - if (s->is_yuv) { - for (i = 0; i < h; i++) { - const int iwx = i * slinesizex; - const int iwy = i * slinesizey; - const int iwd = i * slinesized; - for (j = 0; j < w; j++) { - const int x = spx[iwx + j]; - const int y = spy[iwy + j]; - const int z = spd[iwd + j]; - const int pos = y * dlinesize + x; - - if (z < tmin || z > tmax) - continue; - - dpd[pos] = FFMIN(dpd[pos] + intensity, 255); - } - } - } else { - for (i = 0; i < h; i++) { - const int iwx = i * slinesizex; - const int iwy = i * slinesizey; - const int iwd = i * slinesized; - for (j = 0; j < w; j++) { - const int x = spx[iwx + j]; - const int y = spy[iwy + j]; - const int z = spd[iwd + j]; - const int pos = y * dlinesize + x; + case TINT: + for (i = 0; i < h; i++) { + const int iwx = i * slinesizex; + const int iwy = i * slinesizey; + const int iwd = i * slinesized; + for (j = 0; j < w; j++) { + const int x = spx[iwx + j]; + const int y = spy[iwy + j]; + const int z = spd[iwd + j]; + const int pos = y * dlinesize + x; - if (z < tmin || z > tmax) - continue; + if (z < tmin || z > tmax) + continue; - dst[0][pos] = FFMIN(dst[0][pos] + intensity, 255); - dst[1][pos] = FFMIN(dst[1][pos] + intensity, 255); - dst[2][pos] = FFMIN(dst[2][pos] + intensity, 255); - } + dpd[pos] = FFMIN(dpd[pos] + intensity, 255); } } break; @@ -766,7 +766,28 @@ static void vectorscope8(VectorscopeContext *s, AVFrame *in, AVFrame *out, int p } } - if (s->mode == COLOR) { + if (s->mode == TINT && s->is_yuv && + (s->tint[0] != 128 || s->tint[1] != 128)) { + for (i = 0; i < out->height; i++) { + for (j = 0; j < out->width; j++) { + const int pos = i * dlinesize + j; + if (dpd[pos]) { + dp1[pos] = s->tint[0]; + dp2[pos] = s->tint[1]; + } + } + } + } else if (s->mode == TINT && !s->is_yuv) { + for (i = 0; i < out->height; i++) { + for (j = 0; j < out->width; j++) { + const int pos = i * dlinesize + j; + if (dpd[pos]) { + dpx[pos] = av_clip_uint8(dpd[pos] + dpd[pos] * s->ftint[0]); + dpy[pos] = av_clip_uint8(dpd[pos] + dpd[pos] * s->ftint[1]); + } + } + } + } else if (s->mode == COLOR) { for (i = 0; i < out->height; i++) { for (j = 0; j < out->width; j++) { if (!dpd[i * out->linesize[pd] + j]) { @@ -870,6 +891,28 @@ static void draw_dots(uint8_t *dst, int L, int v, float o) dst[-l + 2] = dst[-l + 2] * f + V; } +static void draw_idots(uint8_t *dst, int L, float o) +{ + const float f = 1. - o; + int l = L * 2; + + dst[ l - 3] = dst[ l - 3] * f + (255 - dst[ l - 3]) * o; + dst[ l + 3] = dst[ l + 3] * f + (255 - dst[ l + 3]) * o; + dst[-l - 3] = dst[-l - 3] * f + (255 - dst[-l - 3]) * o; + dst[-l + 3] = dst[-l + 3] * f + (255 - dst[-l + 3]) * o; + + l += L; + + dst[ l - 3] = dst[ l - 3] * f + (255 - dst[ l - 3]) * o; + dst[ l + 3] = dst[ l + 3] * f + (255 - dst[ l + 3]) * o; + dst[ l - 2] = dst[ l - 2] * f + (255 - dst[ l - 2]) * o; + dst[ l + 2] = dst[ l + 2] * f + (255 - dst[ l + 2]) * o; + dst[-l - 3] = dst[-l - 3] * f + (255 - dst[-l - 3]) * o; + dst[-l + 3] = dst[-l + 3] * f + (255 - dst[-l + 3]) * o; + dst[-l - 2] = dst[-l - 2] * f + (255 - dst[-l - 2]) * o; + dst[-l + 2] = dst[-l + 2] * f + (255 - dst[-l + 2]) * o; +} + static void draw_dots16(uint16_t *dst, int L, int v, float o) { const float f = 1. - o; @@ -893,10 +936,83 @@ static void draw_dots16(uint16_t *dst, int L, int v, float o) dst[-l + 2] = dst[-l + 2] * f + V; } +static void draw_idots16(uint16_t *dst, int L, int v, float o) +{ + const float f = 1. - o; + int l = L * 2; + + dst[ l - 3] = dst[ l - 3] * f + (v - dst[ l - 3]) * o; + dst[ l + 3] = dst[ l + 3] * f + (v - dst[ l + 3]) * o; + dst[-l - 3] = dst[-l - 3] * f + (v - dst[-l - 3]) * o; + dst[-l + 3] = dst[-l + 3] * f + (v - dst[-l + 3]) * o; + + l += L; + + dst[ l - 3] = dst[ l - 3] * f + (v - dst[ l - 3]) * o; + dst[ l + 3] = dst[ l + 3] * f + (v - dst[ l + 3]) * o; + dst[ l - 2] = dst[ l - 2] * f + (v - dst[ l - 2]) * o; + dst[ l + 2] = dst[ l + 2] * f + (v - dst[ l + 2]) * o; + dst[-l - 3] = dst[-l - 3] * f + (v - dst[-l - 3]) * o; + dst[-l + 3] = dst[-l + 3] * f + (v - dst[-l + 3]) * o; + dst[-l - 2] = dst[-l - 2] * f + (v - dst[-l - 2]) * o; + dst[-l + 2] = dst[-l + 2] * f + (v - dst[-l + 2]) * o; +} + static void none_graticule(VectorscopeContext *s, AVFrame *out, int X, int Y, int D, int P) { } +static void draw_ihtext(AVFrame *out, int x, int y, float o1, float o2, const char *txt, const uint8_t color[4]) +{ + const uint8_t *font; + int font_height; + int i, plane; + + font = avpriv_cga_font, font_height = 8; + + for (plane = 0; plane < 4 && out->data[plane]; plane++) { + for (i = 0; txt[i]; i++) { + int char_y, mask; + + uint8_t *p = out->data[plane] + y * out->linesize[plane] + (x + i * 8); + for (char_y = font_height - 1; char_y >= 0; char_y--) { + for (mask = 0x80; mask; mask >>= 1) { + if (font[txt[i] * font_height + char_y] & mask) + p[0] = p[0] * o2 + (255 - p[0]) * o1; + p++; + } + p += out->linesize[plane] - 8; + } + } + } +} + +static void draw_ihtext16(AVFrame *out, int x, int y, float o1, float o2, const char *txt, const uint16_t color[4]) +{ + const uint8_t *font; + int font_height; + int i, plane; + + font = avpriv_cga_font, font_height = 8; + + for (plane = 0; plane < 4 && out->data[plane]; plane++) { + for (i = 0; txt[i]; i++) { + int char_y, mask; + int v = color[plane]; + + uint16_t *p = (uint16_t *)(out->data[plane] + y * out->linesize[plane]) + (x + i * 8); + for (char_y = font_height - 1; char_y >= 0; char_y--) { + for (mask = 0x80; mask; mask >>= 1) { + if (font[txt[i] * font_height + char_y] & mask) + p[0] = p[0] * o2 + (v - p[0]) * o1; + p++; + } + p += out->linesize[plane] / 2 - 8; + } + } + } +} + static void draw_htext(AVFrame *out, int x, int y, float o1, float o2, const char *txt, const uint8_t color[4]) { const uint8_t *font; @@ -1201,6 +1317,123 @@ static void green_graticule(VectorscopeContext *s, AVFrame *out, int X, int Y, i } } +static void invert_graticule16(VectorscopeContext *s, AVFrame *out, int X, int Y, int D, int P) +{ + const int max = s->size - 1; + const float o = s->opacity; + int i; + + for (i = 0; i < 12; i++) { + int x = positions[P][i][X]; + int y = positions[P][i][Y]; + + draw_idots16((uint16_t *)(out->data[D] + y * out->linesize[D] + x * 2), out->linesize[D] / 2, max, o); + draw_idots16((uint16_t *)(out->data[X] + y * out->linesize[X] + x * 2), out->linesize[X] / 2, max, o); + draw_idots16((uint16_t *)(out->data[Y] + y * out->linesize[Y] + x * 2), out->linesize[Y] / 2, max, o); + if (out->data[3]) + draw_dots16((uint16_t *)(out->data[3] + y * out->linesize[3] + x * 2), out->linesize[3] / 2, max, o); + } + + if (s->flags & 1) { + int x = positions[P][12][X]; + int y = positions[P][12][Y]; + + draw_idots16((uint16_t *)(out->data[D] + y * out->linesize[D] + x * 2), out->linesize[D] / 2, max, o); + draw_idots16((uint16_t *)(out->data[X] + y * out->linesize[X] + x * 2), out->linesize[X] / 2, max, o); + draw_idots16((uint16_t *)(out->data[Y] + y * out->linesize[Y] + x * 2), out->linesize[Y] / 2, max, o); + if (out->data[3]) + draw_dots16((uint16_t *)(out->data[3] + y * out->linesize[3] + x * 2), out->linesize[3] / 2, max, o); + } + + if (s->flags & 2) { + int x = positions[P][13][X]; + int y = positions[P][13][Y]; + + draw_idots16((uint16_t *)(out->data[D] + y * out->linesize[D] + x * 2), out->linesize[D] / 2, max, o); + draw_idots16((uint16_t *)(out->data[X] + y * out->linesize[X] + x * 2), out->linesize[X] / 2, max, o); + draw_idots16((uint16_t *)(out->data[Y] + y * out->linesize[Y] + x * 2), out->linesize[Y] / 2, max, o); + if (out->data[3]) + draw_dots16((uint16_t *)(out->data[3] + y * out->linesize[3] + x * 2), out->linesize[3] / 2, max, o); + } + + for (i = 0; i < 6 && s->flags & 4; i++) { + uint16_t color[4] = { max, max, max, max }; + int x = positions[P][i][X]; + int y = positions[P][i][Y]; + + if (x > max / 2) + x += 8; + else + x -= 14; + if (y > max / 2) + y += 8; + else + y -= 14; + + x = av_clip(x, 0, out->width - 9); + y = av_clip(y, 0, out->height - 9); + draw_ihtext16(out, x, y, o, 1. - o, positions_name[i], color); + } +} + +static void invert_graticule(VectorscopeContext *s, AVFrame *out, int X, int Y, int D, int P) +{ + const float o = s->opacity; + int i; + + for (i = 0; i < 12; i++) { + int x = positions[P][i][X]; + int y = positions[P][i][Y]; + + draw_idots(out->data[D] + y * out->linesize[D] + x, out->linesize[D], o); + draw_idots(out->data[X] + y * out->linesize[X] + x, out->linesize[X], o); + draw_idots(out->data[Y] + y * out->linesize[Y] + x, out->linesize[Y], o); + if (out->data[3]) + draw_idots(out->data[3] + y * out->linesize[3] + x, out->linesize[3], o); + } + + if (s->flags & 1) { + int x = positions[P][12][X]; + int y = positions[P][12][Y]; + + draw_idots(out->data[D] + y * out->linesize[D] + x, out->linesize[D], o); + draw_idots(out->data[X] + y * out->linesize[X] + x, out->linesize[X], o); + draw_idots(out->data[Y] + y * out->linesize[Y] + x, out->linesize[Y], o); + if (out->data[3]) + draw_idots(out->data[3] + y * out->linesize[3] + x, out->linesize[3], o); + } + + if (s->flags & 2) { + int x = positions[P][13][X]; + int y = positions[P][13][Y]; + + draw_idots(out->data[D] + y * out->linesize[D] + x, out->linesize[D], o); + draw_idots(out->data[X] + y * out->linesize[X] + x, out->linesize[X], o); + draw_idots(out->data[Y] + y * out->linesize[Y] + x, out->linesize[Y], o); + if (out->data[3]) + draw_idots(out->data[3] + y * out->linesize[3] + x, out->linesize[3], o); + } + + for (i = 0; i < 6 && s->flags & 4; i++) { + uint8_t color[4] = { 255, 255, 255, 255 }; + int x = positions[P][i][X]; + int y = positions[P][i][Y]; + + if (x > 128) + x += 8; + else + x -= 14; + if (y > 128) + y += 8; + else + y -= 14; + + x = av_clip(x, 0, out->width - 9); + y = av_clip(y, 0, out->height - 9); + draw_ihtext(out, x, y, o, 1. - o, positions_name[i], color); + } +} + static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; @@ -1262,9 +1495,9 @@ static int config_input(AVFilterLink *inlink) return AVERROR(EINVAL); } - if (s->mode == GRAY && s->is_yuv) + if (s->mode == TINT && s->is_yuv) { s->pd = 0; - else { + } else { if ((s->x == 1 && s->y == 2) || (s->x == 2 && s->y == 1)) s->pd = 0; else if ((s->x == 0 && s->y == 2) || (s->x == 2 && s->y == 0)) @@ -1281,19 +1514,26 @@ static int config_input(AVFilterLink *inlink) s->graticulef = none_graticule; if (s->is_yuv && s->size == 256) { - if (s->graticule == 1) + if (s->graticule == GRAT_GREEN) s->graticulef = green_graticule; - else if (s->graticule == 2) + else if (s->graticule == GRAT_COLOR) s->graticulef = color_graticule; + else if (s->graticule == GRAT_INVERT) + s->graticulef = invert_graticule; } else if (s->is_yuv) { - if (s->graticule == 1) + if (s->graticule == GRAT_GREEN) s->graticulef = green_graticule16; - else if (s->graticule == 2) + else if (s->graticule == GRAT_COLOR) s->graticulef = color_graticule16; + else if (s->graticule == GRAT_INVERT) + s->graticulef = invert_graticule16; } s->bg_color[3] = s->bgopacity * (s->size - 1); + s->tint[0] = .5f * (s->ftint[0] + 1.f) * (s->size - 1); + s->tint[1] = .5f * (s->ftint[1] + 1.f) * (s->size - 1); + switch (inlink->format) { case AV_PIX_FMT_GBRP12: case AV_PIX_FMT_GBRP10: @@ -1306,8 +1546,8 @@ static int config_input(AVFilterLink *inlink) break; default: s->bg_color[0] = 0; - s->bg_color[1] = s->size / 2 - 1; - s->bg_color[2] = s->size / 2 - 1; + s->bg_color[1] = s->size / 2; + s->bg_color[2] = s->size / 2; } s->hsub = desc->log2_chroma_w; diff --git a/libavfilter/vf_vfrdet.c b/libavfilter/vf_vfrdet.c index cac96e29a2c..abfa19cdcde 100644 --- a/libavfilter/vf_vfrdet.c +++ b/libavfilter/vf_vfrdet.c @@ -29,6 +29,7 @@ typedef struct VFRDETContext { int64_t delta; int64_t min_delta; int64_t max_delta; + int64_t avg_delta; uint64_t vfr; uint64_t cfr; @@ -44,6 +45,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) if (s->delta == AV_NOPTS_VALUE) { s->delta = delta; + s->min_delta = delta; + s->max_delta = delta; } if (s->delta != delta) { @@ -51,6 +54,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->delta = delta; s->min_delta = FFMIN(delta, s->min_delta); s->max_delta = FFMAX(delta, s->max_delta); + s->avg_delta += delta; } else { s->cfr++; } @@ -79,7 +83,7 @@ static av_cold void uninit(AVFilterContext *ctx) av_log(ctx, AV_LOG_INFO, "VFR:%f (%"PRIu64"/%"PRIu64")", s->vfr / (float)(s->vfr + s->cfr), s->vfr, s->cfr); if (s->vfr) - av_log(ctx, AV_LOG_INFO, " min: %"PRId64" max: %"PRId64")", s->min_delta, s->max_delta); + av_log(ctx, AV_LOG_INFO, " min: %"PRId64" max: %"PRId64" avg: %"PRId64, s->min_delta, s->max_delta, s->avg_delta / s->vfr); av_log(ctx, AV_LOG_INFO, "\n"); } diff --git a/libavfilter/vf_vibrance.c b/libavfilter/vf_vibrance.c index aac61c0f108..8e1a55caca8 100644 --- a/libavfilter/vf_vibrance.c +++ b/libavfilter/vf_vibrance.c @@ -224,7 +224,7 @@ static const AVFilterPad vibrance_outputs[] = { }; #define OFFSET(x) offsetof(VibranceContext, x) -#define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +#define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption vibrance_options[] = { { "intensity", "set the intensity value", OFFSET(intensity), AV_OPT_TYPE_FLOAT, {.dbl=0}, -2, 2, VF }, @@ -249,4 +249,5 @@ AVFilter ff_vf_vibrance = { .inputs = vibrance_inputs, .outputs = vibrance_outputs, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vf_vmafmotion.c b/libavfilter/vf_vmafmotion.c index 5c2a9745381..88d0b350959 100644 --- a/libavfilter/vf_vmafmotion.c +++ b/libavfilter/vf_vmafmotion.c @@ -176,8 +176,8 @@ static void convolution_y_##bits##bit(const uint16_t *filter, int filt_w, \ } \ } -conv_y_fn(uint8_t, 8); -conv_y_fn(uint16_t, 10); +conv_y_fn(uint8_t, 8) +conv_y_fn(uint16_t, 10) static void vmafmotiondsp_init(VMAFMotionDSPContext *dsp, int bpp) { dsp->convolution_x = convolution_x; diff --git a/libavfilter/vf_vpp_qsv.c b/libavfilter/vf_vpp_qsv.c index 915cf748c40..3194295f5f6 100644 --- a/libavfilter/vf_vpp_qsv.c +++ b/libavfilter/vf_vpp_qsv.c @@ -36,12 +36,15 @@ #include "libavformat/avformat.h" #include "qsvvpp.h" +#include "transpose.h" #define OFFSET(x) offsetof(VPPContext, x) #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM) /* number of video enhancement filters */ -#define ENH_FILTERS_COUNT (5) +#define ENH_FILTERS_COUNT (7) +#define QSV_HAVE_ROTATION QSV_VERSION_ATLEAST(1, 17) +#define QSV_HAVE_MIRRORING QSV_VERSION_ATLEAST(1, 19) typedef struct VPPContext{ const AVClass *class; @@ -54,6 +57,8 @@ typedef struct VPPContext{ mfxExtVPPDenoise denoise_conf; mfxExtVPPDetail detail_conf; mfxExtVPPProcAmp procamp_conf; + mfxExtVPPRotation rotation_conf; + mfxExtVPPMirroring mirroring_conf; int out_width; int out_height; @@ -74,6 +79,10 @@ typedef struct VPPContext{ int crop_x; int crop_y; + int transpose; + int rotate; /* rotate angle : [0, 90, 180, 270] */ + int hflip; /* flip mode : 0 = off, 1 = HORIZONTAL flip */ + /* param for the procamp */ int procamp; /* enable procamp */ float hue; @@ -100,10 +109,19 @@ static const AVOption 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}, - { "cw", "set the width crop area expression", OFFSET(cw), AV_OPT_TYPE_STRING, { .str = "iw" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "ch", "set the height crop area expression", OFFSET(ch), AV_OPT_TYPE_STRING, { .str = "ih" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "cx", "set the x crop area expression", OFFSET(cx), AV_OPT_TYPE_STRING, { .str = "(in_w-out_w)/2" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "cy", "set the y crop area expression", OFFSET(cy), AV_OPT_TYPE_STRING, { .str = "(in_h-out_h)/2" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "transpose", "set transpose direction", OFFSET(transpose), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 6, FLAGS, "transpose"}, + { "cclock_hflip", "rotate counter-clockwise with horizontal flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit = "transpose" }, + { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .flags=FLAGS, .unit = "transpose" }, + { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .flags=FLAGS, .unit = "transpose" }, + { "clock_hflip", "rotate clockwise with horizontal flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .flags=FLAGS, .unit = "transpose" }, + { "reversal", "rotate by half-turn", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_REVERSAL }, .flags=FLAGS, .unit = "transpose" }, + { "hflip", "flip horizontally", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_HFLIP }, .flags=FLAGS, .unit = "transpose" }, + { "vflip", "flip vertically", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_VFLIP }, .flags=FLAGS, .unit = "transpose" }, + + { "cw", "set the width crop area expression", OFFSET(cw), AV_OPT_TYPE_STRING, { .str = "iw" }, 0, 0, FLAGS }, + { "ch", "set the height crop area expression", OFFSET(ch), AV_OPT_TYPE_STRING, { .str = "ih" }, 0, 0, FLAGS }, + { "cx", "set the x crop area expression", OFFSET(cx), AV_OPT_TYPE_STRING, { .str = "(in_w-out_w)/2" }, 0, 0, FLAGS }, + { "cy", "set the y crop area expression", OFFSET(cy), AV_OPT_TYPE_STRING, { .str = "(in_h-out_h)/2" }, 0, 0, FLAGS }, { "w", "Output video width", OFFSET(ow), AV_OPT_TYPE_STRING, { .str="cw" }, 0, 255, .flags = FLAGS }, { "width", "Output video width", OFFSET(ow), AV_OPT_TYPE_STRING, { .str="cw" }, 0, 255, .flags = FLAGS }, @@ -294,7 +312,9 @@ static int config_output(AVFilterLink *outlink) } else in_format = inlink->format; - param.out_sw_format = (vpp->out_format == AV_PIX_FMT_NONE) ? in_format : vpp->out_format; + if (vpp->out_format == AV_PIX_FMT_NONE) + vpp->out_format = in_format; + param.out_sw_format = vpp->out_format; if (vpp->use_crop) { crop.in_idx = 0; @@ -356,8 +376,87 @@ static int config_output(AVFilterLink *outlink) param.ext_buf[param.num_ext_buf++] = (mfxExtBuffer*)&vpp->procamp_conf; } + if (vpp->transpose >= 0) { +#ifdef QSV_HAVE_ROTATION + switch (vpp->transpose) { + case TRANSPOSE_CCLOCK_FLIP: + vpp->rotate = MFX_ANGLE_270; + vpp->hflip = MFX_MIRRORING_HORIZONTAL; + break; + case TRANSPOSE_CLOCK: + vpp->rotate = MFX_ANGLE_90; + vpp->hflip = MFX_MIRRORING_DISABLED; + break; + case TRANSPOSE_CCLOCK: + vpp->rotate = MFX_ANGLE_270; + vpp->hflip = MFX_MIRRORING_DISABLED; + break; + case TRANSPOSE_CLOCK_FLIP: + vpp->rotate = MFX_ANGLE_90; + vpp->hflip = MFX_MIRRORING_HORIZONTAL; + break; + case TRANSPOSE_REVERSAL: + vpp->rotate = MFX_ANGLE_180; + vpp->hflip = MFX_MIRRORING_DISABLED; + break; + case TRANSPOSE_HFLIP: + vpp->rotate = MFX_ANGLE_0; + vpp->hflip = MFX_MIRRORING_HORIZONTAL; + break; + case TRANSPOSE_VFLIP: + vpp->rotate = MFX_ANGLE_180; + vpp->hflip = MFX_MIRRORING_HORIZONTAL; + break; + default: + av_log(ctx, AV_LOG_ERROR, "Failed to set transpose mode to %d.\n", vpp->transpose); + return AVERROR(EINVAL); + } +#else + av_log(ctx, AV_LOG_WARNING, "The QSV VPP transpose option is " + "not supported with this MSDK version.\n"); + vpp->transpose = 0; +#endif + } + + if (vpp->rotate) { +#ifdef QSV_HAVE_ROTATION + memset(&vpp->rotation_conf, 0, sizeof(mfxExtVPPRotation)); + vpp->rotation_conf.Header.BufferId = MFX_EXTBUFF_VPP_ROTATION; + vpp->rotation_conf.Header.BufferSz = sizeof(mfxExtVPPRotation); + vpp->rotation_conf.Angle = vpp->rotate; + + if (MFX_ANGLE_90 == vpp->rotate || MFX_ANGLE_270 == vpp->rotate) { + FFSWAP(int, vpp->out_width, vpp->out_height); + FFSWAP(int, outlink->w, outlink->h); + av_log(ctx, AV_LOG_DEBUG, "Swap width and height for clock/cclock rotation.\n"); + } + + param.ext_buf[param.num_ext_buf++] = (mfxExtBuffer*)&vpp->rotation_conf; +#else + av_log(ctx, AV_LOG_WARNING, "The QSV VPP rotate option is " + "not supported with this MSDK version.\n"); + vpp->rotate = 0; +#endif + } + + if (vpp->hflip) { +#ifdef QSV_HAVE_MIRRORING + memset(&vpp->mirroring_conf, 0, sizeof(mfxExtVPPMirroring)); + vpp->mirroring_conf.Header.BufferId = MFX_EXTBUFF_VPP_MIRRORING; + vpp->mirroring_conf.Header.BufferSz = sizeof(mfxExtVPPMirroring); + vpp->mirroring_conf.Type = vpp->hflip; + + param.ext_buf[param.num_ext_buf++] = (mfxExtBuffer*)&vpp->mirroring_conf; +#else + av_log(ctx, AV_LOG_WARNING, "The QSV VPP hflip option is " + "not supported with this MSDK version.\n"); + vpp->hflip = 0; +#endif + } + if (vpp->use_frc || vpp->use_crop || vpp->deinterlace || vpp->denoise || - vpp->detail || vpp->procamp || inlink->w != outlink->w || inlink->h != outlink->h) + vpp->detail || vpp->procamp || vpp->rotate || vpp->hflip || + inlink->w != outlink->w || inlink->h != outlink->h || in_format != vpp->out_format) return ff_qsvvpp_create(ctx, &vpp->qsv, ¶m); else { av_log(ctx, AV_LOG_VERBOSE, "qsv vpp pass through mode.\n"); diff --git a/libavfilter/vf_w3fdif.c b/libavfilter/vf_w3fdif.c index c6a65507784..5d64dbd9539 100644 --- a/libavfilter/vf_w3fdif.c +++ b/libavfilter/vf_w3fdif.c @@ -81,6 +81,10 @@ static int query_formats(AVFilterContext *ctx) 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_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, + AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA422P16, + AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16, + AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_NONE }; @@ -274,6 +278,11 @@ static int config_input(AVFilterLink *inlink) s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); s->planeheight[0] = s->planeheight[3] = inlink->h; + if (inlink->h < 3) { + av_log(ctx, AV_LOG_ERROR, "Video of less than 3 lines is not supported\n"); + return AVERROR(EINVAL); + } + s->nb_planes = av_pix_fmt_count_planes(inlink->format); s->nb_threads = ff_filter_get_nb_threads(ctx); s->work_line = av_calloc(s->nb_threads, sizeof(*s->work_line)); diff --git a/libavfilter/vf_waveform.c b/libavfilter/vf_waveform.c index 80336284070..b2c5b46d80c 100644 --- a/libavfilter/vf_waveform.c +++ b/libavfilter/vf_waveform.c @@ -45,6 +45,7 @@ enum FilterType { COLOR, ACOLOR, XFLAT, + YFLAT, NB_FILTERS }; @@ -62,6 +63,14 @@ enum ScaleType { NB_SCALES }; +enum GraticuleType { + GRAT_NONE, + GRAT_GREEN, + GRAT_ORANGE, + GRAT_INVERT, + NB_GRATICULES +}; + typedef struct GraticuleLine { const char *name; uint16_t pos; @@ -103,10 +112,17 @@ typedef struct WaveformContext { GraticuleLines *glines; int nb_glines; int rgb; + float ftint[2]; + int tint[2]; int (*waveform_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); void (*graticulef)(struct WaveformContext *s, AVFrame *out); + void (*blend_line)(uint8_t *dst, int size, int linesize, float o1, float o2, + int v, int step); + void (*draw_text)(AVFrame *out, int x, int y, int mult, + float o1, float o2, const char *txt, + const uint8_t color[4]); const AVPixFmtDescriptor *desc; const AVPixFmtDescriptor *odesc; } WaveformContext; @@ -145,11 +161,13 @@ static const AVOption waveform_options[] = { { "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" }, - { "graticule", "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "graticule" }, - { "g", "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "graticule" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "graticule" }, - { "green", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "graticule" }, - { "orange", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "graticule" }, + { "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" }, { "opacity", "set graticule opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGS }, { "o", "set graticule opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGS }, { "flags", "set graticule flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=1}, 0, 3, FLAGS, "flags" }, @@ -163,6 +181,10 @@ static const AVOption waveform_options[] = { { "ire", NULL, 0, AV_OPT_TYPE_CONST, {.i64=IRE}, 0, 0, FLAGS, "scale" }, { "bgopacity", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGS }, { "b", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGS }, + { "tint0", "set 1st tint", OFFSET(ftint[0]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS}, + { "t0", "set 1st tint", OFFSET(ftint[0]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS}, + { "tint1", "set 2nd tint", OFFSET(ftint[1]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS}, + { "t1", "set 2nd tint", OFFSET(ftint[1]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, FLAGS}, { NULL } }; @@ -183,6 +205,7 @@ static const enum AVPixelFormat in_lowpass_pix_fmts[] = { AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV440P12, + AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_NONE }; @@ -200,6 +223,7 @@ static const enum AVPixelFormat in_color_pix_fmts[] = { AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV440P12, + AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_NONE }; @@ -215,6 +239,7 @@ static const enum AVPixelFormat in_flat_pix_fmts[] = { AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV440P12, + AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_NONE }; @@ -254,7 +279,7 @@ static const enum AVPixelFormat out_yuv10_lowpass_pix_fmts[] = { }; static const enum AVPixelFormat out_yuv12_lowpass_pix_fmts[] = { - AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_NONE }; @@ -290,9 +315,9 @@ static int query_formats(AVFilterContext *ctx) WaveformContext *s = ctx->priv; const enum AVPixelFormat *out_pix_fmts; const enum AVPixelFormat *in_pix_fmts; - const AVPixFmtDescriptor *desc; - AVFilterFormats *avff; - int depth, rgb, i, ret, ncomp; + const AVPixFmtDescriptor *desc, *desc2; + AVFilterFormats *avff, *avff2; + int depth, depth2, rgb, i, ret, ncomp, ncomp2; if (!ctx->inputs[0]->in_formats || !ctx->inputs[0]->in_formats->nb_formats) { @@ -303,6 +328,7 @@ static int query_formats(AVFilterContext *ctx) case LOWPASS: in_pix_fmts = in_lowpass_pix_fmts; break; case CHROMA: case XFLAT: + case YFLAT: case AFLAT: case FLAT: in_pix_fmts = in_flat_pix_fmts; break; case ACOLOR: @@ -316,10 +342,16 @@ static int query_formats(AVFilterContext *ctx) } avff = ctx->inputs[0]->in_formats; + avff2 = ctx->inputs[0]->out_formats; desc = av_pix_fmt_desc_get(avff->formats[0]); + desc2 = av_pix_fmt_desc_get(avff2->formats[0]); ncomp = desc->nb_components; + ncomp2 = desc2->nb_components; rgb = desc->flags & AV_PIX_FMT_FLAG_RGB; depth = desc->comp[0].depth; + depth2 = desc2->comp[0].depth; + if (ncomp != ncomp2 || depth != depth2) + return AVERROR(EAGAIN); for (i = 1; i < avff->nb_formats; i++) { desc = av_pix_fmt_desc_get(avff->formats[i]); if (rgb != (desc->flags & AV_PIX_FMT_FLAG_RGB) || @@ -655,10 +687,11 @@ static av_always_inline void lowpass16(WaveformContext *s, int jobnr, int nb_jobs) { const int plane = s->desc->comp[component].plane; + const int dplane = (s->rgb || s->display == OVERLAY) ? plane : 0; const int shift_w = s->shift_w[component]; const int shift_h = s->shift_h[component]; const int src_linesize = in->linesize[plane] / 2; - const int dst_linesize = out->linesize[plane] / 2; + const int dst_linesize = out->linesize[dplane] / 2; const int dst_signed_linesize = dst_linesize * (mirror == 1 ? -1 : 1); const int limit = s->max - 1; const int max = limit - intensity; @@ -670,7 +703,7 @@ static av_always_inline void lowpass16(WaveformContext *s, const int slicew_end = column ? (src_w * (jobnr+1)) / nb_jobs : src_w; const int step = column ? 1 << shift_w : 1 << shift_h; const uint16_t *src_data = (const uint16_t *)in->data[plane] + sliceh_start * src_linesize; - uint16_t *dst_data = (uint16_t *)out->data[plane] + (offset_y + sliceh_start * step) * dst_linesize + offset_x; + uint16_t *dst_data = (uint16_t *)out->data[dplane] + (offset_y + sliceh_start * step) * dst_linesize + offset_x; uint16_t * const dst_bottom_line = dst_data + dst_linesize * (s->size - 1); uint16_t * const dst_line = (mirror ? dst_bottom_line : dst_data); const uint16_t *p; @@ -707,6 +740,56 @@ static av_always_inline void lowpass16(WaveformContext *s, src_data += src_linesize; dst_data += dst_linesize * step; } + + if (s->display != OVERLAY && column && !s->rgb) { + const int mult = s->max / 256; + const int bg = s->bg_color[0] * mult; + const int t0 = s->tint[0]; + const int t1 = s->tint[1]; + uint16_t *dst0, *dst1; + const uint16_t *src; + int x; + + src = (const uint16_t *)(out->data[0]) + offset_y * dst_linesize + offset_x; + dst0 = (uint16_t *)(out->data[1]) + offset_y * dst_linesize + offset_x; + dst1 = (uint16_t *)(out->data[2]) + offset_y * dst_linesize + offset_x; + for (y = 0; y < s->max; y++) { + for (x = slicew_start * step; x < slicew_end * step; x++) { + if (src[x] != bg) { + dst0[x] = t0; + dst1[x] = t1; + } + } + + src += dst_linesize; + dst0 += dst_linesize; + dst1 += dst_linesize; + } + } else if (s->display != OVERLAY && !s->rgb) { + const int mult = s->max / 256; + const int bg = s->bg_color[0] * mult; + const int t0 = s->tint[0]; + const int t1 = s->tint[1]; + uint16_t *dst0, *dst1; + const uint16_t *src; + int x; + + src = (const uint16_t *)out->data[0] + (offset_y + sliceh_start * step) * dst_linesize + offset_x; + dst0 = (uint16_t *)(out->data[1]) + (offset_y + sliceh_start * step) * dst_linesize + offset_x; + dst1 = (uint16_t *)(out->data[2]) + (offset_y + sliceh_start * step) * dst_linesize + offset_x; + for (y = sliceh_start * step; y < sliceh_end * step; y++) { + for (x = 0; x < s->max; x++) { + if (src[x] != bg) { + dst0[x] = t0; + dst1[x] = t1; + } + } + + src += dst_linesize; + dst0 += dst_linesize; + dst1 += dst_linesize; + } + } } #define LOWPASS16_FUNC(name, column, mirror) \ @@ -742,10 +825,11 @@ static av_always_inline void lowpass(WaveformContext *s, int jobnr, int nb_jobs) { const int plane = s->desc->comp[component].plane; + const int dplane = (s->rgb || s->display == OVERLAY) ? plane : 0; const int shift_w = s->shift_w[component]; const int shift_h = s->shift_h[component]; const int src_linesize = in->linesize[plane]; - const int dst_linesize = out->linesize[plane]; + const int dst_linesize = out->linesize[dplane]; const int dst_signed_linesize = dst_linesize * (mirror == 1 ? -1 : 1); const int max = 255 - intensity; const int src_h = AV_CEIL_RSHIFT(in->height, shift_h); @@ -756,7 +840,7 @@ static av_always_inline void lowpass(WaveformContext *s, const int slicew_end = column ? (src_w * (jobnr+1)) / nb_jobs : src_w; const int step = column ? 1 << shift_w : 1 << shift_h; const uint8_t *src_data = in->data[plane] + sliceh_start * src_linesize; - uint8_t *dst_data = out->data[plane] + (offset_y + sliceh_start * step) * dst_linesize + offset_x; + uint8_t *dst_data = out->data[dplane] + (offset_y + sliceh_start * step) * dst_linesize + offset_x; uint8_t * const dst_bottom_line = dst_data + dst_linesize * (s->size - 1); uint8_t * const dst_line = (mirror ? dst_bottom_line : dst_data); const uint8_t *p; @@ -771,48 +855,76 @@ static av_always_inline void lowpass(WaveformContext *s, for (p = src_data + slicew_start; p < src_data_end; p++) { uint8_t *target; + int i = 0; + if (column) { - target = dst + dst_signed_linesize * *p; - dst += step; - update(target, max, intensity); + do { + target = dst++ + dst_signed_linesize * *p; + update(target, max, intensity); + } while (++i < step); } else { uint8_t *row = dst_data; - if (mirror) - target = row - *p - 1; - else - target = row + *p; - update(target, max, intensity); - row += dst_linesize; + do { + if (mirror) + target = row - *p - 1; + else + target = row + *p; + update(target, max, intensity); + row += dst_linesize; + } while (++i < step); } } src_data += src_linesize; dst_data += dst_linesize * step; } - if (column && step > 1) { + if (s->display != OVERLAY && column && !s->rgb) { + const int bg = s->bg_color[0]; const int dst_h = 256; - uint8_t *dst; - int x, z; - - dst = out->data[plane] + offset_y * dst_linesize + offset_x; + const int t0 = s->tint[0]; + const int t1 = s->tint[1]; + uint8_t *dst0, *dst1; + const uint8_t *src; + int x; + + src = out->data[0] + offset_y * dst_linesize + offset_x; + dst0 = out->data[1] + offset_y * dst_linesize + offset_x; + dst1 = out->data[2] + offset_y * dst_linesize + offset_x; for (y = 0; y < dst_h; y++) { - for (x = slicew_start * step; x < slicew_end * step; x+=step) { - for (z = 1; z < step; z++) { - dst[x + z] = dst[x]; + for (x = slicew_start * step; x < slicew_end * step; x++) { + if (src[x] != bg) { + dst0[x] = t0; + dst1[x] = t1; } } - dst += dst_linesize; + + src += dst_linesize; + dst0 += dst_linesize; + dst1 += dst_linesize; } - } else if (step > 1) { + } else if (s->display != OVERLAY && !s->rgb) { + const int bg = s->bg_color[0]; const int dst_w = 256; - uint8_t *dst; - int z; + const int t0 = s->tint[0]; + const int t1 = s->tint[1]; + uint8_t *dst0, *dst1; + const uint8_t *src; + int x; + + src = out->data[0] + (offset_y + sliceh_start * step) * dst_linesize + offset_x; + dst0 = out->data[1] + (offset_y + sliceh_start * step) * dst_linesize + offset_x; + dst1 = out->data[2] + (offset_y + sliceh_start * step) * dst_linesize + offset_x; + for (y = sliceh_start * step; y < sliceh_end * step; y++) { + for (x = 0; x < dst_w; x++) { + if (src[x] != bg) { + dst0[x] = t0; + dst1[x] = t1; + } + } - dst = out->data[plane] + (offset_y + sliceh_start * step) * dst_linesize + offset_x; - for (y = sliceh_start * step; y < sliceh_end * step; y+=step) { - for (z = 1; z < step; z++) - memcpy(dst + dst_linesize * z, dst, dst_w); - dst += dst_linesize * step; + src += dst_linesize; + dst0 += dst_linesize; + dst1 += dst_linesize; } } } @@ -1118,7 +1230,7 @@ FLAT_FUNC(column, 1, 0) FLAT_FUNC(row_mirror, 0, 1) FLAT_FUNC(row, 0, 0) -#define AFLAT16(name, update_cr, column, mirror) \ +#define AFLAT16(name, update_cb, update_cr, column, mirror) \ static int name(AVFilterContext *ctx, \ void *arg, int jobnr, \ int nb_jobs) \ @@ -1184,7 +1296,7 @@ static int name(AVFilterContext *ctx, update16(target, max, intensity, limit); \ \ target = d1 + x + d1_signed_linesize * (c0 + c1); \ - update16(target, max, intensity, limit); \ + update_cb(target, max, intensity, limit); \ \ target = d2 + x + d2_signed_linesize * (c0 + c2); \ update_cr(target, max, intensity, limit); \ @@ -1225,14 +1337,14 @@ static int name(AVFilterContext *ctx, target = d0_data - c0; \ update16(target, max, intensity, limit); \ target = d1_data - (c0 + c1); \ - update16(target, max, intensity, limit); \ + update_cb(target, max, intensity, limit); \ target = d2_data - (c0 + c2); \ update_cr(target, max, intensity, limit); \ } else { \ target = d0_data + c0; \ update16(target, max, intensity, limit); \ target = d1_data + (c0 + c1); \ - update16(target, max, intensity, limit); \ + update_cb(target, max, intensity, limit); \ target = d2_data + (c0 + c2); \ update_cr(target, max, intensity, limit); \ } \ @@ -1252,7 +1364,7 @@ static int name(AVFilterContext *ctx, return 0; \ } -#define AFLAT(name, update_cr, column, mirror) \ +#define AFLAT(name, update_cb, update_cr, column, mirror) \ static int name(AVFilterContext *ctx, \ void *arg, int jobnr, \ int nb_jobs) \ @@ -1316,7 +1428,7 @@ static int name(AVFilterContext *ctx, update(target, max, intensity); \ \ target = d1 + x + d1_signed_linesize * (c0 + c1); \ - update(target, max, intensity); \ + update_cb(target, max, intensity); \ \ target = d2 + x + d2_signed_linesize * (c0 + c2); \ update_cr(target, max, intensity); \ @@ -1325,8 +1437,8 @@ static int name(AVFilterContext *ctx, c0_data += c0_linesize; \ if (!c1_shift_h || (y & c1_shift_h)) \ c1_data += c1_linesize; \ - if (!c1_shift_h || (y & c1_shift_h)) \ - c2_data += c1_linesize; \ + if (!c2_shift_h || (y & c2_shift_h)) \ + c2_data += c2_linesize; \ d0_data += d0_linesize; \ d1_data += d1_linesize; \ d2_data += d2_linesize; \ @@ -1357,14 +1469,14 @@ static int name(AVFilterContext *ctx, target = d0_data - c0; \ update(target, max, intensity); \ target = d1_data - (c0 + c1); \ - update(target, max, intensity); \ + update_cb(target, max, intensity); \ target = d2_data - (c0 + c2); \ update_cr(target, max, intensity); \ } else { \ target = d0_data + c0; \ update(target, max, intensity); \ target = d1_data + (c0 + c1); \ - update(target, max, intensity); \ + update_cb(target, max, intensity); \ target = d2_data + (c0 + c2); \ update_cr(target, max, intensity); \ } \ @@ -1384,23 +1496,31 @@ static int name(AVFilterContext *ctx, return 0; \ } -AFLAT16(aflat16_row, update16, 0, 0) -AFLAT16(aflat16_row_mirror, update16, 0, 1) -AFLAT16(aflat16_column, update16, 1, 0) -AFLAT16(aflat16_column_mirror, update16, 1, 1) -AFLAT16(xflat16_row, update16_cr, 0, 0) -AFLAT16(xflat16_row_mirror, update16_cr, 0, 1) -AFLAT16(xflat16_column, update16_cr, 1, 0) -AFLAT16(xflat16_column_mirror, update16_cr, 1, 1) - -AFLAT(aflat_row, update, 0, 0) -AFLAT(aflat_row_mirror, update, 0, 1) -AFLAT(aflat_column, update, 1, 0) -AFLAT(aflat_column_mirror, update, 1, 1) -AFLAT(xflat_row, update_cr, 0, 0) -AFLAT(xflat_row_mirror, update_cr, 0, 1) -AFLAT(xflat_column, update_cr, 1, 0) -AFLAT(xflat_column_mirror, update_cr, 1, 1) +AFLAT16(aflat16_row, update16, update16, 0, 0) +AFLAT16(aflat16_row_mirror, update16, update16, 0, 1) +AFLAT16(aflat16_column, update16, update16, 1, 0) +AFLAT16(aflat16_column_mirror, update16, update16, 1, 1) +AFLAT16(xflat16_row, update16, update16_cr, 0, 0) +AFLAT16(xflat16_row_mirror, update16, update16_cr, 0, 1) +AFLAT16(xflat16_column, update16, update16_cr, 1, 0) +AFLAT16(xflat16_column_mirror, update16, update16_cr, 1, 1) +AFLAT16(yflat16_row, update16_cr, update16_cr, 0, 0) +AFLAT16(yflat16_row_mirror, update16_cr, update16_cr, 0, 1) +AFLAT16(yflat16_column, update16_cr, update16_cr, 1, 0) +AFLAT16(yflat16_column_mirror, update16_cr, update16_cr, 1, 1) + +AFLAT(aflat_row, update, update, 0, 0) +AFLAT(aflat_row_mirror, update, update, 0, 1) +AFLAT(aflat_column, update, update, 1, 0) +AFLAT(aflat_column_mirror, update, update, 1, 1) +AFLAT(xflat_row, update, update_cr, 0, 0) +AFLAT(xflat_row_mirror, update, update_cr, 0, 1) +AFLAT(xflat_column, update, update_cr, 1, 0) +AFLAT(xflat_column_mirror, update, update_cr, 1, 1) +AFLAT(yflat_row, update_cr, update_cr, 0, 0) +AFLAT(yflat_row_mirror, update_cr, update_cr, 0, 1) +AFLAT(yflat_column, update_cr, update_cr, 1, 0) +AFLAT(yflat_column_mirror, update_cr, update_cr, 1, 1) static av_always_inline void chroma16(WaveformContext *s, AVFrame *in, AVFrame *out, @@ -2470,8 +2590,9 @@ static void blend_vline(uint8_t *dst, int height, int linesize, float o1, float } } -static void blend_vline16(uint16_t *dst, int height, int linesize, float o1, float o2, int v, int step) +static void blend_vline16(uint8_t *ddst, int height, int linesize, float o1, float o2, int v, int step) { + uint16_t *dst = (uint16_t *)ddst; int y; for (y = 0; y < height; y += step) { @@ -2481,7 +2602,7 @@ static void blend_vline16(uint16_t *dst, int height, int linesize, float o1, flo } } -static void blend_hline(uint8_t *dst, int width, float o1, float o2, int v, int step) +static void blend_hline(uint8_t *dst, int width, int unused, float o1, float o2, int v, int step) { int x; @@ -2490,8 +2611,9 @@ static void blend_hline(uint8_t *dst, int width, float o1, float o2, int v, int } } -static void blend_hline16(uint16_t *dst, int width, float o1, float o2, int v, int step) +static void blend_hline16(uint8_t *ddst, int width, int unused, float o1, float o2, int v, int step) { + uint16_t *dst = (uint16_t *)ddst; int x; for (x = 0; x < width; x += step) { @@ -2499,7 +2621,7 @@ static void blend_hline16(uint16_t *dst, int width, float o1, float o2, int v, i } } -static void draw_htext(AVFrame *out, int x, int y, float o1, float o2, const char *txt, const uint8_t color[4]) +static void draw_htext(AVFrame *out, int x, int y, int mult, float o1, float o2, const char *txt, const uint8_t color[4]) { const uint8_t *font; int font_height; @@ -2551,7 +2673,7 @@ static void draw_htext16(AVFrame *out, int x, int y, int mult, float o1, float o } } -static void draw_vtext(AVFrame *out, int x, int y, float o1, float o2, const char *txt, const uint8_t color[4]) +static void draw_vtext(AVFrame *out, int x, int y, int mult, float o1, float o2, const char *txt, const uint8_t color[4]) { const uint8_t *font; int font_height; @@ -2601,6 +2723,150 @@ static void draw_vtext16(AVFrame *out, int x, int y, int mult, float o1, float o } } +static void iblend_vline(uint8_t *dst, int height, int linesize, float o1, float o2, int v, int step) +{ + int y; + + for (y = 0; y < height; y += step) { + dst[0] = (v - dst[0]) * o1 + dst[0] * o2; + + dst += linesize * step; + } +} + +static void iblend_vline16(uint8_t *ddst, int height, int linesize, float o1, float o2, int v, int step) +{ + uint16_t *dst = (uint16_t *)ddst; + int y; + + for (y = 0; y < height; y += step) { + dst[0] = (v - dst[0]) * o1 + dst[0] * o2; + + dst += (linesize / 2) * step; + } +} + +static void iblend_hline(uint8_t *dst, int width, int unused, float o1, float o2, int v, int step) +{ + int x; + + for (x = 0; x < width; x += step) { + dst[x] = (v - dst[x]) * o1 + dst[x] * o2; + } +} + +static void iblend_hline16(uint8_t *ddst, int width, int unused, float o1, float o2, int v, int step) +{ + uint16_t *dst = (uint16_t *)ddst; + int x; + + for (x = 0; x < width; x += step) { + dst[x] = (v - dst[x]) * o1 + dst[x] * o2; + } +} + +static void idraw_htext(AVFrame *out, int x, int y, int mult, float o1, float o2, const char *txt, const uint8_t color[4]) +{ + const uint8_t *font; + int font_height; + int i, plane; + + font = avpriv_cga_font, font_height = 8; + + for (plane = 0; plane < 4 && out->data[plane]; plane++) { + for (i = 0; txt[i]; i++) { + int char_y, mask; + int v = color[plane]; + + uint8_t *p = out->data[plane] + y * out->linesize[plane] + (x + i * 8); + 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) + p[0] = p[0] * o2 + (v - p[0]) * o1; + p++; + } + p += out->linesize[plane] - 8; + } + } + } +} + +static void idraw_htext16(AVFrame *out, int x, int y, int mult, float o1, float o2, const char *txt, const uint8_t color[4]) +{ + const uint8_t *font; + int font_height; + int i, plane; + + font = avpriv_cga_font, font_height = 8; + + for (plane = 0; plane < 4 && out->data[plane]; plane++) { + for (i = 0; txt[i]; i++) { + int char_y, mask; + int v = color[plane] * mult; + + uint16_t *p = (uint16_t *)(out->data[plane] + y * out->linesize[plane]) + (x + i * 8); + 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) + p[0] = p[0] * o2 + (v - p[0]) * o1; + p++; + } + p += out->linesize[plane] / 2 - 8; + } + } + } +} + +static void idraw_vtext(AVFrame *out, int x, int y, int mult, float o1, float o2, const char *txt, const uint8_t color[4]) +{ + const uint8_t *font; + int font_height; + int i, plane; + + font = avpriv_cga_font, font_height = 8; + + for (plane = 0; plane < 4 && out->data[plane]; plane++) { + for (i = 0; txt[i]; i++) { + int char_y, mask; + int v = color[plane]; + + for (char_y = font_height - 1; char_y >= 0; char_y--) { + uint8_t *p = out->data[plane] + (y + i * 10) * out->linesize[plane] + x; + for (mask = 0x80; mask; mask >>= 1) { + if (font[txt[i] * font_height + font_height - 1 - char_y] & mask) + p[char_y] = p[char_y] * o2 + (v - p[char_y]) * o1; + p += out->linesize[plane]; + } + } + } + } +} + +static void idraw_vtext16(AVFrame *out, int x, int y, int mult, float o1, float o2, const char *txt, const uint8_t color[4]) +{ + const uint8_t *font; + int font_height; + int i, plane; + + font = avpriv_cga_font, font_height = 8; + + for (plane = 0; plane < 4 && out->data[plane]; plane++) { + for (i = 0; txt[i]; i++) { + int char_y, mask; + int v = color[plane] * mult; + + for (char_y = 0; char_y < font_height; char_y++) { + uint16_t *p = (uint16_t *)(out->data[plane] + (y + i * 10) * out->linesize[plane]) + x; + for (mask = 0x80; mask; mask >>= 1) { + if (font[txt[i] * font_height + font_height - 1 - char_y] & mask) + p[char_y] = p[char_y] * o2 + (v - p[char_y]) * o1; + p += out->linesize[plane] / 2; + } + } + } + } +} + static void graticule_none(WaveformContext *s, AVFrame *out) { } @@ -2626,7 +2892,7 @@ static void graticule_row(WaveformContext *s, AVFrame *out) int x = offset_x + (s->mirror ? s->size - 1 - pos : pos); uint8_t *dst = out->data[p] + offset_y * out->linesize[p] + x; - blend_vline(dst, height, out->linesize[p], o1, o2, v, step); + s->blend_line(dst, height, out->linesize[p], o1, o2, v, step); } } @@ -2638,7 +2904,7 @@ static void graticule_row(WaveformContext *s, AVFrame *out) if (x < 0) x = 4; - draw_vtext(out, x, offset_y + 2, o1, o2, name, s->grat_yuva_color); + s->draw_text(out, x, offset_y + 2, 1, o1, o2, name, s->grat_yuva_color); } offset_x += s->size * (s->display == STACK); @@ -2666,9 +2932,9 @@ static void graticule16_row(WaveformContext *s, AVFrame *out) for (l = 0; l < s->nb_glines ; l++) { const uint16_t pos = s->glines[l].line[C].pos; int x = offset_x + (s->mirror ? s->size - 1 - pos : pos); - uint16_t *dst = (uint16_t *)(out->data[p] + offset_y * out->linesize[p]) + x; + uint8_t *dst = (uint8_t *)(out->data[p] + offset_y * out->linesize[p]) + x * 2; - blend_vline16(dst, height, out->linesize[p], o1, o2, v, step); + s->blend_line(dst, height, out->linesize[p], o1, o2, v, step); } } @@ -2680,7 +2946,7 @@ static void graticule16_row(WaveformContext *s, AVFrame *out) if (x < 0) x = 4; - draw_vtext16(out, x, offset_y + 2, mult, o1, o2, name, s->grat_yuva_color); + s->draw_text(out, x, offset_y + 2, mult, o1, o2, name, s->grat_yuva_color); } offset_x += s->size * (s->display == STACK); @@ -2709,7 +2975,7 @@ static void graticule_column(WaveformContext *s, AVFrame *out) int y = offset_y + (s->mirror ? s->size - 1 - pos : pos); uint8_t *dst = out->data[p] + y * out->linesize[p] + offset_x; - blend_hline(dst, width, o1, o2, v, step); + s->blend_line(dst, width, 1, o1, o2, v, step); } } @@ -2721,7 +2987,7 @@ static void graticule_column(WaveformContext *s, AVFrame *out) if (y < 0) y = 4; - draw_htext(out, 2 + offset_x, y, o1, o2, name, s->grat_yuva_color); + s->draw_text(out, 2 + offset_x, y, 1, o1, o2, name, s->grat_yuva_color); } offset_y += s->size * (s->display == STACK); @@ -2749,9 +3015,9 @@ static void graticule16_column(WaveformContext *s, AVFrame *out) for (l = 0; l < s->nb_glines ; l++) { const uint16_t pos = s->glines[l].line[C].pos; int y = offset_y + (s->mirror ? s->size - 1 - pos : pos); - uint16_t *dst = (uint16_t *)(out->data[p] + y * out->linesize[p]) + offset_x; + uint8_t *dst = (uint8_t *)(out->data[p] + y * out->linesize[p]) + offset_x * 2; - blend_hline16(dst, width, o1, o2, v, step); + s->blend_line(dst, width, 1, o1, o2, v, step); } } @@ -2763,7 +3029,7 @@ static void graticule16_column(WaveformContext *s, AVFrame *out) if (y < 0) y = 4; - draw_htext16(out, 2 + offset_x, y, mult, o1, o2, name, s->grat_yuva_color); + s->draw_text(out, 2 + offset_x, y, mult, o1, o2, name, s->grat_yuva_color); } offset_y += s->size * (s->display == STACK); @@ -2791,6 +3057,7 @@ static int config_input(AVFilterLink *inlink) switch (s->filter) { case XFLAT: + case YFLAT: case AFLAT: s->size = 256 * 2; break; case FLAT: s->size = 256 * 3; break; default: s->size = 256; break; @@ -2854,12 +3121,35 @@ static int config_input(AVFilterLink *inlink) case 0x1016: s->waveform_slice = xflat16_row_mirror; break; case 0x0116: s->waveform_slice = xflat16_column; break; case 0x0016: s->waveform_slice = xflat16_row; break; + case 0x1107: s->waveform_slice = yflat_column_mirror; break; + case 0x1007: s->waveform_slice = yflat_row_mirror; break; + case 0x0107: s->waveform_slice = yflat_column; break; + case 0x0007: s->waveform_slice = yflat_row; break; + case 0x1117: s->waveform_slice = yflat16_column_mirror; break; + case 0x1017: s->waveform_slice = yflat16_row_mirror; break; + case 0x0117: s->waveform_slice = yflat16_column; break; + case 0x0017: s->waveform_slice = yflat16_row; break; } s->grat_yuva_color[0] = 255; - s->grat_yuva_color[2] = s->graticule == 2 ? 255 : 0; + s->grat_yuva_color[1] = s->graticule == GRAT_INVERT ? 255 : 0; + s->grat_yuva_color[2] = s->graticule == GRAT_ORANGE || s->graticule == GRAT_INVERT ? 255 : 0; s->grat_yuva_color[3] = 255; + if (s->mode == 0 && s->graticule != GRAT_INVERT) { + s->blend_line = s->bits <= 8 ? blend_vline : blend_vline16; + s->draw_text = s->bits <= 8 ? draw_vtext : draw_vtext16; + } else if (s->graticule != GRAT_INVERT) { + s->blend_line = s->bits <= 8 ? blend_hline : blend_hline16; + s->draw_text = s->bits <= 8 ? draw_htext : draw_htext16; + } else if (s->mode == 0 && s->graticule == GRAT_INVERT) { + s->blend_line = s->bits <= 8 ? iblend_vline : iblend_vline16; + s->draw_text = s->bits <= 8 ? idraw_vtext : idraw_vtext16; + } else if (s->graticule == GRAT_INVERT) { + s->blend_line = s->bits <= 8 ? iblend_hline : iblend_hline16; + s->draw_text = s->bits <= 8 ? idraw_htext : idraw_htext16; + } + switch (s->filter) { case LOWPASS: case COLOR: @@ -2867,10 +3157,11 @@ static int config_input(AVFilterLink *inlink) case CHROMA: case AFLAT: case XFLAT: + case YFLAT: case FLAT: - if (s->graticule && s->mode == 1) + if (s->graticule > GRAT_NONE && s->mode == 1) s->graticulef = s->bits > 8 ? graticule16_column : graticule_column; - else if (s->graticule && s->mode == 0) + else if (s->graticule > GRAT_NONE && s->mode == 0) s->graticulef = s->bits > 8 ? graticule16_row : graticule_row; break; } @@ -2935,6 +3226,7 @@ static int config_input(AVFilterLink *inlink) } break; case XFLAT: + case YFLAT: case AFLAT: switch (s->scale) { case DIGITAL: @@ -2995,6 +3287,9 @@ static int config_input(AVFilterLink *inlink) s->size = s->size << (s->bits - 8); + s->tint[0] = .5f * (s->ftint[0] + 1.f) * (s->size - 1); + s->tint[1] = .5f * (s->ftint[1] + 1.f) * (s->size - 1); + switch (inlink->format) { case AV_PIX_FMT_GBRAP: case AV_PIX_FMT_GBRP: @@ -3131,10 +3426,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) td.offset_x = offset_x; ctx->internal->execute(ctx, s->waveform_slice, &td, NULL, ff_filter_get_nb_threads(ctx)); switch (s->filter) { + case LOWPASS: + if (s->bits <= 8) + envelope(s, out, plane, s->rgb || s->display == OVERLAY ? plane : 0, s->mode ? offset_x : offset_y); + else + envelope16(s, out, plane, s->rgb || s->display == OVERLAY ? plane : 0, s->mode ? offset_x : offset_y); + break; case ACOLOR: case CHROMA: case COLOR: - case LOWPASS: if (s->bits <= 8) envelope(s, out, plane, plane, s->mode ? offset_x : offset_y); else @@ -3151,6 +3451,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) break; case AFLAT: case XFLAT: + case YFLAT: if (s->bits <= 8) { envelope(s, out, plane, (plane + 0) % s->ncomp, s->mode ? offset_x : offset_y); envelope(s, out, plane, (plane + 1) % s->ncomp, s->mode ? offset_x : offset_y); diff --git a/libavfilter/vf_weave.c b/libavfilter/vf_weave.c index 663d79f5115..8951b09095f 100644 --- a/libavfilter/vf_weave.c +++ b/libavfilter/vf_weave.c @@ -49,6 +49,26 @@ static const AVOption weave_options[] = { AVFILTER_DEFINE_CLASS(weave); +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats = NULL; + int ret; + + for (int fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); + + if (!(desc->flags & AV_PIX_FMT_FLAG_PAL) && + !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { + if ((ret = ff_add_format(&formats, fmt)) < 0) { + ff_formats_unref(&formats); + return ret; + } + } + } + + return ff_set_common_formats(ctx, formats); +} + static int config_props_output(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; @@ -156,6 +176,7 @@ AVFilter ff_vf_weave = { .description = NULL_IF_CONFIG_SMALL("Weave input video fields into frames."), .priv_size = sizeof(WeaveContext), .priv_class = &weave_class, + .query_formats = query_formats, .uninit = uninit, .inputs = weave_inputs, .outputs = weave_outputs, @@ -179,6 +200,7 @@ AVFilter ff_vf_doubleweave = { .description = NULL_IF_CONFIG_SMALL("Weave input video fields into double number of frames."), .priv_size = sizeof(WeaveContext), .priv_class = &doubleweave_class, + .query_formats = query_formats, .init = init, .uninit = uninit, .inputs = weave_inputs, diff --git a/libavfilter/vf_xbr.c b/libavfilter/vf_xbr.c index 2c71871d22d..a381be4553e 100644 --- a/libavfilter/vf_xbr.c +++ b/libavfilter/vf_xbr.c @@ -380,7 +380,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) return ff_filter_frame(outlink, out); } -static int init(AVFilterContext *ctx) +static av_cold int init(AVFilterContext *ctx) { XBRContext *s = ctx->priv; static const xbrfunc_t xbrfuncs[] = {xbr2x, xbr3x, xbr4x}; @@ -395,7 +395,7 @@ static int init(AVFilterContext *ctx) int startg = FFMAX3(-bg, -rg, 0); int endg = FFMIN3(255-bg, 255-rg, 255); uint32_t y = (uint32_t)(( 299*rg + 1000*startg + 114*bg)/1000); - c = bg + (rg<<16) + 0x010101 * startg; + c = bg + rg * (1 << 16) + 0x010101 * startg; for (g = startg; g <= endg; g++) { s->rgbtoyuv[c] = ((y++) << 16) + (u << 8) + v; c+= 0x010101; diff --git a/libavfilter/vf_xfade.c b/libavfilter/vf_xfade.c new file mode 100644 index 00000000000..1b5ebef9ed4 --- /dev/null +++ b/libavfilter/vf_xfade.c @@ -0,0 +1,1606 @@ +/* + * Copyright (c) 2020 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/imgutils.h" +#include "libavutil/eval.h" +#include "libavutil/opt.h" +#include "libavutil/pixfmt.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "filters.h" +#include "video.h" + +enum XFadeTransitions { + CUSTOM = -1, + FADE, + WIPELEFT, + WIPERIGHT, + WIPEUP, + WIPEDOWN, + SLIDELEFT, + SLIDERIGHT, + SLIDEUP, + SLIDEDOWN, + CIRCLECROP, + RECTCROP, + DISTANCE, + FADEBLACK, + FADEWHITE, + RADIAL, + SMOOTHLEFT, + SMOOTHRIGHT, + SMOOTHUP, + SMOOTHDOWN, + CIRCLEOPEN, + CIRCLECLOSE, + VERTOPEN, + VERTCLOSE, + HORZOPEN, + HORZCLOSE, + DISSOLVE, + PIXELIZE, + DIAGTL, + DIAGTR, + DIAGBL, + DIAGBR, + HLSLICE, + HRSLICE, + VUSLICE, + VDSLICE, + NB_TRANSITIONS, +}; + +typedef struct XFadeContext { + const AVClass *class; + + int transition; + int64_t duration; + int64_t offset; + char *custom_str; + + int nb_planes; + int depth; + + int64_t duration_pts; + int64_t offset_pts; + int64_t first_pts; + int64_t last_pts; + int64_t pts; + int xfade_is_over; + int need_second; + int eof[2]; + AVFrame *xf[2]; + int max_value; + uint16_t black[4]; + uint16_t white[4]; + + void (*transitionf)(AVFilterContext *ctx, const AVFrame *a, const AVFrame *b, AVFrame *out, float progress, + int slice_start, int slice_end, int jobnr); + + AVExpr *e; +} XFadeContext; + +static const char *const var_names[] = { "X", "Y", "W", "H", "A", "B", "PLANE", "P", NULL }; +enum { VAR_X, VAR_Y, VAR_W, VAR_H, VAR_A, VAR_B, VAR_PLANE, VAR_PROGRESS, VAR_VARS_NB }; + +typedef struct ThreadData { + const AVFrame *xf[2]; + AVFrame *out; + float progress; +} ThreadData; + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUVA444P, + AV_PIX_FMT_YUVJ444P, + AV_PIX_FMT_YUV444P, + AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GRAY8, + AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_GBRP9, + AV_PIX_FMT_YUV444P10, + AV_PIX_FMT_YUVA444P10, + AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GRAY10, + AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_YUVA444P12, + AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GRAY12, + AV_PIX_FMT_YUV444P14, AV_PIX_FMT_GBRP14, + AV_PIX_FMT_YUV444P16, + AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_GRAY16, + AV_PIX_FMT_NONE + }; + + AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); + if (!fmts_list) + return AVERROR(ENOMEM); + return ff_set_common_formats(ctx, fmts_list); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + XFadeContext *s = ctx->priv; + + av_expr_free(s->e); +} + +#define OFFSET(x) offsetof(XFadeContext, x) +#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" }, + { "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 }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(xfade); + +#define CUSTOM_TRANSITION(name, type, div) \ +static void custom##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; \ + \ + double values[VAR_VARS_NB]; \ + values[VAR_W] = out->width; \ + values[VAR_H] = out->height; \ + values[VAR_PROGRESS] = progress; \ + \ + 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]); \ + \ + values[VAR_PLANE] = p; \ + \ + for (int y = 0; y < height; y++) { \ + values[VAR_Y] = slice_start + y; \ + for (int x = 0; x < out->width; x++) { \ + values[VAR_X] = x; \ + values[VAR_A] = xf0[x]; \ + values[VAR_B] = xf1[x]; \ + dst[x] = av_expr_eval(s->e, values, s); \ + } \ + \ + dst += out->linesize[p] / div; \ + xf0 += a->linesize[p] / div; \ + xf1 += b->linesize[p] / div; \ + } \ + } \ +} + +CUSTOM_TRANSITION(8, uint8_t, 1) +CUSTOM_TRANSITION(16, uint16_t, 2) + +static inline float mix(float a, float b, float mix) +{ + return a * mix + b * (1.f - mix); +} + +static inline float fract(float a) +{ + return a - floorf(a); +} + +static inline float smoothstep(float edge0, float edge1, float x) +{ + float t; + + t = av_clipf((x - edge0) / (edge1 - edge0), 0.f, 1.f); + + return t * t * (3.f - 2.f * t); +} + +#define FADE_TRANSITION(name, type, div) \ +static void fade##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; \ + \ + 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 < out->width; x++) { \ + dst[x] = mix(xf0[x], xf1[x], progress); \ + } \ + \ + dst += out->linesize[p] / div; \ + xf0 += a->linesize[p] / div; \ + xf1 += b->linesize[p] / div; \ + } \ + } \ +} + +FADE_TRANSITION(8, uint8_t, 1) +FADE_TRANSITION(16, uint16_t, 2) + +#define WIPELEFT_TRANSITION(name, type, div) \ +static void wipeleft##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 z = out->width * progress; \ + \ + 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 < out->width; x++) { \ + dst[x] = x > z ? xf1[x] : xf0[x]; \ + } \ + \ + dst += out->linesize[p] / div; \ + xf0 += a->linesize[p] / div; \ + xf1 += b->linesize[p] / div; \ + } \ + } \ +} + +WIPELEFT_TRANSITION(8, uint8_t, 1) +WIPELEFT_TRANSITION(16, uint16_t, 2) + +#define WIPERIGHT_TRANSITION(name, type, div) \ +static void wiperight##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 z = out->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]); \ + 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 < out->width; x++) { \ + dst[x] = x > z ? xf0[x] : xf1[x]; \ + } \ + \ + dst += out->linesize[p] / div; \ + xf0 += a->linesize[p] / div; \ + xf1 += b->linesize[p] / div; \ + } \ + } \ +} + +WIPERIGHT_TRANSITION(8, uint8_t, 1) +WIPERIGHT_TRANSITION(16, uint16_t, 2) + +#define WIPEUP_TRANSITION(name, type, div) \ +static void wipeup##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 z = out->height * progress; \ + \ + 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 < out->width; x++) { \ + dst[x] = slice_start + y > z ? xf1[x] : xf0[x]; \ + } \ + \ + dst += out->linesize[p] / div; \ + xf0 += a->linesize[p] / div; \ + xf1 += b->linesize[p] / div; \ + } \ + } \ +} + +WIPEUP_TRANSITION(8, uint8_t, 1) +WIPEUP_TRANSITION(16, uint16_t, 2) + +#define WIPEDOWN_TRANSITION(name, type, div) \ +static void wipedown##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 z = out->height * (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]); \ + 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 < out->width; x++) { \ + dst[x] = slice_start + y > z ? xf0[x] : xf1[x]; \ + } \ + \ + dst += out->linesize[p] / div; \ + xf0 += a->linesize[p] / div; \ + xf1 += b->linesize[p] / div; \ + } \ + } \ +} + +WIPEDOWN_TRANSITION(8, uint8_t, 1) +WIPEDOWN_TRANSITION(16, uint16_t, 2) + +#define SLIDELEFT_TRANSITION(name, type, div) \ +static void slideleft##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 = -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[zz]; \ + } \ + \ + dst += out->linesize[p] / div; \ + xf0 += a->linesize[p] / div; \ + xf1 += b->linesize[p] / div; \ + } \ + } \ +} + +SLIDELEFT_TRANSITION(8, uint8_t, 1) +SLIDELEFT_TRANSITION(16, uint16_t, 2) + +#define SLIDERIGHT_TRANSITION(name, type, div) \ +static void slideright##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 = 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 < out->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]; \ + } \ + \ + dst += out->linesize[p] / div; \ + xf0 += a->linesize[p] / div; \ + xf1 += b->linesize[p] / div; \ + } \ + } \ +} + +SLIDERIGHT_TRANSITION(8, uint8_t, 1) +SLIDERIGHT_TRANSITION(16, uint16_t, 2) + +#define SLIDEUP_TRANSITION(name, type, div) \ +static void slideup##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 z = -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] + zz * b->linesize[p]); \ + \ + for (int x = 0; x < out->width; x++) { \ + dst[x] = (zy > 0) && (zy < height) ? xf1[x] : xf0[x]; \ + } \ + \ + dst += out->linesize[p] / div; \ + } \ + } \ +} + +SLIDEUP_TRANSITION(8, uint8_t, 1) +SLIDEUP_TRANSITION(16, uint16_t, 2) + +#define SLIDEDOWN_TRANSITION(name, type, div) \ +static void slidedown##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 z = 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] + zz * b->linesize[p]); \ + \ + for (int x = 0; x < out->width; x++) { \ + dst[x] = (zy > 0) && (zy < height) ? xf1[x] : xf0[x]; \ + } \ + \ + dst += out->linesize[p] / div; \ + } \ + } \ +} + +SLIDEDOWN_TRANSITION(8, uint8_t, 1) +SLIDEDOWN_TRANSITION(16, uint16_t, 2) + +#define CIRCLECROP_TRANSITION(name, type, div) \ +static void circlecrop##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; \ + const int height = out->height; \ + float z = powf(2.f * fabsf(progress - 0.5f), 3.f) * hypotf(width/2, height/2); \ + \ + for (int p = 0; p < s->nb_planes; p++) { \ + const int bg = s->black[p]; \ + type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ + const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ + \ + for (int x = 0; x < width; x++) { \ + float dist = hypotf(x - width / 2, y - height / 2); \ + int val = progress < 0.5f ? xf1[x] : xf0[x]; \ + dst[x] = (z < dist) ? bg : val; \ + } \ + \ + dst += out->linesize[p] / div; \ + } \ + } \ +} + +CIRCLECROP_TRANSITION(8, uint8_t, 1) +CIRCLECROP_TRANSITION(16, uint16_t, 2) + +#define RECTCROP_TRANSITION(name, type, div) \ +static void rectcrop##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; \ + const int height = out->height; \ + int zh = fabsf(progress - 0.5f) * height; \ + int zw = fabsf(progress - 0.5f) * width; \ + \ + for (int p = 0; p < s->nb_planes; p++) { \ + const int bg = s->black[p]; \ + type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ + const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ + \ + for (int x = 0; x < width; x++) { \ + int dist = FFABS(x - width / 2) < zw && \ + FFABS(y - height / 2) < zh; \ + int val = progress < 0.5f ? xf1[x] : xf0[x]; \ + dst[x] = !dist ? bg : val; \ + } \ + \ + dst += out->linesize[p] / div; \ + } \ + } \ +} + +RECTCROP_TRANSITION(8, uint8_t, 1) +RECTCROP_TRANSITION(16, uint16_t, 2) + +#define DISTANCE_TRANSITION(name, type, div) \ +static void distance##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; \ + const float max = s->max_value; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + for (int x = 0; x < width; x++) { \ + float dist = 0.f; \ + 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]); \ + \ + dist += (xf0[x] / max - xf1[x] / max) * \ + (xf0[x] / max - xf1[x] / max); \ + } \ + \ + dist = sqrtf(dist) <= progress; \ + 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(mix(xf0[x], xf1[x], dist), xf1[x], progress); \ + } \ + } \ + } \ +} + +DISTANCE_TRANSITION(8, uint8_t, 1) +DISTANCE_TRANSITION(16, uint16_t, 2) + +#define FADEBLACK_TRANSITION(name, type, div) \ +static void fadeblack##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 float phase = 0.2f; \ + \ + 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]); \ + const int bg = s->black[p]; \ + \ + for (int y = 0; y < height; y++) { \ + for (int x = 0; x < out->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); \ + } \ + \ + dst += out->linesize[p] / div; \ + xf0 += a->linesize[p] / div; \ + xf1 += b->linesize[p] / div; \ + } \ + } \ +} + +FADEBLACK_TRANSITION(8, uint8_t, 1) +FADEBLACK_TRANSITION(16, uint16_t, 2) + +#define FADEWHITE_TRANSITION(name, type, div) \ +static void fadewhite##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 float phase = 0.2f; \ + \ + 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]); \ + const int bg = s->white[p]; \ + \ + for (int y = 0; y < height; y++) { \ + for (int x = 0; x < out->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); \ + } \ + \ + dst += out->linesize[p] / div; \ + xf0 += a->linesize[p] / div; \ + xf1 += b->linesize[p] / div; \ + } \ + } \ +} + +FADEWHITE_TRANSITION(8, uint8_t, 1) +FADEWHITE_TRANSITION(16, uint16_t, 2) + +#define RADIAL_TRANSITION(name, type, div) \ +static void radial##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; \ + const int height = out->height; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + for (int x = 0; x < width; x++) { \ + const float smooth = atan2f(x - width / 2, y - height / 2) - \ + (progress - 0.5f) * (M_PI * 2.5f); \ + 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, 1.f, smooth)); \ + } \ + } \ + } \ +} + +RADIAL_TRANSITION(8, uint8_t, 1) +RADIAL_TRANSITION(16, uint16_t, 2) + +#define SMOOTHLEFT_TRANSITION(name, type, div) \ +static void smoothleft##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; \ + const float w = width; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + for (int x = 0; x < width; x++) { \ + const float smooth = 1.f + x / w - progress * 2.f; \ + \ + 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, 1.f, smooth)); \ + } \ + } \ + } \ +} + +SMOOTHLEFT_TRANSITION(8, uint8_t, 1) +SMOOTHLEFT_TRANSITION(16, uint16_t, 2) + +#define SMOOTHRIGHT_TRANSITION(name, type, div) \ +static void smoothright##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; \ + const float w = width; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + for (int x = 0; x < width; x++) { \ + const float smooth = 1.f + (w - 1 - x) / w - progress * 2.f; \ + \ + 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, 1.f, smooth)); \ + } \ + } \ + } \ +} + +SMOOTHRIGHT_TRANSITION(8, uint8_t, 1) +SMOOTHRIGHT_TRANSITION(16, uint16_t, 2) + +#define SMOOTHUP_TRANSITION(name, type, div) \ +static void smoothup##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; \ + const float h = out->height; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + const float smooth = 1.f + y / h - progress * 2.f; \ + for (int x = 0; x < width; x++) { \ + 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, 1.f, smooth)); \ + } \ + } \ + } \ +} + +SMOOTHUP_TRANSITION(8, uint8_t, 1) +SMOOTHUP_TRANSITION(16, uint16_t, 2) + +#define SMOOTHDOWN_TRANSITION(name, type, div) \ +static void smoothdown##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; \ + const float h = out->height; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + const float smooth = 1.f + (h - 1 - y) / h - progress * 2.f; \ + for (int x = 0; x < width; x++) { \ + 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, 1.f, smooth)); \ + } \ + } \ + } \ +} + +SMOOTHDOWN_TRANSITION(8, uint8_t, 1) +SMOOTHDOWN_TRANSITION(16, uint16_t, 2) + +#define CIRCLEOPEN_TRANSITION(name, type, div) \ +static void circleopen##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; \ + const int height = out->height; \ + const float z = hypotf(width / 2, height / 2); \ + const float p = (progress - 0.5f) * 3.f; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + for (int x = 0; x < width; x++) { \ + const float smooth = hypotf(x - width / 2, y - height / 2) / z + p; \ + 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(xf0[x], xf1[x], smoothstep(0.f, 1.f, smooth)); \ + } \ + } \ + } \ +} + +CIRCLEOPEN_TRANSITION(8, uint8_t, 1) +CIRCLEOPEN_TRANSITION(16, uint16_t, 2) + +#define CIRCLECLOSE_TRANSITION(name, type, div) \ +static void circleclose##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; \ + const int height = out->height; \ + const float z = hypotf(width / 2, height / 2); \ + const float p = (1.f - progress - 0.5f) * 3.f; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + for (int x = 0; x < width; x++) { \ + const float smooth = hypotf(x - width / 2, y - height / 2) / z + p; \ + 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, 1.f, smooth)); \ + } \ + } \ + } \ +} + +CIRCLECLOSE_TRANSITION(8, uint8_t, 1) +CIRCLECLOSE_TRANSITION(16, uint16_t, 2) + +#define VERTOPEN_TRANSITION(name, type, div) \ +static void vertopen##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; \ + 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 = 2.f - fabsf((x - w2) / w2) - progress * 2.f; \ + 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, 1.f, smooth)); \ + } \ + } \ + } \ +} + +VERTOPEN_TRANSITION(8, uint8_t, 1) +VERTOPEN_TRANSITION(16, uint16_t, 2) + +#define VERTCLOSE_TRANSITION(name, type, div) \ +static void vertclose##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; \ + 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++) { \ + 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, 1.f, smooth)); \ + } \ + } \ + } \ +} + +VERTCLOSE_TRANSITION(8, uint8_t, 1) +VERTCLOSE_TRANSITION(16, uint16_t, 2) + +#define HORZOPEN_TRANSITION(name, type, div) \ +static void horzopen##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; \ + 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++) { \ + 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, 1.f, smooth)); \ + } \ + } \ + } \ +} + +HORZOPEN_TRANSITION(8, uint8_t, 1) +HORZOPEN_TRANSITION(16, uint16_t, 2) + +#define HORZCLOSE_TRANSITION(name, type, div) \ +static void horzclose##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; \ + 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++) { \ + 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, 1.f, smooth)); \ + } \ + } \ + } \ +} + +HORZCLOSE_TRANSITION(8, uint8_t, 1) +HORZCLOSE_TRANSITION(16, uint16_t, 2) + +static float frand(int x, int y) +{ + const float r = sinf(x * 12.9898f + y * 78.233f) * 43758.545f; + + return r - floorf(r); +} + +#define DISSOLVE_TRANSITION(name, type, div) \ +static void dissolve##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++) { \ + 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++) { \ + 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] = smooth >= 0.5f ? xf0[x] : xf1[x]; \ + } \ + } \ + } \ +} + +DISSOLVE_TRANSITION(8, uint8_t, 1) +DISSOLVE_TRANSITION(16, uint16_t, 2) + +#define PIXELIZE_TRANSITION(name, type, div) \ +static void pixelize##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 w = out->width; \ + const int h = out->height; \ + const float d = fminf(progress, 1.f - progress); \ + const float dist = ceilf(d * 50.f) / 50.f; \ + const float sqx = 2.f * dist * FFMIN(w, h) / 20.f; \ + const float sqy = 2.f * dist * FFMIN(w, h) / 20.f; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + 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++) { \ + 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]); \ + \ + dst[x] = mix(xf0[sx], xf1[sx], progress); \ + } \ + } \ + } \ +} + +PIXELIZE_TRANSITION(8, uint8_t, 1) +PIXELIZE_TRANSITION(16, uint16_t, 2) + +#define DIAGTL_TRANSITION(name, type, div) \ +static void diagtl##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; \ + const float w = width; \ + const float h = out->height; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + 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++) { \ + 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, 1.f, smooth)); \ + } \ + } \ + } \ +} + +DIAGTL_TRANSITION(8, uint8_t, 1) +DIAGTL_TRANSITION(16, uint16_t, 2) + +#define DIAGTR_TRANSITION(name, type, div) \ +static void diagtr##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; \ + const float w = width; \ + const float h = out->height; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + 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++) { \ + 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, 1.f, smooth)); \ + } \ + } \ + } \ +} + +DIAGTR_TRANSITION(8, uint8_t, 1) +DIAGTR_TRANSITION(16, uint16_t, 2) + +#define DIAGBL_TRANSITION(name, type, div) \ +static void diagbl##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; \ + const float w = width; \ + const float h = out->height; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + 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++) { \ + 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, 1.f, smooth)); \ + } \ + } \ + } \ +} + +DIAGBL_TRANSITION(8, uint8_t, 1) +DIAGBL_TRANSITION(16, uint16_t, 2) + +#define DIAGBR_TRANSITION(name, type, div) \ +static void diagbr##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; \ + const float w = width; \ + const float h = out->height; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + for (int x = 0; x < width; x++) { \ + 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++) { \ + 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, 1.f, smooth)); \ + } \ + } \ + } \ +} + +DIAGBR_TRANSITION(8, uint8_t, 1) +DIAGBR_TRANSITION(16, uint16_t, 2) + +#define HLSLICE_TRANSITION(name, type, div) \ +static void hlslice##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; \ + const float w = width; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + for (int x = 0; x < width; x++) { \ + 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++) { \ + 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], ss); \ + } \ + } \ + } \ +} + +HLSLICE_TRANSITION(8, uint8_t, 1) +HLSLICE_TRANSITION(16, uint16_t, 2) + +#define HRSLICE_TRANSITION(name, type, div) \ +static void hrslice##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; \ + const float w = width; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + for (int x = 0; x < width; x++) { \ + const float xx = (w - 1 - x) / w; \ + 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++) { \ + 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], ss); \ + } \ + } \ + } \ +} + +HRSLICE_TRANSITION(8, uint8_t, 1) +HRSLICE_TRANSITION(16, uint16_t, 2) + +#define VUSLICE_TRANSITION(name, type, div) \ +static void vuslice##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; \ + const float h = out->height; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + const float smooth = smoothstep(-0.5f, 0.f, y / h - progress * 1.5f); \ + 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++) { \ + 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], ss); \ + } \ + } \ + } \ +} + +VUSLICE_TRANSITION(8, uint8_t, 1) +VUSLICE_TRANSITION(16, uint16_t, 2) + +#define VDSLICE_TRANSITION(name, type, div) \ +static void vdslice##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; \ + const float h = out->height; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + const float yy = (h - 1 - y) / h; \ + const float smooth = smoothstep(-0.5f, 0.f, yy - progress * 1.5f); \ + 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++) { \ + 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], ss); \ + } \ + } \ + } \ +} + +VDSLICE_TRANSITION(8, uint8_t, 1) +VDSLICE_TRANSITION(16, uint16_t, 2) + +static inline double getpix(void *priv, double x, double y, int plane, int nb) +{ + XFadeContext *s = priv; + AVFrame *in = s->xf[nb]; + const uint8_t *src = in->data[FFMIN(plane, s->nb_planes - 1)]; + int linesize = in->linesize[FFMIN(plane, s->nb_planes - 1)]; + const int w = in->width; + const int h = in->height; + + int xi, yi; + + xi = av_clipd(x, 0, w - 1); + yi = av_clipd(y, 0, h - 1); + + if (s->depth > 8) { + const uint16_t *src16 = (const uint16_t*)src; + + linesize /= 2; + return src16[xi + yi * linesize]; + } else { + return src[xi + yi * linesize]; + } +} + +static double a0(void *priv, double x, double y) { return getpix(priv, x, y, 0, 0); } +static double a1(void *priv, double x, double y) { return getpix(priv, x, y, 1, 0); } +static double a2(void *priv, double x, double y) { return getpix(priv, x, y, 2, 0); } +static double a3(void *priv, double x, double y) { return getpix(priv, x, y, 3, 0); } + +static double b0(void *priv, double x, double y) { return getpix(priv, x, y, 0, 1); } +static double b1(void *priv, double x, double y) { return getpix(priv, x, y, 1, 1); } +static double b2(void *priv, double x, double y) { return getpix(priv, x, y, 2, 1); } +static double b3(void *priv, double x, double y) { return getpix(priv, x, y, 3, 1); } + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AVFilterLink *inlink0 = ctx->inputs[0]; + AVFilterLink *inlink1 = ctx->inputs[1]; + XFadeContext *s = ctx->priv; + const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink0->format); + int is_rgb; + + if (inlink0->format != inlink1->format) { + av_log(ctx, AV_LOG_ERROR, "inputs must be of same pixel format\n"); + return AVERROR(EINVAL); + } + if (inlink0->w != inlink1->w || inlink0->h != inlink1->h) { + av_log(ctx, 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", + ctx->input_pads[0].name, inlink0->w, inlink0->h, + ctx->input_pads[1].name, inlink1->w, inlink1->h); + return AVERROR(EINVAL); + } + + if (inlink0->time_base.num != inlink1->time_base.num || + inlink0->time_base.den != inlink1->time_base.den) { + av_log(ctx, AV_LOG_ERROR, "First input link %s timebase " + "(%d/%d) do not match the corresponding " + "second input link %s timebase (%d/%d)\n", + ctx->input_pads[0].name, inlink0->time_base.num, inlink0->time_base.den, + ctx->input_pads[1].name, inlink1->time_base.num, inlink1->time_base.den); + return AVERROR(EINVAL); + } + + outlink->w = inlink0->w; + outlink->h = inlink0->h; + outlink->time_base = inlink0->time_base; + outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio; + outlink->frame_rate = inlink0->frame_rate; + + s->depth = pix_desc->comp[0].depth; + is_rgb = !!(pix_desc->flags & AV_PIX_FMT_FLAG_RGB); + s->nb_planes = av_pix_fmt_count_planes(inlink0->format); + s->max_value = (1 << s->depth) - 1; + s->black[0] = 0; + s->black[1] = s->black[2] = is_rgb ? 0 : s->max_value / 2; + s->black[3] = s->max_value; + s->white[0] = s->white[3] = s->max_value; + s->white[1] = s->white[2] = is_rgb ? s->max_value : s->max_value / 2; + + s->first_pts = s->last_pts = s->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; + case FADE: s->transitionf = s->depth <= 8 ? fade8_transition : fade16_transition; break; + case WIPELEFT: s->transitionf = s->depth <= 8 ? wipeleft8_transition : wipeleft16_transition; break; + case WIPERIGHT: s->transitionf = s->depth <= 8 ? wiperight8_transition : wiperight16_transition; break; + case WIPEUP: s->transitionf = s->depth <= 8 ? wipeup8_transition : wipeup16_transition; break; + case WIPEDOWN: s->transitionf = s->depth <= 8 ? wipedown8_transition : wipedown16_transition; break; + case SLIDELEFT: s->transitionf = s->depth <= 8 ? slideleft8_transition : slideleft16_transition; break; + case SLIDERIGHT: s->transitionf = s->depth <= 8 ? slideright8_transition : slideright16_transition; break; + case SLIDEUP: s->transitionf = s->depth <= 8 ? slideup8_transition : slideup16_transition; break; + case SLIDEDOWN: s->transitionf = s->depth <= 8 ? slidedown8_transition : slidedown16_transition; break; + case CIRCLECROP: s->transitionf = s->depth <= 8 ? circlecrop8_transition : circlecrop16_transition; break; + case RECTCROP: s->transitionf = s->depth <= 8 ? rectcrop8_transition : rectcrop16_transition; break; + case DISTANCE: s->transitionf = s->depth <= 8 ? distance8_transition : distance16_transition; break; + case FADEBLACK: s->transitionf = s->depth <= 8 ? fadeblack8_transition : fadeblack16_transition; break; + case FADEWHITE: s->transitionf = s->depth <= 8 ? fadewhite8_transition : fadewhite16_transition; break; + case RADIAL: s->transitionf = s->depth <= 8 ? radial8_transition : radial16_transition; break; + case SMOOTHLEFT: s->transitionf = s->depth <= 8 ? smoothleft8_transition : smoothleft16_transition; break; + case SMOOTHRIGHT:s->transitionf = s->depth <= 8 ? smoothright8_transition: smoothright16_transition;break; + case SMOOTHUP: s->transitionf = s->depth <= 8 ? smoothup8_transition : smoothup16_transition; break; + case SMOOTHDOWN: s->transitionf = s->depth <= 8 ? smoothdown8_transition : smoothdown16_transition; break; + case CIRCLEOPEN: s->transitionf = s->depth <= 8 ? circleopen8_transition : circleopen16_transition; break; + case CIRCLECLOSE:s->transitionf = s->depth <= 8 ? circleclose8_transition: circleclose16_transition;break; + case VERTOPEN: s->transitionf = s->depth <= 8 ? vertopen8_transition : vertopen16_transition; break; + case VERTCLOSE: s->transitionf = s->depth <= 8 ? vertclose8_transition : vertclose16_transition; break; + case HORZOPEN: s->transitionf = s->depth <= 8 ? horzopen8_transition : horzopen16_transition; break; + case HORZCLOSE: s->transitionf = s->depth <= 8 ? horzclose8_transition : horzclose16_transition; break; + case DISSOLVE: s->transitionf = s->depth <= 8 ? dissolve8_transition : dissolve16_transition; break; + case PIXELIZE: s->transitionf = s->depth <= 8 ? pixelize8_transition : pixelize16_transition; break; + case DIAGTL: s->transitionf = s->depth <= 8 ? diagtl8_transition : diagtl16_transition; break; + case DIAGTR: s->transitionf = s->depth <= 8 ? diagtr8_transition : diagtr16_transition; break; + case DIAGBL: s->transitionf = s->depth <= 8 ? diagbl8_transition : diagbl16_transition; break; + case DIAGBR: s->transitionf = s->depth <= 8 ? diagbr8_transition : diagbr16_transition; break; + case HLSLICE: s->transitionf = s->depth <= 8 ? hlslice8_transition : hlslice16_transition; break; + case HRSLICE: s->transitionf = s->depth <= 8 ? hrslice8_transition : hrslice16_transition; break; + case VUSLICE: s->transitionf = s->depth <= 8 ? vuslice8_transition : vuslice16_transition; break; + case VDSLICE: s->transitionf = s->depth <= 8 ? vdslice8_transition : vdslice16_transition; break; + } + + if (s->transition == CUSTOM) { + static const char *const func2_names[] = { + "a0", "a1", "a2", "a3", + "b0", "b1", "b2", "b3", + NULL + }; + double (*func2[])(void *, double, double) = { + a0, a1, a2, a3, + b0, b1, b2, b3, + NULL }; + int ret; + + if (!s->custom_str) + return AVERROR(EINVAL); + ret = av_expr_parse(&s->e, s->custom_str, var_names, + NULL, NULL, func2_names, func2, 0, ctx); + if (ret < 0) + return ret; + } + + return 0; +} + +static int xfade_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + XFadeContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + ThreadData *td = arg; + int slice_start = (outlink->h * jobnr ) / nb_jobs; + int slice_end = (outlink->h * (jobnr+1)) / nb_jobs; + + s->transitionf(ctx, td->xf[0], td->xf[1], td->out, td->progress, slice_start, slice_end, jobnr); + + return 0; +} + +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); + ThreadData td; + AVFrame *out; + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) + return AVERROR(ENOMEM); + + td.xf[0] = a, td.xf[1] = b, td.out = out, td.progress = progress; + ctx->internal->execute(ctx, xfade_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); + + out->pts = s->pts; + + return ff_filter_frame(outlink, out); +} + +static int xfade_activate(AVFilterContext *ctx) +{ + XFadeContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *in = NULL; + int ret = 0, status; + int64_t pts; + + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); + + if (s->xfade_is_over) { + 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 (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; + } + 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); + } + + s->need_second = 1; + } + } + + 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; + } + + if (ff_inlink_queued_frames(ctx->inputs[0]) > 0 && + ff_inlink_queued_frames(ctx->inputs[1]) > 0) { + ff_filter_set_ready(ctx, 100); + return 0; + } + + 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_request_frame(ctx->inputs[0]); + if (!s->eof[1] && (s->need_second || s->eof[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); + return 0; + } + + return FFERROR_NOT_READY; +} + +static const AVFilterPad xfade_inputs[] = { + { + .name = "main", + .type = AVMEDIA_TYPE_VIDEO, + }, + { + .name = "xfade", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +static const AVFilterPad xfade_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, + { NULL } +}; + +AVFilter ff_vf_xfade = { + .name = "xfade", + .description = NULL_IF_CONFIG_SMALL("Cross fade one video with another video."), + .priv_size = sizeof(XFadeContext), + .priv_class = &xfade_class, + .query_formats = query_formats, + .activate = xfade_activate, + .uninit = uninit, + .inputs = xfade_inputs, + .outputs = xfade_outputs, + .flags = AVFILTER_FLAG_SLICE_THREADS, +}; diff --git a/libavfilter/vf_xfade_opencl.c b/libavfilter/vf_xfade_opencl.c new file mode 100644 index 00000000000..47360431474 --- /dev/null +++ b/libavfilter/vf_xfade_opencl.c @@ -0,0 +1,439 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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/log.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" + +#include "avfilter.h" +#include "filters.h" +#include "internal.h" +#include "opencl.h" +#include "opencl_source.h" +#include "video.h" + +enum XFadeTransitions { + CUSTOM, + FADE, + WIPELEFT, + WIPERIGHT, + WIPEUP, + WIPEDOWN, + SLIDELEFT, + SLIDERIGHT, + SLIDEUP, + SLIDEDOWN, + NB_TRANSITIONS, +}; + +typedef struct XFadeOpenCLContext { + OpenCLFilterContext ocf; + + int transition; + const char *source_file; + const char *kernel_name; + int64_t duration; + int64_t offset; + + int initialised; + cl_kernel kernel; + cl_command_queue command_queue; + + int nb_planes; + + int64_t duration_pts; + int64_t offset_pts; + int64_t first_pts; + int64_t last_pts; + int64_t pts; + int xfade_is_over; + int need_second; + int eof[2]; + AVFrame *xf[2]; +} XFadeOpenCLContext; + +static int xfade_opencl_load(AVFilterContext *avctx, + enum AVPixelFormat main_format, + enum AVPixelFormat xfade_format) +{ + XFadeOpenCLContext *ctx = avctx->priv; + cl_int cle; + const AVPixFmtDescriptor *main_desc; + int err, main_planes; + const char *kernel_name; + + main_desc = av_pix_fmt_desc_get(main_format); + if (main_format != xfade_format) { + av_log(avctx, AV_LOG_ERROR, "Input formats are not same.\n"); + return AVERROR(EINVAL); + } + + main_planes = 0; + for (int i = 0; i < main_desc->nb_components; i++) + main_planes = FFMAX(main_planes, + main_desc->comp[i].plane + 1); + + ctx->nb_planes = main_planes; + + 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); + } + if (err < 0) + return err; + + ctx->command_queue = clCreateCommandQueue(ctx->ocf.hwctx->context, + ctx->ocf.hwctx->device_id, + 0, &cle); + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to create OpenCL " + "command queue %d.\n", cle); + + switch (ctx->transition) { + case CUSTOM: kernel_name = ctx->kernel_name; break; + case FADE: kernel_name = "fade"; break; + case WIPELEFT: kernel_name = "wipeleft"; break; + case WIPERIGHT: kernel_name = "wiperight"; break; + case WIPEUP: kernel_name = "wipeup"; break; + case WIPEDOWN: kernel_name = "wipedown"; break; + case SLIDELEFT: kernel_name = "slideleft"; break; + case SLIDERIGHT: kernel_name = "slideright"; break; + case SLIDEUP: kernel_name = "slideup"; break; + case SLIDEDOWN: kernel_name = "slidedown"; break; + default: + err = AVERROR_BUG; + goto fail; + } + + ctx->kernel = clCreateKernel(ctx->ocf.program, kernel_name, &cle); + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to create kernel %d.\n", cle); + + ctx->initialised = 1; + + return 0; + +fail: + if (ctx->command_queue) + clReleaseCommandQueue(ctx->command_queue); + if (ctx->kernel) + clReleaseKernel(ctx->kernel); + return err; +} + +static int xfade_frame(AVFilterContext *avctx, AVFrame *a, AVFrame *b) +{ + AVFilterLink *outlink = avctx->outputs[0]; + XFadeOpenCLContext *ctx = avctx->priv; + AVFrame *output; + cl_int cle; + cl_float progress = av_clipf(1.f - ((cl_float)(ctx->pts - ctx->first_pts - ctx->offset_pts) / ctx->duration_pts), 0.f, 1.f); + size_t global_work[2]; + int kernel_arg = 0; + int err, plane; + + if (!ctx->initialised) { + AVHWFramesContext *main_fc = + (AVHWFramesContext*)a->hw_frames_ctx->data; + AVHWFramesContext *xfade_fc = + (AVHWFramesContext*)b->hw_frames_ctx->data; + + err = xfade_opencl_load(avctx, main_fc->sw_format, + xfade_fc->sw_format); + if (err < 0) + return err; + } + + output = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!output) { + err = AVERROR(ENOMEM); + goto fail; + } + + for (plane = 0; plane < ctx->nb_planes; plane++) { + cl_mem mem; + kernel_arg = 0; + + mem = (cl_mem)output->data[plane]; + CL_SET_KERNEL_ARG(ctx->kernel, kernel_arg, cl_mem, &mem); + kernel_arg++; + + mem = (cl_mem)ctx->xf[0]->data[plane]; + CL_SET_KERNEL_ARG(ctx->kernel, kernel_arg, cl_mem, &mem); + kernel_arg++; + + mem = (cl_mem)ctx->xf[1]->data[plane]; + CL_SET_KERNEL_ARG(ctx->kernel, kernel_arg, cl_mem, &mem); + kernel_arg++; + + CL_SET_KERNEL_ARG(ctx->kernel, kernel_arg, cl_float, &progress); + kernel_arg++; + + err = ff_opencl_filter_work_size_from_image(avctx, global_work, + output, plane, 0); + if (err < 0) + goto fail; + + cle = clEnqueueNDRangeKernel(ctx->command_queue, ctx->kernel, 2, NULL, + global_work, NULL, 0, NULL, NULL); + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to enqueue xfade kernel " + "for plane %d: %d.\n", plane, cle); + } + + cle = clFinish(ctx->command_queue); + CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to finish command queue: %d.\n", cle); + + err = av_frame_copy_props(output, ctx->xf[0]); + if (err < 0) + goto fail; + + output->pts = ctx->pts; + + return ff_filter_frame(outlink, output); + +fail: + av_frame_free(&output); + return err; +} + +static int xfade_opencl_config_output(AVFilterLink *outlink) +{ + AVFilterContext *avctx = outlink->src; + XFadeOpenCLContext *ctx = avctx->priv; + AVFilterLink *inlink0 = avctx->inputs[0]; + AVFilterLink *inlink1 = avctx->inputs[1]; + int err; + + err = ff_opencl_filter_config_output(outlink); + if (err < 0) + return err; + + if (inlink0->w != inlink1->w || inlink0->h != inlink1->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[0].name, inlink0->w, inlink0->h, + avctx->input_pads[1].name, inlink1->w, inlink1->h); + return AVERROR(EINVAL); + } + + if (inlink0->time_base.num != inlink1->time_base.num || + inlink0->time_base.den != inlink1->time_base.den) { + av_log(avctx, AV_LOG_ERROR, "First input link %s timebase " + "(%d/%d) do not match the corresponding " + "second input link %s timebase (%d/%d)\n", + avctx->input_pads[0].name, inlink0->time_base.num, inlink0->time_base.den, + avctx->input_pads[1].name, inlink1->time_base.num, inlink1->time_base.den); + return AVERROR(EINVAL); + } + + ctx->first_pts = ctx->last_pts = ctx->pts = AV_NOPTS_VALUE; + + outlink->time_base = inlink0->time_base; + outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio; + outlink->frame_rate = inlink0->frame_rate; + + if (ctx->duration) + ctx->duration_pts = av_rescale_q(ctx->duration, AV_TIME_BASE_Q, outlink->time_base); + if (ctx->offset) + ctx->offset_pts = av_rescale_q(ctx->offset, AV_TIME_BASE_Q, outlink->time_base); + + return 0; +} + +static int xfade_opencl_activate(AVFilterContext *avctx) +{ + XFadeOpenCLContext *ctx = avctx->priv; + AVFilterLink *outlink = avctx->outputs[0]; + AVFrame *in = NULL; + int ret = 0, status; + int64_t pts; + + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, avctx); + + if (ctx->xfade_is_over) { + ret = ff_inlink_consume_frame(avctx->inputs[1], &in); + if (ret < 0) { + return ret; + } else if (ret > 0) { + in->pts = (in->pts - ctx->last_pts) + ctx->pts; + return ff_filter_frame(outlink, in); + } else if (ff_inlink_acknowledge_status(avctx->inputs[1], &status, &pts)) { + ff_outlink_set_status(outlink, status, ctx->pts); + return 0; + } else if (!ret) { + if (ff_outlink_frame_wanted(outlink)) { + ff_inlink_request_frame(avctx->inputs[1]); + return 0; + } + } + } + + if (ff_inlink_queued_frames(avctx->inputs[0]) > 0) { + ctx->xf[0] = ff_inlink_peek_frame(avctx->inputs[0], 0); + if (ctx->xf[0]) { + if (ctx->first_pts == AV_NOPTS_VALUE) { + ctx->first_pts = ctx->xf[0]->pts; + } + ctx->pts = ctx->xf[0]->pts; + if (ctx->first_pts + ctx->offset_pts > ctx->xf[0]->pts) { + ctx->xf[0] = NULL; + ctx->need_second = 0; + ff_inlink_consume_frame(avctx->inputs[0], &in); + return ff_filter_frame(outlink, in); + } + + ctx->need_second = 1; + } + } + + if (ctx->xf[0] && ff_inlink_queued_frames(avctx->inputs[1]) > 0) { + ff_inlink_consume_frame(avctx->inputs[0], &ctx->xf[0]); + ff_inlink_consume_frame(avctx->inputs[1], &ctx->xf[1]); + + ctx->last_pts = ctx->xf[1]->pts; + ctx->pts = ctx->xf[0]->pts; + if (ctx->xf[0]->pts - (ctx->first_pts + ctx->offset_pts) > ctx->duration_pts) + ctx->xfade_is_over = 1; + ret = xfade_frame(avctx, ctx->xf[0], ctx->xf[1]); + av_frame_free(&ctx->xf[0]); + av_frame_free(&ctx->xf[1]); + return ret; + } + + if (ff_inlink_queued_frames(avctx->inputs[0]) > 0 && + ff_inlink_queued_frames(avctx->inputs[1]) > 0) { + ff_filter_set_ready(avctx, 100); + return 0; + } + + if (ff_outlink_frame_wanted(outlink)) { + if (!ctx->eof[0] && ff_outlink_get_status(avctx->inputs[0])) { + ctx->eof[0] = 1; + ctx->xfade_is_over = 1; + } + if (!ctx->eof[1] && ff_outlink_get_status(avctx->inputs[1])) { + ctx->eof[1] = 1; + } + if (!ctx->eof[0] && !ctx->xf[0]) + ff_inlink_request_frame(avctx->inputs[0]); + if (!ctx->eof[1] && (ctx->need_second || ctx->eof[0])) + ff_inlink_request_frame(avctx->inputs[1]); + if (ctx->eof[0] && ctx->eof[1] && ( + ff_inlink_queued_frames(avctx->inputs[0]) <= 0 || + ff_inlink_queued_frames(avctx->inputs[1]) <= 0)) + ff_outlink_set_status(outlink, AVERROR_EOF, AV_NOPTS_VALUE); + return 0; + } + + return FFERROR_NOT_READY; +} + +static av_cold void xfade_opencl_uninit(AVFilterContext *avctx) +{ + XFadeOpenCLContext *ctx = avctx->priv; + cl_int cle; + + if (ctx->kernel) { + cle = clReleaseKernel(ctx->kernel); + if (cle != CL_SUCCESS) + av_log(avctx, AV_LOG_ERROR, "Failed to release " + "kernel: %d.\n", cle); + } + + if (ctx->command_queue) { + cle = clReleaseCommandQueue(ctx->command_queue); + if (cle != CL_SUCCESS) + av_log(avctx, AV_LOG_ERROR, "Failed to release " + "command queue: %d.\n", cle); + } + + ff_opencl_filter_uninit(avctx); +} + +static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h) +{ + XFadeOpenCLContext *s = inlink->dst->priv; + + return s->xfade_is_over || !s->need_second ? + ff_null_get_video_buffer (inlink, w, h) : + ff_default_get_video_buffer(inlink, w, h); +} + +#define OFFSET(x) offsetof(XFadeOpenCLContext, x) +#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" }, + { "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 }, + { "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_opencl); + +static const AVFilterPad xfade_opencl_inputs[] = { + { + .name = "main", + .type = AVMEDIA_TYPE_VIDEO, + .get_video_buffer = get_video_buffer, + .config_props = &ff_opencl_filter_config_input, + }, + { + .name = "xfade", + .type = AVMEDIA_TYPE_VIDEO, + .get_video_buffer = get_video_buffer, + .config_props = &ff_opencl_filter_config_input, + }, + { NULL } +}; + +static const AVFilterPad xfade_opencl_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &xfade_opencl_config_output, + }, + { NULL } +}; + +AVFilter ff_vf_xfade_opencl = { + .name = "xfade_opencl", + .description = NULL_IF_CONFIG_SMALL("Cross fade one video with another video."), + .priv_size = sizeof(XFadeOpenCLContext), + .priv_class = &xfade_opencl_class, + .init = &ff_opencl_filter_init, + .uninit = &xfade_opencl_uninit, + .query_formats = &ff_opencl_filter_query_formats, + .activate = &xfade_opencl_activate, + .inputs = xfade_opencl_inputs, + .outputs = xfade_opencl_outputs, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_xmedian.c b/libavfilter/vf_xmedian.c index 672b3a7e78a..15857d6f149 100644 --- a/libavfilter/vf_xmedian.c +++ b/libavfilter/vf_xmedian.c @@ -35,9 +35,13 @@ typedef struct XMedianContext { const AVClass *class; const AVPixFmtDescriptor *desc; int nb_inputs; + int nb_frames; int planes; + float percentile; + int tmedian; int radius; + int index; int depth; int max; int nb_planes; @@ -75,6 +79,11 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, 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_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, + AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA422P16, + AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_NONE }; AVFilterFormats *formats = ff_make_format_list(pixel_fmts); @@ -88,12 +97,23 @@ static av_cold int init(AVFilterContext *ctx) XMedianContext *s = ctx->priv; int ret; - s->radius = s->nb_inputs / 2; + s->tmedian = !strcmp(ctx->filter->name, "tmedian"); + + if (!s->tmedian) { + s->radius = s->nb_inputs / 2; + } else { + s->nb_inputs = s->radius * 2 + 1; + } + + if (s->nb_inputs & 1) + s->index = s->radius * 2.f * s->percentile; + else + s->index = av_clip(s->radius * 2.f * s->percentile, 1, s->nb_inputs - 1); s->frames = av_calloc(s->nb_inputs, sizeof(*s->frames)); if (!s->frames) return AVERROR(ENOMEM); - for (int i = 0; i < s->nb_inputs; i++) { + for (int i = 0; i < s->nb_inputs && !s->tmedian; i++) { AVFilterPad pad = { 0 }; pad.type = AVMEDIA_TYPE_VIDEO; @@ -129,6 +149,7 @@ static int median_frames16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jo AVFrame *out = td->out; const int nb_inputs = s->nb_inputs; const int radius = s->radius; + const int index = s->index; int values[256]; for (int p = 0; p < s->nb_planes; p++) { @@ -138,8 +159,8 @@ static int median_frames16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jo if (!((1 << p) & s->planes)) { av_image_copy_plane((uint8_t *)dst, out->linesize[p], - in[0]->data[p] + slice_start * in[radius]->linesize[p], - in[0]->linesize[p], + in[radius]->data[p] + slice_start * in[radius]->linesize[p], + in[radius]->linesize[p], s->linesize[p], slice_end - slice_start); continue; } @@ -152,10 +173,10 @@ static int median_frames16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jo } AV_QSORT(values, nb_inputs, int, comparei); - if (radius & 1) - dst[x] = values[radius]; + if (nb_inputs & 1) + dst[x] = values[index]; else - dst[x] = (values[radius] + values[radius - 1]) >> 1; + dst[x] = (values[index] + values[index - 1]) >> 1; } dst += out->linesize[p] / 2; @@ -173,6 +194,7 @@ static int median_frames8(AVFilterContext *ctx, void *arg, int jobnr, int nb_job AVFrame *out = td->out; const int nb_inputs = s->nb_inputs; const int radius = s->radius; + const int index = s->index; int values[256]; for (int p = 0; p < s->nb_planes; p++) { @@ -182,8 +204,8 @@ static int median_frames8(AVFilterContext *ctx, void *arg, int jobnr, int nb_job if (!((1 << p) & s->planes)) { av_image_copy_plane(dst, out->linesize[p], - in[0]->data[p] + slice_start * in[0]->linesize[p], - in[0]->linesize[p], + in[radius]->data[p] + slice_start * in[radius]->linesize[p], + in[radius]->linesize[p], s->linesize[p], slice_end - slice_start); continue; } @@ -194,10 +216,10 @@ static int median_frames8(AVFilterContext *ctx, void *arg, int jobnr, int nb_job values[i] = in[i]->data[p][y * in[i]->linesize[p] + x]; AV_QSORT(values, nb_inputs, int, comparei); - if (radius & 1) - dst[x] = values[radius]; + if (nb_inputs & 1) + dst[x] = values[index]; else - dst[x] = (values[radius] + values[radius - 1]) >> 1; + dst[x] = (values[index] + values[index - 1]) >> 1; } dst += out->linesize[p]; @@ -246,7 +268,7 @@ static int config_output(AVFilterLink *outlink) FFFrameSyncIn *in; int i, ret; - for (int i = 1; i < s->nb_inputs; i++) { + for (int i = 1; i < s->nb_inputs && !s->tmedian; i++) { if (ctx->inputs[i]->h != height || ctx->inputs[i]->w != width) { av_log(ctx, AV_LOG_ERROR, "Input %d size (%dx%d) does not match input %d size (%dx%d).\n", i, ctx->inputs[i]->w, ctx->inputs[i]->h, 0, width, height); return AVERROR(EINVAL); @@ -273,6 +295,9 @@ static int config_output(AVFilterLink *outlink) s->height[1] = s->height[2] = AV_CEIL_RSHIFT(inlink->h, s->desc->log2_chroma_h); s->height[0] = s->height[3] = inlink->h; + if (s->tmedian) + return 0; + outlink->w = width; outlink->h = height; outlink->frame_rate = frame_rate; @@ -305,10 +330,12 @@ static av_cold void uninit(AVFilterContext *ctx) XMedianContext *s = ctx->priv; ff_framesync_uninit(&s->fs); - av_freep(&s->frames); - for (int i = 0; i < ctx->nb_inputs; i++) + for (int i = 0; i < ctx->nb_inputs && !s->tmedian; i++) av_freep(&ctx->input_pads[i].name); + for (int i = 0; i < s->nb_frames && s->frames && s->tmedian; i++) + av_frame_free(&s->frames[i]); + av_freep(&s->frames); } static int activate(AVFilterContext *ctx) @@ -323,6 +350,7 @@ static int activate(AVFilterContext *ctx) static const AVOption xmedian_options[] = { { "inputs", "set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64=3}, 3, 255, .flags = FLAGS }, { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15}, 0, 15, .flags = FLAGS }, + { "percentile", "set percentile", OFFSET(percentile),AV_OPT_TYPE_FLOAT,{.dbl=0.5}, 0, 1, .flags = FLAGS }, { NULL }, }; @@ -335,6 +363,7 @@ static const AVFilterPad outputs[] = { { NULL } }; +#if CONFIG_XMEDIAN_FILTER AVFILTER_DEFINE_CLASS(xmedian); AVFilter ff_vf_xmedian = { @@ -349,3 +378,86 @@ AVFilter ff_vf_xmedian = { .activate = activate, .flags = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_SLICE_THREADS, }; + +#endif /* CONFIG_XMEDIAN_FILTER */ + +#if CONFIG_TMEDIAN_FILTER +static int tmedian_filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + XMedianContext *s = ctx->priv; + ThreadData td; + AVFrame *out; + + if (s->nb_frames < s->nb_inputs) { + s->frames[s->nb_frames] = in; + s->nb_frames++; + if (s->nb_frames < s->nb_inputs) + return 0; + } else { + 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; + } + + if (ctx->is_disabled) { + out = av_frame_clone(s->frames[0]); + if (!out) + return AVERROR(ENOMEM); + return ff_filter_frame(outlink, out); + } + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) + return AVERROR(ENOMEM); + out->pts = s->frames[0]->pts; + + td.out = out; + td.in = s->frames; + ctx->internal->execute(ctx, s->median_frames, &td, NULL, FFMIN(s->height[0], ff_filter_get_nb_threads(ctx))); + + return ff_filter_frame(outlink, out); +} + +static const AVOption tmedian_options[] = { + { "radius", "set median filter radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64=1}, 1, 127, .flags = FLAGS }, + { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15}, 0, 15, .flags = FLAGS }, + { "percentile", "set percentile", OFFSET(percentile), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, .flags = FLAGS }, + { NULL }, +}; + +static const AVFilterPad tmedian_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = tmedian_filter_frame, + }, + { NULL } +}; + +static const AVFilterPad tmedian_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(tmedian); + +AVFilter ff_vf_tmedian = { + .name = "tmedian", + .description = NULL_IF_CONFIG_SMALL("Pick median pixels from successive frames."), + .priv_size = sizeof(XMedianContext), + .priv_class = &tmedian_class, + .query_formats = query_formats, + .inputs = tmedian_inputs, + .outputs = tmedian_outputs, + .init = init, + .uninit = uninit, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, +}; + +#endif /* CONFIG_TMEDIAN_FILTER */ diff --git a/libavfilter/vf_yadif.c b/libavfilter/vf_yadif.c index 3107924932a..43dea67addc 100644 --- a/libavfilter/vf_yadif.c +++ b/libavfilter/vf_yadif.c @@ -265,42 +265,19 @@ static av_cold void uninit(AVFilterContext *ctx) static int query_formats(AVFilterContext *ctx) { 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_GRAY8, - AV_PIX_FMT_YUVJ420P, - AV_PIX_FMT_YUVJ422P, - AV_PIX_FMT_YUVJ444P, - AV_PIX_FMT_GRAY16, - AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV440P, + AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY16, + AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, - AV_PIX_FMT_YUV420P9, - AV_PIX_FMT_YUV422P9, - AV_PIX_FMT_YUV444P9, - 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_YUV420P14, - AV_PIX_FMT_YUV422P14, - AV_PIX_FMT_YUV444P14, - AV_PIX_FMT_YUV420P16, - AV_PIX_FMT_YUV422P16, - AV_PIX_FMT_YUV444P16, - AV_PIX_FMT_YUVA420P, - AV_PIX_FMT_YUVA422P, - AV_PIX_FMT_YUVA444P, - 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_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, + 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_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14, + AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, + AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, + 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_NONE }; @@ -311,26 +288,26 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_formats(ctx, fmts_list); } -static int config_props(AVFilterLink *link) +static int config_output(AVFilterLink *outlink) { - AVFilterContext *ctx = link->src; + AVFilterContext *ctx = outlink->src; YADIFContext *s = ctx->priv; - 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; + outlink->time_base.num = ctx->inputs[0]->time_base.num; + outlink->time_base.den = ctx->inputs[0]->time_base.den * 2; + outlink->w = ctx->inputs[0]->w; + outlink->h = ctx->inputs[0]->h; if(s->mode & 1) - link->frame_rate = av_mul_q(ctx->inputs[0]->frame_rate, + outlink->frame_rate = av_mul_q(ctx->inputs[0]->frame_rate, (AVRational){2, 1}); - if (link->w < 3 || link->h < 3) { + 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); } - s->csp = av_pix_fmt_desc_get(link->format); + s->csp = av_pix_fmt_desc_get(outlink->format); s->filter = filter; if (s->csp->comp[0].depth > 8) { s->filter_line = filter_line_c_16bit; @@ -369,7 +346,7 @@ static const AVFilterPad avfilter_vf_yadif_outputs[] = { .name = "default", .type = AVMEDIA_TYPE_VIDEO, .request_frame = ff_yadif_request_frame, - .config_props = config_props, + .config_props = config_output, }, { NULL } }; diff --git a/libavfilter/vf_yaepblur.c b/libavfilter/vf_yaepblur.c new file mode 100644 index 00000000000..0fb7b84afcc --- /dev/null +++ b/libavfilter/vf_yaepblur.c @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2019 Leo Zhang + + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 + * yaep(yet another edge preserving) blur filter + * + * This implementation is based on an algorithm described in + * "J. S. Lee, Digital image enhancement and noise filtering by use of local statistics, IEEE Trans. Pattern + * Anal. Mach. Intell. PAMI-2, 1980." + */ + +#include "libavutil/opt.h" +#include "libavutil/imgutils.h" +#include "avfilter.h" +#include "internal.h" + +typedef struct YAEPContext { + const AVClass *class; + + int planes; + int radius; + int sigma; + + int nb_planes; + int planewidth[4]; + int planeheight[4]; + int depth; + + uint64_t *sat; ///< summed area table + uint64_t *square_sat; ///< square summed area table + int sat_linesize; + + int (*pre_calculate_row)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); + int (*filter_slice )(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); +} YAEPContext; + +static av_cold void uninit(AVFilterContext *ctx) +{ + YAEPContext *s = ctx->priv; + av_freep(&s->sat); + av_freep(&s->square_sat); +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, + AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, + AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, + 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_YUV440P12, + 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_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9, + AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, + AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, + 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_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_NONE + }; + + return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); +} + +typedef struct ThreadData { + int width; + int height; + int src_linesize; + int dst_linesize; + uint8_t *src; + uint8_t *dst; +} ThreadData; + +#define PRE_CALCULATE_ROW(type, name) \ +static int pre_calculate_row_##name(AVFilterContext *ctx, void *arg, \ + int jobnr, int nb_jobs) \ +{ \ + ThreadData *td = arg; \ + YAEPContext *s = ctx->priv; \ + \ + const int width = td->width; \ + const int height = td->height; \ + const int linesize = td->src_linesize / sizeof(type); \ + const int sat_linesize = s->sat_linesize; \ + \ + const int starty = height * jobnr / nb_jobs; \ + const int endy = height * (jobnr+1) / nb_jobs; \ + \ + uint64_t *sat = s->sat + (starty + 1) * sat_linesize; \ + uint64_t *square_sat = s->square_sat + (starty + 1) * sat_linesize; \ + const type *src = (const type *)td->src + starty * linesize; \ + \ + int x, y; \ + \ + for (y = starty; y < endy; y++) { \ + for (x = 0; x < width; x++) { \ + sat[x+1] = sat[x] + src[x]; \ + square_sat[x+1] = square_sat[x] + (uint64_t)src[x] * src[x]; \ + } \ + sat += sat_linesize; \ + square_sat += sat_linesize; \ + src += linesize; \ + } \ + \ + return 0; \ +} + +PRE_CALCULATE_ROW(uint8_t, byte) +PRE_CALCULATE_ROW(uint16_t, word) + +static int pre_calculate_col(AVFilterContext *ctx, void *arg, + int jobnr, int nb_jobs) +{ + ThreadData *td = arg; + YAEPContext *s = ctx->priv; + + const int width = td->width; + const int height = td->height; + const int sat_linesize = s->sat_linesize; + + const int startx = width * jobnr / nb_jobs; + const int endx = width * (jobnr + 1) / nb_jobs; + + uint64_t *sat, *square_sat; + int x, y; + + for (x = startx; x < endx; x++) { + sat = s->sat + x + 1; + square_sat = s->square_sat + x + 1; + for (y = 0; y < height; y++) { + *(sat+sat_linesize) += *sat; + *(square_sat+sat_linesize) += *square_sat; + sat += sat_linesize; + square_sat += sat_linesize; + } + } + + return 0; +} + +#define FILTER_SLICE(type, name) \ +static int filter_slice_##name(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \ +{ \ + ThreadData *td = arg; \ + YAEPContext *s = ctx->priv; \ + \ + const int width = td->width; \ + const int height = td->height; \ + const int src_linesize = td->src_linesize / sizeof(type); \ + const int dst_linesize = td->dst_linesize / sizeof(type); \ + const int sat_linesize = s->sat_linesize; \ + const int sigma = s->sigma; \ + const int radius = s->radius; \ + \ + uint64_t *sat = s->sat; \ + uint64_t *square_sat = s->square_sat; \ + const type *src = (const type *)td->src; \ + type *dst = (type *)td->dst; \ + \ + const int starty = height * jobnr / nb_jobs; \ + const int endy = height * (jobnr + 1) / nb_jobs; \ + \ + int x, y; \ + int lower_x, higher_x; \ + int lower_y, higher_y; \ + int dist_y, count; \ + uint64_t sum, square_sum, mean, var; \ + \ + for (y = starty; y < endy; y++) { \ + lower_y = y - radius < 0 ? 0 : y - radius; \ + higher_y = y + radius + 1 > height ? height : y + radius + 1; \ + dist_y = higher_y - lower_y; \ + for (x = 0; x < width; x++) { \ + lower_x = x - radius < 0 ? 0 : x - radius; \ + higher_x = x + radius + 1 > width ? width : x + radius + 1; \ + count = dist_y * (higher_x - lower_x); \ + sum = sat[higher_y * sat_linesize + higher_x] \ + - sat[higher_y * sat_linesize + lower_x] \ + - sat[lower_y * sat_linesize + higher_x] \ + + sat[lower_y * sat_linesize + lower_x]; \ + square_sum = square_sat[higher_y * sat_linesize + higher_x] \ + - square_sat[higher_y * sat_linesize + lower_x] \ + - square_sat[lower_y * sat_linesize + higher_x] \ + + square_sat[lower_y * sat_linesize + lower_x]; \ + mean = sum / count; \ + var = (square_sum - sum * sum / count) / count; \ + dst[y * dst_linesize + x] = (sigma * mean + var * src[y * src_linesize + x]) / (sigma + var); \ + } \ + } \ + return 0; \ +} + +FILTER_SLICE(uint8_t, byte) +FILTER_SLICE(uint16_t, word) + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) +{ + AVFilterContext *ctx = inlink->dst; + YAEPContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *out; + int plane; + const int nb_threads = ff_filter_get_nb_threads(ctx); + ThreadData td; + + if (av_frame_is_writable(in)) { + out = in; + } else { + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + av_frame_copy_props(out, in); + } + + for (plane = 0; plane < s->nb_planes; plane++) { + if (!s->radius || !(s->planes & (1<data[plane], out->linesize[plane], + in->data[plane], in->linesize[plane], + s->planewidth[plane] * ((s->depth + 7) / 8), + s->planeheight[plane]); + } + continue; + } + + td.width = s->planewidth[plane]; + td.height = s->planeheight[plane]; + td.src = in->data[plane]; + td.src_linesize = in->linesize[plane]; + ctx->internal->execute(ctx, s->pre_calculate_row, &td, NULL, FFMIN(td.height, nb_threads)); + ctx->internal->execute(ctx, pre_calculate_col, &td, NULL, FFMIN(td.width, nb_threads)); + + td.dst = out->data[plane]; + td.dst_linesize = out->linesize[plane]; + ctx->internal->execute(ctx, s->filter_slice, &td, NULL, FFMIN(td.height, nb_threads)); + } + + if (out != in) + av_frame_free(&in); + + return ff_filter_frame(outlink, out); +} + +static int config_input(AVFilterLink *inlink) +{ + YAEPContext *s = inlink->dst->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + + s->depth = desc->comp[0].depth; + s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); + s->planewidth[0] = s->planewidth[3] = inlink->w; + s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); + s->planeheight[0] = s->planeheight[3] = inlink->h; + s->nb_planes = av_pix_fmt_count_planes(inlink->format); + + s->radius = FFMIN(s->radius, AV_CEIL_RSHIFT(FFMIN(inlink->w, inlink->h), 1)); + + if (s->depth <= 8) { + s->pre_calculate_row = pre_calculate_row_byte; + s->filter_slice = filter_slice_byte; + } else { + s->pre_calculate_row = pre_calculate_row_word; + s->filter_slice = filter_slice_word; + } + + // padding one row on the top, and padding one col on the left, that is why + 1 below + s->sat_linesize = inlink->w + 1; + s->sat = av_mallocz_array(inlink->h + 1, s->sat_linesize*sizeof(*s->sat)); + if (!s->sat) + return AVERROR(ENOMEM); + + s->square_sat = av_mallocz_array(inlink->h + 1, s->sat_linesize*sizeof(*s->square_sat)); + if (!s->square_sat) + return AVERROR(ENOMEM); + + return 0; +} + +static const AVFilterPad yaep_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + .filter_frame = filter_frame, + }, + { NULL } +}; + +static const AVFilterPad yaep_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +#define OFFSET(x) offsetof(YAEPContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM + +static const AVOption yaep_options[] = { + { "radius", "set window radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64=3}, 0, INT_MAX, .flags=FLAGS }, + { "r" , "set window radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64=3}, 0, INT_MAX, .flags=FLAGS }, + { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=1}, 0, 0xF, .flags=FLAGS }, + { "p", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=1}, 0, 0xF, .flags=FLAGS }, + { "sigma", "set blur strength", OFFSET(sigma), AV_OPT_TYPE_INT, {.i64=128}, 1, INT_MAX, .flags=FLAGS }, + { "s", "set blur strength", OFFSET(sigma), AV_OPT_TYPE_INT, {.i64=128}, 1, INT_MAX, .flags=FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(yaep); + +AVFilter ff_vf_yaepblur = { + .name = "yaepblur", + .description = NULL_IF_CONFIG_SMALL("Yet another edge preserving blur filter."), + .priv_size = sizeof(YAEPContext), + .priv_class = &yaep_class, + .uninit = uninit, + .query_formats = query_formats, + .inputs = yaep_inputs, + .outputs = yaep_outputs, + .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 dea10d51371..59c9b19ec85 100644 --- a/libavfilter/vf_zoompan.c +++ b/libavfilter/vf_zoompan.c @@ -245,6 +245,8 @@ static int output_single_frame(AVFilterContext *ctx, AVFrame *in, double *var_va } return ret; error: + sws_freeContext(s->sws); + s->sws = NULL; av_frame_free(&out); return ret; } @@ -257,6 +259,8 @@ static int activate(AVFilterContext *ctx) int status, ret = 0; int64_t pts; + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + if (s->in && ff_outlink_frame_wanted(outlink)) { double zoom = -1, dx = -1, dy = -1; @@ -344,6 +348,10 @@ static av_cold void uninit(AVFilterContext *ctx) sws_freeContext(s->sws); s->sws = NULL; + av_expr_free(s->x_expr); + av_expr_free(s->y_expr); + av_expr_free(s->zoom_expr); + av_frame_free(&s->in); } static const AVFilterPad inputs[] = { diff --git a/libavfilter/vf_zscale.c b/libavfilter/vf_zscale.c index f0309272fa9..023a5d10e96 100644 --- a/libavfilter/vf_zscale.c +++ b/libavfilter/vf_zscale.c @@ -44,6 +44,8 @@ #include "libavutil/imgutils.h" #include "libavutil/avassert.h" +#define ZIMG_ALIGNMENT 32 + static const char *const var_names[] = { "in_w", "iw", "in_h", "ih", @@ -502,6 +504,44 @@ static int graph_build(zimg_filter_graph **graph, zimg_graph_builder_params *par return 0; } +static int realign_frame(const AVPixFmtDescriptor *desc, AVFrame **frame) +{ + AVFrame *aligned = NULL; + int ret = 0, plane; + + /* Realign any unaligned input frame. */ + for (plane = 0; plane < 3; plane++) { + int p = desc->comp[plane].plane; + if ((uintptr_t)(*frame)->data[p] % ZIMG_ALIGNMENT || (*frame)->linesize[p] % ZIMG_ALIGNMENT) { + if (!(aligned = av_frame_alloc())) { + ret = AVERROR(ENOMEM); + goto fail; + } + + aligned->format = (*frame)->format; + aligned->width = (*frame)->width; + aligned->height = (*frame)->height; + + if ((ret = av_frame_get_buffer(aligned, ZIMG_ALIGNMENT)) < 0) + goto fail; + + if ((ret = av_frame_copy(aligned, *frame)) < 0) + goto fail; + + if ((ret = av_frame_copy_props(aligned, *frame)) < 0) + goto fail; + + av_frame_free(frame); + *frame = aligned; + return 0; + } + } + +fail: + av_frame_free(&aligned); + return ret; +} + static int filter_frame(AVFilterLink *link, AVFrame *in) { ZScaleContext *s = link->dst->priv; @@ -512,12 +552,14 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) zimg_image_buffer dst_buf = { ZIMG_API_VERSION }; char buf[32]; int ret = 0, plane; - AVFrame *out; + AVFrame *out = NULL; - out = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!out) { - av_frame_free(&in); - return AVERROR(ENOMEM); + if ((ret = realign_frame(desc, &in)) < 0) + goto fail; + + if (!(out = ff_get_video_buffer(outlink, outlink->w, outlink->h))) { + ret = AVERROR(ENOMEM); + goto fail; } av_frame_copy_props(out, in); @@ -546,11 +588,8 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) link->dst->inputs[0]->w = in->width; link->dst->inputs[0]->h = in->height; - if ((ret = config_props(outlink)) < 0) { - av_frame_free(&in); - av_frame_free(&out); - return ret; - } + if ((ret = config_props(outlink)) < 0) + goto fail; zimg_image_format_default(&s->src_format, ZIMG_API_VERSION); zimg_image_format_default(&s->dst_format, ZIMG_API_VERSION); @@ -702,7 +741,7 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) return ff_filter_frame(outlink, out); } -static void uninit(AVFilterContext *ctx) +static av_cold void uninit(AVFilterContext *ctx) { ZScaleContext *s = ctx->priv; @@ -738,12 +777,13 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar #define OFFSET(x) offsetof(ZScaleContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define TFLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption zscale_options[] = { - { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, - { "width", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, - { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, - { "height", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, + { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, .flags = TFLAGS }, + { "width", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, .flags = TFLAGS }, + { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, .flags = TFLAGS }, + { "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" }, @@ -789,6 +829,7 @@ static const AVOption zscale_options[] = { { "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" }, diff --git a/libavfilter/vsink_nullsink.c b/libavfilter/vsink_nullsink.c index 281721bc555..060a5a371f3 100644 --- a/libavfilter/vsink_nullsink.c +++ b/libavfilter/vsink_nullsink.c @@ -38,9 +38,7 @@ static const AVFilterPad avfilter_vsink_nullsink_inputs[] = { AVFilter ff_vsink_nullsink = { .name = "nullsink", .description = NULL_IF_CONFIG_SMALL("Do absolutely nothing with the input video."), - - .priv_size = 0, - - .inputs = avfilter_vsink_nullsink_inputs, - .outputs = NULL, + .priv_size = 0, + .inputs = avfilter_vsink_nullsink_inputs, + .outputs = NULL, }; diff --git a/libavfilter/vsrc_cellauto.c b/libavfilter/vsrc_cellauto.c index 7a6d9659f7a..6fd812c54f3 100644 --- a/libavfilter/vsrc_cellauto.c +++ b/libavfilter/vsrc_cellauto.c @@ -50,7 +50,7 @@ typedef struct CellAutoContext { uint64_t pts; AVRational frame_rate; double random_fill_ratio; - uint32_t random_seed; + int64_t random_seed; int stitch, scroll, start_full; int64_t generation; ///< the generation number, starting from 0 AVLFG lfg; @@ -72,8 +72,8 @@ static const AVOption cellauto_options[] = { { "rule", "set rule", OFFSET(rule), AV_OPT_TYPE_INT, {.i64 = 110}, 0, 255, FLAGS }, { "random_fill_ratio", "set fill ratio for filling initial grid randomly", OFFSET(random_fill_ratio), AV_OPT_TYPE_DOUBLE, {.dbl = 1/M_PHI}, 0, 1, FLAGS }, { "ratio", "set fill ratio for filling initial grid randomly", OFFSET(random_fill_ratio), AV_OPT_TYPE_DOUBLE, {.dbl = 1/M_PHI}, 0, 1, FLAGS }, - { "random_seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.i64 = -1}, -1, UINT32_MAX, FLAGS }, - { "seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.i64 = -1}, -1, UINT32_MAX, FLAGS }, + { "random_seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT64, {.i64 = -1}, -1, UINT32_MAX, FLAGS }, + { "seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT64, {.i64 = -1}, -1, UINT32_MAX, FLAGS }, { "scroll", "scroll pattern downward", OFFSET(scroll), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, { "start_full", "start filling the whole video", OFFSET(start_full), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, { "full", "start filling the whole video", OFFSET(start_full), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, @@ -199,7 +199,7 @@ static av_cold int init(AVFilterContext *ctx) } av_log(ctx, AV_LOG_VERBOSE, - "s:%dx%d r:%d/%d rule:%d stitch:%d scroll:%d full:%d seed:%"PRIu32"\n", + "s:%dx%d r:%d/%d rule:%d stitch:%d scroll:%d full:%d seed:%"PRId64"\n", s->w, s->h, s->frame_rate.num, s->frame_rate.den, s->rule, s->stitch, s->scroll, s->start_full, s->random_seed); diff --git a/libavfilter/vsrc_gradients.c b/libavfilter/vsrc_gradients.c new file mode 100644 index 00000000000..984227017e9 --- /dev/null +++ b/libavfilter/vsrc_gradients.c @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2020 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 "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 +#include + +typedef struct GradientsContext { + const AVClass *class; + int w, h; + int type; + AVRational frame_rate; + uint64_t pts; + + uint8_t color_rgba[8][4]; + int nb_colors; + int x0, y0, x1, y1; + float fx0, fy0, fx1, fy1; + + int64_t seed; + + AVLFG lfg; + int (*draw_slice)(AVFilterContext *ctx, void *arg, int job, int nb_jobs); +} GradientsContext; + +#define OFFSET(x) offsetof(GradientsContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption gradients_options[] = { + {"size", "set frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="640x480"}, 0, 0, FLAGS }, + {"s", "set frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="640x480"}, 0, 0, FLAGS }, + {"rate", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, + {"r", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, + {"c0", "set 1st color", OFFSET(color_rgba[0]), AV_OPT_TYPE_COLOR, {.str = "random"}, 0, 0, FLAGS }, + {"c1", "set 2nd color", OFFSET(color_rgba[1]), AV_OPT_TYPE_COLOR, {.str = "random"}, 0, 0, FLAGS }, + {"c2", "set 3rd color", OFFSET(color_rgba[2]), AV_OPT_TYPE_COLOR, {.str = "random"}, 0, 0, FLAGS }, + {"c3", "set 4th color", OFFSET(color_rgba[3]), AV_OPT_TYPE_COLOR, {.str = "random"}, 0, 0, FLAGS }, + {"c4", "set 5th color", OFFSET(color_rgba[4]), AV_OPT_TYPE_COLOR, {.str = "random"}, 0, 0, FLAGS }, + {"c5", "set 6th color", OFFSET(color_rgba[5]), AV_OPT_TYPE_COLOR, {.str = "random"}, 0, 0, FLAGS }, + {"c6", "set 7th color", OFFSET(color_rgba[6]), AV_OPT_TYPE_COLOR, {.str = "random"}, 0, 0, FLAGS }, + {"c7", "set 8th color", OFFSET(color_rgba[7]), AV_OPT_TYPE_COLOR, {.str = "random"}, 0, 0, FLAGS }, + {"x0", "set gradient line source x0", OFFSET(x0), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, FLAGS }, + {"y0", "set gradient line source y0", OFFSET(y0), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, FLAGS }, + {"x1", "set gradient line destination x1", OFFSET(x1), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, FLAGS }, + {"y1", "set gradient line destination y1", OFFSET(y1), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, FLAGS }, + {"nb_colors", "set the number of colors", OFFSET(nb_colors), AV_OPT_TYPE_INT, {.i64=2}, 2, 8, FLAGS }, + {"n", "set the number of colors", OFFSET(nb_colors), AV_OPT_TYPE_INT, {.i64=2}, 2, 8, FLAGS }, + {"seed", "set the seed", OFFSET(seed), AV_OPT_TYPE_INT64, {.i64=-1}, -1, UINT32_MAX, FLAGS }, + {NULL}, +}; + +AVFILTER_DEFINE_CLASS(gradients); + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_RGBA, + AV_PIX_FMT_RGBA64, + AV_PIX_FMT_NONE + }; + + AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); + if (!fmts_list) + return AVERROR(ENOMEM); + return ff_set_common_formats(ctx, fmts_list); +} + +static uint32_t lerp_color(uint8_t c0[4], uint8_t c1[4], float x) +{ + const float y = 1.f - x; + + return (lrint(c0[0] * y + c1[0] * x)) << 0 | + (lrint(c0[1] * y + c1[1] * x)) << 8 | + (lrint(c0[2] * y + c1[2] * x)) << 16 | + (lrint(c0[3] * y + c1[3] * x)) << 24; +} + +static uint64_t lerp_color16(uint8_t c0[4], uint8_t c1[4], float x) +{ + const float y = 1.f - x; + + return (llrint((c0[0] * y + c1[0] * x) * 256)) << 0 | + (llrint((c0[1] * y + c1[1] * x) * 256)) << 16 | + (llrint((c0[2] * y + c1[2] * x) * 256)) << 32 | + (llrint((c0[3] * y + c1[3] * x) * 256)) << 48; +} + +static uint32_t lerp_colors(uint8_t arr[3][4], int nb_colors, float step) +{ + float scl; + int i; + + if (nb_colors == 1 || step <= 0.0) { + return arr[0][0] | (arr[0][1] << 8) | (arr[0][2] << 16) | (arr[0][3] << 24); + } else if (step >= 1.0) { + i = nb_colors - 1; + return arr[i][0] | (arr[i][1] << 8) | (arr[i][2] << 16) | (arr[i][3] << 24); + } + + scl = step * (nb_colors - 1); + i = floorf(scl); + + return lerp_color(arr[i], arr[i + 1], scl - i); +} + +static uint64_t lerp_colors16(uint8_t arr[3][4], int nb_colors, float step) +{ + float scl; + int i; + + if (nb_colors == 1 || step <= 0.0) { + return ((uint64_t)arr[0][0] << 8) | ((uint64_t)arr[0][1] << 24) | ((uint64_t)arr[0][2] << 40) | ((uint64_t)arr[0][3] << 56); + } else if (step >= 1.0) { + i = nb_colors - 1; + return ((uint64_t)arr[i][0] << 8) | ((uint64_t)arr[i][1] << 24) | ((uint64_t)arr[i][2] << 40) | ((uint64_t)arr[i][3] << 56); + } + + scl = step * (nb_colors - 1); + i = floorf(scl); + + return lerp_color16(arr[i], arr[i + 1], scl - i); +} + +static float project(float origin_x, float origin_y, + float dest_x, float dest_y, + int point_x, int point_y) +{ + // Rise and run of line. + float od_x = dest_x - origin_x; + float od_y = dest_y - origin_y; + + // Distance-squared of line. + float od_s_q = od_x * od_x + od_y * od_y; + + // Rise and run of projection. + float op_x = point_x - origin_x; + float op_y = point_y - origin_y; + float op_x_od = op_x * od_x + op_y * od_y; + + // Normalize and clamp range. + return av_clipf(op_x_od / od_s_q, 0.f, 1.f); +} + +static int draw_gradients_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs) +{ + GradientsContext *s = ctx->priv; + AVFrame *frame = arg; + const int width = frame->width; + 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; + uint32_t *dst = (uint32_t *)frame->data[0] + start * linesize; + + 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); + dst[x] = lerp_colors(s->color_rgba, s->nb_colors, factor);; + } + + dst += linesize; + } + + return 0; +} + +static int draw_gradients_slice16(AVFilterContext *ctx, void *arg, int job, int nb_jobs) +{ + GradientsContext *s = ctx->priv; + AVFrame *frame = arg; + const int width = frame->width; + 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; + uint64_t *dst = (uint64_t *)frame->data[0] + start * linesize; + + 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); + dst[x] = lerp_colors16(s->color_rgba, s->nb_colors, factor);; + } + + dst += linesize; + } + + return 0; +}static int config_output(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->src; + GradientsContext *s = ctx->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + + if (av_image_check_size(s->w, s->h, 0, ctx) < 0) + return AVERROR(EINVAL); + + inlink->w = s->w; + inlink->h = s->h; + inlink->time_base = av_inv_q(s->frame_rate); + inlink->sample_aspect_ratio = (AVRational) {1, 1}; + if (s->seed == -1) + s->seed = av_get_random_seed(); + av_lfg_init(&s->lfg, s->seed); + + s->draw_slice = desc->comp[0].depth == 16 ? draw_gradients_slice16 : draw_gradients_slice; + + if (s->x0 < 0 || s->x0 >= s->w) + s->x0 = av_lfg_get(&s->lfg) % s->w; + if (s->y0 < 0 || s->y0 >= s->h) + s->y0 = av_lfg_get(&s->lfg) % s->h; + if (s->x1 < 0 || s->x1 >= s->w) + s->x1 = av_lfg_get(&s->lfg) % s->w; + if (s->y1 < 0 || s->y1 >= s->h) + s->y1 = av_lfg_get(&s->lfg) % s->h; + + return 0; +} + +static int gradients_request_frame(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + GradientsContext *s = ctx->priv; + AVFrame *frame = ff_get_video_buffer(outlink, s->w, s->h); + float angle = fmodf(s->pts / 100.f, 2.f * M_PI); + const float w2 = s->w / 2.f; + const float h2 = s->h / 2.f; + + 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; + + s->fx1 = (s->x1 - w2) * cosf(angle) - (s->y1 - h2) * sinf(angle) + w2; + s->fy1 = (s->x1 - w2) * sinf(angle) + (s->y1 - h2) * cosf(angle) + h2; + + if (!frame) + return AVERROR(ENOMEM); + + frame->sample_aspect_ratio = (AVRational) {1, 1}; + frame->pts = s->pts++; + + ctx->internal->execute(ctx, s->draw_slice, frame, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); + + return ff_filter_frame(outlink, frame); +} + +static const AVFilterPad gradients_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = gradients_request_frame, + .config_props = config_output, + }, + { NULL } +}; + +AVFilter ff_vsrc_gradients = { + .name = "gradients", + .description = NULL_IF_CONFIG_SMALL("Draw a gradients."), + .priv_size = sizeof(GradientsContext), + .priv_class = &gradients_class, + .query_formats = query_formats, + .inputs = NULL, + .outputs = gradients_outputs, + .flags = AVFILTER_FLAG_SLICE_THREADS, +}; diff --git a/libavfilter/vsrc_life.c b/libavfilter/vsrc_life.c index a87ceef15d4..6218eb1ae67 100644 --- a/libavfilter/vsrc_life.c +++ b/libavfilter/vsrc_life.c @@ -63,7 +63,7 @@ typedef struct LifeContext { uint64_t pts; AVRational frame_rate; double random_fill_ratio; - uint32_t random_seed; + int64_t random_seed; int stitch; int mold; uint8_t life_color[4]; @@ -84,16 +84,16 @@ static const AVOption life_options[] = { { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 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 }, - { "rule", "set rule", OFFSET(rule_str), AV_OPT_TYPE_STRING, {.str = "B3/S23"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "rule", "set rule", OFFSET(rule_str), AV_OPT_TYPE_STRING, {.str = "B3/S23"}, 0, 0, FLAGS }, { "random_fill_ratio", "set fill ratio for filling initial grid randomly", OFFSET(random_fill_ratio), AV_OPT_TYPE_DOUBLE, {.dbl=1/M_PHI}, 0, 1, FLAGS }, { "ratio", "set fill ratio for filling initial grid randomly", OFFSET(random_fill_ratio), AV_OPT_TYPE_DOUBLE, {.dbl=1/M_PHI}, 0, 1, FLAGS }, - { "random_seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.i64=-1}, -1, UINT32_MAX, FLAGS }, - { "seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT, {.i64=-1}, -1, UINT32_MAX, FLAGS }, + { "random_seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT64, {.i64=-1}, -1, UINT32_MAX, FLAGS }, + { "seed", "set the seed for filling the initial grid randomly", OFFSET(random_seed), AV_OPT_TYPE_INT64, {.i64=-1}, -1, UINT32_MAX, FLAGS }, { "stitch", "stitch boundaries", OFFSET(stitch), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, { "mold", "set mold speed for dead cells", OFFSET(mold), AV_OPT_TYPE_INT, {.i64=0}, 0, 0xFF, FLAGS }, - { "life_color", "set life color", OFFSET( life_color), AV_OPT_TYPE_COLOR, {.str="white"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "death_color", "set death color", OFFSET(death_color), AV_OPT_TYPE_COLOR, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "mold_color", "set mold color", OFFSET( mold_color), AV_OPT_TYPE_COLOR, {.str="black"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "life_color", "set life color", OFFSET( life_color), AV_OPT_TYPE_COLOR, {.str="white"}, 0, 0, FLAGS }, + { "death_color", "set death color", OFFSET(death_color), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, FLAGS }, + { "mold_color", "set mold color", OFFSET( mold_color), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, FLAGS }, { NULL } }; @@ -260,7 +260,7 @@ static av_cold int init(AVFilterContext *ctx) } av_log(ctx, AV_LOG_VERBOSE, - "s:%dx%d r:%d/%d rule:%s stay_rule:%d born_rule:%d stitch:%d seed:%"PRIu32"\n", + "s:%dx%d r:%d/%d rule:%s stay_rule:%d born_rule:%d stitch:%d seed:%"PRId64"\n", life->w, life->h, life->frame_rate.num, life->frame_rate.den, life->rule_str, life->stay_rule, life->born_rule, life->stitch, life->random_seed); diff --git a/libavfilter/vsrc_mandelbrot.c b/libavfilter/vsrc_mandelbrot.c index 6ad108151f6..761c9151037 100644 --- a/libavfilter/vsrc_mandelbrot.c +++ b/libavfilter/vsrc_mandelbrot.c @@ -87,10 +87,10 @@ typedef struct MBContext { #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM static const AVOption mandelbrot_options[] = { - {"size", "set frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="640x480"}, CHAR_MIN, CHAR_MAX, FLAGS }, - {"s", "set frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="640x480"}, CHAR_MIN, CHAR_MAX, FLAGS }, - {"rate", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, CHAR_MIN, CHAR_MAX, FLAGS }, - {"r", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, CHAR_MIN, CHAR_MAX, FLAGS }, + {"size", "set frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="640x480"}, 0, 0, FLAGS }, + {"s", "set frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="640x480"}, 0, 0, FLAGS }, + {"rate", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, + {"r", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, {"maxiter", "set max iterations number", OFFSET(maxiter), AV_OPT_TYPE_INT, {.i64=7189}, 1, INT_MAX, FLAGS }, {"start_x", "set the initial x position", OFFSET(start_x), AV_OPT_TYPE_DOUBLE, {.dbl=-0.743643887037158704752191506114774}, -100, 100, FLAGS }, {"start_y", "set the initial y position", OFFSET(start_y), AV_OPT_TYPE_DOUBLE, {.dbl=-0.131825904205311970493132056385139}, -100, 100, FLAGS }, diff --git a/libavfilter/vsrc_mptestsrc.c b/libavfilter/vsrc_mptestsrc.c index c5fdea75dc7..2ea736b0b28 100644 --- a/libavfilter/vsrc_mptestsrc.c +++ b/libavfilter/vsrc_mptestsrc.c @@ -54,6 +54,7 @@ typedef struct MPTestContext { const AVClass *class; AVRational frame_rate; int64_t pts, max_pts, duration; + int64_t max_frames; int hsub, vsub; int test; ///< test_type } MPTestContext; @@ -79,6 +80,10 @@ static const AVOption mptestsrc_options[]= { { "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" }, + { "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), + AV_OPT_TYPE_INT64, {.i64 = 30}, 1, INT64_MAX, FLAGS }, { NULL } }; @@ -303,7 +308,8 @@ static int request_frame(AVFilterLink *outlink) AVFrame *picref; int w = WIDTH, h = HEIGHT, cw = AV_CEIL_RSHIFT(w, test->hsub), ch = AV_CEIL_RSHIFT(h, test->vsub); - unsigned int frame = outlink->frame_count_in; + uint64_t frame = outlink->frame_count_in / test->max_frames; + uint64_t mod = outlink->frame_count_in % test->max_frames; enum test_type tt = test->test; int i; @@ -322,20 +328,20 @@ static int request_frame(AVFilterLink *outlink) memset(picref->data[2] + i*picref->linesize[2], 128, cw); } - if (tt == TEST_ALL && frame%30) /* draw a black frame at the beginning of each test */ - tt = (frame/30)%(TEST_NB-1); + if (tt == TEST_ALL && mod) /* draw a black frame at the beginning of each test */ + tt = frame%(TEST_NB-1); switch (tt) { - case TEST_DC_LUMA: dc_test(picref->data[0], picref->linesize[0], 256, 256, frame%30); break; - case TEST_DC_CHROMA: dc_test(picref->data[1], picref->linesize[1], 256, 256, frame%30); break; - case TEST_FREQ_LUMA: freq_test(picref->data[0], picref->linesize[0], frame%30); break; - case TEST_FREQ_CHROMA: freq_test(picref->data[1], picref->linesize[1], frame%30); break; - case TEST_AMP_LUMA: amp_test(picref->data[0], picref->linesize[0], frame%30); break; - case TEST_AMP_CHROMA: amp_test(picref->data[1], picref->linesize[1], frame%30); break; - case TEST_CBP: cbp_test(picref->data , picref->linesize , frame%30); break; - case TEST_MV: mv_test(picref->data[0], picref->linesize[0], frame%30); break; - case TEST_RING1: ring1_test(picref->data[0], picref->linesize[0], frame%30); break; - case TEST_RING2: ring2_test(picref->data[0], picref->linesize[0], frame%30); break; + case TEST_DC_LUMA: dc_test(picref->data[0], picref->linesize[0], 256, 256, mod); break; + case TEST_DC_CHROMA: dc_test(picref->data[1], picref->linesize[1], 256, 256, mod); break; + case TEST_FREQ_LUMA: freq_test(picref->data[0], picref->linesize[0], mod); break; + case TEST_FREQ_CHROMA: freq_test(picref->data[1], picref->linesize[1], mod); break; + case TEST_AMP_LUMA: amp_test(picref->data[0], picref->linesize[0], mod); break; + case TEST_AMP_CHROMA: amp_test(picref->data[1], picref->linesize[1], mod); break; + case TEST_CBP: cbp_test(picref->data , picref->linesize , mod); break; + case TEST_MV: mv_test(picref->data[0], picref->linesize[0], mod); break; + case TEST_RING1: ring1_test(picref->data[0], picref->linesize[0], mod); break; + case TEST_RING2: ring2_test(picref->data[0], picref->linesize[0], mod); break; } return ff_filter_frame(outlink, picref); diff --git a/libavfilter/vsrc_sierpinski.c b/libavfilter/vsrc_sierpinski.c new file mode 100644 index 00000000000..49d4cdfa959 --- /dev/null +++ b/libavfilter/vsrc_sierpinski.c @@ -0,0 +1,235 @@ +/* + * 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 + */ + +/** + * @file + * Sierpinski carpet fractal renderer + */ + +#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 +#include + +typedef struct SierpinskiContext { + const AVClass *class; + int w, h; + int type; + AVRational frame_rate; + uint64_t pts; + + int64_t seed; + int jump; + + int pos_x, pos_y; + int dest_x, dest_y; + + AVLFG lfg; + int (*draw_slice)(AVFilterContext *ctx, void *arg, int job, int nb_jobs); +} SierpinskiContext; + +#define OFFSET(x) offsetof(SierpinskiContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption sierpinski_options[] = { + {"size", "set frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="640x480"}, 0, 0, FLAGS }, + {"s", "set frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="640x480"}, 0, 0, FLAGS }, + {"rate", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, + {"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" }, + {NULL}, +}; + +AVFILTER_DEFINE_CLASS(sierpinski); + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_0BGR32, + AV_PIX_FMT_NONE + }; + + AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); + if (!fmts_list) + return AVERROR(ENOMEM); + return ff_set_common_formats(ctx, fmts_list); +} + +static int fill_sierpinski(SierpinskiContext *s, int x, int y) +{ + int pos_x = x + s->pos_x; + int pos_y = y + s->pos_y; + + while (pos_x != 0 && pos_y != 0) { + if (FFABS(pos_x % 3) == 1 && FFABS(pos_y % 3) == 1) + return 1; + + pos_x /= 3; + pos_y /= 3; + } + + return 0; +} + +static int draw_triangle_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs) +{ + SierpinskiContext *s = ctx->priv; + AVFrame *frame = arg; + const int width = frame->width; + const int height = frame->height; + const int start = (height * job ) / nb_jobs; + const int end = (height * (job+1)) / nb_jobs; + uint8_t *dst = frame->data[0] + start * frame->linesize[0]; + + for (int y = start; y < end; y++) { + for (int x = 0; x < width; x++) { + if ((s->pos_x + x) & (s->pos_y + y)) { + AV_WL32(&dst[x*4], 0x00000000); + } else { + AV_WL32(&dst[x*4], 0xFFFFFFFF); + } + } + + dst += frame->linesize[0]; + } + + return 0; +} + +static int draw_carpet_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs) +{ + SierpinskiContext *s = ctx->priv; + AVFrame *frame = arg; + const int width = frame->width; + const int height = frame->height; + const int start = (height * job ) / nb_jobs; + const int end = (height * (job+1)) / nb_jobs; + uint8_t *dst = frame->data[0] + start * frame->linesize[0]; + + for (int y = start; y < end; y++) { + for (int x = 0; x < width; x++) { + if (fill_sierpinski(s, x, y)) { + AV_WL32(&dst[x*4], 0x00000000); + } else { + AV_WL32(&dst[x*4], 0xFFFFFFFF); + } + } + + dst += frame->linesize[0]; + } + + return 0; +} + +static int config_output(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->src; + SierpinskiContext *s = ctx->priv; + + if (av_image_check_size(s->w, s->h, 0, ctx) < 0) + return AVERROR(EINVAL); + + inlink->w = s->w; + inlink->h = s->h; + inlink->time_base = av_inv_q(s->frame_rate); + inlink->sample_aspect_ratio = (AVRational) {1, 1}; + if (s->seed == -1) + s->seed = av_get_random_seed(); + av_lfg_init(&s->lfg, s->seed); + + s->draw_slice = s->type ? draw_triangle_slice : draw_carpet_slice; + + return 0; +} + +static void draw_sierpinski(AVFilterContext *ctx, AVFrame *frame) +{ + SierpinskiContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + + if (s->pos_x == s->dest_x && s->pos_y == s->dest_y) { + unsigned int rnd = av_lfg_get(&s->lfg); + int mod = 2 * s->jump + 1; + + s->dest_x += (int)((rnd & 0xffff) % mod) - s->jump; + s->dest_y += (int)((rnd >> 16) % mod) - s->jump; + } else { + if (s->pos_x < s->dest_x) + s->pos_x++; + else if (s->pos_x > s->dest_x) + s->pos_x--; + + if (s->pos_y < s->dest_y) + s->pos_y++; + else if (s->pos_y > s->dest_y) + s->pos_y--; + } + + ctx->internal->execute(ctx, s->draw_slice, frame, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); +} + +static int sierpinski_request_frame(AVFilterLink *link) +{ + SierpinskiContext *s = link->src->priv; + AVFrame *frame = ff_get_video_buffer(link, s->w, s->h); + + if (!frame) + return AVERROR(ENOMEM); + + frame->sample_aspect_ratio = (AVRational) {1, 1}; + frame->pts = s->pts++; + + draw_sierpinski(link->src, frame); + + return ff_filter_frame(link, frame); +} + +static const AVFilterPad sierpinski_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = sierpinski_request_frame, + .config_props = config_output, + }, + { NULL } +}; + +AVFilter ff_vsrc_sierpinski = { + .name = "sierpinski", + .description = NULL_IF_CONFIG_SMALL("Render a Sierpinski fractal."), + .priv_size = sizeof(SierpinskiContext), + .priv_class = &sierpinski_class, + .query_formats = query_formats, + .inputs = NULL, + .outputs = sierpinski_outputs, + .flags = AVFILTER_FLAG_SLICE_THREADS, +}; diff --git a/libavfilter/vsrc_testsrc.c b/libavfilter/vsrc_testsrc.c index f06714807ff..e8c33b00a0d 100644 --- a/libavfilter/vsrc_testsrc.c +++ b/libavfilter/vsrc_testsrc.c @@ -83,6 +83,7 @@ typedef struct TestSourceContext { #define OFFSET(x) offsetof(TestSourceContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define FLAGSR AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM #define SIZE_OPTIONS \ { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "320x240"}, 0, 0, FLAGS },\ @@ -181,8 +182,8 @@ static int request_frame(AVFilterLink *outlink) #if CONFIG_COLOR_FILTER static const AVOption color_options[] = { - { "color", "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "black"}, CHAR_MIN, CHAR_MAX, FLAGS }, - { "c", "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "black"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "color", "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGSR }, + { "c", "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGSR }, COMMON_OPTIONS { NULL } }; @@ -236,20 +237,13 @@ static int color_process_command(AVFilterContext *ctx, const char *cmd, const ch TestSourceContext *test = ctx->priv; int ret; - if (!strcmp(cmd, "color") || !strcmp(cmd, "c")) { - uint8_t color_rgba[4]; - - ret = av_parse_color(color_rgba, args, -1, ctx); - if (ret < 0) - return ret; - - memcpy(test->color_rgba, color_rgba, sizeof(color_rgba)); - ff_draw_color(&test->draw, &test->color, test->color_rgba); - test->draw_once_reset = 1; - return 0; - } + ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); + if (ret < 0) + return ret; - return AVERROR(ENOSYS); + ff_draw_color(&test->draw, &test->color, test->color_rgba); + test->draw_once_reset = 1; + return 0; } static const AVFilterPad color_outputs[] = { @@ -280,7 +274,7 @@ AVFilter ff_vsrc_color = { #if CONFIG_HALDCLUTSRC_FILTER static const AVOption haldclutsrc_options[] = { - { "level", "set level", OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 6}, 2, 8, FLAGS }, + { "level", "set level", OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 6}, 2, 16, FLAGS }, COMMON_OPTIONS_NOSIZE { NULL } }; @@ -968,10 +962,10 @@ AVFILTER_DEFINE_CLASS(rgbtestsrc); #define A 3 static void rgbtest_put_pixel(uint8_t *dst, int dst_linesize, - int x, int y, int r, int g, int b, enum AVPixelFormat fmt, + int x, int y, unsigned r, unsigned g, unsigned b, enum AVPixelFormat fmt, uint8_t rgba_map[4]) { - int32_t v; + uint32_t v; uint8_t *p; switch (fmt) { @@ -991,7 +985,7 @@ static void rgbtest_put_pixel(uint8_t *dst, int dst_linesize, case AV_PIX_FMT_BGRA: case AV_PIX_FMT_ARGB: case AV_PIX_FMT_ABGR: - v = (r << (rgba_map[R]*8)) + (g << (rgba_map[G]*8)) + (b << (rgba_map[B]*8)) + (255 << (rgba_map[A]*8)); + v = (r << (rgba_map[R]*8)) + (g << (rgba_map[G]*8)) + (b << (rgba_map[B]*8)) + (255U << (rgba_map[A]*8)); p = dst + 4*x + y*dst_linesize; AV_WL32(p, v); break; diff --git a/libavfilter/vulkan.c b/libavfilter/vulkan.c new file mode 100644 index 00000000000..0ac5711182e --- /dev/null +++ b/libavfilter/vulkan.c @@ -0,0 +1,1440 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 "formats.h" +#include "vulkan.h" +#include "glslang.h" + +/* Generic macro for creating contexts which need to keep their addresses + * if another context is created. */ +#define FN_CREATING(ctx, type, shortname, array, num) \ +static av_always_inline type *create_ ##shortname(ctx *dctx) \ +{ \ + type **array, *sctx = av_mallocz(sizeof(*sctx)); \ + if (!sctx) \ + return NULL; \ + \ + array = av_realloc_array(dctx->array, sizeof(*dctx->array), dctx->num + 1);\ + if (!array) { \ + av_free(sctx); \ + return NULL; \ + } \ + \ + dctx->array = array; \ + dctx->array[dctx->num++] = sctx; \ + \ + return sctx; \ +} + +const VkComponentMapping ff_comp_identity_map = { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, +}; + +/* Converts return values to strings */ +const char *ff_vk_ret2str(VkResult res) +{ +#define CASE(VAL) case VAL: return #VAL + switch (res) { + CASE(VK_SUCCESS); + CASE(VK_NOT_READY); + CASE(VK_TIMEOUT); + CASE(VK_EVENT_SET); + CASE(VK_EVENT_RESET); + CASE(VK_INCOMPLETE); + CASE(VK_ERROR_OUT_OF_HOST_MEMORY); + CASE(VK_ERROR_OUT_OF_DEVICE_MEMORY); + CASE(VK_ERROR_INITIALIZATION_FAILED); + CASE(VK_ERROR_DEVICE_LOST); + CASE(VK_ERROR_MEMORY_MAP_FAILED); + CASE(VK_ERROR_LAYER_NOT_PRESENT); + CASE(VK_ERROR_EXTENSION_NOT_PRESENT); + CASE(VK_ERROR_FEATURE_NOT_PRESENT); + CASE(VK_ERROR_INCOMPATIBLE_DRIVER); + CASE(VK_ERROR_TOO_MANY_OBJECTS); + CASE(VK_ERROR_FORMAT_NOT_SUPPORTED); + CASE(VK_ERROR_FRAGMENTED_POOL); + CASE(VK_ERROR_SURFACE_LOST_KHR); + CASE(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); + CASE(VK_SUBOPTIMAL_KHR); + CASE(VK_ERROR_OUT_OF_DATE_KHR); + CASE(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR); + CASE(VK_ERROR_VALIDATION_FAILED_EXT); + CASE(VK_ERROR_INVALID_SHADER_NV); + CASE(VK_ERROR_OUT_OF_POOL_MEMORY); + CASE(VK_ERROR_INVALID_EXTERNAL_HANDLE); + CASE(VK_ERROR_NOT_PERMITTED_EXT); + default: return "Unknown error"; + } +#undef CASE +} + +static int vk_alloc_mem(AVFilterContext *avctx, VkMemoryRequirements *req, + VkMemoryPropertyFlagBits req_flags, void *alloc_extension, + VkMemoryPropertyFlagBits *mem_flags, VkDeviceMemory *mem) +{ + VkResult ret; + int index = -1; + VkPhysicalDeviceProperties props; + VkPhysicalDeviceMemoryProperties mprops; + VulkanFilterContext *s = avctx->priv; + + VkMemoryAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = alloc_extension, + }; + + vkGetPhysicalDeviceProperties(s->hwctx->phys_dev, &props); + vkGetPhysicalDeviceMemoryProperties(s->hwctx->phys_dev, &mprops); + + /* Align if we need to */ + if (req_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) + req->size = FFALIGN(req->size, props.limits.minMemoryMapAlignment); + + alloc_info.allocationSize = req->size; + + /* The vulkan spec requires memory types to be sorted in the "optimal" + * order, so the first matching type we find will be the best/fastest one */ + for (int i = 0; i < mprops.memoryTypeCount; i++) { + /* The memory type must be supported by the requirements (bitfield) */ + if (!(req->memoryTypeBits & (1 << i))) + continue; + + /* The memory type flags must include our properties */ + if ((mprops.memoryTypes[i].propertyFlags & req_flags) != req_flags) + continue; + + /* Found a suitable memory type */ + index = i; + break; + } + + if (index < 0) { + av_log(avctx, AV_LOG_ERROR, "No memory type found for flags 0x%x\n", + req_flags); + return AVERROR(EINVAL); + } + + alloc_info.memoryTypeIndex = index; + + ret = vkAllocateMemory(s->hwctx->act_dev, &alloc_info, + s->hwctx->alloc, mem); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to allocate memory: %s\n", + ff_vk_ret2str(ret)); + return AVERROR(ENOMEM); + } + + *mem_flags |= mprops.memoryTypes[index].propertyFlags; + + return 0; +} + +int ff_vk_create_buf(AVFilterContext *avctx, FFVkBuffer *buf, size_t size, + VkBufferUsageFlags usage, VkMemoryPropertyFlagBits flags) +{ + int err; + VkResult ret; + int use_ded_mem; + VulkanFilterContext *s = avctx->priv; + + VkBufferCreateInfo buf_spawn = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = NULL, + .usage = usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .size = size, /* Gets FFALIGNED during alloc if host visible + but should be ok */ + }; + + VkBufferMemoryRequirementsInfo2 req_desc = { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2, + }; + VkMemoryDedicatedAllocateInfo ded_alloc = { + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, + .pNext = NULL, + }; + VkMemoryDedicatedRequirements ded_req = { + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, + }; + VkMemoryRequirements2 req = { + .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, + .pNext = &ded_req, + }; + + ret = vkCreateBuffer(s->hwctx->act_dev, &buf_spawn, NULL, &buf->buf); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to create buffer: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + req_desc.buffer = buf->buf; + + vkGetBufferMemoryRequirements2(s->hwctx->act_dev, &req_desc, &req); + + /* In case the implementation prefers/requires dedicated allocation */ + use_ded_mem = ded_req.prefersDedicatedAllocation | + ded_req.requiresDedicatedAllocation; + if (use_ded_mem) + ded_alloc.buffer = buf->buf; + + err = vk_alloc_mem(avctx, &req.memoryRequirements, flags, + use_ded_mem ? &ded_alloc : (void *)ded_alloc.pNext, + &buf->flags, &buf->mem); + if (err) + return err; + + ret = vkBindBufferMemory(s->hwctx->act_dev, buf->buf, buf->mem, 0); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to bind memory to buffer: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + return 0; +} + +int ff_vk_map_buffers(AVFilterContext *avctx, FFVkBuffer *buf, uint8_t *mem[], + int nb_buffers, int invalidate) +{ + VkResult ret; + VulkanFilterContext *s = avctx->priv; + VkMappedMemoryRange *inval_list = NULL; + int inval_count = 0; + + for (int i = 0; i < nb_buffers; i++) { + ret = vkMapMemory(s->hwctx->act_dev, buf[i].mem, 0, + VK_WHOLE_SIZE, 0, (void **)&mem[i]); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to map buffer memory: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + } + + if (!invalidate) + return 0; + + for (int i = 0; i < nb_buffers; i++) { + const VkMappedMemoryRange ival_buf = { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = buf[i].mem, + .size = VK_WHOLE_SIZE, + }; + if (buf[i].flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) + continue; + inval_list = av_fast_realloc(s->scratch, &s->scratch_size, + (++inval_count)*sizeof(*inval_list)); + if (!inval_list) + return AVERROR(ENOMEM); + inval_list[inval_count - 1] = ival_buf; + } + + if (inval_count) { + ret = vkInvalidateMappedMemoryRanges(s->hwctx->act_dev, inval_count, + inval_list); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to invalidate memory: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + } + + return 0; +} + +int ff_vk_unmap_buffers(AVFilterContext *avctx, FFVkBuffer *buf, int nb_buffers, + int flush) +{ + int err = 0; + VkResult ret; + VulkanFilterContext *s = avctx->priv; + VkMappedMemoryRange *flush_list = NULL; + int flush_count = 0; + + if (flush) { + for (int i = 0; i < nb_buffers; i++) { + const VkMappedMemoryRange flush_buf = { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = buf[i].mem, + .size = VK_WHOLE_SIZE, + }; + if (buf[i].flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) + continue; + flush_list = av_fast_realloc(s->scratch, &s->scratch_size, + (++flush_count)*sizeof(*flush_list)); + if (!flush_list) + return AVERROR(ENOMEM); + flush_list[flush_count - 1] = flush_buf; + } + } + + if (flush_count) { + ret = vkFlushMappedMemoryRanges(s->hwctx->act_dev, flush_count, + flush_list); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to flush memory: %s\n", + ff_vk_ret2str(ret)); + err = AVERROR_EXTERNAL; /* We still want to try to unmap them */ + } + } + + for (int i = 0; i < nb_buffers; i++) + vkUnmapMemory(s->hwctx->act_dev, buf[i].mem); + + return err; +} + +void ff_vk_free_buf(AVFilterContext *avctx, FFVkBuffer *buf) +{ + VulkanFilterContext *s = avctx->priv; + if (!buf) + return; + + if (buf->buf != VK_NULL_HANDLE) + vkDestroyBuffer(s->hwctx->act_dev, buf->buf, s->hwctx->alloc); + if (buf->mem != VK_NULL_HANDLE) + vkFreeMemory(s->hwctx->act_dev, buf->mem, s->hwctx->alloc); +} + +int ff_vk_add_push_constant(AVFilterContext *avctx, VulkanPipeline *pl, + int offset, int size, VkShaderStageFlagBits stage) +{ + VkPushConstantRange *pc; + + pl->push_consts = av_realloc_array(pl->push_consts, sizeof(*pl->push_consts), + pl->push_consts_num + 1); + if (!pl->push_consts) + return AVERROR(ENOMEM); + + pc = &pl->push_consts[pl->push_consts_num++]; + memset(pc, 0, sizeof(*pc)); + + pc->stageFlags = stage; + pc->offset = offset; + pc->size = size; + + return 0; +} + +FN_CREATING(VulkanFilterContext, FFVkExecContext, exec_ctx, exec_ctx, exec_ctx_num) +int ff_vk_create_exec_ctx(AVFilterContext *avctx, FFVkExecContext **ctx) +{ + VkResult ret; + FFVkExecContext *e; + VulkanFilterContext *s = avctx->priv; + + int queue_family = s->queue_family_idx; + int nb_queues = s->queue_count; + + VkCommandPoolCreateInfo cqueue_create = { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = queue_family, + }; + VkCommandBufferAllocateInfo cbuf_create = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = nb_queues, + }; + + e = create_exec_ctx(s); + if (!e) + return AVERROR(ENOMEM); + + e->queues = av_mallocz(nb_queues * sizeof(*e->queues)); + if (!e->queues) + return AVERROR(ENOMEM); + + e->bufs = av_mallocz(nb_queues * sizeof(*e->bufs)); + if (!e->bufs) + return AVERROR(ENOMEM); + + /* Create command pool */ + ret = vkCreateCommandPool(s->hwctx->act_dev, &cqueue_create, + s->hwctx->alloc, &e->pool); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Command pool creation failure: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + cbuf_create.commandPool = e->pool; + + /* Allocate command buffer */ + ret = vkAllocateCommandBuffers(s->hwctx->act_dev, &cbuf_create, e->bufs); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Command buffer alloc failure: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + for (int i = 0; i < nb_queues; i++) { + FFVkQueueCtx *q = &e->queues[i]; + vkGetDeviceQueue(s->hwctx->act_dev, queue_family, i, &q->queue); + } + + *ctx = e; + + return 0; +} + +void ff_vk_discard_exec_deps(AVFilterContext *avctx, FFVkExecContext *e) +{ + VulkanFilterContext *s = avctx->priv; + FFVkQueueCtx *q = &e->queues[s->cur_queue_idx]; + + for (int j = 0; j < q->nb_buf_deps; j++) + av_buffer_unref(&q->buf_deps[j]); + q->nb_buf_deps = 0; + + for (int j = 0; j < q->nb_frame_deps; j++) + av_frame_free(&q->frame_deps[j]); + q->nb_frame_deps = 0; + + e->sem_wait_cnt = 0; + e->sem_sig_cnt = 0; +} + +int ff_vk_start_exec_recording(AVFilterContext *avctx, FFVkExecContext *e) +{ + VkResult ret; + VulkanFilterContext *s = avctx->priv; + FFVkQueueCtx *q = &e->queues[s->cur_queue_idx]; + + VkCommandBufferBeginInfo cmd_start = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + + /* Create the fence and don't wait for it initially */ + if (!q->fence) { + VkFenceCreateInfo fence_spawn = { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + }; + ret = vkCreateFence(s->hwctx->act_dev, &fence_spawn, s->hwctx->alloc, + &q->fence); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to queue frame fence: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + } else { + vkWaitForFences(s->hwctx->act_dev, 1, &q->fence, VK_TRUE, UINT64_MAX); + vkResetFences(s->hwctx->act_dev, 1, &q->fence); + } + + /* Discard queue dependencies */ + ff_vk_discard_exec_deps(avctx, e); + + ret = vkBeginCommandBuffer(e->bufs[s->cur_queue_idx], &cmd_start); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to start command recoding: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + return 0; +} + +VkCommandBuffer ff_vk_get_exec_buf(AVFilterContext *avctx, FFVkExecContext *e) +{ + VulkanFilterContext *s = avctx->priv; + return e->bufs[s->cur_queue_idx]; +} + +int ff_vk_add_exec_dep(AVFilterContext *avctx, FFVkExecContext *e, + AVFrame *frame, VkPipelineStageFlagBits in_wait_dst_flag) +{ + AVFrame **dst; + VulkanFilterContext *s = avctx->priv; + AVVkFrame *f = (AVVkFrame *)frame->data[0]; + FFVkQueueCtx *q = &e->queues[s->cur_queue_idx]; + AVHWFramesContext *fc = (AVHWFramesContext *)frame->hw_frames_ctx->data; + int planes = av_pix_fmt_count_planes(fc->sw_format); + + for (int i = 0; i < planes; i++) { + e->sem_wait = av_fast_realloc(e->sem_wait, &e->sem_wait_alloc, + (e->sem_wait_cnt + 1)*sizeof(*e->sem_wait)); + if (!e->sem_wait) { + ff_vk_discard_exec_deps(avctx, e); + return AVERROR(ENOMEM); + } + + e->sem_wait_dst = av_fast_realloc(e->sem_wait_dst, &e->sem_wait_dst_alloc, + (e->sem_wait_cnt + 1)*sizeof(*e->sem_wait_dst)); + if (!e->sem_wait_dst) { + ff_vk_discard_exec_deps(avctx, e); + return AVERROR(ENOMEM); + } + + e->sem_sig = av_fast_realloc(e->sem_sig, &e->sem_sig_alloc, + (e->sem_sig_cnt + 1)*sizeof(*e->sem_sig)); + if (!e->sem_sig) { + ff_vk_discard_exec_deps(avctx, e); + return AVERROR(ENOMEM); + } + + e->sem_wait[e->sem_wait_cnt] = f->sem[i]; + e->sem_wait_dst[e->sem_wait_cnt] = in_wait_dst_flag; + e->sem_wait_cnt++; + + e->sem_sig[e->sem_sig_cnt] = f->sem[i]; + e->sem_sig_cnt++; + } + + dst = av_fast_realloc(q->frame_deps, &q->frame_deps_alloc_size, + (q->nb_frame_deps + 1) * sizeof(*dst)); + if (!dst) { + ff_vk_discard_exec_deps(avctx, e); + return AVERROR(ENOMEM); + } + + q->frame_deps = dst; + q->frame_deps[q->nb_frame_deps] = av_frame_clone(frame); + if (!q->frame_deps[q->nb_frame_deps]) { + ff_vk_discard_exec_deps(avctx, e); + return AVERROR(ENOMEM); + } + q->nb_frame_deps++; + + return 0; +} + +int ff_vk_submit_exec_queue(AVFilterContext *avctx, FFVkExecContext *e) +{ + VkResult ret; + VulkanFilterContext *s = avctx->priv; + FFVkQueueCtx *q = &e->queues[s->cur_queue_idx]; + + VkSubmitInfo s_info = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &e->bufs[s->cur_queue_idx], + + .pWaitSemaphores = e->sem_wait, + .pWaitDstStageMask = e->sem_wait_dst, + .waitSemaphoreCount = e->sem_wait_cnt, + + .pSignalSemaphores = e->sem_sig, + .signalSemaphoreCount = e->sem_sig_cnt, + }; + + ret = vkEndCommandBuffer(e->bufs[s->cur_queue_idx]); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to finish command buffer: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + ret = vkQueueSubmit(q->queue, 1, &s_info, q->fence); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to submit command buffer: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + /* Rotate queues */ + s->cur_queue_idx = (s->cur_queue_idx + 1) % s->queue_count; + + return 0; +} + +int ff_vk_add_dep_exec_ctx(AVFilterContext *avctx, FFVkExecContext *e, + AVBufferRef **deps, int nb_deps) +{ + AVBufferRef **dst; + VulkanFilterContext *s = avctx->priv; + FFVkQueueCtx *q = &e->queues[s->cur_queue_idx]; + + if (!deps || !nb_deps) + return 0; + + dst = av_fast_realloc(q->buf_deps, &q->buf_deps_alloc_size, + (q->nb_buf_deps + nb_deps) * sizeof(*dst)); + if (!dst) + goto err; + + q->buf_deps = dst; + + for (int i = 0; i < nb_deps; i++) { + q->buf_deps[q->nb_buf_deps] = deps[i]; + if (!q->buf_deps[q->nb_buf_deps]) + goto err; + q->nb_buf_deps++; + } + + return 0; + +err: + ff_vk_discard_exec_deps(avctx, e); + return AVERROR(ENOMEM); +} + +int ff_vk_filter_query_formats(AVFilterContext *avctx) +{ + static const enum AVPixelFormat pixel_formats[] = { + AV_PIX_FMT_VULKAN, AV_PIX_FMT_NONE, + }; + AVFilterFormats *pix_fmts = ff_make_format_list(pixel_formats); + if (!pix_fmts) + return AVERROR(ENOMEM); + + return ff_set_common_formats(avctx, pix_fmts); +} + +static int vulkan_filter_set_device(AVFilterContext *avctx, + AVBufferRef *device) +{ + VulkanFilterContext *s = avctx->priv; + + av_buffer_unref(&s->device_ref); + + s->device_ref = av_buffer_ref(device); + if (!s->device_ref) + return AVERROR(ENOMEM); + + s->device = (AVHWDeviceContext*)s->device_ref->data; + s->hwctx = s->device->hwctx; + + return 0; +} + +static int vulkan_filter_set_frames(AVFilterContext *avctx, + AVBufferRef *frames) +{ + VulkanFilterContext *s = avctx->priv; + + av_buffer_unref(&s->frames_ref); + + s->frames_ref = av_buffer_ref(frames); + if (!s->frames_ref) + return AVERROR(ENOMEM); + + return 0; +} + +int ff_vk_filter_config_input(AVFilterLink *inlink) +{ + int err; + AVFilterContext *avctx = inlink->dst; + VulkanFilterContext *s = avctx->priv; + AVHWFramesContext *input_frames; + + if (!inlink->hw_frames_ctx) { + av_log(avctx, 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; + + /* Default output parameters match input parameters. */ + 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; + + return 0; +} + +int ff_vk_filter_config_output_inplace(AVFilterLink *outlink) +{ + int err; + AVFilterContext *avctx = outlink->src; + VulkanFilterContext *s = avctx->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; + } + + outlink->hw_frames_ctx = av_buffer_ref(s->frames_ref); + if (!outlink->hw_frames_ctx) + return AVERROR(ENOMEM); + + outlink->w = s->output_width; + outlink->h = s->output_height; + + return 0; +} + +int ff_vk_filter_config_output(AVFilterLink *outlink) +{ + int err; + AVFilterContext *avctx = outlink->src; + VulkanFilterContext *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); + } + + err = vulkan_filter_set_device(avctx, avctx->hw_device_ctx); + if (err < 0) + return err; + } + + output_frames_ref = av_hwframe_ctx_alloc(s->device_ref); + if (!output_frames_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + 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; + + 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; + } + + outlink->hw_frames_ctx = output_frames_ref; + outlink->w = s->output_width; + outlink->h = s->output_height; + + return 0; +fail: + av_buffer_unref(&output_frames_ref); + return err; +} + +int ff_vk_filter_init(AVFilterContext *avctx) +{ + VulkanFilterContext *s = avctx->priv; + + s->output_format = AV_PIX_FMT_NONE; + + if (glslang_init()) + return AVERROR_EXTERNAL; + + return 0; +} + +FN_CREATING(VulkanFilterContext, VkSampler, sampler, samplers, samplers_num) +VkSampler *ff_vk_init_sampler(AVFilterContext *avctx, int unnorm_coords, + VkFilter filt) +{ + VkResult ret; + VulkanFilterContext *s = avctx->priv; + + VkSamplerCreateInfo sampler_info = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .magFilter = filt, + .minFilter = sampler_info.magFilter, + .mipmapMode = unnorm_coords ? VK_SAMPLER_MIPMAP_MODE_NEAREST : + VK_SAMPLER_MIPMAP_MODE_LINEAR, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .addressModeV = sampler_info.addressModeU, + .addressModeW = sampler_info.addressModeU, + .anisotropyEnable = VK_FALSE, + .compareOp = VK_COMPARE_OP_NEVER, + .borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, + .unnormalizedCoordinates = unnorm_coords, + }; + + VkSampler *sampler = create_sampler(s); + if (!sampler) + return NULL; + + ret = vkCreateSampler(s->hwctx->act_dev, &sampler_info, + s->hwctx->alloc, sampler); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to init sampler: %s\n", + ff_vk_ret2str(ret)); + return NULL; + } + + return sampler; +} + +int ff_vk_mt_is_np_rgb(enum AVPixelFormat pix_fmt) +{ + if (pix_fmt == AV_PIX_FMT_ABGR || pix_fmt == AV_PIX_FMT_BGRA || + pix_fmt == AV_PIX_FMT_RGBA || pix_fmt == AV_PIX_FMT_RGB24 || + pix_fmt == AV_PIX_FMT_BGR24 || pix_fmt == AV_PIX_FMT_RGB48 || + pix_fmt == AV_PIX_FMT_RGBA64 || pix_fmt == AV_PIX_FMT_RGB565 || + pix_fmt == AV_PIX_FMT_BGR565 || pix_fmt == AV_PIX_FMT_BGR0 || + pix_fmt == AV_PIX_FMT_0BGR || pix_fmt == AV_PIX_FMT_RGB0) + return 1; + return 0; +} + +const char *ff_vk_shader_rep_fmt(enum AVPixelFormat pixfmt) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pixfmt); + const int high = desc->comp[0].depth > 8; + return high ? "rgba16f" : "rgba8"; +} + +typedef struct ImageViewCtx { + VkImageView view; +} ImageViewCtx; + +static void destroy_imageview(void *opaque, uint8_t *data) +{ + VulkanFilterContext *s = opaque; + ImageViewCtx *iv = (ImageViewCtx *)data; + vkDestroyImageView(s->hwctx->act_dev, iv->view, s->hwctx->alloc); + av_free(iv); +} + +int ff_vk_create_imageview(AVFilterContext *avctx, FFVkExecContext *e, + VkImageView *v, VkImage img, VkFormat fmt, + const VkComponentMapping map) +{ + int err; + AVBufferRef *buf; + VulkanFilterContext *s = avctx->priv; + VkImageViewCreateInfo imgview_spawn = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = NULL, + .image = img, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = fmt, + .components = map, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + + ImageViewCtx *iv = av_mallocz(sizeof(*iv)); + + VkResult ret = vkCreateImageView(s->hwctx->act_dev, &imgview_spawn, + s->hwctx->alloc, &iv->view); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to create imageview: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + buf = av_buffer_create((uint8_t *)iv, sizeof(*iv), destroy_imageview, s, 0); + if (!buf) { + destroy_imageview(s, (uint8_t *)iv); + return AVERROR(ENOMEM); + } + + /* Add to queue dependencies */ + err = ff_vk_add_dep_exec_ctx(avctx, e, &buf, 1); + if (err) { + av_buffer_unref(&buf); + return err; + } + + *v = iv->view; + + return 0; +} + +FN_CREATING(VulkanPipeline, SPIRVShader, shader, shaders, shaders_num) +SPIRVShader *ff_vk_init_shader(AVFilterContext *avctx, VulkanPipeline *pl, + const char *name, VkShaderStageFlags stage) +{ + SPIRVShader *shd = create_shader(pl); + if (!shd) + return NULL; + + av_bprint_init(&shd->src, 0, AV_BPRINT_SIZE_UNLIMITED); + + shd->shader.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shd->shader.stage = stage; + + shd->name = name; + + GLSLF(0, #version %i ,460); + GLSLC(0, #define IS_WITHIN(v1, v2) ((v1.x < v2.x) && (v1.y < v2.y)) ); + GLSLC(0, ); + + return shd; +} + +void ff_vk_set_compute_shader_sizes(AVFilterContext *avctx, SPIRVShader *shd, + int local_size[3]) +{ + shd->local_size[0] = local_size[0]; + shd->local_size[1] = local_size[1]; + shd->local_size[2] = local_size[2]; + + av_bprintf(&shd->src, "layout (local_size_x = %i, " + "local_size_y = %i, local_size_z = %i) in;\n\n", + shd->local_size[0], shd->local_size[1], shd->local_size[2]); +} + +static void print_shader(AVFilterContext *avctx, SPIRVShader *shd, int prio) +{ + int line = 0; + const char *p = shd->src.str; + const char *start = p; + + AVBPrint buf; + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); + + for (int i = 0; i < strlen(p); i++) { + if (p[i] == '\n') { + av_bprintf(&buf, "%i\t", ++line); + av_bprint_append_data(&buf, start, &p[i] - start + 1); + start = &p[i + 1]; + } + } + + av_log(avctx, prio, "Shader %s: \n%s", shd->name, buf.str); + av_bprint_finalize(&buf, NULL); +} + +int ff_vk_compile_shader(AVFilterContext *avctx, SPIRVShader *shd, + const char *entrypoint) +{ + VkResult ret; + VulkanFilterContext *s = avctx->priv; + VkShaderModuleCreateInfo shader_create; + GLSlangResult *res; + + static const enum GLSlangStage emap[] = { + [VK_SHADER_STAGE_VERTEX_BIT] = GLSLANG_VERTEX, + [VK_SHADER_STAGE_FRAGMENT_BIT] = GLSLANG_FRAGMENT, + [VK_SHADER_STAGE_COMPUTE_BIT] = GLSLANG_COMPUTE, + }; + + shd->shader.pName = entrypoint; + + res = glslang_compile(shd->src.str, emap[shd->shader.stage]); + if (!res) + return AVERROR(ENOMEM); + + if (res->rval) { + av_log(avctx, AV_LOG_ERROR, "Error compiling shader %s: %s!\n", + shd->name, av_err2str(res->rval)); + print_shader(avctx, shd, AV_LOG_ERROR); + if (res->error_msg) + av_log(avctx, AV_LOG_ERROR, "%s", res->error_msg); + av_free(res->error_msg); + return res->rval; + } + + print_shader(avctx, shd, AV_LOG_VERBOSE); + + shader_create.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shader_create.pNext = NULL; + shader_create.codeSize = res->size; + shader_create.flags = 0; + shader_create.pCode = res->data; + + ret = vkCreateShaderModule(s->hwctx->act_dev, &shader_create, NULL, + &shd->shader.module); + + /* Free the GLSlangResult struct */ + av_free(res->data); + av_free(res); + + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to create shader module: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + av_log(avctx, AV_LOG_VERBOSE, "Shader %s linked! Size: %zu bytes\n", + shd->name, shader_create.codeSize); + + return 0; +} + +static const struct descriptor_props { + size_t struct_size; /* Size of the opaque which updates the descriptor */ + const char *type; + int is_uniform; + int mem_quali; /* Can use a memory qualifier */ + int dim_needed; /* Must indicate dimension */ + int buf_content; /* Must indicate buffer contents */ +} descriptor_props[] = { + [VK_DESCRIPTOR_TYPE_SAMPLER] = { sizeof(VkDescriptorImageInfo), "sampler", 1, 0, 0, 0, }, + [VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE] = { sizeof(VkDescriptorImageInfo), "texture", 1, 0, 1, 0, }, + [VK_DESCRIPTOR_TYPE_STORAGE_IMAGE] = { sizeof(VkDescriptorImageInfo), "image", 1, 1, 1, 0, }, + [VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT] = { sizeof(VkDescriptorImageInfo), "subpassInput", 1, 0, 0, 0, }, + [VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER] = { sizeof(VkDescriptorImageInfo), "sampler", 1, 0, 1, 0, }, + [VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER] = { sizeof(VkDescriptorBufferInfo), NULL, 1, 0, 0, 1, }, + [VK_DESCRIPTOR_TYPE_STORAGE_BUFFER] = { sizeof(VkDescriptorBufferInfo), "buffer", 0, 1, 0, 1, }, + [VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] = { sizeof(VkDescriptorBufferInfo), NULL, 1, 0, 0, 1, }, + [VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC] = { sizeof(VkDescriptorBufferInfo), "buffer", 0, 1, 0, 1, }, + [VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER] = { sizeof(VkBufferView), "samplerBuffer", 1, 0, 0, 0, }, + [VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER] = { sizeof(VkBufferView), "imageBuffer", 1, 0, 0, 0, }, +}; + +int ff_vk_add_descriptor_set(AVFilterContext *avctx, VulkanPipeline *pl, + SPIRVShader *shd, VulkanDescriptorSetBinding *desc, + int num, int only_print_to_shader) +{ + VkResult ret; + VkDescriptorSetLayout *layout; + VulkanFilterContext *s = avctx->priv; + + if (only_print_to_shader) + goto print; + + pl->desc_layout = av_realloc_array(pl->desc_layout, sizeof(*pl->desc_layout), + pl->desc_layout_num + 1); + if (!pl->desc_layout) + return AVERROR(ENOMEM); + + layout = &pl->desc_layout[pl->desc_layout_num]; + memset(layout, 0, sizeof(*layout)); + + { /* Create descriptor set layout descriptions */ + VkDescriptorSetLayoutCreateInfo desc_create_layout = { 0 }; + VkDescriptorSetLayoutBinding *desc_binding; + + desc_binding = av_mallocz(sizeof(*desc_binding)*num); + if (!desc_binding) + return AVERROR(ENOMEM); + + for (int i = 0; i < num; i++) { + desc_binding[i].binding = i; + desc_binding[i].descriptorType = desc[i].type; + desc_binding[i].descriptorCount = FFMAX(desc[i].elems, 1); + desc_binding[i].stageFlags = desc[i].stages; + desc_binding[i].pImmutableSamplers = desc[i].samplers; + } + + desc_create_layout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + desc_create_layout.pBindings = desc_binding; + desc_create_layout.bindingCount = num; + + ret = vkCreateDescriptorSetLayout(s->hwctx->act_dev, &desc_create_layout, + s->hwctx->alloc, layout); + av_free(desc_binding); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to init descriptor set " + "layout: %s\n", ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + } + + { /* Pool each descriptor by type and update pool counts */ + for (int i = 0; i < num; i++) { + int j; + for (j = 0; j < pl->pool_size_desc_num; j++) + if (pl->pool_size_desc[j].type == desc[i].type) + break; + if (j >= pl->pool_size_desc_num) { + pl->pool_size_desc = av_realloc_array(pl->pool_size_desc, + sizeof(*pl->pool_size_desc), + ++pl->pool_size_desc_num); + if (!pl->pool_size_desc) + return AVERROR(ENOMEM); + memset(&pl->pool_size_desc[j], 0, sizeof(VkDescriptorPoolSize)); + } + pl->pool_size_desc[j].type = desc[i].type; + pl->pool_size_desc[j].descriptorCount += FFMAX(desc[i].elems, 1); + } + } + + { /* Create template creation struct */ + VkDescriptorUpdateTemplateCreateInfo *dt; + VkDescriptorUpdateTemplateEntry *des_entries; + + /* Freed after descriptor set initialization */ + des_entries = av_mallocz(num*sizeof(VkDescriptorUpdateTemplateEntry)); + if (!des_entries) + return AVERROR(ENOMEM); + + for (int i = 0; i < num; i++) { + des_entries[i].dstBinding = i; + des_entries[i].descriptorType = desc[i].type; + des_entries[i].descriptorCount = FFMAX(desc[i].elems, 1); + des_entries[i].dstArrayElement = 0; + des_entries[i].offset = ((uint8_t *)desc[i].updater) - (uint8_t *)s; + des_entries[i].stride = descriptor_props[desc[i].type].struct_size; + } + + pl->desc_template_info = av_realloc_array(pl->desc_template_info, + sizeof(*pl->desc_template_info), + pl->desc_layout_num + 1); + if (!pl->desc_template_info) + return AVERROR(ENOMEM); + + dt = &pl->desc_template_info[pl->desc_layout_num]; + memset(dt, 0, sizeof(*dt)); + + dt->sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO; + dt->templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET; + dt->descriptorSetLayout = *layout; + dt->pDescriptorUpdateEntries = des_entries; + dt->descriptorUpdateEntryCount = num; + } + + pl->desc_layout_num++; + +print: + /* Write shader info */ + for (int i = 0; i < num; i++) { + const struct descriptor_props *prop = &descriptor_props[desc[i].type]; + GLSLA("layout (set = %i, binding = %i", pl->desc_layout_num - 1, i); + + if (desc[i].mem_layout) + GLSLA(", %s", desc[i].mem_layout); + GLSLA(")"); + + if (prop->is_uniform) + GLSLA(" uniform"); + + if (prop->mem_quali && desc[i].mem_quali) + GLSLA(" %s", desc[i].mem_quali); + + if (prop->type) + GLSLA(" %s", prop->type); + + if (prop->dim_needed) + GLSLA("%iD", desc[i].dimensions); + + GLSLA(" %s", desc[i].name); + + if (prop->buf_content) + GLSLA(" {\n %s\n}", desc[i].buf_content); + else if (desc[i].elems > 0) + GLSLA("[%i]", desc[i].elems); + + GLSLA(";\n"); + } + GLSLA("\n"); + + return 0; +} + +void ff_vk_update_descriptor_set(AVFilterContext *avctx, VulkanPipeline *pl, + int set_id) +{ + VulkanFilterContext *s = avctx->priv; + + vkUpdateDescriptorSetWithTemplate(s->hwctx->act_dev, + pl->desc_set[s->cur_queue_idx * pl->desc_layout_num + set_id], + pl->desc_template[set_id], + s); +} + +void ff_vk_update_push_exec(AVFilterContext *avctx, FFVkExecContext *e, + VkShaderStageFlagBits stage, int offset, + size_t size, void *src) +{ + VulkanFilterContext *s = avctx->priv; + vkCmdPushConstants(e->bufs[s->cur_queue_idx], e->bound_pl->pipeline_layout, + stage, offset, size, src); +} + +int ff_vk_init_pipeline_layout(AVFilterContext *avctx, VulkanPipeline *pl) +{ + VkResult ret; + VulkanFilterContext *s = avctx->priv; + + pl->descriptor_sets_num = pl->desc_layout_num * s->queue_count; + + { /* Init descriptor set pool */ + VkDescriptorPoolCreateInfo pool_create_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .poolSizeCount = pl->pool_size_desc_num, + .pPoolSizes = pl->pool_size_desc, + .maxSets = pl->descriptor_sets_num, + }; + + ret = vkCreateDescriptorPool(s->hwctx->act_dev, &pool_create_info, + s->hwctx->alloc, &pl->desc_pool); + av_freep(&pl->pool_size_desc); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to init descriptor set " + "pool: %s\n", ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + } + + { /* Allocate descriptor sets */ + VkDescriptorSetAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = pl->desc_pool, + .descriptorSetCount = pl->descriptor_sets_num, + .pSetLayouts = pl->desc_layout, + }; + + pl->desc_set = av_malloc(pl->descriptor_sets_num*sizeof(*pl->desc_set)); + if (!pl->desc_set) + return AVERROR(ENOMEM); + + ret = vkAllocateDescriptorSets(s->hwctx->act_dev, &alloc_info, + pl->desc_set); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to allocate descriptor set: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + } + + { /* Finally create the pipeline layout */ + VkPipelineLayoutCreateInfo spawn_pipeline_layout = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = pl->desc_layout_num, + .pSetLayouts = pl->desc_layout, + .pushConstantRangeCount = pl->push_consts_num, + .pPushConstantRanges = pl->push_consts, + }; + + ret = vkCreatePipelineLayout(s->hwctx->act_dev, &spawn_pipeline_layout, + s->hwctx->alloc, &pl->pipeline_layout); + av_freep(&pl->push_consts); + pl->push_consts_num = 0; + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to init pipeline layout: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + } + + { /* Descriptor template (for tightly packed descriptors) */ + VkDescriptorUpdateTemplateCreateInfo *desc_template_info; + + pl->desc_template = av_malloc(pl->descriptor_sets_num*sizeof(*pl->desc_template)); + if (!pl->desc_template) + return AVERROR(ENOMEM); + + /* Create update templates for the descriptor sets */ + for (int i = 0; i < pl->descriptor_sets_num; i++) { + desc_template_info = &pl->desc_template_info[i % pl->desc_layout_num]; + desc_template_info->pipelineLayout = pl->pipeline_layout; + ret = vkCreateDescriptorUpdateTemplate(s->hwctx->act_dev, + desc_template_info, + s->hwctx->alloc, + &pl->desc_template[i]); + av_free((void *)desc_template_info->pDescriptorUpdateEntries); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to init descriptor " + "template: %s\n", ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + } + + av_freep(&pl->desc_template_info); + } + + return 0; +} + +FN_CREATING(VulkanFilterContext, VulkanPipeline, pipeline, pipelines, pipelines_num) +VulkanPipeline *ff_vk_create_pipeline(AVFilterContext *avctx) +{ + return create_pipeline(avctx->priv); +} + +int ff_vk_init_compute_pipeline(AVFilterContext *avctx, VulkanPipeline *pl) +{ + int i; + VkResult ret; + VulkanFilterContext *s = avctx->priv; + + VkComputePipelineCreateInfo pipe = { + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + .layout = pl->pipeline_layout, + }; + + for (i = 0; i < pl->shaders_num; i++) { + if (pl->shaders[i]->shader.stage & VK_SHADER_STAGE_COMPUTE_BIT) { + pipe.stage = pl->shaders[i]->shader; + break; + } + } + if (i == pl->shaders_num) { + av_log(avctx, AV_LOG_ERROR, "Can't init compute pipeline, no shader\n"); + return AVERROR(EINVAL); + } + + ret = vkCreateComputePipelines(s->hwctx->act_dev, VK_NULL_HANDLE, 1, &pipe, + s->hwctx->alloc, &pl->pipeline); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to init compute pipeline: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + pl->bind_point = VK_PIPELINE_BIND_POINT_COMPUTE; + + return 0; +} + +void ff_vk_bind_pipeline_exec(AVFilterContext *avctx, FFVkExecContext *e, + VulkanPipeline *pl) +{ + VulkanFilterContext *s = avctx->priv; + + vkCmdBindPipeline(e->bufs[s->cur_queue_idx], pl->bind_point, pl->pipeline); + + vkCmdBindDescriptorSets(e->bufs[s->cur_queue_idx], pl->bind_point, + pl->pipeline_layout, 0, pl->descriptor_sets_num, + pl->desc_set, 0, 0); + + e->bound_pl = pl; +} + +static void free_exec_ctx(VulkanFilterContext *s, FFVkExecContext *e) +{ + /* Make sure all queues have finished executing */ + for (int i = 0; i < s->queue_count; i++) { + FFVkQueueCtx *q = &e->queues[i]; + + if (q->fence) { + vkWaitForFences(s->hwctx->act_dev, 1, &q->fence, VK_TRUE, UINT64_MAX); + vkResetFences(s->hwctx->act_dev, 1, &q->fence); + } + + /* Free the fence */ + if (q->fence) + vkDestroyFence(s->hwctx->act_dev, q->fence, s->hwctx->alloc); + + /* Free buffer dependencies */ + for (int j = 0; j < q->nb_buf_deps; j++) + av_buffer_unref(&q->buf_deps[j]); + av_free(q->buf_deps); + + /* Free frame dependencies */ + for (int j = 0; j < q->nb_frame_deps; j++) + av_frame_free(&q->frame_deps[j]); + av_free(q->frame_deps); + } + + if (e->bufs) + vkFreeCommandBuffers(s->hwctx->act_dev, e->pool, s->queue_count, e->bufs); + if (e->pool) + vkDestroyCommandPool(s->hwctx->act_dev, e->pool, s->hwctx->alloc); + + av_freep(&e->bufs); + av_freep(&e->queues); + av_freep(&e->sem_sig); + av_freep(&e->sem_wait); + av_freep(&e->sem_wait_dst); + av_free(e); +} + +static void free_pipeline(VulkanFilterContext *s, VulkanPipeline *pl) +{ + for (int i = 0; i < pl->shaders_num; i++) { + SPIRVShader *shd = pl->shaders[i]; + av_bprint_finalize(&shd->src, NULL); + vkDestroyShaderModule(s->hwctx->act_dev, shd->shader.module, + s->hwctx->alloc); + av_free(shd); + } + + vkDestroyPipeline(s->hwctx->act_dev, pl->pipeline, s->hwctx->alloc); + vkDestroyPipelineLayout(s->hwctx->act_dev, pl->pipeline_layout, + s->hwctx->alloc); + + for (int i = 0; i < pl->desc_layout_num; i++) { + if (pl->desc_template && pl->desc_template[i]) + vkDestroyDescriptorUpdateTemplate(s->hwctx->act_dev, pl->desc_template[i], + s->hwctx->alloc); + if (pl->desc_layout && pl->desc_layout[i]) + vkDestroyDescriptorSetLayout(s->hwctx->act_dev, pl->desc_layout[i], + s->hwctx->alloc); + } + + /* Also frees the descriptor sets */ + if (pl->desc_pool) + vkDestroyDescriptorPool(s->hwctx->act_dev, pl->desc_pool, + s->hwctx->alloc); + + av_freep(&pl->desc_set); + av_freep(&pl->shaders); + av_freep(&pl->desc_layout); + av_freep(&pl->desc_template); + av_freep(&pl->push_consts); + pl->push_consts_num = 0; + + /* Only freed in case of failure */ + av_freep(&pl->pool_size_desc); + if (pl->desc_template_info) { + for (int i = 0; i < pl->descriptor_sets_num; i++) + av_free((void *)pl->desc_template_info[i].pDescriptorUpdateEntries); + av_freep(&pl->desc_template_info); + } + + av_free(pl); +} + +void ff_vk_filter_uninit(AVFilterContext *avctx) +{ + VulkanFilterContext *s = avctx->priv; + + glslang_uninit(); + + for (int i = 0; i < s->exec_ctx_num; i++) + free_exec_ctx(s, s->exec_ctx[i]); + av_freep(&s->exec_ctx); + + for (int i = 0; i < s->samplers_num; i++) { + vkDestroySampler(s->hwctx->act_dev, *s->samplers[i], s->hwctx->alloc); + av_free(s->samplers[i]); + } + av_freep(&s->samplers); + + for (int i = 0; i < s->pipelines_num; i++) + free_pipeline(s, s->pipelines[i]); + av_freep(&s->pipelines); + + av_freep(&s->scratch); + s->scratch_size = 0; + + av_buffer_unref(&s->device_ref); + av_buffer_unref(&s->frames_ref); +} diff --git a/libavfilter/vulkan.h b/libavfilter/vulkan.h new file mode 100644 index 00000000000..f9a4dc5839a --- /dev/null +++ b/libavfilter/vulkan.h @@ -0,0 +1,376 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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_H +#define AVFILTER_VULKAN_H + +#include "avfilter.h" +#include "libavutil/pixdesc.h" +#include "libavutil/bprint.h" +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_vulkan.h" + +/* GLSL management macros */ +#define INDENT(N) INDENT_##N +#define INDENT_0 +#define INDENT_1 INDENT_0 " " +#define INDENT_2 INDENT_1 INDENT_1 +#define INDENT_3 INDENT_2 INDENT_1 +#define INDENT_4 INDENT_3 INDENT_1 +#define INDENT_5 INDENT_4 INDENT_1 +#define INDENT_6 INDENT_5 INDENT_1 +#define C(N, S) INDENT(N) #S "\n" +#define GLSLC(N, S) av_bprintf(&shd->src, C(N, S)) +#define GLSLA(...) av_bprintf(&shd->src, __VA_ARGS__) +#define GLSLF(N, S, ...) av_bprintf(&shd->src, C(N, S), __VA_ARGS__) +#define GLSLD(D) GLSLC(0, ); \ + av_bprint_append_data(&shd->src, D, strlen(D)); \ + GLSLC(0, ) + +/* Helper, pretty much every Vulkan return value needs to be checked */ +#define RET(x) \ + do { \ + if ((err = (x)) < 0) \ + goto fail; \ + } while (0) + +/* Gets the queues count for a single queue family */ +#define GET_QUEUE_COUNT(hwctx, graph, comp, tx) ( \ + graph ? hwctx->nb_graphics_queues : \ + comp ? (hwctx->nb_comp_queues ? \ + hwctx->nb_comp_queues : hwctx->nb_graphics_queues) : \ + tx ? (hwctx->nb_tx_queues ? hwctx->nb_tx_queues : \ + (hwctx->nb_comp_queues ? \ + hwctx->nb_comp_queues : hwctx->nb_graphics_queues)) : \ + 0 \ +) + +/* Useful for attaching immutable samplers to arrays */ +#define DUP_SAMPLER_ARRAY4(x) (VkSampler []){ x, x, x, x, } + +typedef struct SPIRVShader { + const char *name; /* Name for id/debugging purposes */ + AVBPrint src; + int local_size[3]; /* Compute shader workgroup sizes */ + VkPipelineShaderStageCreateInfo shader; +} SPIRVShader; + +typedef struct VulkanDescriptorSetBinding { + const char *name; + VkDescriptorType type; + const char *mem_layout; /* Storage images (rgba8, etc.) and buffers (std430, etc.) */ + const char *mem_quali; /* readonly, writeonly, etc. */ + const char *buf_content; /* For buffers */ + uint32_t dimensions; /* Needed for e.g. sampler%iD */ + uint32_t elems; /* 0 - scalar, 1 or more - vector */ + VkShaderStageFlags stages; + const VkSampler *samplers; /* Immutable samplers, length - #elems */ + void *updater; /* Pointer to VkDescriptor*Info */ +} VulkanDescriptorSetBinding; + +typedef struct FFVkBuffer { + VkBuffer buf; + VkDeviceMemory mem; + VkMemoryPropertyFlagBits flags; +} FFVkBuffer; + +typedef struct VulkanPipeline { + VkPipelineBindPoint bind_point; + + /* Contexts */ + VkPipelineLayout pipeline_layout; + VkPipeline pipeline; + + /* Shaders */ + SPIRVShader **shaders; + int shaders_num; + + /* Push consts */ + VkPushConstantRange *push_consts; + int push_consts_num; + + /* Descriptors */ + VkDescriptorSetLayout *desc_layout; + VkDescriptorPool desc_pool; + VkDescriptorSet *desc_set; + VkDescriptorUpdateTemplate *desc_template; + int desc_layout_num; + int descriptor_sets_num; + int pool_size_desc_num; + + /* Temporary, used to store data in between initialization stages */ + VkDescriptorUpdateTemplateCreateInfo *desc_template_info; + VkDescriptorPoolSize *pool_size_desc; +} VulkanPipeline; + +typedef struct FFVkQueueCtx { + VkFence fence; + VkQueue queue; + + /* Buffer dependencies */ + AVBufferRef **buf_deps; + int nb_buf_deps; + int buf_deps_alloc_size; + + /* Frame dependencies */ + AVFrame **frame_deps; + int nb_frame_deps; + int frame_deps_alloc_size; +} FFVkQueueCtx; + +typedef struct FFVkExecContext { + VkCommandPool pool; + VkCommandBuffer *bufs; + FFVkQueueCtx *queues; + + AVBufferRef ***deps; + int *nb_deps; + int *dep_alloc_size; + + VulkanPipeline *bound_pl; + + VkSemaphore *sem_wait; + int sem_wait_alloc; /* Allocated sem_wait */ + int sem_wait_cnt; + + VkPipelineStageFlagBits *sem_wait_dst; + int sem_wait_dst_alloc; /* Allocated sem_wait_dst */ + + VkSemaphore *sem_sig; + int sem_sig_alloc; /* Allocated sem_sig */ + int sem_sig_cnt; +} FFVkExecContext; + +typedef struct VulkanFilterContext { + const AVClass *class; + + AVBufferRef *device_ref; + AVBufferRef *frames_ref; /* For in-place filtering */ + AVHWDeviceContext *device; + AVVulkanDeviceContext *hwctx; + + /* State - mirrored with the exec ctx */ + int cur_queue_idx; + int queue_family_idx; + int queue_count; + + /* Properties */ + int output_width; + int output_height; + enum AVPixelFormat output_format; + enum AVPixelFormat input_format; + + /* Samplers */ + VkSampler **samplers; + int samplers_num; + + /* Exec contexts */ + FFVkExecContext **exec_ctx; + int exec_ctx_num; + + /* Pipelines (each can have 1 shader of each type) */ + VulkanPipeline **pipelines; + int pipelines_num; + + void *scratch; /* Scratch memory used only in functions */ + unsigned int scratch_size; +} VulkanFilterContext; + +/* Identity mapping - r = r, b = b, g = g, a = a */ +extern const VkComponentMapping ff_comp_identity_map; + +/** + * General lavfi IO functions + */ +int ff_vk_filter_query_formats (AVFilterContext *avctx); +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); +void ff_vk_filter_uninit (AVFilterContext *avctx); + +/** + * Converts Vulkan return values to strings + */ +const char *ff_vk_ret2str(VkResult res); + +/** + * Returns 1 if the image is any sort of supported RGB + */ +int ff_vk_mt_is_np_rgb(enum AVPixelFormat pix_fmt); + +/** + * Gets the glsl format string for a pixel format + */ +const char *ff_vk_shader_rep_fmt(enum AVPixelFormat pixfmt); + +/** + * Create a Vulkan sampler, will be auto-freed in ff_vk_filter_uninit() + */ +VkSampler *ff_vk_init_sampler(AVFilterContext *avctx, int unnorm_coords, + VkFilter filt); + +/** + * Create an imageview. + * Guaranteed to remain alive until the queue submission has finished executing, + * and will be destroyed after that. + */ +int ff_vk_create_imageview(AVFilterContext *avctx, FFVkExecContext *e, + VkImageView *v, VkImage img, VkFormat fmt, + const VkComponentMapping map); + +/** + * Define a push constant for a given stage into a pipeline. + * Must be called before the pipeline layout has been initialized. + */ +int ff_vk_add_push_constant(AVFilterContext *avctx, VulkanPipeline *pl, + int offset, int size, VkShaderStageFlagBits stage); + +/** + * Inits a pipeline. Everything in it will be auto-freed when calling + * ff_vk_filter_uninit(). + */ +VulkanPipeline *ff_vk_create_pipeline(AVFilterContext *avctx); + +/** + * Inits a shader for a specific pipeline. Will be auto-freed on uninit. + */ +SPIRVShader *ff_vk_init_shader(AVFilterContext *avctx, VulkanPipeline *pl, + const char *name, VkShaderStageFlags stage); + +/** + * Writes the workgroup size for a shader. + */ +void ff_vk_set_compute_shader_sizes(AVFilterContext *avctx, SPIRVShader *shd, + int local_size[3]); + +/** + * Adds a descriptor set to the shader and registers them in the pipeline. + */ +int ff_vk_add_descriptor_set(AVFilterContext *avctx, VulkanPipeline *pl, + SPIRVShader *shd, VulkanDescriptorSetBinding *desc, + int num, int only_print_to_shader); + +/** + * Compiles the shader, entrypoint must be set to "main". + */ +int ff_vk_compile_shader(AVFilterContext *avctx, SPIRVShader *shd, + const char *entrypoint); + +/** + * Initializes the pipeline layout after all shaders and descriptor sets have + * been finished. + */ +int ff_vk_init_pipeline_layout(AVFilterContext *avctx, VulkanPipeline *pl); + +/** + * Initializes a compute pipeline. Will pick the first shader with the + * COMPUTE flag set. + */ +int ff_vk_init_compute_pipeline(AVFilterContext *avctx, VulkanPipeline *pl); + +/** + * Updates a descriptor set via the updaters defined. + * Can be called immediately after pipeline creation, but must be called + * at least once before queue submission. + */ +void ff_vk_update_descriptor_set(AVFilterContext *avctx, VulkanPipeline *pl, + int set_id); + +/** + * Init an execution context for command recording and queue submission. + * WIll be auto-freed on uninit. + */ +int ff_vk_create_exec_ctx(AVFilterContext *avctx, FFVkExecContext **ctx); + +/** + * Begin recording to the command buffer. Previous execution must have been + * completed, which ff_vk_submit_exec_queue() will ensure. + */ +int ff_vk_start_exec_recording(AVFilterContext *avctx, FFVkExecContext *e); + +/** + * Add a command to bind the completed pipeline and its descriptor sets. + * Must be called after ff_vk_start_exec_recording() and before submission. + */ +void ff_vk_bind_pipeline_exec(AVFilterContext *avctx, FFVkExecContext *e, + VulkanPipeline *pl); + +/** + * Updates push constants. + * Must be called after binding a pipeline if any push constants were defined. + */ +void ff_vk_update_push_exec(AVFilterContext *avctx, FFVkExecContext *e, + VkShaderStageFlagBits stage, int offset, + size_t size, void *src); + +/** + * Gets the command buffer to use for this submission from the exe context. + */ +VkCommandBuffer ff_vk_get_exec_buf(AVFilterContext *avctx, FFVkExecContext *e); + +/** + * Adds a generic AVBufferRef as a queue depenency. + */ +int ff_vk_add_dep_exec_ctx(AVFilterContext *avctx, FFVkExecContext *e, + AVBufferRef **deps, int nb_deps); + +/** + * Discards all queue dependencies + */ +void ff_vk_discard_exec_deps(AVFilterContext *avctx, FFVkExecContext *e); + +/** + * Adds a frame as a queue dependency. This also manages semaphore signalling. + * Must be called before submission. + */ +int ff_vk_add_exec_dep(AVFilterContext *avctx, FFVkExecContext *e, + AVFrame *frame, VkPipelineStageFlagBits in_wait_dst_flag); + +/** + * Submits a command buffer to the queue for execution. + * Will block until execution has finished in order to simplify resource + * management. + */ +int ff_vk_submit_exec_queue(AVFilterContext *avctx, FFVkExecContext *e); + +/** + * Create a VkBuffer with the specified parameters. + */ +int ff_vk_create_buf(AVFilterContext *avctx, FFVkBuffer *buf, size_t size, + VkBufferUsageFlags usage, VkMemoryPropertyFlagBits flags); + +/** + * Maps the buffer to userspace. Set invalidate to 1 if reading the contents + * is necessary. + */ +int ff_vk_map_buffers(AVFilterContext *avctx, FFVkBuffer *buf, uint8_t *mem[], + int nb_buffers, int invalidate); + +/** + * Unmaps the buffer from userspace. Set flush to 1 to write and sync. + */ +int ff_vk_unmap_buffers(AVFilterContext *avctx, FFVkBuffer *buf, int nb_buffers, + int flush); + +/** + * Frees a buffer. + */ +void ff_vk_free_buf(AVFilterContext *avctx, FFVkBuffer *buf); + +#endif /* AVFILTER_VULKAN_H */ diff --git a/libavfilter/window_func.h b/libavfilter/window_func.h index 1de8f1fbdb6..494c5b1c661 100644 --- a/libavfilter/window_func.h +++ b/libavfilter/window_func.h @@ -133,7 +133,7 @@ static inline void generate_window_func(float *lut, int N, int win_func, for (c = 1 - 1 / (b*b), n = (N-1) / 2; n >= 0; --n) { for (sum = !n, b = t = j = 1; j <= n && sum != t; b *= (n-j) * (1./j), ++j) t = sum, sum += (b *= c * (N - n - j) * (1./j)); - sum /= (N - 1 - n), sum /= (norm = norm ? norm : sum); + sum /= (N - 1 - n), norm = norm ? norm : sum, sum /= norm; lut[n] = sum; lut[N - 1 - n] = sum; } diff --git a/libavfilter/x86/Makefile b/libavfilter/x86/Makefile index 6b0361bed29..016a5b3511d 100644 --- a/libavfilter/x86/Makefile +++ b/libavfilter/x86/Makefile @@ -2,10 +2,12 @@ OBJS-$(CONFIG_SCENE_SAD) += x86/scene_sad_init.o OBJS-$(CONFIG_AFIR_FILTER) += x86/af_afir_init.o OBJS-$(CONFIG_ANLMDN_FILTER) += x86/af_anlmdn_init.o +OBJS-$(CONFIG_ATADENOISE_FILTER) += x86/vf_atadenoise_init.o OBJS-$(CONFIG_BLEND_FILTER) += x86/vf_blend_init.o OBJS-$(CONFIG_BWDIF_FILTER) += x86/vf_bwdif_init.o OBJS-$(CONFIG_COLORSPACE_FILTER) += x86/colorspacedsp_init.o -OBJS-$(CONFIG_EQ_FILTER) += x86/vf_eq.o +OBJS-$(CONFIG_CONVOLUTION_FILTER) += x86/vf_convolution_init.o +OBJS-$(CONFIG_EQ_FILTER) += x86/vf_eq_init.o OBJS-$(CONFIG_FSPP_FILTER) += x86/vf_fspp_init.o OBJS-$(CONFIG_GBLUR_FILTER) += x86/vf_gblur_init.o OBJS-$(CONFIG_GRADFUN_FILTER) += x86/vf_gradfun_init.o @@ -15,6 +17,7 @@ OBJS-$(CONFIG_HQDN3D_FILTER) += x86/vf_hqdn3d_init.o OBJS-$(CONFIG_IDET_FILTER) += x86/vf_idet_init.o OBJS-$(CONFIG_INTERLACE_FILTER) += x86/vf_tinterlace_init.o OBJS-$(CONFIG_LIMITER_FILTER) += x86/vf_limiter_init.o +OBJS-$(CONFIG_MASKEDCLAMP_FILTER) += x86/vf_maskedclamp_init.o OBJS-$(CONFIG_MASKEDMERGE_FILTER) += x86/vf_maskedmerge_init.o OBJS-$(CONFIG_NOISE_FILTER) += x86/vf_noise.o OBJS-$(CONFIG_OVERLAY_FILTER) += x86/vf_overlay_init.o @@ -29,7 +32,9 @@ OBJS-$(CONFIG_STEREO3D_FILTER) += x86/vf_stereo3d_init.o OBJS-$(CONFIG_TBLEND_FILTER) += x86/vf_blend_init.o OBJS-$(CONFIG_THRESHOLD_FILTER) += x86/vf_threshold_init.o OBJS-$(CONFIG_TINTERLACE_FILTER) += x86/vf_tinterlace_init.o +OBJS-$(CONFIG_TRANSPOSE_FILTER) += x86/vf_transpose_init.o OBJS-$(CONFIG_VOLUME_FILTER) += x86/af_volume_init.o +OBJS-$(CONFIG_V360_FILTER) += x86/vf_v360_init.o OBJS-$(CONFIG_W3FDIF_FILTER) += x86/vf_w3fdif_init.o OBJS-$(CONFIG_YADIF_FILTER) += x86/vf_yadif_init.o @@ -37,9 +42,12 @@ X86ASM-OBJS-$(CONFIG_SCENE_SAD) += x86/scene_sad.o X86ASM-OBJS-$(CONFIG_AFIR_FILTER) += x86/af_afir.o X86ASM-OBJS-$(CONFIG_ANLMDN_FILTER) += x86/af_anlmdn.o +X86ASM-OBJS-$(CONFIG_ATADENOISE_FILTER) += x86/vf_atadenoise.o X86ASM-OBJS-$(CONFIG_BLEND_FILTER) += x86/vf_blend.o X86ASM-OBJS-$(CONFIG_BWDIF_FILTER) += x86/vf_bwdif.o X86ASM-OBJS-$(CONFIG_COLORSPACE_FILTER) += x86/colorspacedsp.o +X86ASM-OBJS-$(CONFIG_CONVOLUTION_FILTER) += x86/vf_convolution.o +X86ASM-OBJS-$(CONFIG_EQ_FILTER) += x86/vf_eq.o X86ASM-OBJS-$(CONFIG_FRAMERATE_FILTER) += x86/vf_framerate.o X86ASM-OBJS-$(CONFIG_FSPP_FILTER) += x86/vf_fspp.o X86ASM-OBJS-$(CONFIG_GBLUR_FILTER) += x86/vf_gblur.o @@ -49,6 +57,7 @@ X86ASM-OBJS-$(CONFIG_HQDN3D_FILTER) += x86/vf_hqdn3d.o X86ASM-OBJS-$(CONFIG_IDET_FILTER) += x86/vf_idet.o X86ASM-OBJS-$(CONFIG_INTERLACE_FILTER) += x86/vf_interlace.o X86ASM-OBJS-$(CONFIG_LIMITER_FILTER) += x86/vf_limiter.o +X86ASM-OBJS-$(CONFIG_MASKEDCLAMP_FILTER) += x86/vf_maskedclamp.o X86ASM-OBJS-$(CONFIG_MASKEDMERGE_FILTER) += x86/vf_maskedmerge.o X86ASM-OBJS-$(CONFIG_OVERLAY_FILTER) += x86/vf_overlay.o X86ASM-OBJS-$(CONFIG_PP7_FILTER) += x86/vf_pp7.o @@ -63,6 +72,8 @@ X86ASM-OBJS-$(CONFIG_STEREO3D_FILTER) += x86/vf_stereo3d.o X86ASM-OBJS-$(CONFIG_TBLEND_FILTER) += x86/vf_blend.o X86ASM-OBJS-$(CONFIG_THRESHOLD_FILTER) += x86/vf_threshold.o X86ASM-OBJS-$(CONFIG_TINTERLACE_FILTER) += x86/vf_interlace.o +X86ASM-OBJS-$(CONFIG_TRANSPOSE_FILTER) += x86/vf_transpose.o X86ASM-OBJS-$(CONFIG_VOLUME_FILTER) += x86/af_volume.o +X86ASM-OBJS-$(CONFIG_V360_FILTER) += x86/vf_v360.o X86ASM-OBJS-$(CONFIG_W3FDIF_FILTER) += x86/vf_w3fdif.o X86ASM-OBJS-$(CONFIG_YADIF_FILTER) += x86/vf_yadif.o x86/yadif-16.o x86/yadif-10.o diff --git a/libavfilter/x86/scene_sad_init.c b/libavfilter/x86/scene_sad_init.c index f8104dcb4f8..2c3729ceee6 100644 --- a/libavfilter/x86/scene_sad_init.c +++ b/libavfilter/x86/scene_sad_init.c @@ -37,9 +37,9 @@ static void FUNC_NAME(SCENE_SAD_PARAMS) { \ } #if HAVE_X86ASM -SCENE_SAD_FUNC(scene_sad_sse2, ff_scene_sad_sse2, 16); +SCENE_SAD_FUNC(scene_sad_sse2, ff_scene_sad_sse2, 16) #if HAVE_AVX2_EXTERNAL -SCENE_SAD_FUNC(scene_sad_avx2, ff_scene_sad_avx2, 32); +SCENE_SAD_FUNC(scene_sad_avx2, ff_scene_sad_avx2, 32) #endif #endif diff --git a/libavfilter/x86/vf_atadenoise.asm b/libavfilter/x86/vf_atadenoise.asm new file mode 100644 index 00000000000..4945ad3f5e1 --- /dev/null +++ b/libavfilter/x86/vf_atadenoise.asm @@ -0,0 +1,279 @@ +;***************************************************************************** +;* x86-optimized functions for atadenoise filter +;* +;* 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 +;****************************************************************************** + +%if ARCH_X86_64 + +%include "libavutil/x86/x86util.asm" + +SECTION_RODATA +pw_one: times 8 dw 1 +pw_ones: times 8 dw 65535 + +SECTION .text + +;------------------------------------------------------------------------------ +; void ff_filter_row(const uint8_t *src, uint8_t *dst, +; const uint8_t **srcf, +; int w, int mid, int size, +; int thra, int thrb) +;------------------------------------------------------------------------------ + +INIT_XMM sse4 +cglobal atadenoise_filter_row8, 8,10,13, src, dst, srcf, w, mid, size, i, j, srcfx, x + movsxdifnidn wq, wd + movsxdifnidn midq, midd + movsxdifnidn sizeq, sized + add srcq, wq + add dstq, wq + mov xq, wq + dec sizeq + neg xq + movd m4, r6m + SPLATW m4, m4 + movd m5, r7m + SPLATW m5, m5 + pxor m2, m2 + mova m10, [pw_ones] + + .loop: + mov iq, midq + mov jq, midq + pxor m3, m3 + pxor m11, m11 + movu m0, [srcq + xq] + punpcklbw m0, m2 + mova m7, m0 + mova m8, [pw_one] + mova m12, [pw_ones] + + .loop0: + inc iq + dec jq + + mov srcfxq, [srcfq + jq * 8] + add srcfxq, wq + + movu m1, [srcfxq + xq] + punpcklbw m1, m2 + mova m9, m1 + psubw m1, m0 + pabsw m1, m1 + paddw m11, m1 + pcmpgtw m1, m4 + mova m6, m11 + pcmpgtw m6, m5 + por m6, m1 + pxor m6, m10 + pand m12, m6 + pand m9, m12 + paddw m7, m9 + mova m6, m12 + psrlw m6, 15 + paddw m8, m6 + + mov srcfxq, [srcfq + iq * 8] + add srcfxq, wq + + movu m1, [srcfxq + xq] + punpcklbw m1, m2 + mova m9, m1 + psubw m1, m0 + pabsw m1, m1 + paddw m3, m1 + pcmpgtw m1, m4 + mova m6, m3 + pcmpgtw m6, m5 + por m6, m1 + pxor m6, m10 + pand m12, m6 + pand m9, m12 + paddw m7, m9 + mova m6, m12 + psrlw m6, 15 + paddw m8, m6 + + ptest m12, m12 + jz .finish + + cmp iq, sizeq + jl .loop0 + + .finish: + mova m9, m8 + psrlw m9, 1 + paddw m7, m9 + + mova m1, m7 + mova m6, m8 + + punpcklwd m7, m2 + punpcklwd m8, m2 + cvtdq2ps m7, m7 + cvtdq2ps m8, m8 + divps m7, m8 + cvttps2dq m7, m7 + packssdw m7, m7 + packuswb m7, m7 + + movd [dstq + xq], m7 + + punpckhwd m1, m2 + punpckhwd m6, m2 + cvtdq2ps m1, m1 + cvtdq2ps m6, m6 + divps m1, m6 + cvttps2dq m1, m1 + packssdw m1, m1 + packuswb m1, m1 + + movd [dstq + xq + 4], m1 + + add xq, mmsize/2 + jl .loop + RET + +INIT_XMM sse4 +cglobal atadenoise_filter_row8_serial, 8,10,13, src, dst, srcf, w, mid, size, i, j, srcfx, x + movsxdifnidn wq, wd + movsxdifnidn midq, midd + movsxdifnidn sizeq, sized + add srcq, wq + add dstq, wq + mov xq, wq + dec sizeq + neg xq + movd m4, r6m + SPLATW m4, m4 + movd m5, r7m + SPLATW m5, m5 + pxor m2, m2 + mova m10, [pw_ones] + + .loop: + mov iq, midq + mov jq, midq + pxor m3, m3 + pxor m11, m11 + movu m0, [srcq + xq] + punpcklbw m0, m2 + mova m7, m0 + mova m8, [pw_one] + mova m12, [pw_ones] + + .loop0: + dec jq + + mov srcfxq, [srcfq + jq * 8] + add srcfxq, wq + + movu m1, [srcfxq + xq] + punpcklbw m1, m2 + mova m9, m1 + psubw m1, m0 + pabsw m1, m1 + paddw m11, m1 + pcmpgtw m1, m4 + mova m6, m11 + pcmpgtw m6, m5 + por m6, m1 + pxor m6, m10 + pand m12, m6 + pand m9, m12 + paddw m7, m9 + mova m6, m12 + psrlw m6, 15 + paddw m8, m6 + + ptest m12, m12 + jz .end_loop0 + + cmp jq, 0 + jg .loop0 + + .end_loop0: + mova m12, [pw_ones] + + .loop1: + inc iq + + mov srcfxq, [srcfq + iq * 8] + add srcfxq, wq + + movu m1, [srcfxq + xq] + punpcklbw m1, m2 + mova m9, m1 + psubw m1, m0 + pabsw m1, m1 + paddw m3, m1 + pcmpgtw m1, m4 + mova m6, m3 + pcmpgtw m6, m5 + por m6, m1 + pxor m6, m10 + pand m12, m6 + pand m9, m12 + paddw m7, m9 + mova m6, m12 + psrlw m6, 15 + paddw m8, m6 + + ptest m12, m12 + jz .finish + + cmp iq, sizeq + jl .loop1 + + .finish: + mova m9, m8 + psrlw m9, 1 + paddw m7, m9 + + mova m1, m7 + mova m6, m8 + + punpcklwd m7, m2 + punpcklwd m8, m2 + cvtdq2ps m7, m7 + cvtdq2ps m8, m8 + divps m7, m8 + cvttps2dq m7, m7 + packssdw m7, m7 + packuswb m7, m7 + + movd [dstq + xq], m7 + + punpckhwd m1, m2 + punpckhwd m6, m2 + cvtdq2ps m1, m1 + cvtdq2ps m6, m6 + divps m1, m6 + cvttps2dq m1, m1 + packssdw m1, m1 + packuswb m1, m1 + + movd [dstq + xq + 4], m1 + + add xq, mmsize/2 + jl .loop + RET + +%endif diff --git a/libavfilter/x86/vf_atadenoise_init.c b/libavfilter/x86/vf_atadenoise_init.c new file mode 100644 index 00000000000..1f69b1af3f6 --- /dev/null +++ b/libavfilter/x86/vf_atadenoise_init.c @@ -0,0 +1,49 @@ +/* + * 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/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/mem.h" +#include "libavutil/x86/asm.h" +#include "libavutil/x86/cpu.h" +#include "libavfilter/atadenoise.h" + +void ff_atadenoise_filter_row8_sse4(const uint8_t *src, uint8_t *dst, + const uint8_t **srcf, + int w, int mid, int size, + int thra, int thrb); + +void ff_atadenoise_filter_row8_serial_sse4(const uint8_t *src, uint8_t *dst, + const uint8_t **srcf, + int w, int mid, int size, + int thra, int thrb); + +av_cold void ff_atadenoise_init_x86(ATADenoiseDSPContext *dsp, int depth, int algorithm) +{ + int cpu_flags = av_get_cpu_flags(); + + if (ARCH_X86_64 && EXTERNAL_SSE4(cpu_flags) && depth <= 8 && algorithm == PARALLEL) { + dsp->filter_row = ff_atadenoise_filter_row8_sse4; + } + + if (ARCH_X86_64 && EXTERNAL_SSE4(cpu_flags) && depth <= 8 && algorithm == SERIAL) { + dsp->filter_row = ff_atadenoise_filter_row8_serial_sse4; + } +} diff --git a/libavfilter/x86/vf_convolution.asm b/libavfilter/x86/vf_convolution.asm new file mode 100644 index 00000000000..754d4d10648 --- /dev/null +++ b/libavfilter/x86/vf_convolution.asm @@ -0,0 +1,156 @@ +;***************************************************************************** +;* x86-optimized functions for convolution filter +;* +;* This file is part of FFmpeg. +;* +;* FFmpeg is free software; you can redistribute it and/or +;* modify it under the terms of the GNU Lesser General Public +;* License as published by the Free Software Foundation; either +;* version 2.1 of the License, or (at your option) any later version. +;* +;* FFmpeg is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;* Lesser General Public License for more details. +;* +;* You should have received a copy of the GNU Lesser General 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 +half: dd 0.5 + +SECTION .text + +; void filter_3x3_sse4(uint8_t *dst, int width, +; float rdiv, float bias, const int *const matrix, +; const uint8_t *c[], int peak, int radius, +; int dstride, int stride) + + +%macro PROCESS_V 1 + movss m2, [matrixq + 4 * %1] + VBROADCASTSS m2, m2 + movss m3, [c%1q + xq] + punpcklbw m3, m6 + punpcklwd m3, m6 + pmulld m2, m3 + paddd m4, m2 +%endmacro + +%macro PROCESS_S 1 + movzx ptrd, byte [c%1q + xq] + imul ptrd, [matrixq + 4 * %1] + add rd, ptrd +%endmacro + +%macro FILTER_3X3 0 +%if UNIX64 +cglobal filter_3x3, 4, 15, 7, dst, width, matrix, ptr, c0, c1, c2, c3, c4, c5, c6, c7, c8, r, x +%else +cglobal filter_3x3, 4, 15, 7, dst, width, rdiv, bias, matrix, ptr, c0, c1, c2, c3, c4, c5, c6, c7, c8, r, x +%endif + +%if WIN64 + SWAP m0, m2 + SWAP m1, m3 + mov r2q, matrixmp + mov r3q, ptrmp + DEFINE_ARGS dst, width, matrix, ptr, c0, c1, c2, c3, c4, c5, c6, c7, c8, r, x +%endif + movsxdifnidn widthq, widthd + VBROADCASTSS m0, m0 + VBROADCASTSS m1, m1 + pxor m6, m6 + movss m5, [half] + VBROADCASTSS m5, m5 + mov c0q, [ptrq + 0*gprsize] + mov c1q, [ptrq + 1*gprsize] + mov c2q, [ptrq + 2*gprsize] + mov c3q, [ptrq + 3*gprsize] + mov c4q, [ptrq + 4*gprsize] + mov c5q, [ptrq + 5*gprsize] + mov c6q, [ptrq + 6*gprsize] + mov c7q, [ptrq + 7*gprsize] + mov c8q, [ptrq + 8*gprsize] + + xor xq, xq + cmp widthq, mmsize/4 + jl .loop2 + + mov rq, widthq + and rq, mmsize/4-1 + sub widthq, rq + +.loop1: + pxor m4, m4 ; sum = 0; + + PROCESS_V 0 + PROCESS_V 1 + PROCESS_V 2 + PROCESS_V 3 + PROCESS_V 4 + PROCESS_V 5 + PROCESS_V 6 + PROCESS_V 7 + PROCESS_V 8 + + cvtdq2ps m4, m4 + mulps m4, m0 ; sum *= rdiv + addps m4, m1 ; sum += bias + addps m4, m5 ; sum += 0.5 + cvttps2dq m4, m4 + packssdw m4, m4 + packuswb m4, m4 + movss [dstq + xq], m4 + + add xq, mmsize/4 + cmp xq, widthq + jl .loop1 + + add widthq, rq + cmp xq, widthq + jge .end + +.loop2: + ; reuse r to hold sum, init with zero + xor rd, rd + + PROCESS_S 0 + PROCESS_S 1 + PROCESS_S 2 + PROCESS_S 3 + PROCESS_S 4 + PROCESS_S 5 + PROCESS_S 6 + PROCESS_S 7 + PROCESS_S 8 + + pxor m4, m4 + cvtsi2ss m4, rd + mulss m4, m0 ; sum *= rdiv + addss m4, m1 ; sum += bias + addss m4, m5 ; sum += 0.5 + ; we don't have simple scalar instructions to convert + ; from 32bit to 8bit with saturation, so here + ; just use packed version SSE instructions for simplicity. + cvttps2dq m4, m4 ; trunc to integer + packssdw m4, m4 + packuswb m4, m4 + movd rd, m4 + mov [dstq + xq], rb + + add xq, 1 + cmp xq, widthq + jl .loop2 +.end: + RET +%endmacro + +%if ARCH_X86_64 +INIT_XMM sse4 +FILTER_3X3 +%endif diff --git a/libavcodec/x86/opus_dsp_init.c b/libavfilter/x86/vf_convolution_init.c similarity index 56% rename from libavcodec/x86/opus_dsp_init.c rename to libavfilter/x86/vf_convolution_init.c index a9f8a961590..51432406ed0 100644 --- a/libavcodec/x86/opus_dsp_init.c +++ b/libavfilter/x86/vf_convolution_init.c @@ -1,6 +1,4 @@ /* - * Opus encoder assembly optimizations - * Copyright (C) 2017 Ivan Kalvachev * * This file is part of FFmpeg. * @@ -21,25 +19,28 @@ #include "config.h" +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" #include "libavutil/x86/cpu.h" -#include "libavcodec/opus_pvq.h" +#include "libavfilter/convolution.h" -extern float ff_pvq_search_approx_sse2(float *X, int *y, int K, int N); -extern float ff_pvq_search_approx_sse4(float *X, int *y, int K, int N); -extern float ff_pvq_search_exact_avx (float *X, int *y, int K, int N); +void ff_filter_3x3_sse4(uint8_t *dst, int width, + float rdiv, float bias, const int *const matrix, + const uint8_t *c[], int peak, int radius, + int dstride, int stride); -av_cold void ff_opus_dsp_init_x86(CeltPVQ *s) +av_cold void ff_convolution_init_x86(ConvolutionContext *s) { +#if ARCH_X86_64 + int i; int cpu_flags = av_get_cpu_flags(); - -#if CONFIG_OPUS_ENCODER - if (EXTERNAL_SSE2(cpu_flags)) - s->pvq_search = ff_pvq_search_approx_sse2; - - if (EXTERNAL_SSE4(cpu_flags)) - s->pvq_search = ff_pvq_search_approx_sse4; - - if (EXTERNAL_AVX_FAST(cpu_flags)) - s->pvq_search = ff_pvq_search_exact_avx; + for (i = 0; i < 4; i++) { + if (s->mode[i] == MATRIX_SQUARE) { + if (s->matrix_length[i] == 9) { + if (EXTERNAL_SSE4(cpu_flags)) + s->filter[i] = ff_filter_3x3_sse4; + } + } + } #endif } diff --git a/libavfilter/x86/vf_eq.asm b/libavfilter/x86/vf_eq.asm new file mode 100644 index 00000000000..a30a2870294 --- /dev/null +++ b/libavfilter/x86/vf_eq.asm @@ -0,0 +1,90 @@ +;***************************************************************************** +;* x86-optimized functions for eq filter +;* +;* Original MPlayer filters by Richard Felker. +;* +;* This file is part of FFmpeg. +;* +;* FFmpeg is free software; you can redistribute it and/or modify +;* it under the terms of the GNU General Public License as published by +;* the Free Software Foundation; either version 2 of the License, or +;* (at your option) any later version. +;* +;* FFmpeg is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;* GNU General Public License for more details. +;* +;* You should have received a copy of the GNU General Public License along +;* with 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 + +%macro PROCESS_ONE_LINE 1 +cglobal process_one_line, 5, 7, 5, src, dst, contrast, brightness, w + movd m3, contrastd + movd m4, brightnessd + movsx r5d, contrastw + movsx r6d, brightnessw + SPLATW m3, m3, 0 + SPLATW m4, m4, 0 + + DEFINE_ARGS src, dst, tmp, scalar, w + xor tmpd, tmpd + pxor m0, m0 + pxor m1, m1 + mov scalard, wd + and scalard, mmsize-1 + sar wd, %1 + cmp wd, 1 + jl .loop1 + + .loop0: + movu m1, [srcq] + mova m2, m1 + punpcklbw m1, m0 + punpckhbw m2, m0 + psllw m1, 4 + psllw m2, 4 + pmulhw m1, m3 + pmulhw m2, m3 + paddw m1, m4 + paddw m2, m4 + packuswb m1, m2 + movu [dstq], m1 + add srcq, mmsize + add dstq, mmsize + sub wd, 1 + cmp wd, 0 + jne .loop0 + + .loop1: + cmp scalard, 0 + je .end + movzx tmpd, byte [srcq] + imul tmpd, r5d + sar tmpd, 12 + add tmpd, r6d + movd m1, tmpd + packuswb m1, m0 + movd tmpd, m1 + mov [dstq], tmpb + inc srcq + inc dstq + dec scalard + jmp .loop1 + + .end: + RET + +%endmacro + +INIT_MMX mmxext +PROCESS_ONE_LINE 3 + +INIT_XMM sse2 +PROCESS_ONE_LINE 4 diff --git a/libavfilter/x86/vf_eq.c b/libavfilter/x86/vf_eq.c deleted file mode 100644 index 16f399505fa..00000000000 --- a/libavfilter/x86/vf_eq.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * Original MPlayer filters by Richard Felker. - * - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with 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/mem.h" -#include "libavutil/x86/asm.h" -#include "libavfilter/vf_eq.h" - -#if HAVE_MMX_INLINE && HAVE_6REGS -static void process_MMX(EQParameters *param, uint8_t *dst, int dst_stride, - const uint8_t *src, int src_stride, int w, int h) -{ - int i; - int pel; - int dstep = dst_stride - w; - int sstep = src_stride - w; - short brvec[4]; - short contvec[4]; - int contrast = (int) (param->contrast * 256 * 16); - int brightness = ((int) (100.0 * param->brightness + 100.0) * 511) / 200 - 128 - contrast / 32; - - brvec[0] = brvec[1] = brvec[2] = brvec[3] = brightness; - contvec[0] = contvec[1] = contvec[2] = contvec[3] = contrast; - - while (h--) { - __asm__ volatile ( - "movq (%5), %%mm3 \n\t" - "movq (%6), %%mm4 \n\t" - "pxor %%mm0, %%mm0 \n\t" - "movl %4, %%eax \n\t" - ".p2align 4 \n\t" - "1: \n\t" - "movq (%0), %%mm1 \n\t" - "movq (%0), %%mm2 \n\t" - "punpcklbw %%mm0, %%mm1\n\t" - "punpckhbw %%mm0, %%mm2\n\t" - "psllw $4, %%mm1 \n\t" - "psllw $4, %%mm2 \n\t" - "pmulhw %%mm4, %%mm1 \n\t" - "pmulhw %%mm4, %%mm2 \n\t" - "paddw %%mm3, %%mm1 \n\t" - "paddw %%mm3, %%mm2 \n\t" - "packuswb %%mm2, %%mm1 \n\t" - "add $8, %0 \n\t" - "movq %%mm1, (%1) \n\t" - "add $8, %1 \n\t" - "decl %%eax \n\t" - "jnz 1b \n\t" - : "=r" (src), "=r" (dst) - : "0" (src), "1" (dst), "r" (w>>3), "r" (brvec), "r" (contvec) - : "%eax" - ); - - for (i = w&7; i; i--) { - pel = ((*src++ * contrast) >> 12) + brightness; - if (pel & ~255) - pel = (-pel) >> 31; - *dst++ = pel; - } - - src += sstep; - dst += dstep; - } - __asm__ volatile ( "emms \n\t" ::: "memory" ); -} -#endif - -av_cold void ff_eq_init_x86(EQContext *eq) -{ -#if HAVE_MMX_INLINE && HAVE_6REGS - int cpu_flags = av_get_cpu_flags(); - - if (cpu_flags & AV_CPU_FLAG_MMX) { - eq->process = process_MMX; - } -#endif -} diff --git a/libavfilter/x86/vf_eq_init.c b/libavfilter/x86/vf_eq_init.c new file mode 100644 index 00000000000..274325074a3 --- /dev/null +++ b/libavfilter/x86/vf_eq_init.c @@ -0,0 +1,76 @@ +/* + * + * Original MPlayer filters by Richard Felker. + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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/mem.h" +#include "libavutil/x86/cpu.h" +#include "libavutil/x86/asm.h" +#include "libavfilter/vf_eq.h" + +extern void ff_process_one_line_mmxext(const uint8_t *src, uint8_t *dst, short contrast, + short brightness, int w); +extern void ff_process_one_line_sse2(const uint8_t *src, uint8_t *dst, short contrast, + short brightness, int w); + +#if HAVE_X86ASM +static void process_mmxext(EQParameters *param, uint8_t *dst, int dst_stride, + const uint8_t *src, int src_stride, int w, int h) +{ + short contrast = (short) (param->contrast * 256 * 16); + short brightness = ((short) (100.0 * param->brightness + 100.0) * 511) + / 200 - 128 - contrast / 32; + + while (h--) { + ff_process_one_line_mmxext(src, dst, contrast, brightness, w); + src += src_stride; + dst += dst_stride; + } + emms_c(); +} + +static void process_sse2(EQParameters *param, uint8_t *dst, int dst_stride, + const uint8_t *src, int src_stride, int w, int h) +{ + short contrast = (short) (param->contrast * 256 * 16); + short brightness = ((short) (100.0 * param->brightness + 100.0) * 511) + / 200 - 128 - contrast / 32; + + while (h--) { + ff_process_one_line_sse2(src, dst, contrast, brightness, w); + src += src_stride; + dst += dst_stride; + } +} +#endif + +av_cold void ff_eq_init_x86(EQContext *eq) +{ +#if HAVE_X86ASM + int cpu_flags = av_get_cpu_flags(); + if (EXTERNAL_MMXEXT(cpu_flags)) { + eq->process = process_mmxext; + } + if (EXTERNAL_SSE2(cpu_flags)) { + eq->process = process_sse2; + } +#endif +} diff --git a/libavfilter/x86/vf_gblur.asm b/libavfilter/x86/vf_gblur.asm index 762c953c852..a25b1659f52 100644 --- a/libavfilter/x86/vf_gblur.asm +++ b/libavfilter/x86/vf_gblur.asm @@ -100,7 +100,7 @@ cglobal horiz_slice, 4, 9, 9, ptr, width, height, steps, nu, bscale, x, y, step, add widthq, remainq cmp xq, widthq - je .end_scalar + jge .end_scalar .loop_scalar: ; ptr[x] += nu * ptr[x-1] @@ -148,7 +148,7 @@ cglobal horiz_slice, 4, 9, 9, ptr, width, height, steps, nu, bscale, x, y, step, jg .loop_x_back cmp xq, 0 - je .end_scalar_back + jle .end_scalar_back .loop_scalar_back: ; ptr[x-1] += nu * ptr[x] diff --git a/libavfilter/x86/vf_interlace.asm b/libavfilter/x86/vf_interlace.asm index a6c65b805dc..f4a405c7543 100644 --- a/libavfilter/x86/vf_interlace.asm +++ b/libavfilter/x86/vf_interlace.asm @@ -49,7 +49,7 @@ SECTION .text pxor m2, m6, [srcq+hq] pavg%1 m0, m2 pxor m0, m6 - mova [dstq+hq], m0 + movu [dstq+hq], m0 add hq, mmsize jge .end @@ -66,8 +66,8 @@ SECTION .text pavg%1 m1, m3 pxor m0, m6 pxor m1, m6 - mova [dstq+hq], m0 - mova [dstq+hq+mmsize], m1 + movu [dstq+hq], m0 + movu [dstq+hq+mmsize], m1 add hq, 2*mmsize jl .loop @@ -140,7 +140,7 @@ cglobal lowpass_line_complex, 5, 5, 8, dst, h, src, mref, pref pand m0, m6 pandn m6, m1 por m0, m6 - mova [dstq], m0 + movu [dstq], m0 add dstq, mmsize add srcq, mmsize @@ -201,8 +201,8 @@ cglobal lowpass_line_complex_12, 5, 5, 8, 16, dst, h, src, mref, pref, clip_max pandn m7, m3 por m0, m6 por m1, m7 - mova [dstq], m0 - mova [dstq+mmsize], m1 + movu [dstq], m0 + movu [dstq+mmsize], m1 add dstq, 2*mmsize add srcq, 2*mmsize diff --git a/libavfilter/x86/vf_interlace_init.c b/libavfilter/x86/vf_interlace_init.c deleted file mode 100644 index 0de0fea382e..00000000000 --- a/libavfilter/x86/vf_interlace_init.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2014 Kieran Kunhya - * - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with 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/internal.h" -#include "libavutil/mem.h" -#include "libavutil/x86/cpu.h" - -#include "libavfilter/interlace.h" - -void ff_lowpass_line_sse2(uint8_t *dstp, ptrdiff_t linesize, - const uint8_t *srcp, ptrdiff_t mref, - ptrdiff_t pref, int clip_max); -void ff_lowpass_line_avx (uint8_t *dstp, ptrdiff_t linesize, - const uint8_t *srcp, ptrdiff_t mref, - ptrdiff_t pref, int clip_max); -void ff_lowpass_line_avx2 (uint8_t *dstp, ptrdiff_t linesize, - const uint8_t *srcp, ptrdiff_t mref, - ptrdiff_t pref, int clip_max); - -void ff_lowpass_line_16_sse2(uint8_t *dstp, ptrdiff_t linesize, - const uint8_t *srcp, ptrdiff_t mref, - ptrdiff_t pref, int clip_max); -void ff_lowpass_line_16_avx (uint8_t *dstp, ptrdiff_t linesize, - const uint8_t *srcp, ptrdiff_t mref, - ptrdiff_t pref, int clip_max); -void ff_lowpass_line_16_avx2 (uint8_t *dstp, ptrdiff_t linesize, - const uint8_t *srcp, ptrdiff_t mref, - ptrdiff_t pref, int clip_max); - -void ff_lowpass_line_complex_sse2(uint8_t *dstp, ptrdiff_t linesize, - const uint8_t *srcp, ptrdiff_t mref, - ptrdiff_t pref, int clip_max); - -void ff_lowpass_line_complex_12_sse2(uint8_t *dstp, ptrdiff_t linesize, - const uint8_t *srcp, ptrdiff_t mref, - ptrdiff_t pref, int clip_max); - -av_cold void ff_interlace_init_x86(InterlaceContext *s, int depth) -{ - int cpu_flags = av_get_cpu_flags(); - - if (depth > 8) { - if (EXTERNAL_SSE2(cpu_flags)) { - if (s->lowpass == VLPF_LIN) - s->lowpass_line = ff_lowpass_line_16_sse2; - else if (s->lowpass == VLPF_CMP) - s->lowpass_line = ff_lowpass_line_complex_12_sse2; - } - if (EXTERNAL_AVX(cpu_flags)) - if (s->lowpass == VLPF_LIN) - s->lowpass_line = ff_lowpass_line_16_avx; - if (EXTERNAL_AVX2_FAST(cpu_flags)) - if (s->lowpass == VLPF_LIN) - s->lowpass_line = ff_lowpass_line_16_avx2; - } else { - if (EXTERNAL_SSE2(cpu_flags)) { - if (s->lowpass == VLPF_LIN) - s->lowpass_line = ff_lowpass_line_sse2; - else if (s->lowpass == VLPF_CMP) - s->lowpass_line = ff_lowpass_line_complex_sse2; - } - if (EXTERNAL_AVX(cpu_flags)) - if (s->lowpass == VLPF_LIN) - s->lowpass_line = ff_lowpass_line_avx; - if (EXTERNAL_AVX2_FAST(cpu_flags)) - if (s->lowpass == VLPF_LIN) - s->lowpass_line = ff_lowpass_line_avx2; - } -} diff --git a/libavfilter/x86/vf_maskedclamp.asm b/libavfilter/x86/vf_maskedclamp.asm new file mode 100644 index 00000000000..d586610c926 --- /dev/null +++ b/libavfilter/x86/vf_maskedclamp.asm @@ -0,0 +1,95 @@ +;***************************************************************************** +;* x86-optimized functions for maskedclamp filter +;* +;* 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/x86/x86util.asm" + +SECTION .text + +;------------------------------------------------------------------------------ +; void ff_maskedclamp(const uint8_t *src, uint8_t *dst, +; const uint8_t *darksrc, +; const uint8_t *brightsrc, +; int w, int undershoot, int overshoot) +;------------------------------------------------------------------------------ + +INIT_XMM sse2 +cglobal maskedclamp8, 5,5,5, src, dst, dark, bright, w, undershoot, overshoot + movsxdifnidn wq, wd + + add srcq, wq + add darkq, wq + add brightq, wq + add dstq, wq + neg wq + + movd m3, r5m + punpcklbw m3, m3 + SPLATW m3, m3 + + movd m4, r6m + punpcklbw m4, m4 + SPLATW m4, m4 + + .loop: + movu m0, [srcq + wq] + movu m1, [darkq + wq] + movu m2, [brightq + wq] + + psubusb m1, m3 + paddusb m2, m4 + CLIPUB m0, m1, m2 + mova [dstq + wq], m0 + + add wq, mmsize + jl .loop + RET + +INIT_XMM sse4 +cglobal maskedclamp16, 5,5,5, src, dst, dark, bright, w, undershoot, overshoot + shl wd, 1 + + add srcq, wq + add darkq, wq + add brightq, wq + add dstq, wq + neg wq + + movd m3, r5m + SPLATW m3, m3 + + movd m4, r6m + SPLATW m4, m4 + + .loop: + movu m0, [srcq + wq] + movu m1, [darkq + wq] + movu m2, [brightq + wq] + + psubusw m1, m3 + paddusw m2, m4 + pmaxuw m0, m1 + pminuw m0, m2 + mova [dstq + wq], m0 + + add wq, mmsize + jl .loop + RET diff --git a/libavfilter/x86/vf_maskedclamp_init.c b/libavfilter/x86/vf_maskedclamp_init.c new file mode 100644 index 00000000000..53153f80eae --- /dev/null +++ b/libavfilter/x86/vf_maskedclamp_init.c @@ -0,0 +1,47 @@ +/* + * 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/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/mem.h" +#include "libavutil/x86/asm.h" +#include "libavutil/x86/cpu.h" +#include "libavfilter/maskedclamp.h" + +void ff_maskedclamp8_sse2(const uint8_t *bsrc, uint8_t *dst, + const uint8_t *darksrc, const uint8_t *brightsrc, + int w, int undershoot, int overshoot); + +void ff_maskedclamp16_sse4(const uint8_t *bsrc, uint8_t *dst, + const uint8_t *darksrc, const uint8_t *brightsrc, + int w, int undershoot, int overshoot); + +av_cold void ff_maskedclamp_init_x86(MaskedClampDSPContext *dsp, int depth) +{ + int cpu_flags = av_get_cpu_flags(); + + if (EXTERNAL_SSE2(cpu_flags) && depth <= 8) { + dsp->maskedclamp = ff_maskedclamp8_sse2; + } + + if (EXTERNAL_SSE4(cpu_flags) && depth > 8) { + dsp->maskedclamp = ff_maskedclamp16_sse4; + } +} diff --git a/libavfilter/x86/vf_ssim.asm b/libavfilter/x86/vf_ssim.asm index 3293e667012..78809305dec 100644 --- a/libavfilter/x86/vf_ssim.asm +++ b/libavfilter/x86/vf_ssim.asm @@ -169,8 +169,9 @@ SSIM_4X4_LINE 8 %endif INIT_XMM sse4 -cglobal ssim_end_line, 3, 3, 6, sum0, sum1, w +cglobal ssim_end_line, 3, 3, 7, sum0, sum1, w pxor m0, m0 + pxor m6, m6 .loop: mova m1, [sum0q+mmsize*0] mova m2, [sum0q+mmsize*1] @@ -214,34 +215,46 @@ cglobal ssim_end_line, 3, 3, 6, sum0, sum1, w mulps m4, m5 mulps m3, m1 divps m4, m3 ; ssim_endl - addps m0, m4 ; ssim + mova m5, m4 + cvtps2pd m3, m5 + movhlps m5, m5 + cvtps2pd m5, m5 + addpd m0, m3 ; ssim + addpd m6, m5 ; ssim add sum0q, mmsize*4 add sum1q, mmsize*4 sub wd, 4 jg .loop - ; subps the ones we added too much + ; subpd the ones we added too much test wd, wd jz .end add wd, 4 + test wd, 3 + jz .skip3 test wd, 2 jz .skip2 - psrldq m4, 8 -.skip2: test wd, 1 jz .skip1 - psrldq m4, 4 +.skip3: + psrldq m5, 8 + subpd m6, m5 + jmp .end +.skip2: + psrldq m5, 8 + subpd m6, m5 + subpd m0, m3 + jmp .end .skip1: - subps m0, m4 + psrldq m3, 16 + subpd m6, m5 .end: + addpd m0, m6 movhlps m4, m0 - addps m0, m4 - movss m4, m0 - shufps m0, m0, 1 - addss m0, m4 + addpd m0, m4 %if ARCH_X86_32 - movss r0m, m0 - fld r0mp + movsd r0m, m0 + fld qword r0m %endif RET diff --git a/libavfilter/x86/vf_ssim_init.c b/libavfilter/x86/vf_ssim_init.c index 599c928403a..cbaa20ef166 100644 --- a/libavfilter/x86/vf_ssim_init.c +++ b/libavfilter/x86/vf_ssim_init.c @@ -28,7 +28,7 @@ void ff_ssim_4x4_line_ssse3(const uint8_t *buf, ptrdiff_t buf_stride, void ff_ssim_4x4_line_xop (const uint8_t *buf, ptrdiff_t buf_stride, const uint8_t *ref, ptrdiff_t ref_stride, int (*sums)[4], int w); -float ff_ssim_end_line_sse4(const int (*sum0)[4], const int (*sum1)[4], int w); +double ff_ssim_end_line_sse4(const int (*sum0)[4], const int (*sum1)[4], int w); void ff_ssim_init_x86(SSIMDSPContext *dsp) { diff --git a/libavfilter/x86/vf_transpose.asm b/libavfilter/x86/vf_transpose.asm new file mode 100644 index 00000000000..c532c899eef --- /dev/null +++ b/libavfilter/x86/vf_transpose.asm @@ -0,0 +1,87 @@ +;***************************************************************************** +;* x86-optimized functions for transpose filter +;* +;* 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/x86/x86util.asm" + +SECTION .text + +;------------------------------------------------------------------------------ +; void ff_transpose_8x8(uint8_t *src, ptrdiff_t src_linesize, +; uint8_t *dst, ptrdiff_t dst_linesize) +;------------------------------------------------------------------------------ + +INIT_XMM sse2 +cglobal transpose_8x8_8, 4,5,8, src, src_linesize, dst, dst_linesize, linesize3 + lea linesize3q, [src_linesizeq * 3] + movq m0, [srcq + src_linesizeq * 0] + movq m1, [srcq + src_linesizeq * 1] + movq m2, [srcq + src_linesizeq * 2] + movq m3, [srcq + linesize3q] + lea srcq, [srcq + src_linesizeq * 4] + movq m4, [srcq + src_linesizeq * 0] + movq m5, [srcq + src_linesizeq * 1] + movq m6, [srcq + src_linesizeq * 2] + movq m7, [srcq + linesize3q] + + TRANSPOSE_8X8B 0, 1, 2, 3, 4, 5, 6, 7 + + lea linesize3q, [dst_linesizeq * 3] + movq [dstq + dst_linesizeq * 0], m0 + movq [dstq + dst_linesizeq * 1], m1 + movq [dstq + dst_linesizeq * 2], m2 + movq [dstq + linesize3q], m3 + lea dstq, [dstq + dst_linesizeq * 4] + movq [dstq + dst_linesizeq * 0], m4 + movq [dstq + dst_linesizeq * 1], m5 + movq [dstq + dst_linesizeq * 2], m6 + movq [dstq + linesize3q], m7 + RET + +cglobal transpose_8x8_16, 4,5,9, ARCH_X86_32 * 32, src, src_linesize, dst, dst_linesize, linesize3 + lea linesize3q, [src_linesizeq * 3] + movu m0, [srcq + src_linesizeq * 0] + movu m1, [srcq + src_linesizeq * 1] + movu m2, [srcq + src_linesizeq * 2] + movu m3, [srcq + linesize3q] + lea srcq, [srcq + src_linesizeq * 4] + movu m4, [srcq + src_linesizeq * 0] + movu m5, [srcq + src_linesizeq * 1] + movu m6, [srcq + src_linesizeq * 2] + movu m7, [srcq + linesize3q] + +%if ARCH_X86_64 + TRANSPOSE8x8W 0, 1, 2, 3, 4, 5, 6, 7, 8 +%else + TRANSPOSE8x8W 0, 1, 2, 3, 4, 5, 6, 7, [rsp], [rsp + 16] +%endif + + lea linesize3q, [dst_linesizeq * 3] + movu [dstq + dst_linesizeq * 0], m0 + movu [dstq + dst_linesizeq * 1], m1 + movu [dstq + dst_linesizeq * 2], m2 + movu [dstq + linesize3q], m3 + lea dstq, [dstq + dst_linesizeq * 4] + movu [dstq + dst_linesizeq * 0], m4 + movu [dstq + dst_linesizeq * 1], m5 + movu [dstq + dst_linesizeq * 2], m6 + movu [dstq + linesize3q], m7 + RET diff --git a/libavfilter/x86/vf_transpose_init.c b/libavfilter/x86/vf_transpose_init.c new file mode 100644 index 00000000000..6bb9908725e --- /dev/null +++ b/libavfilter/x86/vf_transpose_init.c @@ -0,0 +1,49 @@ +/* + * 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/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/mem.h" +#include "libavutil/x86/asm.h" +#include "libavutil/x86/cpu.h" +#include "libavfilter/transpose.h" + +void ff_transpose_8x8_8_sse2(uint8_t *src, + ptrdiff_t src_linesize, + uint8_t *dst, + ptrdiff_t dst_linesize); + +void ff_transpose_8x8_16_sse2(uint8_t *src, + ptrdiff_t src_linesize, + uint8_t *dst, + ptrdiff_t dst_linesize); + +av_cold void ff_transpose_init_x86(TransVtable *v, int pixstep) +{ + int cpu_flags = av_get_cpu_flags(); + + if (EXTERNAL_SSE2(cpu_flags) && pixstep == 1) { + v->transpose_8x8 = ff_transpose_8x8_8_sse2; + } + + if (EXTERNAL_SSE2(cpu_flags) && pixstep == 2) { + v->transpose_8x8 = ff_transpose_8x8_16_sse2; + } +} diff --git a/libavfilter/x86/vf_v360.asm b/libavfilter/x86/vf_v360.asm new file mode 100644 index 00000000000..8e7e4591b47 --- /dev/null +++ b/libavfilter/x86/vf_v360.asm @@ -0,0 +1,256 @@ +;***************************************************************************** +;* x86-optimized functions for v360 filter +;* +;* This file is part of FFmpeg. +;* +;* FFmpeg is free software; you can redistribute it and/or +;* modify it under the terms of the GNU Lesser General Public +;* License as published by the Free Software Foundation; either +;* version 2.1 of the License, or (at your option) any later version. +;* +;* FFmpeg is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;* Lesser General Public License for more details. +;* +;* You should have received a copy of the GNU Lesser General 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" + +%if HAVE_AVX2_EXTERNAL + +SECTION_RODATA + +pb_mask: db 0,4,8,12,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 +pw_mask: db 0,1,4, 5, 8, 9,12,13,-1,-1,-1,-1,-1,-1,-1,-1 +pd_255: times 4 dd 255 +pd_65535: times 4 dd 65535 + +SECTION .text + +; void ff_remap2_8bit_line_avx2(uint8_t *dst, int width, const uint8_t *src, ptrdiff_t in_linesize, +; const uint16_t *u, const uint16_t *v, const int16_t *ker); + +INIT_YMM avx2 +cglobal remap1_8bit_line, 6, 7, 6, dst, width, src, in_linesize, u, v, x + movsxdifnidn widthq, widthd + xor xq, xq + movd xm0, in_linesized + pcmpeqw m4, m4 + VBROADCASTI128 m3, [pb_mask] + vpbroadcastd m0, xm0 + + .loop: + pmovsxwd m1, [vq + xq * 2] + pmovsxwd m2, [uq + xq * 2] + + pmulld m1, m0 + paddd m1, m2 + mova m2, m4 + vpgatherdd m5, [srcq + m1], m2 + pshufb m1, m5, m3 + vextracti128 xm2, m1, 1 + movd [dstq+xq], xm1 + movd [dstq+xq+4], xm2 + + add xq, mmsize / 4 + cmp xq, widthq + jl .loop + RET + +INIT_YMM avx2 +cglobal remap1_16bit_line, 6, 7, 6, dst, width, src, in_linesize, u, v, x + movsxdifnidn widthq, widthd + xor xq, xq + movd xm0, in_linesized + pcmpeqw m4, m4 + VBROADCASTI128 m3, [pw_mask] + vpbroadcastd m0, xm0 + + .loop: + pmovsxwd m1, [vq + xq * 2] + pmovsxwd m2, [uq + xq * 2] + + pslld m2, 0x1 + pmulld m1, m0 + paddd m1, m2 + mova m2, m4 + vpgatherdd m5, [srcq + m1], m2 + pshufb m1, m5, m3 + vextracti128 xm2, m1, 1 + movq [dstq+xq*2], xm1 + movq [dstq+xq*2+8], xm2 + + add xq, mmsize / 4 + cmp xq, widthq + jl .loop + RET + +INIT_YMM avx2 +cglobal remap2_8bit_line, 7, 8, 8, dst, width, src, in_linesize, u, v, ker, x + movsxdifnidn widthq, widthd + movd xm0, in_linesized +%if ARCH_X86_32 +DEFINE_ARGS dst, width, src, x, u, v, ker +%endif + xor xq, xq + pcmpeqw m7, m7 + vpbroadcastd m0, xm0 + vpbroadcastd m6, [pd_255] + + .loop: + pmovsxwd m1, [kerq + xq * 8] + pmovsxwd m2, [vq + xq * 8] + pmovsxwd m3, [uq + xq * 8] + + pmulld m4, m2, m0 + paddd m4, m3 + mova m3, m7 + vpgatherdd m2, [srcq + m4], m3 + pand m2, m6 + pmulld m2, m1 + phaddd m2, m2 + phaddd m1, m2, m2 + psrld m1, m1, 0xe + vextracti128 xm2, m1, 1 + + pextrb [dstq+xq], xm1, 0 + pextrb [dstq+xq+1], xm2, 0 + + add xq, mmsize / 16 + cmp xq, widthq + jl .loop + RET + +INIT_YMM avx2 +cglobal remap2_16bit_line, 7, 8, 8, dst, width, src, in_linesize, u, v, ker, x + movsxdifnidn widthq, widthd + movd xm0, in_linesized +%if ARCH_X86_32 +DEFINE_ARGS dst, width, src, x, u, v, ker +%endif + xor xq, xq + pcmpeqw m7, m7 + vpbroadcastd m0, xm0 + vpbroadcastd m6, [pd_65535] + + .loop: + pmovsxwd m1, [kerq + xq * 8] + pmovsxwd m2, [vq + xq * 8] + pmovsxwd m3, [uq + xq * 8] + + pslld m3, 0x1 + pmulld m4, m2, m0 + paddd m4, m3 + mova m3, m7 + vpgatherdd m2, [srcq + m4], m3 + pand m2, m6 + pmulld m2, m1 + phaddd m2, m2 + phaddd m1, m2, m2 + psrld m1, m1, 0xe + vextracti128 xm2, m1, 1 + + pextrw [dstq+xq*2], xm1, 0 + pextrw [dstq+xq*2+2], xm2, 0 + + add xq, mmsize / 16 + cmp xq, widthq + jl .loop + RET + +%if ARCH_X86_64 + +INIT_YMM avx2 +cglobal remap3_8bit_line, 7, 11, 8, dst, width, src, in_linesize, u, v, ker, x, y, tmp, z + movsxdifnidn widthq, widthd + xor zq, zq + xor yq, yq + xor xq, xq + movd xm0, in_linesized + pcmpeqw m7, m7 + vpbroadcastd m0, xm0 + vpbroadcastd m6, [pd_255] + + .loop: + pmovsxwd m1, [kerq + yq] + pmovsxwd m2, [vq + yq] + pmovsxwd m3, [uq + yq] + + pmulld m4, m2, m0 + paddd m4, m3 + mova m3, m7 + vpgatherdd m2, [srcq + m4], m3 + pand m2, m6 + pmulld m2, m1 + HADDD m2, m1 + movzx tmpq, word [vq + yq + 16] + imul tmpq, in_linesizeq + movzx zq, word [uq + yq + 16] + add tmpq, zq + movzx zq, byte [srcq + tmpq] + movzx tmpq, word [kerq + yq + 16] + imul zd, tmpd + movd xm1, zd + paddd m2, m1 + psrld m2, m2, 0xe + + packuswb m2, m2 + pextrb [dstq+xq], xm2, 0 + + add xq, 1 + add yq, 18 + cmp xq, widthq + jl .loop + RET + +INIT_YMM avx2 +cglobal remap4_8bit_line, 7, 9, 11, dst, width, src, in_linesize, u, v, ker, x, y + movsxdifnidn widthq, widthd + xor yq, yq + xor xq, xq + movd xm0, in_linesized + pcmpeqw m7, m7 + vpbroadcastd m0, xm0 + vpbroadcastd m6, [pd_255] + + .loop: + pmovsxwd m1, [kerq + yq] + pmovsxwd m5, [kerq + yq + 16] + pmovsxwd m2, [vq + yq] + pmovsxwd m8, [vq + yq + 16] + pmovsxwd m3, [uq + yq] + pmovsxwd m9, [uq + yq + 16] + + pmulld m4, m2, m0 + pmulld m10, m8, m0 + paddd m4, m3 + paddd m10, m9 + mova m3, m7 + vpgatherdd m2, [srcq + m4], m3 + mova m3, m7 + vpgatherdd m4, [srcq + m10], m3 + pand m2, m6 + pand m4, m6 + pmulld m2, m1 + pmulld m4, m5 + + paddd m2, m4 + HADDD m2, m1 + psrld m2, m2, 0xe + packuswb m2, m2 + + pextrb [dstq+xq], xm2, 0 + + add xq, 1 + add yq, 32 + cmp xq, widthq + jl .loop + RET + +%endif +%endif diff --git a/libavfilter/x86/vf_v360_init.c b/libavfilter/x86/vf_v360_init.c new file mode 100644 index 00000000000..7d001a6948b --- /dev/null +++ b/libavfilter/x86/vf_v360_init.c @@ -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 + */ + +#include "config.h" + +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/x86/cpu.h" +#include "libavfilter/v360.h" + +void ff_remap1_8bit_line_avx2(uint8_t *dst, int width, const uint8_t *src, ptrdiff_t in_linesize, + const int16_t *const u, const int16_t *const v, const int16_t *const ker); + +void ff_remap2_8bit_line_avx2(uint8_t *dst, int width, const uint8_t *src, ptrdiff_t in_linesize, + const int16_t *const u, const int16_t *const v, const int16_t *const ker); + +void ff_remap3_8bit_line_avx2(uint8_t *dst, int width, const uint8_t *src, ptrdiff_t in_linesize, + const int16_t *const u, const int16_t *const v, const int16_t *const ker); + +void ff_remap4_8bit_line_avx2(uint8_t *dst, int width, const uint8_t *src, ptrdiff_t in_linesize, + const int16_t *const u, const int16_t *const v, const int16_t *const ker); + +void ff_remap1_16bit_line_avx2(uint8_t *dst, int width, const uint8_t *src, ptrdiff_t in_linesize, + const int16_t *const u, const int16_t *const v, const int16_t *const ker); + +void ff_remap2_16bit_line_avx2(uint8_t *dst, int width, const uint8_t *src, ptrdiff_t in_linesize, + const int16_t *const u, const int16_t *const v, const int16_t *const ker); + +av_cold void ff_v360_init_x86(V360Context *s, int depth) +{ + int cpu_flags = av_get_cpu_flags(); + + if (EXTERNAL_AVX2_FAST(cpu_flags) && s->interp == NEAREST && depth <= 8) + s->remap_line = ff_remap1_8bit_line_avx2; + + if (EXTERNAL_AVX2_FAST(cpu_flags) && s->interp == BILINEAR && depth <= 8) + s->remap_line = ff_remap2_8bit_line_avx2; + + if (EXTERNAL_AVX2_FAST(cpu_flags) && s->interp == NEAREST && depth > 8) + s->remap_line = ff_remap1_16bit_line_avx2; + + if (EXTERNAL_AVX2_FAST(cpu_flags) && s->interp == BILINEAR && depth > 8) + s->remap_line = ff_remap2_16bit_line_avx2; + +#if ARCH_X86_64 + if (EXTERNAL_AVX2_FAST(cpu_flags) && s->interp == LAGRANGE9 && depth <= 8) + s->remap_line = ff_remap3_8bit_line_avx2; + + if (EXTERNAL_AVX2_FAST(cpu_flags) && (s->interp == BICUBIC || + s->interp == LANCZOS || + s->interp == SPLINE16 || + s->interp == GAUSSIAN) && depth <= 8) + s->remap_line = ff_remap4_8bit_line_avx2; +#endif +} diff --git a/libavformat/.gitignore b/libavformat/.gitignore new file mode 100644 index 00000000000..fb70c122c43 --- /dev/null +++ b/libavformat/.gitignore @@ -0,0 +1,3 @@ +/protocol_list.c +/muxer_list.c +/demuxer_list.c diff --git a/libavformat/4xm.c b/libavformat/4xm.c index a984fc9fdf3..6a227a0b0d2 100644 --- a/libavformat/4xm.c +++ b/libavformat/4xm.c @@ -59,8 +59,10 @@ #define GET_LIST_HEADER() \ fourcc_tag = avio_rl32(pb); \ size = avio_rl32(pb); \ - if (fourcc_tag != LIST_TAG) \ - return AVERROR_INVALIDDATA; \ + if (fourcc_tag != LIST_TAG) { \ + ret = AVERROR_INVALIDDATA; \ + goto fail; \ + } \ fourcc_tag = avio_rl32(pb); typedef struct AudioTrack { @@ -210,12 +212,13 @@ static int fourxm_read_header(AVFormatContext *s) unsigned int size; int header_size; FourxmDemuxContext *fourxm = s->priv_data; - unsigned char *header; + unsigned char *header = NULL; int i, ret; fourxm->track_count = 0; fourxm->tracks = NULL; fourxm->fps = (AVRational){1,1}; + fourxm->video_stream_index = -1; /* skip the first 3 32-bit numbers */ avio_skip(pb, 12); @@ -241,7 +244,8 @@ static int fourxm_read_header(AVFormatContext *s) size = AV_RL32(&header[i + 4]); if (size > header_size - i - 8 && (fourcc_tag == vtrk_TAG || fourcc_tag == strk_TAG)) { av_log(s, AV_LOG_ERROR, "chunk larger than array %d>%d\n", size, header_size - i - 8); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail; } if (fourcc_tag == std__TAG) { @@ -321,8 +325,12 @@ static int fourxm_read_packet(AVFormatContext *s, case cfr2_TAG: /* allocate 8 more bytes than 'size' to account for fourcc * and size */ - if (size + 8 < size || av_new_packet(pkt, size + 8)) - return AVERROR(EIO); + if (size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE - 8) + return AVERROR_INVALIDDATA; + if (fourxm->video_stream_index < 0) + return AVERROR_INVALIDDATA; + if ((ret = av_new_packet(pkt, size + 8)) < 0) + return ret; pkt->stream_index = fourxm->video_stream_index; pkt->pts = fourxm->video_pts; pkt->pos = avio_tell(s->pb); @@ -346,7 +354,7 @@ static int fourxm_read_packet(AVFormatContext *s, fourxm->tracks[track_number].channels > 0) { ret = av_get_packet(s->pb, pkt, size); if (ret < 0) - return AVERROR(EIO); + return ret; pkt->stream_index = fourxm->tracks[track_number].stream_index; pkt->pts = fourxm->tracks[track_number].audio_pts; diff --git a/libavformat/Makefile b/libavformat/Makefile index a434b005a46..0658fa37108 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -82,16 +82,18 @@ OBJS-$(CONFIG_ADX_MUXER) += rawenc.o OBJS-$(CONFIG_AEA_DEMUXER) += aea.o pcm.o OBJS-$(CONFIG_AFC_DEMUXER) += afc.o OBJS-$(CONFIG_AIFF_DEMUXER) += aiffdec.o pcm.o isom.o \ - mov_chan.o + mov_chan.o replaygain.o OBJS-$(CONFIG_AIFF_MUXER) += aiffenc.o id3v2enc.o OBJS-$(CONFIG_AIX_DEMUXER) += aixdec.o +OBJS-$(CONFIG_ALP_DEMUXER) += alp.o OBJS-$(CONFIG_AMR_DEMUXER) += amr.o -OBJS-$(CONFIG_AMR_MUXER) += amr.o +OBJS-$(CONFIG_AMR_MUXER) += amr.o rawenc.o OBJS-$(CONFIG_AMRNB_DEMUXER) += amr.o OBJS-$(CONFIG_AMRWB_DEMUXER) += amr.o OBJS-$(CONFIG_ANM_DEMUXER) += anm.o OBJS-$(CONFIG_APC_DEMUXER) += apc.o OBJS-$(CONFIG_APE_DEMUXER) += ape.o apetag.o img2.o +OBJS-$(CONFIG_APM_DEMUXER) += apm.o riffdec.o OBJS-$(CONFIG_APNG_DEMUXER) += apngdec.o OBJS-$(CONFIG_APNG_MUXER) += apngenc.o OBJS-$(CONFIG_APTX_DEMUXER) += aptxdec.o rawdec.o @@ -99,6 +101,7 @@ OBJS-$(CONFIG_APTX_MUXER) += rawenc.o OBJS-$(CONFIG_APTX_HD_DEMUXER) += aptxdec.o rawdec.o OBJS-$(CONFIG_APTX_HD_MUXER) += rawenc.o OBJS-$(CONFIG_AQTITLE_DEMUXER) += aqtitledec.o subtitles.o +OBJS-$(CONFIG_ARGO_ASF_DEMUXER) += argo_asf.o OBJS-$(CONFIG_ASF_DEMUXER) += asfdec_f.o asf.o asfcrypt.o \ avlanguage.o OBJS-$(CONFIG_ASF_O_DEMUXER) += asfdec_o.o asf.o asfcrypt.o \ @@ -148,6 +151,7 @@ OBJS-$(CONFIG_DASH_DEMUXER) += dash.o dashdec.o OBJS-$(CONFIG_DAUD_DEMUXER) += dauddec.o OBJS-$(CONFIG_DAUD_MUXER) += daudenc.o OBJS-$(CONFIG_DCSTR_DEMUXER) += dcstr.o +OBJS-$(CONFIG_DERF_DEMUXER) += derf.o pcm.o OBJS-$(CONFIG_DFA_DEMUXER) += dfa.o OBJS-$(CONFIG_DHAV_DEMUXER) += dhav.o OBJS-$(CONFIG_DIRAC_DEMUXER) += diracdec.o rawdec.o @@ -175,7 +179,7 @@ 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 +OBJS-$(CONFIG_FILMSTRIP_MUXER) += filmstripenc.o rawenc.o OBJS-$(CONFIG_FITS_DEMUXER) += fitsdec.o OBJS-$(CONFIG_FITS_MUXER) += fitsenc.o OBJS-$(CONFIG_FLAC_DEMUXER) += flacdec.o rawdec.o \ @@ -195,12 +199,13 @@ OBJS-$(CONFIG_FRAMEHASH_MUXER) += hashenc.o framehash.o OBJS-$(CONFIG_FRAMEMD5_MUXER) += hashenc.o framehash.o OBJS-$(CONFIG_FRM_DEMUXER) += frmdec.o OBJS-$(CONFIG_FSB_DEMUXER) += fsb.o +OBJS-$(CONFIG_FWSE_DEMUXER) += fwse.o pcm.o OBJS-$(CONFIG_GIF_MUXER) += gif.o OBJS-$(CONFIG_GIF_DEMUXER) += gifdec.o OBJS-$(CONFIG_GSM_DEMUXER) += gsmdec.o OBJS-$(CONFIG_GSM_MUXER) += rawenc.o OBJS-$(CONFIG_GXF_DEMUXER) += gxf.o -OBJS-$(CONFIG_GXF_MUXER) += gxfenc.o audiointerleave.o +OBJS-$(CONFIG_GXF_MUXER) += gxfenc.o OBJS-$(CONFIG_G722_DEMUXER) += g722.o rawdec.o OBJS-$(CONFIG_G722_MUXER) += rawenc.o OBJS-$(CONFIG_G723_1_DEMUXER) += g723_1.o @@ -219,7 +224,8 @@ OBJS-$(CONFIG_H263_MUXER) += rawenc.o OBJS-$(CONFIG_H264_DEMUXER) += h264dec.o rawdec.o OBJS-$(CONFIG_H264_MUXER) += rawenc.o OBJS-$(CONFIG_HASH_MUXER) += hashenc.o -OBJS-$(CONFIG_HCOM_DEMUXER) += hcom.o +OBJS-$(CONFIG_HCA_DEMUXER) += hca.o +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 @@ -233,7 +239,7 @@ OBJS-$(CONFIG_IDF_DEMUXER) += bintext.o sauce.o OBJS-$(CONFIG_IFF_DEMUXER) += iff.o OBJS-$(CONFIG_IFV_DEMUXER) += ifv.o OBJS-$(CONFIG_ILBC_DEMUXER) += ilbc.o -OBJS-$(CONFIG_ILBC_MUXER) += ilbc.o +OBJS-$(CONFIG_ILBC_MUXER) += ilbc.o rawenc.o OBJS-$(CONFIG_IMAGE2_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE2_MUXER) += img2enc.o img2.o OBJS-$(CONFIG_IMAGE2PIPE_DEMUXER) += img2dec.o img2.o @@ -278,6 +284,8 @@ OBJS-$(CONFIG_JACOSUB_DEMUXER) += jacosubdec.o subtitles.o OBJS-$(CONFIG_JACOSUB_MUXER) += jacosubenc.o rawenc.o OBJS-$(CONFIG_JV_DEMUXER) += jvdec.o OBJS-$(CONFIG_KUX_DEMUXER) += flvdec.o +OBJS-$(CONFIG_KVAG_DEMUXER) += kvag.o +OBJS-$(CONFIG_KVAG_MUXER) += kvag.o rawenc.o OBJS-$(CONFIG_LATM_MUXER) += latmenc.o rawenc.o OBJS-$(CONFIG_LMLM4_DEMUXER) += lmlm4.o OBJS-$(CONFIG_LOAS_DEMUXER) += loasdec.o rawdec.o @@ -290,11 +298,11 @@ OBJS-$(CONFIG_M4V_MUXER) += rawenc.o OBJS-$(CONFIG_MATROSKA_DEMUXER) += matroskadec.o matroska.o \ rmsipr.o flac_picture.o \ oggparsevorbis.o vorbiscomment.o \ - flac_picture.o replaygain.o + replaygain.o OBJS-$(CONFIG_MATROSKA_MUXER) += matroskaenc.o matroska.o \ av1.o avc.o hevc.o \ - flacenc_header.o avlanguage.o vorbiscomment.o wv.o \ - webmdashenc.o webm_chunk.o + flacenc_header.o avlanguage.o \ + vorbiscomment.o wv.o OBJS-$(CONFIG_MD5_MUXER) += hashenc.o OBJS-$(CONFIG_MGSTS_DEMUXER) += mgsts.o OBJS-$(CONFIG_MICRODVD_DEMUXER) += microdvddec.o subtitles.o @@ -340,19 +348,19 @@ OBJS-$(CONFIG_MUSX_DEMUXER) += musx.o OBJS-$(CONFIG_MV_DEMUXER) += mvdec.o OBJS-$(CONFIG_MVI_DEMUXER) += mvi.o OBJS-$(CONFIG_MXF_DEMUXER) += mxfdec.o mxf.o -OBJS-$(CONFIG_MXF_MUXER) += mxfenc.o mxf.o audiointerleave.o avc.o +OBJS-$(CONFIG_MXF_MUXER) += mxfenc.o mxf.o avc.o OBJS-$(CONFIG_MXG_DEMUXER) += mxg.o OBJS-$(CONFIG_NC_DEMUXER) += ncdec.o OBJS-$(CONFIG_NISTSPHERE_DEMUXER) += nistspheredec.o pcm.o -OBJS-$(CONFIG_NSP_DEMUXER) += nspdec.o +OBJS-$(CONFIG_NSP_DEMUXER) += nspdec.o pcm.o OBJS-$(CONFIG_NSV_DEMUXER) += nsvdec.o OBJS-$(CONFIG_NULL_MUXER) += nullenc.o OBJS-$(CONFIG_NUT_DEMUXER) += nutdec.o nut.o isom.o OBJS-$(CONFIG_NUT_MUXER) += nutenc.o nut.o OBJS-$(CONFIG_NUV_DEMUXER) += nuv.o +OBJS-$(CONFIG_AV1_DEMUXER) += av1dec.o OBJS-$(CONFIG_OGG_DEMUXER) += oggdec.o \ oggparsecelt.o \ - oggparsedaala.o \ oggparsedirac.o \ oggparseflac.o \ oggparseogm.o \ @@ -420,6 +428,7 @@ OBJS-$(CONFIG_PCM_VIDC_DEMUXER) += pcmdec.o pcm.o OBJS-$(CONFIG_PCM_VIDC_MUXER) += pcmenc.o rawenc.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 @@ -436,7 +445,7 @@ OBJS-$(CONFIG_ROQ_MUXER) += idroqenc.o rawenc.o OBJS-$(CONFIG_RSD_DEMUXER) += rsd.o OBJS-$(CONFIG_RPL_DEMUXER) += rpl.o OBJS-$(CONFIG_RSO_DEMUXER) += rsodec.o rso.o pcm.o -OBJS-$(CONFIG_RSO_MUXER) += rsoenc.o rso.o +OBJS-$(CONFIG_RSO_MUXER) += rsoenc.o rso.o rawenc.o OBJS-$(CONFIG_RTP_MPEGTS_MUXER) += rtpenc_mpegts.o OBJS-$(CONFIG_RTP_MUXER) += rtp.o \ rtpenc_aac.o \ @@ -470,7 +479,7 @@ OBJS-$(CONFIG_SCC_MUXER) += sccenc.o subtitles.o OBJS-$(CONFIG_SDP_DEMUXER) += rtsp.o OBJS-$(CONFIG_SDR2_DEMUXER) += sdr2.o OBJS-$(CONFIG_SDS_DEMUXER) += sdsdec.o -OBJS-$(CONFIG_SDX_DEMUXER) += sdxdec.o +OBJS-$(CONFIG_SDX_DEMUXER) += sdxdec.o pcm.o OBJS-$(CONFIG_SEGAFILM_DEMUXER) += segafilm.o OBJS-$(CONFIG_SEGAFILM_MUXER) += segafilmenc.o OBJS-$(CONFIG_SEGMENT_MUXER) += segment.o @@ -478,6 +487,7 @@ OBJS-$(CONFIG_SER_DEMUXER) += serdec.o OBJS-$(CONFIG_SHORTEN_DEMUXER) += shortendec.o rawdec.o OBJS-$(CONFIG_SIFF_DEMUXER) += siff.o OBJS-$(CONFIG_SINGLEJPEG_MUXER) += rawenc.o +OBJS-$(CONFIG_SLN_DEMUXER) += pcmdec.o pcm.o OBJS-$(CONFIG_SMACKER_DEMUXER) += smacker.o OBJS-$(CONFIG_SMJPEG_DEMUXER) += smjpegdec.o smjpeg.o OBJS-$(CONFIG_SMJPEG_MUXER) += smjpegenc.o smjpeg.o @@ -494,6 +504,7 @@ OBJS-$(CONFIG_SRT_DEMUXER) += srtdec.o subtitles.o OBJS-$(CONFIG_SRT_MUXER) += srtenc.o OBJS-$(CONFIG_STL_DEMUXER) += stldec.o subtitles.o OBJS-$(CONFIG_STR_DEMUXER) += psxstr.o +OBJS-$(CONFIG_STREAMHASH_MUXER) += hashenc.o OBJS-$(CONFIG_STREAM_SEGMENT_MUXER) += segment.o OBJS-$(CONFIG_SUBVIEWER1_DEMUXER) += subviewer1dec.o subtitles.o OBJS-$(CONFIG_SUBVIEWER_DEMUXER) += subviewerdec.o subtitles.o @@ -542,10 +553,9 @@ OBJS-$(CONFIG_WC3_DEMUXER) += wc3movie.o OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \ av1.o avc.o hevc.o \ flacenc_header.o avlanguage.o \ - wv.o vorbiscomment.o \ - webmdashenc.o webm_chunk.o -OBJS-$(CONFIG_WEBM_DASH_MANIFEST_MUXER) += webmdashenc.o matroska.o -OBJS-$(CONFIG_WEBM_CHUNK_MUXER) += webm_chunk.o matroska.o + wv.o vorbiscomment.o +OBJS-$(CONFIG_WEBM_DASH_MANIFEST_MUXER) += webmdashenc.o +OBJS-$(CONFIG_WEBM_CHUNK_MUXER) += webm_chunk.o OBJS-$(CONFIG_WEBP_MUXER) += webpenc.o OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.o subtitles.o OBJS-$(CONFIG_WEBVTT_MUXER) += webvttenc.o @@ -587,7 +597,7 @@ OBJS-$(CONFIG_DATA_PROTOCOL) += data_uri.o OBJS-$(CONFIG_FFRTMPCRYPT_PROTOCOL) += rtmpcrypt.o rtmpdigest.o rtmpdh.o OBJS-$(CONFIG_FFRTMPHTTP_PROTOCOL) += rtmphttp.o OBJS-$(CONFIG_FILE_PROTOCOL) += file.o -OBJS-$(CONFIG_FTP_PROTOCOL) += ftp.o +OBJS-$(CONFIG_FTP_PROTOCOL) += ftp.o urldecode.o OBJS-$(CONFIG_GOPHER_PROTOCOL) += gopher.o OBJS-$(CONFIG_HLS_PROTOCOL) += hlsproto.o OBJS-$(CONFIG_HTTP_PROTOCOL) += http.o httpauth.o urldecode.o @@ -623,6 +633,7 @@ OBJS-$(CONFIG_UDPLITE_PROTOCOL) += udp.o ip.o OBJS-$(CONFIG_UNIX_PROTOCOL) += unix.o # external library protocols +OBJS-$(CONFIG_LIBAMQP_PROTOCOL) += libamqp.o OBJS-$(CONFIG_LIBRTMP_PROTOCOL) += librtmp.o OBJS-$(CONFIG_LIBRTMPE_PROTOCOL) += librtmp.o OBJS-$(CONFIG_LIBRTMPS_PROTOCOL) += librtmp.o @@ -631,6 +642,7 @@ OBJS-$(CONFIG_LIBRTMPTE_PROTOCOL) += librtmp.o OBJS-$(CONFIG_LIBSMBCLIENT_PROTOCOL) += libsmbclient.o OBJS-$(CONFIG_LIBSRT_PROTOCOL) += libsrt.o OBJS-$(CONFIG_LIBSSH_PROTOCOL) += libssh.o +OBJS-$(CONFIG_LIBZMQ_PROTOCOL) += libzmq.o # libavdevice dependencies OBJS-$(CONFIG_IEC61883_INDEV) += dv.o @@ -658,3 +670,4 @@ TOOLS = aviocat \ probetest \ seek_print \ sidxindex \ + venc_data_dump diff --git a/libavformat/aacdec.c b/libavformat/aacdec.c index 00ca2319cae..a0aa112a8a2 100644 --- a/libavformat/aacdec.c +++ b/libavformat/aacdec.c @@ -141,13 +141,12 @@ static int handle_id3(AVFormatContext *s, AVPacket *pkt) ret = av_append_packet(s->pb, pkt, ff_id3v2_tag_len(pkt->data) - pkt->size); if (ret < 0) { - av_packet_unref(pkt); return ret; } ffio_init_context(&ioctx, pkt->data, pkt->size, 0, NULL, NULL, NULL, NULL); ff_id3v2_read_dict(&ioctx, &metadata, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); - if ((ret = ff_id3v2_parse_priv_dict(&metadata, &id3v2_extra_meta)) < 0) + if ((ret = ff_id3v2_parse_priv_dict(&metadata, id3v2_extra_meta)) < 0) goto error; if (metadata) { @@ -174,7 +173,6 @@ static int adts_aac_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; if (ret < ADTS_HEADER_SIZE) { - av_packet_unref(pkt); return AVERROR(EIO); } @@ -185,7 +183,6 @@ static int adts_aac_read_packet(AVFormatContext *s, AVPacket *pkt) av_assert2(append > 0); ret = av_append_packet(s->pb, pkt, append); if (ret != append) { - av_packet_unref(pkt); return AVERROR(EIO); } if (!ff_id3v2_match(pkt->data, ID3v2_DEFAULT_MAGIC)) { @@ -201,13 +198,10 @@ static int adts_aac_read_packet(AVFormatContext *s, AVPacket *pkt) fsize = (AV_RB32(pkt->data + 3) >> 13) & 0x1FFF; if (fsize < ADTS_HEADER_SIZE) { - av_packet_unref(pkt); return AVERROR_INVALIDDATA; } ret = av_append_packet(s->pb, pkt, fsize - pkt->size); - if (ret < 0) - av_packet_unref(pkt); return ret; } diff --git a/libavformat/aadec.c b/libavformat/aadec.c index b9dd51ebfc3..63f8176a570 100644 --- a/libavformat/aadec.c +++ b/libavformat/aadec.c @@ -92,7 +92,7 @@ static int aa_read_header(AVFormatContext *s) avio_skip(pb, 4); // magic string toc_size = avio_rb32(pb); // TOC size avio_skip(pb, 4); // unidentified integer - if (toc_size > MAX_TOC_ENTRIES) + if (toc_size > MAX_TOC_ENTRIES || toc_size < 2) return AVERROR_INVALIDDATA; for (i = 0; i < toc_size; i++) { // read TOC avio_skip(pb, 4); // TOC entry index diff --git a/libavformat/adp.c b/libavformat/adp.c index 56f302acfd8..8668c78fe43 100644 --- a/libavformat/adp.c +++ b/libavformat/adp.c @@ -78,7 +78,6 @@ static int adp_read_packet(AVFormatContext *s, AVPacket *pkt) if (ret != size) { if (ret < 0) { - av_packet_unref(pkt); return ret; } av_shrink_packet(pkt, ret); diff --git a/libavformat/adtsenc.c b/libavformat/adtsenc.c index 3c2840c6abe..d937e2bea9c 100644 --- a/libavformat/adtsenc.c +++ b/libavformat/adtsenc.c @@ -53,7 +53,7 @@ static int adts_decode_extradata(AVFormatContext *s, ADTSContext *adts, const ui int off; init_get_bits(&gb, buf, size * 8); - off = avpriv_mpeg4audio_get_config(&m4ac, buf, size * 8, 1); + off = avpriv_mpeg4audio_get_config2(&m4ac, buf, size, 1, s); if (off < 0) return off; skip_bits_long(&gb, off); diff --git a/libavformat/adxdec.c b/libavformat/adxdec.c index 1038a0d67ec..ccd5049acd0 100644 --- a/libavformat/adxdec.c +++ b/libavformat/adxdec.c @@ -65,11 +65,9 @@ static int adx_read_packet(AVFormatContext *s, AVPacket *pkt) ret = av_get_packet(s->pb, pkt, size); if (ret != size) { - av_packet_unref(pkt); return ret < 0 ? ret : AVERROR(EIO); } if (AV_RB16(pkt->data) & 0x8000) { - av_packet_unref(pkt); return AVERROR_EOF; } pkt->size = size; @@ -83,7 +81,7 @@ static int adx_read_header(AVFormatContext *s) { ADXDemuxerContext *c = s->priv_data; AVCodecParameters *par; - + int ret; AVStream *st = avformat_new_stream(s, NULL); if (!st) return AVERROR(ENOMEM); @@ -94,8 +92,8 @@ static int adx_read_header(AVFormatContext *s) c->header_size = avio_rb16(s->pb) + 4; avio_seek(s->pb, -4, SEEK_CUR); - if (ff_get_extradata(s, par, s->pb, c->header_size) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_get_extradata(s, par, s->pb, c->header_size)) < 0) + return ret; if (par->extradata_size < 12) { av_log(s, AV_LOG_ERROR, "Invalid extradata size.\n"); diff --git a/libavformat/afc.c b/libavformat/afc.c index 542cb168fcf..2da04eb5dc0 100644 --- a/libavformat/afc.c +++ b/libavformat/afc.c @@ -31,6 +31,7 @@ static int afc_read_header(AVFormatContext *s) { AFCDemuxContext *c = s->priv_data; AVStream *st; + int ret; st = avformat_new_stream(s, NULL); if (!st) @@ -40,8 +41,8 @@ static int afc_read_header(AVFormatContext *s) st->codecpar->channels = 2; st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO; - if (ff_alloc_extradata(st->codecpar, 1)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, 1)) < 0) + return ret; st->codecpar->extradata[0] = 8 * st->codecpar->channels; c->data_end = avio_rb32(s->pb) + 32LL; diff --git a/libavformat/aiffdec.c b/libavformat/aiffdec.c index fcedb0a8043..c650e9074d5 100644 --- a/libavformat/aiffdec.c +++ b/libavformat/aiffdec.c @@ -29,6 +29,7 @@ #include "isom.h" #include "id3v2.h" #include "mov_chan.h" +#include "replaygain.h" #define AIFF 0 #define AIFF_C_VERSION1 0xA2805140 @@ -242,7 +243,10 @@ static int aiff_read_header(AVFormatContext *s) if (size < 0) return size; - filesize -= size + 8; + if (size >= 0x7fffffff - 8) + filesize = 0; + else + filesize -= size + 8; switch (tag) { case MKTAG('C', 'O', 'M', 'M'): /* Common chunk */ @@ -257,8 +261,8 @@ static int aiff_read_header(AVFormatContext *s) position = avio_tell(pb); ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, size); if (id3v2_extra_meta) - if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0 || - (ret = ff_id3v2_parse_chapters(s, &id3v2_extra_meta)) < 0) { + if ((ret = ff_id3v2_parse_apic(s, id3v2_extra_meta)) < 0 || + (ret = ff_id3v2_parse_chapters(s, id3v2_extra_meta)) < 0) { ff_id3v2_free_extra_meta(&id3v2_extra_meta); return ret; } @@ -297,8 +301,8 @@ static int aiff_read_header(AVFormatContext *s) case MKTAG('w', 'a', 'v', 'e'): if ((uint64_t)size > (1<<30)) return -1; - if (ff_get_extradata(s, st->codecpar, pb, size) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_get_extradata(s, st->codecpar, pb, size)) < 0) + return ret; if ( (st->codecpar->codec_id == AV_CODEC_ID_QDMC || st->codecpar->codec_id == AV_CODEC_ID_QDM2) && size>=12*4 && !st->codecpar->block_align) { st->codecpar->block_align = AV_RB32(st->codecpar->extradata+11*4); @@ -321,8 +325,8 @@ static int aiff_read_header(AVFormatContext *s) } break; case MKTAG('C','H','A','N'): - if(ff_mov_read_chan(s, pb, st, size) < 0) - return AVERROR_INVALIDDATA; + if ((ret = ff_mov_read_chan(s, pb, st, size)) < 0) + return ret; break; case MKTAG('A','P','C','M'): /* XA ADPCM compressed sound chunk */ st->codecpar->codec_id = AV_CODEC_ID_ADPCM_XA; @@ -348,6 +352,10 @@ static int aiff_read_header(AVFormatContext *s) } } + ret = ff_replaygain_export(st, s->metadata); + if (ret < 0) + return ret; + got_sound: if (!st->codecpar->block_align && st->codecpar->codec_id == AV_CODEC_ID_QCELP) { av_log(s, AV_LOG_WARNING, "qcelp without wave chunk, assuming full rate\n"); diff --git a/libavformat/aiffenc.c b/libavformat/aiffenc.c index aab37418f0d..88c45df3347 100644 --- a/libavformat/aiffenc.c +++ b/libavformat/aiffenc.c @@ -36,7 +36,7 @@ typedef struct AIFFOutputContext { int64_t frames; int64_t ssnd; int audio_stream_idx; - AVPacketList *pict_list; + AVPacketList *pict_list, *pict_list_end; int write_id3v2; int id3v2_version; } AIFFOutputContext; @@ -49,10 +49,7 @@ static int put_id3v2_tags(AVFormatContext *s, AIFFOutputContext *aiff) AVIOContext *pb = s->pb; AVPacketList *pict_list = aiff->pict_list; - if (!pb->seekable & AVIO_SEEKABLE_NORMAL) - return 0; - - if (!s->metadata && !aiff->pict_list) + if (!s->metadata && !s->nb_chapters && !aiff->pict_list) return 0; avio_wl32(pb, MKTAG('I', 'D', '3', ' ')); @@ -125,7 +122,7 @@ static int aiff_write_header(AVFormatContext *s) /* First verify if format is ok */ if (!par->codec_tag) - return -1; + return AVERROR(EINVAL); if (par->codec_tag != MKTAG('N','O','N','E')) aifc = 1; @@ -138,7 +135,7 @@ static int aiff_write_header(AVFormatContext *s) if (aifc) { // compressed audio if (!par->block_align) { av_log(s, AV_LOG_ERROR, "block align not set\n"); - return -1; + return AVERROR(EINVAL); } /* Version chunk */ ffio_wfourcc(pb, "FVER"); @@ -169,7 +166,7 @@ static int aiff_write_header(AVFormatContext *s) par->bits_per_coded_sample = av_get_bits_per_sample(par->codec_id); if (!par->bits_per_coded_sample) { av_log(s, AV_LOG_ERROR, "could not compute bits per sample\n"); - return -1; + return AVERROR(EINVAL); } if (!par->block_align) par->block_align = (par->bits_per_coded_sample * par->channels) >> 3; @@ -202,9 +199,6 @@ static int aiff_write_header(AVFormatContext *s) avpriv_set_pts_info(s->streams[aiff->audio_stream_idx], 64, 1, s->streams[aiff->audio_stream_idx]->codecpar->sample_rate); - /* Data is starting here */ - avio_flush(pb); - return 0; } @@ -215,9 +209,6 @@ static int aiff_write_packet(AVFormatContext *s, AVPacket *pkt) if (pkt->stream_index == aiff->audio_stream_idx) avio_write(pb, pkt->data, pkt->size); else { - int ret; - AVPacketList *pict_list, *last; - if (s->streams[pkt->stream_index]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) return 0; @@ -229,24 +220,8 @@ static int aiff_write_packet(AVFormatContext *s, AVPacket *pkt) if (s->streams[pkt->stream_index]->nb_frames >= 1) return 0; - pict_list = av_mallocz(sizeof(AVPacketList)); - if (!pict_list) - return AVERROR(ENOMEM); - - ret = av_packet_ref(&pict_list->pkt, pkt); - if (ret < 0) { - av_freep(&pict_list); - return ret; - } - - if (!aiff->pict_list) - aiff->pict_list = pict_list; - else { - last = aiff->pict_list; - while (last->next) - last = last->next; - last->next = pict_list; - } + return ff_packet_list_put(&aiff->pict_list, &aiff->pict_list_end, + pkt, FF_PACKETLIST_FLAG_REF_PACKET); } return 0; @@ -254,10 +229,9 @@ static int aiff_write_packet(AVFormatContext *s, AVPacket *pkt) static int aiff_write_trailer(AVFormatContext *s) { - int ret; + int ret = 0; AVIOContext *pb = s->pb; AIFFOutputContext *aiff = s->priv_data; - AVPacketList *pict_list = aiff->pict_list; AVCodecParameters *par = s->streams[aiff->audio_stream_idx]->codecpar; /* Chunks sizes must be even */ @@ -289,18 +263,16 @@ static int aiff_write_trailer(AVFormatContext *s) file_size = avio_tell(pb); avio_seek(pb, aiff->form, SEEK_SET); avio_wb32(pb, file_size - aiff->form - 4); - - avio_flush(pb); } - while (pict_list) { - AVPacketList *next = pict_list->next; - av_packet_unref(&pict_list->pkt); - av_freep(&pict_list); - pict_list = next; - } + return ret; +} - return 0; +static void aiff_deinit(AVFormatContext *s) +{ + AIFFOutputContext *aiff = s->priv_data; + + ff_packet_list_free(&aiff->pict_list, &aiff->pict_list_end); } #define OFFSET(x) offsetof(AIFFOutputContext, x) @@ -331,6 +303,7 @@ AVOutputFormat ff_aiff_muxer = { .write_header = aiff_write_header, .write_packet = aiff_write_packet, .write_trailer = aiff_write_trailer, + .deinit = aiff_deinit, .codec_tag = (const AVCodecTag* const []){ ff_codec_aiff_tags, 0 }, .priv_class = &aiff_muxer_class, }; diff --git a/libavformat/allformats.c b/libavformat/allformats.c index cd008348070..a7c5c9db897 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -46,6 +46,7 @@ extern AVInputFormat ff_afc_demuxer; extern AVInputFormat ff_aiff_demuxer; extern AVOutputFormat ff_aiff_muxer; extern AVInputFormat ff_aix_demuxer; +extern AVInputFormat ff_alp_demuxer; extern AVInputFormat ff_amr_demuxer; extern AVOutputFormat ff_amr_muxer; extern AVInputFormat ff_amrnb_demuxer; @@ -53,6 +54,7 @@ extern AVInputFormat ff_amrwb_demuxer; extern AVInputFormat ff_anm_demuxer; extern AVInputFormat ff_apc_demuxer; extern AVInputFormat ff_ape_demuxer; +extern AVInputFormat ff_apm_demuxer; extern AVInputFormat ff_apng_demuxer; extern AVOutputFormat ff_apng_muxer; extern AVInputFormat ff_aptx_demuxer; @@ -60,6 +62,7 @@ extern AVOutputFormat ff_aptx_muxer; extern AVInputFormat ff_aptx_hd_demuxer; extern AVOutputFormat ff_aptx_hd_muxer; extern AVInputFormat ff_aqtitle_demuxer; +extern AVInputFormat ff_argo_asf_demuxer; extern AVInputFormat ff_asf_demuxer; extern AVOutputFormat ff_asf_muxer; extern AVInputFormat ff_asf_o_demuxer; @@ -70,6 +73,7 @@ extern AVOutputFormat ff_ast_muxer; extern AVOutputFormat ff_asf_stream_muxer; extern AVInputFormat ff_au_demuxer; extern AVOutputFormat ff_au_muxer; +extern AVInputFormat ff_av1_demuxer; extern AVInputFormat ff_avi_demuxer; extern AVOutputFormat ff_avi_muxer; extern AVInputFormat ff_avisynth_demuxer; @@ -109,6 +113,7 @@ extern AVOutputFormat ff_data_muxer; extern AVInputFormat ff_daud_demuxer; extern AVOutputFormat ff_daud_muxer; extern AVInputFormat ff_dcstr_demuxer; +extern AVInputFormat ff_derf_demuxer; extern AVInputFormat ff_dfa_demuxer; extern AVInputFormat ff_dhav_demuxer; extern AVInputFormat ff_dirac_demuxer; @@ -152,6 +157,7 @@ extern AVOutputFormat ff_framehash_muxer; extern AVOutputFormat ff_framemd5_muxer; extern AVInputFormat ff_frm_demuxer; extern AVInputFormat ff_fsb_demuxer; +extern AVInputFormat ff_fwse_demuxer; extern AVInputFormat ff_g722_demuxer; extern AVOutputFormat ff_g722_muxer; extern AVInputFormat ff_g723_1_demuxer; @@ -176,6 +182,7 @@ extern AVOutputFormat ff_h263_muxer; extern AVInputFormat ff_h264_demuxer; extern AVOutputFormat ff_h264_muxer; extern AVOutputFormat ff_hash_muxer; +extern AVInputFormat ff_hca_demuxer; extern AVInputFormat ff_hcom_demuxer; extern AVOutputFormat ff_hds_muxer; extern AVInputFormat ff_hevc_demuxer; @@ -212,6 +219,8 @@ extern AVInputFormat ff_jacosub_demuxer; extern AVOutputFormat ff_jacosub_muxer; extern AVInputFormat ff_jv_demuxer; extern AVInputFormat ff_kux_demuxer; +extern AVInputFormat ff_kvag_demuxer; +extern AVOutputFormat ff_kvag_muxer; extern AVOutputFormat ff_latm_muxer; extern AVInputFormat ff_lmlm4_demuxer; extern AVInputFormat ff_loas_demuxer; @@ -333,6 +342,7 @@ extern AVInputFormat ff_pcm_u8_demuxer; extern AVOutputFormat ff_pcm_u8_muxer; extern AVInputFormat ff_pjs_demuxer; extern AVInputFormat ff_pmp_demuxer; +extern AVInputFormat ff_pp_bnk_demuxer; extern AVOutputFormat ff_psp_muxer; extern AVInputFormat ff_pva_demuxer; extern AVInputFormat ff_pvf_demuxer; @@ -393,6 +403,7 @@ extern AVInputFormat ff_srt_demuxer; extern AVOutputFormat ff_srt_muxer; extern AVInputFormat ff_str_demuxer; extern AVInputFormat ff_stl_demuxer; +extern AVOutputFormat ff_streamhash_muxer; extern AVInputFormat ff_subviewer1_demuxer; extern AVInputFormat ff_subviewer_demuxer; extern AVInputFormat ff_sup_demuxer; diff --git a/libavformat/alp.c b/libavformat/alp.c new file mode 100644 index 00000000000..4c2e8f0652f --- /dev/null +++ b/libavformat/alp.c @@ -0,0 +1,146 @@ +/* + * LEGO Racers ALP (.tun & .pcm) demuxer + * + * Copyright (C) 2020 Zane van Iperen (zane@zanevaniperen.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 + */ +#include "avformat.h" +#include "internal.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/internal.h" + +#define ALP_TAG MKTAG('A', 'L', 'P', ' ') +#define ALP_MAX_READ_SIZE 4096 + +typedef struct ALPHeader { + uint32_t magic; /*< Magic Number, {'A', 'L', 'P', ' '} */ + uint32_t header_size; /*< Header size (after this). */ + char adpcm[6]; /*< "ADPCM" */ + uint8_t unk1; /*< Unknown */ + uint8_t num_channels; /*< Channel Count. */ + uint32_t sample_rate; /*< Sample rate, only if header_size >= 12. */ +} ALPHeader; + +static int alp_probe(const AVProbeData *p) +{ + uint32_t i; + + if (AV_RL32(p->buf) != ALP_TAG) + return 0; + + /* Only allowed header sizes are 8 and 12. */ + i = AV_RL32(p->buf + 4); + if (i != 8 && i != 12) + return 0; + + if (strncmp("ADPCM", p->buf + 8, 6) != 0) + return 0; + + return AVPROBE_SCORE_MAX - 1; +} + +static int alp_read_header(AVFormatContext *s) +{ + int ret; + AVStream *st; + ALPHeader hdr; + AVCodecParameters *par; + + if ((hdr.magic = avio_rl32(s->pb)) != ALP_TAG) + return AVERROR_INVALIDDATA; + + hdr.header_size = avio_rl32(s->pb); + + if (hdr.header_size != 8 && hdr.header_size != 12) { + return AVERROR_INVALIDDATA; + } + + if ((ret = avio_read(s->pb, hdr.adpcm, sizeof(hdr.adpcm))) < 0) + return ret; + else if (ret != sizeof(hdr.adpcm)) + return AVERROR(EIO); + + if (strncmp("ADPCM", hdr.adpcm, sizeof(hdr.adpcm)) != 0) + return AVERROR_INVALIDDATA; + + hdr.unk1 = avio_r8(s->pb); + hdr.num_channels = avio_r8(s->pb); + + if (hdr.header_size == 8) { + /* .TUN music file */ + hdr.sample_rate = 11025 * hdr.num_channels; + } else { + /* .PCM sound file */ + hdr.sample_rate = avio_rl32(s->pb); + } + + if (hdr.sample_rate > 44100) { + avpriv_request_sample(s, "Sample Rate > 44100"); + return AVERROR_PATCHWELCOME; + } + + if (!(st = avformat_new_stream(s, NULL))) + return AVERROR(ENOMEM); + + par = st->codecpar; + par->codec_type = AVMEDIA_TYPE_AUDIO; + par->codec_id = AV_CODEC_ID_ADPCM_IMA_ALP; + par->format = AV_SAMPLE_FMT_S16; + par->sample_rate = hdr.sample_rate; + par->channels = hdr.num_channels; + + if (hdr.num_channels == 1) + par->channel_layout = AV_CH_LAYOUT_MONO; + else if (hdr.num_channels == 2) + par->channel_layout = AV_CH_LAYOUT_STEREO; + else + return AVERROR_INVALIDDATA; + + par->bits_per_coded_sample = 4; + par->bits_per_raw_sample = 16; + par->block_align = 1; + par->bit_rate = par->channels * + par->sample_rate * + par->bits_per_coded_sample; + + avpriv_set_pts_info(st, 64, 1, par->sample_rate); + return 0; +} + +static int alp_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret; + AVCodecParameters *par = s->streams[0]->codecpar; + + if ((ret = av_get_packet(s->pb, pkt, ALP_MAX_READ_SIZE)) < 0) + return ret; + + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; + pkt->stream_index = 0; + pkt->duration = ret * 2 / par->channels; + + return 0; +} + +AVInputFormat ff_alp_demuxer = { + .name = "alp", + .long_name = NULL_IF_CONFIG_SMALL("LEGO Racers ALP"), + .read_probe = alp_probe, + .read_header = alp_read_header, + .read_packet = alp_read_packet +}; diff --git a/libavformat/amr.c b/libavformat/amr.c index 42840a50a30..e4f8e4d8606 100644 --- a/libavformat/amr.c +++ b/libavformat/amr.c @@ -29,6 +29,7 @@ Only mono files are supported. #include "libavutil/channel_layout.h" #include "avformat.h" #include "internal.h" +#include "rawenc.h" typedef struct { uint64_t cumulated_size; @@ -60,13 +61,6 @@ static int amr_write_header(AVFormatContext *s) } else { return -1; } - avio_flush(pb); - return 0; -} - -static int amr_write_packet(AVFormatContext *s, AVPacket *pkt) -{ - avio_write(s->pb, pkt->data, pkt->size); return 0; } #endif /* CONFIG_AMR_MUXER */ @@ -90,13 +84,15 @@ static int amr_read_header(AVFormatContext *s) AVStream *st; uint8_t header[9]; - avio_read(pb, header, 6); + if (avio_read(pb, header, 6) != 6) + return AVERROR_INVALIDDATA; st = avformat_new_stream(s, NULL); if (!st) return AVERROR(ENOMEM); if (memcmp(header, AMR_header, 6)) { - avio_read(pb, header + 6, 3); + if (avio_read(pb, header + 6, 3) != 3) + return AVERROR_INVALIDDATA; if (memcmp(header, AMRWB_header, 9)) { return -1; } @@ -154,7 +150,6 @@ static int amr_read_packet(AVFormatContext *s, AVPacket *pkt) read = avio_read(s->pb, pkt->data + 1, size - 1); if (read != size - 1) { - av_packet_unref(pkt); if (read < 0) return read; return AVERROR(EIO); @@ -296,7 +291,7 @@ AVOutputFormat ff_amr_muxer = { .audio_codec = AV_CODEC_ID_AMR_NB, .video_codec = AV_CODEC_ID_NONE, .write_header = amr_write_header, - .write_packet = amr_write_packet, + .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif diff --git a/libavformat/apc.c b/libavformat/apc.c index 835d1b0f6e6..7210bfbb563 100644 --- a/libavformat/apc.c +++ b/libavformat/apc.c @@ -37,6 +37,7 @@ static int apc_read_header(AVFormatContext *s) { AVIOContext *pb = s->pb; AVStream *st; + int ret; avio_rl32(pb); /* CRYO */ avio_rl32(pb); /* _APC */ @@ -53,8 +54,8 @@ static int apc_read_header(AVFormatContext *s) st->codecpar->sample_rate = avio_rl32(pb); /* initial predictor values for adpcm decoder */ - if (ff_get_extradata(s, st->codecpar, pb, 2 * 4) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_get_extradata(s, st->codecpar, pb, 2 * 4)) < 0) + return ret; if (avio_rl32(pb)) { st->codecpar->channels = 2; @@ -78,7 +79,6 @@ static int apc_read_packet(AVFormatContext *s, AVPacket *pkt) { if (av_get_packet(s->pb, pkt, MAX_READ_SIZE) <= 0) return AVERROR(EIO); - pkt->flags &= ~AV_PKT_FLAG_CORRUPT; pkt->stream_index = 0; return 0; } diff --git a/libavformat/ape.c b/libavformat/ape.c index 977e6f3d18c..39a584aa981 100644 --- a/libavformat/ape.c +++ b/libavformat/ape.c @@ -83,6 +83,8 @@ typedef struct APEContext { uint8_t *bittable; } APEContext; +static int ape_read_close(AVFormatContext * s); + static int ape_probe(const AVProbeData * p) { int version = AV_RL16(p->buf+4); @@ -163,7 +165,7 @@ static int ape_read_header(AVFormatContext * s) APEContext *ape = s->priv_data; AVStream *st; uint32_t tag; - int i; + int i, ret; int total_blocks, final_size = 0; int64_t pts, file_size; @@ -281,14 +283,18 @@ static int ape_read_header(AVFormatContext * s) if (ape->seektablelength > 0) { ape->seektable = av_mallocz(ape->seektablelength); - if (!ape->seektable) - return AVERROR(ENOMEM); + if (!ape->seektable) { + ret = AVERROR(ENOMEM); + goto fail; + } for (i = 0; i < ape->seektablelength / sizeof(uint32_t) && !pb->eof_reached; i++) ape->seektable[i] = avio_rl32(pb); if (ape->fileversion < 3810) { ape->bittable = av_mallocz(ape->totalframes); - if (!ape->bittable) - return AVERROR(ENOMEM); + if (!ape->bittable) { + ret = AVERROR(ENOMEM); + goto fail; + } for (i = 0; i < ape->totalframes && !pb->eof_reached; i++) ape->bittable[i] = avio_r8(pb); } @@ -341,8 +347,10 @@ static int ape_read_header(AVFormatContext * s) /* now we are ready: build format streams */ st = avformat_new_stream(s, NULL); - if (!st) - return AVERROR(ENOMEM); + if (!st) { + ret = AVERROR(ENOMEM); + goto fail; + } total_blocks = (ape->totalframes == 0) ? 0 : ((ape->totalframes - 1) * ape->blocksperframe) + ape->finalframeblocks; @@ -358,8 +366,8 @@ static int ape_read_header(AVFormatContext * s) st->duration = total_blocks; avpriv_set_pts_info(st, 64, 1, ape->samplerate); - if (ff_alloc_extradata(st->codecpar, APE_EXTRADATA_SIZE)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, APE_EXTRADATA_SIZE)) < 0) + goto fail; AV_WL16(st->codecpar->extradata + 0, ape->fileversion); AV_WL16(st->codecpar->extradata + 2, ape->compressiontype); AV_WL16(st->codecpar->extradata + 4, ape->formatflags); @@ -378,6 +386,10 @@ static int ape_read_header(AVFormatContext * s) } return 0; +fail: + ape_read_close(s); + + return ret; } static int ape_read_packet(AVFormatContext * s, AVPacket * pkt) @@ -386,14 +398,16 @@ static int ape_read_packet(AVFormatContext * s, AVPacket * pkt) int nblocks; APEContext *ape = s->priv_data; uint32_t extra_size = 8; + int64_t ret64; if (avio_feof(s->pb)) return AVERROR_EOF; if (ape->currentframe >= ape->totalframes) return AVERROR_EOF; - if (avio_seek(s->pb, ape->frames[ape->currentframe].pos, SEEK_SET) < 0) - return AVERROR(EIO); + ret64 = avio_seek(s->pb, ape->frames[ape->currentframe].pos, SEEK_SET); + if (ret64 < 0) + return ret64; /* Calculate how many blocks there are in this frame */ if (ape->currentframe == (ape->totalframes - 1)) @@ -409,14 +423,14 @@ static int ape_read_packet(AVFormatContext * s, AVPacket * pkt) return AVERROR(EIO); } - if (av_new_packet(pkt, ape->frames[ape->currentframe].size + extra_size) < 0) - return AVERROR(ENOMEM); + ret = av_new_packet(pkt, ape->frames[ape->currentframe].size + extra_size); + if (ret < 0) + return ret; AV_WL32(pkt->data , nblocks); AV_WL32(pkt->data + 4, ape->frames[ape->currentframe].skip); ret = avio_read(s->pb, pkt->data + extra_size, ape->frames[ape->currentframe].size); if (ret < 0) { - av_packet_unref(pkt); return ret; } @@ -447,12 +461,13 @@ static int ape_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp AVStream *st = s->streams[stream_index]; APEContext *ape = s->priv_data; int index = av_index_search_timestamp(st, timestamp, flags); + int64_t ret; if (index < 0) return -1; - if (avio_seek(s->pb, st->index_entries[index].pos, SEEK_SET) < 0) - return -1; + if ((ret = avio_seek(s->pb, st->index_entries[index].pos, SEEK_SET)) < 0) + return ret; ape->currentframe = index; return 0; } diff --git a/libavformat/apetag.c b/libavformat/apetag.c index 8cb3f4a23af..454c6c688bd 100644 --- a/libavformat/apetag.c +++ b/libavformat/apetag.c @@ -96,8 +96,8 @@ static int ape_tag_read_field(AVFormatContext *s) st->attached_pic.stream_index = st->index; st->attached_pic.flags |= AV_PKT_FLAG_KEY; } else { - if (ff_get_extradata(s, st->codecpar, s->pb, size) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_get_extradata(s, st->codecpar, s->pb, size)) < 0) + return ret; st->codecpar->codec_type = AVMEDIA_TYPE_ATTACHMENT; } } else { @@ -186,11 +186,11 @@ int ff_ape_write_tag(AVFormatContext *s) { AVDictionaryEntry *e = NULL; int size, ret, count = 0; - AVIOContext *dyn_bc = NULL; - uint8_t *dyn_buf = NULL; + AVIOContext *dyn_bc; + uint8_t *dyn_buf; if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0) - goto end; + return ret; ff_standardize_creation_time(s); while ((e = av_dict_get(s->metadata, "", e, AV_DICT_IGNORE_SUFFIX))) { @@ -211,7 +211,7 @@ int ff_ape_write_tag(AVFormatContext *s) if (!count) goto end; - size = avio_close_dyn_buf(dyn_bc, &dyn_buf); + size = avio_get_dyn_buf(dyn_bc, &dyn_buf); if (size <= 0) goto end; size += APE_TAG_FOOTER_BYTES; @@ -239,9 +239,7 @@ int ff_ape_write_tag(AVFormatContext *s) ffio_fill(s->pb, 0, 8); // reserved end: - if (dyn_bc && !dyn_buf) - avio_close_dyn_buf(dyn_bc, &dyn_buf); - av_freep(&dyn_buf); + ffio_free_dyn_buf(&dyn_bc); return ret; } diff --git a/libavformat/apm.c b/libavformat/apm.c new file mode 100644 index 00000000000..9d2a856cc4c --- /dev/null +++ b/libavformat/apm.c @@ -0,0 +1,188 @@ +/* + * Rayman 2 APM Demuxer + * + * Copyright (C) 2020 Zane van Iperen (zane@zanevaniperen.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 + */ +#include "avformat.h" +#include "internal.h" +#include "riff.h" +#include "libavutil/internal.h" +#include "libavutil/intreadwrite.h" + +#define APM_FILE_HEADER_SIZE 20 +#define APM_VS12_CHUNK_SIZE 76 +#define APM_MAX_READ_SIZE 4096 + +#define APM_TAG_VS12 MKTAG('v', 's', '1', '2') +#define APM_TAG_DATA MKTAG('D', 'A', 'T', 'A') + +typedef struct APMState { + int32_t has_saved; + int32_t predictor_r; + int32_t step_index_r; + int32_t saved_r; + int32_t predictor_l; + int32_t step_index_l; + int32_t saved_l; +} APMState; + +typedef struct APMVS12Chunk { + uint32_t magic; + uint32_t file_size; + uint32_t data_size; + uint32_t unk1; + uint32_t unk2; + APMState state; + uint32_t pad[7]; +} APMVS12Chunk; + +static void apm_parse_vs12(APMVS12Chunk *vs12, const uint8_t *buf) +{ + vs12->magic = AV_RL32(buf + 0); + vs12->file_size = AV_RL32(buf + 4); + vs12->data_size = AV_RL32(buf + 8); + vs12->unk1 = AV_RL32(buf + 12); + vs12->unk2 = AV_RL32(buf + 16); + + vs12->state.has_saved = AV_RL32(buf + 20); + vs12->state.predictor_r = AV_RL32(buf + 24); + vs12->state.step_index_r = AV_RL32(buf + 28); + vs12->state.saved_r = AV_RL32(buf + 32); + vs12->state.predictor_l = AV_RL32(buf + 36); + vs12->state.step_index_l = AV_RL32(buf + 40); + vs12->state.saved_l = AV_RL32(buf + 44); + + for (int i = 0; i < FF_ARRAY_ELEMS(vs12->pad); i++) + vs12->pad[i] = AV_RL32(buf + 48 + (i * 4)); +} + +static int apm_probe(const AVProbeData *p) +{ + if (p->buf_size < 100) + return 0; + + if (AV_RL32(p->buf + 20) != APM_TAG_VS12) + return 0; + + if (AV_RL32(p->buf + 96) != APM_TAG_DATA) + return 0; + + return AVPROBE_SCORE_MAX - 1; +} + +static int apm_read_header(AVFormatContext *s) +{ + int64_t ret; + AVStream *st; + APMVS12Chunk vs12; + uint8_t buf[APM_VS12_CHUNK_SIZE]; + + if (!(st = avformat_new_stream(s, NULL))) + return AVERROR(ENOMEM); + + /* The header starts with a WAVEFORMATEX */ + if ((ret = ff_get_wav_header(s, s->pb, st->codecpar, APM_FILE_HEADER_SIZE, 0)) < 0) + return ret; + + if (st->codecpar->bits_per_coded_sample != 4) + return AVERROR_INVALIDDATA; + + if (st->codecpar->codec_tag != 0x2000) + return AVERROR_INVALIDDATA; + + /* ff_get_wav_header() does most of the work, but we need to fix a few things. */ + st->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_APM; + st->codecpar->codec_tag = 0; + + if (st->codecpar->channels == 2) + st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO; + else if (st->codecpar->channels == 1) + st->codecpar->channel_layout = AV_CH_LAYOUT_MONO; + else + return AVERROR_INVALIDDATA; + + st->codecpar->format = AV_SAMPLE_FMT_S16; + st->codecpar->bits_per_raw_sample = 16; + st->codecpar->bit_rate = st->codecpar->channels * + st->codecpar->sample_rate * + st->codecpar->bits_per_coded_sample; + + if ((ret = avio_read(s->pb, buf, APM_VS12_CHUNK_SIZE)) < 0) + return ret; + else if (ret != APM_VS12_CHUNK_SIZE) + return AVERROR(EIO); + + apm_parse_vs12(&vs12, buf); + + if (vs12.magic != APM_TAG_VS12) { + return AVERROR_INVALIDDATA; + } + + if (vs12.state.has_saved) { + avpriv_request_sample(s, "Saved Samples"); + return AVERROR_PATCHWELCOME; + } + + if (avio_rl32(s->pb) != APM_TAG_DATA) + return AVERROR_INVALIDDATA; + + if ((ret = ff_alloc_extradata(st->codecpar, 16)) < 0) + return ret; + + AV_WL32(st->codecpar->extradata + 0, vs12.state.predictor_l); + AV_WL32(st->codecpar->extradata + 4, vs12.state.step_index_l); + AV_WL32(st->codecpar->extradata + 8, vs12.state.predictor_r); + AV_WL32(st->codecpar->extradata + 12, vs12.state.step_index_r); + + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); + st->start_time = 0; + st->duration = vs12.data_size * + (8 / st->codecpar->bits_per_coded_sample) / + st->codecpar->channels; + return 0; +} + +static int apm_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret; + AVCodecParameters *par = s->streams[0]->codecpar; + + /* + * For future reference: if files with the `has_saved` field set ever + * surface, `saved_l`, and `saved_r` will each contain 8 "saved" samples + * that should be sent to the decoder before the actual data. + */ + + if ((ret = av_get_packet(s->pb, pkt, APM_MAX_READ_SIZE)) < 0) + return ret; + + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; + pkt->stream_index = 0; + pkt->duration = ret * (8 / par->bits_per_coded_sample) / par->channels; + + return 0; +} + +AVInputFormat ff_apm_demuxer = { + .name = "apm", + .long_name = NULL_IF_CONFIG_SMALL("Ubisoft Rayman 2 APM"), + .read_probe = apm_probe, + .read_header = apm_read_header, + .read_packet = apm_read_packet +}; diff --git a/libavformat/apngdec.c b/libavformat/apngdec.c index c8db9c6e1fe..0f1d04a3655 100644 --- a/libavformat/apngdec.c +++ b/libavformat/apngdec.c @@ -127,13 +127,14 @@ static int append_extradata(AVCodecParameters *par, AVIOContext *pb, int len) int new_size, ret; uint8_t *new_extradata; - if (previous_size > INT_MAX - len) + if (previous_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE - len) return AVERROR_INVALIDDATA; new_size = previous_size + len; new_extradata = av_realloc(par->extradata, new_size + AV_INPUT_BUFFER_PADDING_SIZE); if (!new_extradata) return AVERROR(ENOMEM); + memset(new_extradata + new_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); par->extradata = new_extradata; par->extradata_size = new_size; @@ -177,10 +178,9 @@ static int apng_read_header(AVFormatContext *s) return ret; /* extradata will contain every chunk up to the first fcTL (excluded) */ - st->codecpar->extradata = av_malloc(len + 12 + AV_INPUT_BUFFER_PADDING_SIZE); - if (!st->codecpar->extradata) - return AVERROR(ENOMEM); - st->codecpar->extradata_size = len + 12; + ret = ff_alloc_extradata(st->codecpar, len + 12); + if (ret < 0) + return ret; AV_WB32(st->codecpar->extradata, len); AV_WL32(st->codecpar->extradata+4, tag); AV_WB32(st->codecpar->extradata+8, st->codecpar->width); @@ -241,10 +241,6 @@ static int apng_read_header(AVFormatContext *s) } fail: - if (st->codecpar->extradata_size) { - av_freep(&st->codecpar->extradata); - st->codecpar->extradata_size = 0; - } return ret; } diff --git a/libavformat/apngenc.c b/libavformat/apngenc.c index 77c1c916c2e..88cd8054d6d 100644 --- a/libavformat/apngenc.c +++ b/libavformat/apngenc.c @@ -251,7 +251,6 @@ static int apng_write_trailer(AVFormatContext *format_context) if (apng->prev_packet) { ret = flush_packet(format_context, NULL); - av_freep(&apng->prev_packet); if (ret < 0) return ret; } @@ -266,12 +265,18 @@ static int apng_write_trailer(AVFormatContext *format_context) apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8); } - av_freep(&apng->extra_data); - apng->extra_data = 0; - return 0; } +static void apng_deinit(AVFormatContext *s) +{ + APNGMuxContext *apng = s->priv_data; + + av_packet_free(&apng->prev_packet); + av_freep(&apng->extra_data); + apng->extra_data_size = 0; +} + #define OFFSET(x) offsetof(APNGMuxContext, x) #define ENC AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { @@ -300,6 +305,7 @@ AVOutputFormat ff_apng_muxer = { .write_header = apng_write_header, .write_packet = apng_write_packet, .write_trailer = apng_write_trailer, + .deinit = apng_deinit, .priv_class = &apng_muxer_class, .flags = AVFMT_VARIABLE_FPS, }; diff --git a/libavformat/aqtitledec.c b/libavformat/aqtitledec.c index 8cc82a8f39a..81630d73b09 100644 --- a/libavformat/aqtitledec.c +++ b/libavformat/aqtitledec.c @@ -81,11 +81,11 @@ static int aqt_read_header(AVFormatContext *s) if (!new_event) { sub = ff_subtitles_queue_insert(&aqt->q, "\n", 1, 1); if (!sub) - return AVERROR(ENOMEM); + goto fail; } sub = ff_subtitles_queue_insert(&aqt->q, line, strlen(line), !new_event); if (!sub) - return AVERROR(ENOMEM); + goto fail; if (new_event) { sub->pts = frame; sub->duration = -1; @@ -97,6 +97,9 @@ static int aqt_read_header(AVFormatContext *s) ff_subtitles_queue_finalize(s, &aqt->q); return 0; +fail: + ff_subtitles_queue_clean(&aqt->q); + return AVERROR(ENOMEM); } static int aqt_read_packet(AVFormatContext *s, AVPacket *pkt) diff --git a/libavformat/argo_asf.c b/libavformat/argo_asf.c new file mode 100644 index 00000000000..3339425244d --- /dev/null +++ b/libavformat/argo_asf.c @@ -0,0 +1,249 @@ +/* + * Argonaut Games ASF demuxer + * + * Copyright (C) 2020 Zane van Iperen (zane@zanevaniperen.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 + */ +#include "avformat.h" +#include "internal.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/avassert.h" + +#define ASF_TAG MKTAG('A', 'S', 'F', '\0') +#define ASF_FILE_HEADER_SIZE 24 +#define ASF_CHUNK_HEADER_SIZE 20 + +typedef struct ArgoASFFileHeader { + uint32_t magic; /*< Magic Number, {'A', 'S', 'F', '\0'} */ + uint16_t version_major; /*< File Major Version. */ + uint16_t version_minor; /*< File Minor Version. */ + uint32_t num_chunks; /*< No. chunks in the file. */ + uint32_t chunk_offset; /*< Offset to the first chunk from the start of the file. */ + int8_t name[8]; /*< Name. */ +} ArgoASFFileHeader; + +typedef struct ArgoASFChunkHeader { + uint32_t num_blocks; /*< No. blocks in the chunk. */ + uint32_t num_samples; /*< No. samples per channel in a block. */ + uint32_t unk1; /*< Unknown */ + uint16_t sample_rate; /*< Sample rate. */ + uint16_t unk2; /*< Unknown. */ + uint32_t flags; /*< Stream flags. */ +} ArgoASFChunkHeader; + +enum { + ASF_CF_BITS_PER_SAMPLE = (1 << 0), /*< 16-bit if set, 8 otherwise. */ + ASF_CF_STEREO = (1 << 1), /*< Stereo if set, mono otherwise. */ + ASF_CF_ALWAYS1_1 = (1 << 2), /*< Unknown, always seems to be set. */ + ASF_CF_ALWAYS1_2 = (1 << 3), /*< Unknown, always seems to be set. */ + + ASF_CF_ALWAYS1 = ASF_CF_ALWAYS1_1 | ASF_CF_ALWAYS1_2, + ASF_CF_ALWAYS0 = ~(ASF_CF_BITS_PER_SAMPLE | ASF_CF_STEREO | ASF_CF_ALWAYS1) +}; + +typedef struct ArgoASFDemuxContext { + ArgoASFFileHeader fhdr; + ArgoASFChunkHeader ckhdr; + uint32_t blocks_read; +} ArgoASFDemuxContext; + +static void argo_asf_parse_file_header(ArgoASFFileHeader *hdr, const uint8_t *buf) +{ + hdr->magic = AV_RL32(buf + 0); + hdr->version_major = AV_RL16(buf + 4); + hdr->version_minor = AV_RL16(buf + 6); + hdr->num_chunks = AV_RL32(buf + 8); + hdr->chunk_offset = AV_RL32(buf + 12); + for (int i = 0; i < FF_ARRAY_ELEMS(hdr->name); i++) + hdr->name[i] = AV_RL8(buf + 16 + i); +} + +static void argo_asf_parse_chunk_header(ArgoASFChunkHeader *hdr, const uint8_t *buf) +{ + hdr->num_blocks = AV_RL32(buf + 0); + hdr->num_samples = AV_RL32(buf + 4); + hdr->unk1 = AV_RL32(buf + 8); + hdr->sample_rate = AV_RL16(buf + 12); + hdr->unk2 = AV_RL16(buf + 14); + hdr->flags = AV_RL32(buf + 16); +} + +/* + * Known versions: + * 1.1: The sample files in /game-formats/brender/part2.zip + * 1.2: Croc! Legend of the Gobbos + * 2.1: Croc 2 + */ +static int argo_asf_is_known_version(const ArgoASFFileHeader *hdr) +{ + return (hdr->version_major == 1 && hdr->version_minor == 1) || + (hdr->version_major == 1 && hdr->version_minor == 2) || + (hdr->version_major == 2 && hdr->version_minor == 1); +} + +static int argo_asf_probe(const AVProbeData *p) +{ + ArgoASFFileHeader hdr; + + av_assert0(AVPROBE_PADDING_SIZE >= ASF_FILE_HEADER_SIZE); + + argo_asf_parse_file_header(&hdr, p->buf); + + if (hdr.magic != ASF_TAG) + return 0; + + if (!argo_asf_is_known_version(&hdr)) + return AVPROBE_SCORE_EXTENSION / 2; + + return AVPROBE_SCORE_EXTENSION + 1; +} + +static int argo_asf_read_header(AVFormatContext *s) +{ + int64_t ret; + AVIOContext *pb = s->pb; + AVStream *st; + ArgoASFDemuxContext *asf = s->priv_data; + uint8_t buf[FFMAX(ASF_FILE_HEADER_SIZE, ASF_CHUNK_HEADER_SIZE)]; + + if (!(st = avformat_new_stream(s, NULL))) + return AVERROR(ENOMEM); + + if ((ret = avio_read(pb, buf, ASF_FILE_HEADER_SIZE)) < 0) + return ret; + else if (ret != ASF_FILE_HEADER_SIZE) + return AVERROR(EIO); + + argo_asf_parse_file_header(&asf->fhdr, buf); + + if (!argo_asf_is_known_version(&asf->fhdr)) { + avpriv_request_sample(s, "Version %hu.%hu", + asf->fhdr.version_major, asf->fhdr.version_minor + ); + return AVERROR_PATCHWELCOME; + } + + if (asf->fhdr.num_chunks == 0) { + return AVERROR_INVALIDDATA; + } else if (asf->fhdr.num_chunks > 1) { + avpriv_request_sample(s, ">1 chunk"); + return AVERROR_PATCHWELCOME; + } + + if (asf->fhdr.chunk_offset < ASF_FILE_HEADER_SIZE) + return AVERROR_INVALIDDATA; + + if ((ret = avio_skip(pb, asf->fhdr.chunk_offset - ASF_FILE_HEADER_SIZE)) < 0) + return ret; + + if ((ret = avio_read(pb, buf, ASF_CHUNK_HEADER_SIZE)) < 0) + return ret; + else if (ret != ASF_CHUNK_HEADER_SIZE) + return AVERROR(EIO); + + argo_asf_parse_chunk_header(&asf->ckhdr, buf); + + if ((asf->ckhdr.flags & ASF_CF_ALWAYS1) != ASF_CF_ALWAYS1 || (asf->ckhdr.flags & ASF_CF_ALWAYS0) != 0) { + avpriv_request_sample(s, "Nonstandard flags (0x%08X)", asf->ckhdr.flags); + return AVERROR_PATCHWELCOME; + } + + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + st->codecpar->codec_id = AV_CODEC_ID_ADPCM_ARGO; + st->codecpar->format = AV_SAMPLE_FMT_S16P; + + if (asf->ckhdr.flags & ASF_CF_STEREO) { + st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO; + st->codecpar->channels = 2; + } else { + st->codecpar->channel_layout = AV_CH_LAYOUT_MONO; + st->codecpar->channels = 1; + } + + st->codecpar->sample_rate = asf->ckhdr.sample_rate; + + st->codecpar->bits_per_coded_sample = 4; + + if (asf->ckhdr.flags & ASF_CF_BITS_PER_SAMPLE) + st->codecpar->bits_per_raw_sample = 16; + else + st->codecpar->bits_per_raw_sample = 8; + + if (st->codecpar->bits_per_raw_sample != 16) { + /* The header allows for these, but I've never seen any files with them. */ + avpriv_request_sample(s, "Non 16-bit samples"); + return AVERROR_PATCHWELCOME; + } + + /* + * (nchannel control bytes) + ((bytes_per_channel) * nchannel) + * For mono, this is 17. For stereo, this is 34. + */ + st->codecpar->frame_size = st->codecpar->channels + + (asf->ckhdr.num_samples / 2) * + st->codecpar->channels; + + st->codecpar->block_align = st->codecpar->frame_size; + + st->codecpar->bit_rate = st->codecpar->channels * + st->codecpar->sample_rate * + st->codecpar->bits_per_coded_sample; + + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); + st->start_time = 0; + st->duration = asf->ckhdr.num_blocks * asf->ckhdr.num_samples; + st->nb_frames = asf->ckhdr.num_blocks; + return 0; +} + +static int argo_asf_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + ArgoASFDemuxContext *asf = s->priv_data; + + AVStream *st = s->streams[0]; + AVIOContext *pb = s->pb; + int ret; + + if (asf->blocks_read >= asf->ckhdr.num_blocks) + return AVERROR_EOF; + + if ((ret = av_get_packet(pb, pkt, st->codecpar->frame_size)) < 0) + return ret; + else if (ret != st->codecpar->frame_size) + return AVERROR_INVALIDDATA; + + pkt->stream_index = st->index; + pkt->duration = asf->ckhdr.num_samples; + + ++asf->blocks_read; + return 0; +} + +/* + * Not actually sure what ASF stands for. + * - Argonaut Sound File? + * - Audio Stream File? + */ +AVInputFormat ff_argo_asf_demuxer = { + .name = "argo_asf", + .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, + .read_packet = argo_asf_read_packet +}; diff --git a/libavformat/asfdec_f.c b/libavformat/asfdec_f.c index 57dc3b09b95..e9ddca7151d 100644 --- a/libavformat/asfdec_f.c +++ b/libavformat/asfdec_f.c @@ -308,8 +308,8 @@ static void get_id3_tag(AVFormatContext *s, int len) ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, len); if (id3v2_extra_meta) { - ff_id3v2_parse_apic(s, &id3v2_extra_meta); - ff_id3v2_parse_chapters(s, &id3v2_extra_meta); + ff_id3v2_parse_apic(s, id3v2_extra_meta); + ff_id3v2_parse_chapters(s, id3v2_extra_meta); } ff_id3v2_free_extra_meta(&id3v2_extra_meta); } @@ -321,8 +321,7 @@ static void get_tag(AVFormatContext *s, const char *key, int type, int len, int int64_t off = avio_tell(s->pb); #define LEN 22 - if ((unsigned)len >= (UINT_MAX - LEN) / 2) - return; + av_assert0((unsigned)len < (INT_MAX - LEN) / 2); if (!asf->export_xmp && !strncmp(key, "xmp", 3)) goto finish; @@ -712,6 +711,9 @@ static int asf_read_metadata(AVFormatContext *s, int64_t size) value_type = avio_rl16(pb); /* value_type */ value_len = avio_rl32(pb); + if (value_len < 0 || value_len > UINT16_MAX) + return AVERROR_INVALIDDATA; + name_len_utf8 = 2*name_len_utf16 + 1; name = av_malloc(name_len_utf8); if (!name) @@ -857,11 +859,20 @@ static int asf_read_header(AVFormatContext *s) return ret; av_hex_dump_log(s, AV_LOG_DEBUG, pkt.data, pkt.size); av_packet_unref(&pkt); + len= avio_rl32(pb); + if (len > UINT16_MAX) + return AVERROR_INVALIDDATA; get_tag(s, "ASF_Protection_Type", -1, len, 32); + len= avio_rl32(pb); + if (len > UINT16_MAX) + return AVERROR_INVALIDDATA; get_tag(s, "ASF_Key_ID", -1, len, 32); + len= avio_rl32(pb); + if (len > UINT16_MAX) + return AVERROR_INVALIDDATA; get_tag(s, "ASF_License_URL", -1, len, 32); } else if (!ff_guidcmp(&g, &ff_asf_ext_content_encryption)) { av_log(s, AV_LOG_WARNING, diff --git a/libavformat/asfdec_o.c b/libavformat/asfdec_o.c index 8e7f044ce91..1b10e47907a 100644 --- a/libavformat/asfdec_o.c +++ b/libavformat/asfdec_o.c @@ -461,8 +461,8 @@ static void get_id3_tag(AVFormatContext *s, int len) ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, len); if (id3v2_extra_meta) { - ff_id3v2_parse_apic(s, &id3v2_extra_meta); - ff_id3v2_parse_chapters(s, &id3v2_extra_meta); + ff_id3v2_parse_apic(s, id3v2_extra_meta); + ff_id3v2_parse_chapters(s, id3v2_extra_meta); } ff_id3v2_free_extra_meta(&id3v2_extra_meta); } @@ -1165,7 +1165,7 @@ static int asf_read_replicated_data(AVFormatContext *s, ASFPacket *asf_pkt) } else avio_skip(pb, 4); // reading of media object size is already done asf_pkt->dts = avio_rl32(pb); // read presentation time - if (asf->rep_data_len && (asf->rep_data_len >= 8)) + if (asf->rep_data_len >= 8) avio_skip(pb, asf->rep_data_len - 8); // skip replicated data return 0; diff --git a/libavformat/asfenc.c b/libavformat/asfenc.c index 3cfe75a5d5e..73afb13200b 100644 --- a/libavformat/asfenc.c +++ b/libavformat/asfenc.c @@ -22,7 +22,6 @@ #include "libavutil/avassert.h" #include "libavutil/dict.h" #include "libavutil/mathematics.h" -#include "libavutil/parseutils.h" #include "libavutil/opt.h" #include "avformat.h" #include "avlanguage.h" @@ -358,12 +357,12 @@ static int asf_write_markers(AVFormatContext *s) int64_t pres_time = av_rescale_q(c->start, c->time_base, scale); uint64_t offset; int32_t send_time = get_send_time(asf, pres_time, &offset); - int len = 0; + int len = 0, ret; uint8_t *buf; AVIOContext *dyn_buf; if (t) { - if (avio_open_dyn_buf(&dyn_buf) < 0) - return AVERROR(ENOMEM); + if ((ret = avio_open_dyn_buf(&dyn_buf)) < 0) + return ret; avio_put_str16le(dyn_buf, t->value); len = avio_close_dyn_buf(dyn_buf, &buf); } @@ -580,12 +579,12 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, /* title and other info */ if (has_title) { - int len; + int len, ret; uint8_t *buf; AVIOContext *dyn_buf; - if (avio_open_dyn_buf(&dyn_buf) < 0) - return AVERROR(ENOMEM); + if ((ret = avio_open_dyn_buf(&dyn_buf)) < 0) + return ret; hpos = put_header(pb, &ff_asf_comment_header); @@ -715,10 +714,10 @@ static int asf_write_header1(AVFormatContext *s, int64_t file_size, if (desc) { AVIOContext *dyn_buf; uint8_t *buf; - int len; + int len, ret; - if (avio_open_dyn_buf(&dyn_buf) < 0) - return AVERROR(ENOMEM); + if ((ret = avio_open_dyn_buf(&dyn_buf)) < 0) + return ret; avio_put_str16le(dyn_buf, desc); len = avio_close_dyn_buf(dyn_buf, &buf); @@ -802,8 +801,6 @@ static int asf_write_header(AVFormatContext *s) return -1; } - avio_flush(s->pb); - asf->packet_nb_payloads = 0; asf->packet_timestamp_start = -1; asf->packet_timestamp_end = -1; @@ -895,7 +892,8 @@ static void flush_packet(AVFormatContext *s) avio_write(s->pb, asf->packet_buf, s->packet_size - packet_hdr_size); - avio_flush(s->pb); + avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); + asf->nb_packets++; asf->packet_nb_payloads = 0; asf->packet_timestamp_start = -1; @@ -1133,7 +1131,6 @@ static int asf_write_trailer(AVFormatContext *s) return ret; asf_write_index(s, asf->index_ptr, asf->maximum_packet, asf->next_start_sec); } - avio_flush(s->pb); if (asf->is_streamed || !(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) { put_chunk(s, 0x4524, 0, 0); /* end of stream */ diff --git a/libavformat/assdec.c b/libavformat/assdec.c index f66b2966732..8fb9e8e501b 100644 --- a/libavformat/assdec.c +++ b/libavformat/assdec.c @@ -160,6 +160,8 @@ static int ass_read_header(AVFormatContext *s) ff_subtitles_queue_finalize(s, &ass->q); end: + if (res < 0) + ass_read_close(s); av_bprint_finalize(&header, NULL); av_bprint_finalize(&line, NULL); av_bprint_finalize(&rline, NULL); diff --git a/libavformat/assenc.c b/libavformat/assenc.c index d50f18feb19..68c3396e5ad 100644 --- a/libavformat/assenc.c +++ b/libavformat/assenc.c @@ -77,7 +77,6 @@ static int write_header(AVFormatContext *s) avio_printf(s->pb, "[Events]\r\nFormat: %s, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n", ass->ssa_mode ? "Marked" : "Layer"); } - avio_flush(s->pb); return 0; } @@ -95,7 +94,7 @@ static void purge_dialogues(AVFormatContext *s, int force) ass->expected_readorder, dialogue->readorder); ass->expected_readorder = dialogue->readorder; } - avio_printf(s->pb, "Dialogue: %s\r\n", dialogue->line); + avio_print(s->pb, "Dialogue: ", dialogue->line, "\r\n"); if (dialogue == ass->last_added_dialogue) ass->last_added_dialogue = next; av_freep(&dialogue->line); diff --git a/libavformat/astenc.c b/libavformat/astenc.c index 578e658891e..e0b94b8b632 100644 --- a/libavformat/astenc.c +++ b/libavformat/astenc.c @@ -101,8 +101,6 @@ static int ast_write_header(AVFormatContext *s) avio_wb64(pb, 0); avio_wb32(pb, 0); - avio_flush(pb); - return 0; } @@ -180,7 +178,6 @@ static int ast_write_trailer(AVFormatContext *s) } avio_seek(pb, file_size, SEEK_SET); - avio_flush(pb); } return 0; } diff --git a/libavformat/async.c b/libavformat/async.c index 4e295b5e10f..a0bdfa2ee3a 100644 --- a/libavformat/async.c +++ b/libavformat/async.c @@ -293,7 +293,7 @@ static int async_open(URLContext *h, const char *arg, int flags, AVDictionary ** cond_wakeup_main_fail: pthread_mutex_destroy(&c->mutex); mutex_fail: - ffurl_close(c->inner); + ffurl_closep(&c->inner); url_fail: ring_destroy(&c->ring); fifo_fail: @@ -317,7 +317,7 @@ static int async_close(URLContext *h) pthread_cond_destroy(&c->cond_wakeup_background); pthread_cond_destroy(&c->cond_wakeup_main); pthread_mutex_destroy(&c->mutex); - ffurl_close(c->inner); + ffurl_closep(&c->inner); ring_destroy(&c->ring); return 0; diff --git a/libavformat/au.c b/libavformat/au.c index cb48e67feb4..4afee85a948 100644 --- a/libavformat/au.c +++ b/libavformat/au.c @@ -311,7 +311,6 @@ static int au_write_header(AVFormatContext *s) } else { avio_wb64(pb, 0); /* annotation field */ } - avio_flush(pb); return 0; } @@ -327,7 +326,6 @@ static int au_write_trailer(AVFormatContext *s) avio_seek(pb, 8, SEEK_SET); avio_wb32(pb, (uint32_t)(file_size - au->header_size)); avio_seek(pb, file_size, SEEK_SET); - avio_flush(pb); } return 0; diff --git a/libavformat/audiointerleave.c b/libavformat/audiointerleave.c deleted file mode 100644 index dea5d99821c..00000000000 --- a/libavformat/audiointerleave.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Audio Interleaving functions - * - * Copyright (c) 2009 Baptiste Coudurier - * - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General 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/fifo.h" -#include "libavutil/mathematics.h" -#include "avformat.h" -#include "audiointerleave.h" -#include "internal.h" - -void ff_audio_interleave_close(AVFormatContext *s) -{ - int i; - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - AudioInterleaveContext *aic = st->priv_data; - - if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) - av_fifo_freep(&aic->fifo); - } -} - -int ff_audio_interleave_init(AVFormatContext *s, - const int *samples_per_frame, - AVRational time_base) -{ - int i; - - if (!samples_per_frame) - return AVERROR(EINVAL); - - if (!time_base.num) { - av_log(s, AV_LOG_ERROR, "timebase not set for audio interleave\n"); - return AVERROR(EINVAL); - } - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - AudioInterleaveContext *aic = st->priv_data; - - if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - aic->sample_size = (st->codecpar->channels * - av_get_bits_per_sample(st->codecpar->codec_id)) / 8; - if (!aic->sample_size) { - av_log(s, AV_LOG_ERROR, "could not compute sample size\n"); - return AVERROR(EINVAL); - } - aic->samples_per_frame = samples_per_frame; - aic->samples = aic->samples_per_frame; - aic->time_base = time_base; - - aic->fifo_size = 100* *aic->samples; - if (!(aic->fifo= av_fifo_alloc_array(100, *aic->samples))) - return AVERROR(ENOMEM); - } - } - - return 0; -} - -static int interleave_new_audio_packet(AVFormatContext *s, AVPacket *pkt, - int stream_index, int flush) -{ - AVStream *st = s->streams[stream_index]; - AudioInterleaveContext *aic = st->priv_data; - int ret; - int frame_size = *aic->samples * aic->sample_size; - int size = FFMIN(av_fifo_size(aic->fifo), frame_size); - if (!size || (!flush && size == av_fifo_size(aic->fifo))) - return 0; - - ret = av_new_packet(pkt, frame_size); - if (ret < 0) - return ret; - av_fifo_generic_read(aic->fifo, pkt->data, size, NULL); - - if (size < pkt->size) - memset(pkt->data + size, 0, pkt->size - size); - - pkt->dts = pkt->pts = aic->dts; - pkt->duration = av_rescale_q(*aic->samples, st->time_base, aic->time_base); - pkt->stream_index = stream_index; - aic->dts += pkt->duration; - - aic->samples++; - if (!*aic->samples) - aic->samples = aic->samples_per_frame; - - return pkt->size; -} - -int ff_audio_rechunk_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush, - int (*get_packet)(AVFormatContext *, AVPacket *, AVPacket *, int), - int (*compare_ts)(AVFormatContext *, AVPacket *, AVPacket *)) -{ - int i, ret; - - if (pkt) { - AVStream *st = s->streams[pkt->stream_index]; - AudioInterleaveContext *aic = st->priv_data; - if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - unsigned new_size = av_fifo_size(aic->fifo) + pkt->size; - if (new_size > aic->fifo_size) { - if (av_fifo_realloc2(aic->fifo, new_size) < 0) - return AVERROR(ENOMEM); - aic->fifo_size = new_size; - } - av_fifo_generic_write(aic->fifo, pkt->data, pkt->size, NULL); - } else { - // rewrite pts and dts to be decoded time line position - pkt->pts = pkt->dts = aic->dts; - aic->dts += pkt->duration; - if ((ret = ff_interleave_add_packet(s, pkt, compare_ts)) < 0) - return ret; - } - pkt = NULL; - } - - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - AVPacket new_pkt = { 0 }; - while ((ret = interleave_new_audio_packet(s, &new_pkt, i, flush)) > 0) { - if ((ret = ff_interleave_add_packet(s, &new_pkt, compare_ts)) < 0) - return ret; - } - if (ret < 0) - return ret; - } - } - - return get_packet(s, out, NULL, flush); -} diff --git a/libavformat/audiointerleave.h b/libavformat/audiointerleave.h deleted file mode 100644 index 4d77832fa16..00000000000 --- a/libavformat/audiointerleave.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * audio interleaving prototypes and declarations - * - * Copyright (c) 2009 Baptiste Coudurier - * - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General 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_AUDIOINTERLEAVE_H -#define AVFORMAT_AUDIOINTERLEAVE_H - -#include "libavutil/fifo.h" -#include "avformat.h" - -typedef struct AudioInterleaveContext { - AVFifoBuffer *fifo; - unsigned fifo_size; ///< size of currently allocated FIFO - uint64_t dts; ///< current dts - int sample_size; ///< size of one sample all channels included - const int *samples_per_frame; ///< must be 0-terminated - const int *samples; ///< current samples per frame, pointer to samples_per_frame - AVRational time_base; ///< time base of output audio packets -} AudioInterleaveContext; - -int ff_audio_interleave_init(AVFormatContext *s, const int *samples_per_frame, AVRational time_base); -void ff_audio_interleave_close(AVFormatContext *s); - -/** - * Rechunk audio PCM packets per AudioInterleaveContext->samples_per_frame - * and interleave them correctly. - * The first element of AVStream->priv_data must be AudioInterleaveContext - * when using this function. - * - * @param get_packet function will output a packet when streams are correctly interleaved. - * @param compare_ts function will compare AVPackets and decide interleaving order. - */ -int ff_audio_rechunk_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush, - int (*get_packet)(AVFormatContext *, AVPacket *, AVPacket *, int), - int (*compare_ts)(AVFormatContext *, AVPacket *, AVPacket *)); - -#endif /* AVFORMAT_AUDIOINTERLEAVE_H */ diff --git a/libavformat/av1.c b/libavformat/av1.c index b36c5e44ba3..1e7a67d2f2b 100644 --- a/libavformat/av1.c +++ b/libavformat/av1.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "libavutil/mem.h" #include "libavcodec/av1.h" #include "libavcodec/av1_parse.h" @@ -26,14 +27,22 @@ #include "libavcodec/put_bits.h" #include "av1.h" #include "avio.h" +#include "avio_internal.h" -int ff_av1_filter_obus(AVIOContext *pb, const uint8_t *buf, int size) +static int av1_filter_obus(AVIOContext *pb, const uint8_t *buf, + int size, int *offset) { - const uint8_t *end = buf + size; + const uint8_t *start = buf, *end = buf + size; int64_t obu_size; - int start_pos, type, temporal_id, spatial_id; - - size = 0; + int off, start_pos, type, temporal_id, spatial_id; + enum { + START_NOT_FOUND, + START_FOUND, + END_FOUND, + OFFSET_IMPOSSIBLE, + } state = START_NOT_FOUND; + + off = size = 0; while (buf < end) { int len = parse_obu_header(buf, end - buf, &obu_size, &start_pos, &type, &temporal_id, &spatial_id); @@ -45,35 +54,70 @@ int ff_av1_filter_obus(AVIOContext *pb, const uint8_t *buf, int size) case AV1_OBU_REDUNDANT_FRAME_HEADER: case AV1_OBU_TILE_LIST: case AV1_OBU_PADDING: + if (state == START_FOUND) + state = END_FOUND; break; default: - avio_write(pb, buf, len); + if (state == START_NOT_FOUND) { + off = buf - start; + state = START_FOUND; + } else if (state == END_FOUND) { + state = OFFSET_IMPOSSIBLE; + } + if (pb) + avio_write(pb, buf, len); size += len; break; } buf += len; } + if (offset) + *offset = state != OFFSET_IMPOSSIBLE ? off : -1; + return size; } -int ff_av1_filter_obus_buf(const uint8_t *buf, uint8_t **out, int *size) +int ff_av1_filter_obus(AVIOContext *pb, const uint8_t *buf, int size) { - AVIOContext *pb; - int ret; + return av1_filter_obus(pb, buf, size, NULL); +} - ret = avio_open_dyn_buf(&pb); - if (ret < 0) - return ret; +int ff_av1_filter_obus_buf(const uint8_t *in, uint8_t **out, + int *size, int *offset) +{ + AVIOContext pb; + uint8_t *buf; + int len, off, ret; - ret = ff_av1_filter_obus(pb, buf, *size); - if (ret < 0) + len = ret = av1_filter_obus(NULL, in, *size, &off); + if (ret < 0) { return ret; + } + if (off >= 0) { + *out = (uint8_t *)in; + *size = len; + *offset = off; - av_freep(out); - *size = avio_close_dyn_buf(pb, out); + return 0; + } - return ret; + buf = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE); + if (!buf) + return AVERROR(ENOMEM); + + ffio_init_context(&pb, buf, len, 1, NULL, NULL, NULL, NULL); + + ret = av1_filter_obus(&pb, in, *size, NULL); + av_assert1(ret == len); + + memset(buf + len, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + *out = buf; + *size = len; + *offset = 0; + + return 0; } static inline void uvlc(GetBitContext *gb) @@ -254,7 +298,7 @@ static int parse_sequence_header(AV1SequenceParameters *seq_params, const uint8_ if (!reduced_still_picture_header) { int enable_order_hint, seq_force_screen_content_tools; - skip_bits(&gb, 4); // enable_intraintra_compound (1), enable_masked_compound (1) + skip_bits(&gb, 4); // enable_interintra_compound (1), enable_masked_compound (1) // enable_warped_motion (1), enable_dual_filter (1) enable_order_hint = get_bits1(&gb); @@ -323,7 +367,7 @@ int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size) AV1SequenceParameters seq_params; PutBitContext pbc; uint8_t header[4]; - uint8_t *seq = NULL, *meta = NULL; + uint8_t *seq, *meta; int64_t obu_size; int start_pos, type, temporal_id, spatial_id; int ret, nb_seq = 0, seq_size, meta_size; @@ -373,7 +417,7 @@ int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size) buf += len; } - seq_size = avio_close_dyn_buf(seq_pb, &seq); + seq_size = avio_get_dyn_buf(seq_pb, &seq); if (!seq_size) { ret = AVERROR_INVALIDDATA; goto fail; @@ -398,17 +442,13 @@ int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size) avio_write(pb, header, sizeof(header)); avio_write(pb, seq, seq_size); - meta_size = avio_close_dyn_buf(meta_pb, &meta); + meta_size = avio_get_dyn_buf(meta_pb, &meta); if (meta_size) avio_write(pb, meta, meta_size); fail: - if (!seq) - avio_close_dyn_buf(seq_pb, &seq); - if (!meta) - avio_close_dyn_buf(meta_pb, &meta); - av_free(seq); - av_free(meta); + ffio_free_dyn_buf(&seq_pb); + ffio_free_dyn_buf(&meta_pb); return ret; } diff --git a/libavformat/av1.h b/libavformat/av1.h index 441b6c98989..dd5b47dc252 100644 --- a/libavformat/av1.h +++ b/libavformat/av1.h @@ -56,20 +56,24 @@ typedef struct AV1SequenceParameters { int ff_av1_filter_obus(AVIOContext *pb, const uint8_t *buf, int size); /** - * Filter out AV1 OBUs not meant to be present in ISOBMFF sample data and write - * the resulting bitstream to a newly allocated data buffer. + * Filter out AV1 OBUs not meant to be present in ISOBMFF sample data and return + * the result in a data buffer, avoiding allocations and copies if possible. * - * @param pb pointer to the AVIOContext where the filtered bitstream shall be - * written - * @param buf input data buffer - * @param out pointer to pointer that will hold the allocated data buffer + * @param in input data buffer + * @param out pointer to pointer for the returned buffer. In case of success, + * it is independently allocated if and only if `*out` differs from in. * @param size size of the input data buffer. The size of the resulting output - data buffer will be written here + * data buffer will be written here + * @param offset offset of the returned data inside `*out`: It runs from + * `*out + offset` (inclusive) to `*out + offset + size` + * (exclusive); is zero if `*out` is independently allocated. * - * @return the amount of bytes written in case of success, a negative AVERROR - * code in case of failure. On failure, out and size are unchanged + * @return 0 in case of success, a negative AVERROR code in case of failure. + * On failure, *out and *size are unchanged + * @note *out will be treated as unintialized on input and will not be freed. */ -int ff_av1_filter_obus_buf(const uint8_t *buf, uint8_t **out, int *size); +int ff_av1_filter_obus_buf(const uint8_t *in, uint8_t **out, + int *size, int *offset); /** * Parses a Sequence Header from the the provided buffer. @@ -87,7 +91,7 @@ int ff_av1_parse_seq_header(AV1SequenceParameters *seq, const uint8_t *buf, int * Writes AV1 extradata (Sequence Header and Metadata OBUs) to the provided * AVIOContext. * - * @param pb pointer to the AVIOContext where the hvcC shall be written + * @param pb pointer to the AVIOContext where the av1C box shall be written * @param buf input data buffer * @param size size in bytes of the input data buffer * diff --git a/libavformat/av1dec.c b/libavformat/av1dec.c new file mode 100644 index 00000000000..1be2fac1c10 --- /dev/null +++ b/libavformat/av1dec.c @@ -0,0 +1,279 @@ +/* + * AV1 Annex B demuxer + * 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 "config.h" + +#include "libavutil/common.h" +#include "libavutil/opt.h" +#include "libavcodec/av1_parse.h" +#include "avformat.h" +#include "avio_internal.h" +#include "internal.h" + +typedef struct AnnexBContext { + const AVClass *class; + AVBSFContext *bsf; + uint32_t temporal_unit_size; + uint32_t frame_unit_size; + AVRational framerate; +} AnnexBContext; + +static int leb(AVIOContext *pb, uint32_t *len) { + int more, i = 0; + uint8_t byte; + *len = 0; + do { + unsigned bits; + byte = avio_r8(pb); + more = byte & 0x80; + bits = byte & 0x7f; + if (i <= 3 || (i == 4 && bits < (1 << 4))) + *len |= bits << (i * 7); + else if (bits) + 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; +} + +static int read_obu(const uint8_t *buf, int size, int64_t *obu_size, int *type) +{ + int start_pos, temporal_id, spatial_id; + int len; + + len = parse_obu_header(buf, size, obu_size, &start_pos, + type, &temporal_id, &spatial_id); + if (len < 0) + return len; + + return 0; +} + +static int annexb_probe(const AVProbeData *p) +{ + AVIOContext pb; + int64_t obu_size; + uint32_t temporal_unit_size, frame_unit_size, obu_unit_size; + int seq = 0; + int ret, type, cnt = 0; + + ffio_init_context(&pb, p->buf, p->buf_size, 0, + NULL, NULL, NULL, NULL); + + ret = leb(&pb, &temporal_unit_size); + if (ret < 0) + return 0; + cnt += ret; + ret = leb(&pb, &frame_unit_size); + if (ret < 0 || ((int64_t)frame_unit_size + ret) > temporal_unit_size) + return 0; + cnt += ret; + temporal_unit_size -= ret; + ret = leb(&pb, &obu_unit_size); + if (ret < 0 || ((int64_t)obu_unit_size + ret) >= frame_unit_size) + return 0; + cnt += ret; + + temporal_unit_size -= obu_unit_size + ret; + frame_unit_size -= obu_unit_size + ret; + + avio_skip(&pb, obu_unit_size); + if (pb.eof_reached || pb.error) + return 0; + + // Check that the first OBU is a Temporal Delimiter. + ret = read_obu(p->buf + cnt, FFMIN(p->buf_size - cnt, obu_unit_size), &obu_size, &type); + if (ret < 0 || type != AV1_OBU_TEMPORAL_DELIMITER || obu_size > 0) + return 0; + cnt += obu_unit_size; + + do { + ret = leb(&pb, &obu_unit_size); + if (ret < 0 || ((int64_t)obu_unit_size + ret) > frame_unit_size) + return 0; + cnt += ret; + + avio_skip(&pb, obu_unit_size); + if (pb.eof_reached || pb.error) + return 0; + + ret = read_obu(p->buf + cnt, FFMIN(p->buf_size - cnt, obu_unit_size), &obu_size, &type); + if (ret < 0) + return 0; + cnt += obu_unit_size; + + switch (type) { + case AV1_OBU_SEQUENCE_HEADER: + seq = 1; + break; + case AV1_OBU_FRAME: + case AV1_OBU_FRAME_HEADER: + return seq ? AVPROBE_SCORE_EXTENSION + 1 : 0; + case AV1_OBU_TILE_GROUP: + case AV1_OBU_TEMPORAL_DELIMITER: + return 0; + default: + break; + } + + temporal_unit_size -= obu_unit_size + ret; + frame_unit_size -= obu_unit_size + ret; + } while (frame_unit_size); + + return 0; +} + +static int annexb_read_header(AVFormatContext *s) +{ + AnnexBContext *c = s->priv_data; + const AVBitStreamFilter *filter = av_bsf_get_by_name("av1_frame_merge"); + AVStream *st; + int ret; + + if (!filter) { + av_log(c, AV_LOG_ERROR, "av1_frame_merge bitstream filter " + "not found. This is a bug, please report it.\n"); + return AVERROR_BUG; + } + + 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_AV1; + st->need_parsing = AVSTREAM_PARSE_HEADERS; + + st->internal->avctx->framerate = 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) { + av_bsf_free(&c->bsf); + return ret; + } + + ret = av_bsf_init(c->bsf); + if (ret < 0) + av_bsf_free(&c->bsf); + + return ret; +} + +static int annexb_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AnnexBContext *c = s->priv_data; + uint32_t obu_unit_size; + int ret, len; + +retry: + if (avio_feof(s->pb)) { + if (c->temporal_unit_size || c->frame_unit_size) + return AVERROR(EIO); + goto end; + } + + if (!c->temporal_unit_size) { + len = leb(s->pb, &c->temporal_unit_size); + if (len < 0) return AVERROR_INVALIDDATA; + } + + 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) + 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) + 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); + + c->temporal_unit_size -= obu_unit_size + len; + c->frame_unit_size -= obu_unit_size + len; + +end: + ret = av_bsf_send_packet(c->bsf, pkt); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Failed to send packet to " + "av1_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, "av1_frame_merge filter failed to " + "send output packet\n"); + + if (ret == AVERROR(EAGAIN)) + goto retry; + + return ret; +} + +static int annexb_read_close(AVFormatContext *s) +{ + AnnexBContext *c = s->priv_data; + + av_bsf_free(&c->bsf); + return 0; +} + +#define OFFSET(x) offsetof(AnnexBContext, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM +static const AVOption annexb_options[] = { + { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, DEC}, + { NULL }, +}; + +static const AVClass annexb_demuxer_class = { + .class_name = "AV1 Annex B demuxer", + .item_name = av_default_item_name, + .option = annexb_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_av1_demuxer = { + .name = "av1", + .long_name = NULL_IF_CONFIG_SMALL("AV1 Annex B"), + .priv_data_size = sizeof(AnnexBContext), + .read_probe = annexb_probe, + .read_header = annexb_read_header, + .read_packet = annexb_read_packet, + .read_close = annexb_read_close, + .extensions = "obu", + .flags = AVFMT_GENERIC_INDEX, + .priv_class = &annexb_demuxer_class, +}; diff --git a/libavformat/avc.c b/libavformat/avc.c index a041e843574..cc452d71a8b 100644 --- a/libavformat/avc.c +++ b/libavformat/avc.c @@ -25,6 +25,7 @@ #include "avformat.h" #include "avio.h" #include "avc.h" +#include "avio_internal.h" static const uint8_t *ff_avc_find_startcode_internal(const uint8_t *p, const uint8_t *end) { @@ -100,18 +101,17 @@ int ff_avc_parse_nal_units_buf(const uint8_t *buf_in, uint8_t **buf, int *size) ff_avc_parse_nal_units(pb, buf_in, *size); - av_freep(buf); *size = avio_close_dyn_buf(pb, buf); return 0; } int ff_isom_write_avcc(AVIOContext *pb, const uint8_t *data, int len) { - AVIOContext *sps_pb = NULL, *pps_pb = NULL; - uint8_t *buf = NULL, *end, *start = NULL; - uint8_t *sps = NULL, *pps = NULL; - uint32_t sps_size = 0, pps_size = 0; - int ret, nb_sps = 0, nb_pps = 0; + AVIOContext *sps_pb = NULL, *pps_pb = NULL, *sps_ext_pb = NULL; + uint8_t *buf, *end, *start; + uint8_t *sps, *pps, *sps_ext; + uint32_t sps_size = 0, pps_size = 0, sps_ext_size = 0; + int ret, nb_sps = 0, nb_pps = 0, nb_sps_ext = 0; if (len <= 6) return AVERROR_INVALIDDATA; @@ -133,6 +133,9 @@ int ff_isom_write_avcc(AVIOContext *pb, const uint8_t *data, int len) if (ret < 0) goto fail; ret = avio_open_dyn_buf(&pps_pb); + if (ret < 0) + goto fail; + ret = avio_open_dyn_buf(&sps_ext_pb); if (ret < 0) goto fail; @@ -160,12 +163,21 @@ int ff_isom_write_avcc(AVIOContext *pb, const uint8_t *data, int len) } avio_wb16(pps_pb, size); avio_write(pps_pb, buf, size); + } else if (nal_type == 13) { /* SPS_EXT */ + nb_sps_ext++; + if (size > UINT16_MAX || nb_sps_ext >= 256) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + avio_wb16(sps_ext_pb, size); + avio_write(sps_ext_pb, buf, size); } buf += size; } - sps_size = avio_close_dyn_buf(sps_pb, &sps); - pps_size = avio_close_dyn_buf(pps_pb, &pps); + sps_size = avio_get_dyn_buf(sps_pb, &sps); + pps_size = avio_get_dyn_buf(pps_pb, &pps); + sps_ext_size = avio_get_dyn_buf(sps_ext_pb, &sps_ext); if (sps_size < 6 || !pps_size) { ret = AVERROR_INVALIDDATA; @@ -183,13 +195,24 @@ int ff_isom_write_avcc(AVIOContext *pb, const uint8_t *data, int len) avio_w8(pb, nb_pps); /* number of pps */ avio_write(pb, pps, pps_size); + if (sps[3] != 66 && sps[3] != 77 && sps[3] != 88) { + H264SPS seq; + ret = ff_avc_decode_sps(&seq, sps + 3, sps_size - 3); + if (ret < 0) + goto fail; + + avio_w8(pb, 0xfc | seq.chroma_format_idc); /* 6 bits reserved (111111) + chroma_format_idc */ + avio_w8(pb, 0xf8 | (seq.bit_depth_luma - 8)); /* 5 bits reserved (11111) + bit_depth_luma_minus8 */ + avio_w8(pb, 0xf8 | (seq.bit_depth_chroma - 8)); /* 5 bits reserved (11111) + bit_depth_chroma_minus8 */ + avio_w8(pb, nb_sps_ext); /* number of sps ext */ + if (nb_sps_ext) + avio_write(pb, sps_ext, sps_ext_size); + } + fail: - if (!sps) - avio_close_dyn_buf(sps_pb, &sps); - if (!pps) - avio_close_dyn_buf(pps_pb, &pps); - av_free(sps); - av_free(pps); + ffio_free_dyn_buf(&sps_pb); + ffio_free_dyn_buf(&pps_pb); + ffio_free_dyn_buf(&sps_ext_pb); av_free(start); return ret; @@ -308,27 +331,24 @@ static inline int get_se_golomb(GetBitContext *gb) { return ((v >> 1) ^ sign) - sign; } -H264SequenceParameterSet *ff_avc_decode_sps(const uint8_t *buf, int buf_size) +int ff_avc_decode_sps(H264SPS *sps, const uint8_t *buf, int buf_size) { int i, j, ret, rbsp_size, aspect_ratio_idc, pic_order_cnt_type; int num_ref_frames_in_pic_order_cnt_cycle; int delta_scale, lastScale = 8, nextScale = 8; int sizeOfScalingList; - H264SequenceParameterSet *sps = NULL; GetBitContext gb; uint8_t *rbsp_buf; rbsp_buf = ff_nal_unit_extract_rbsp(buf, buf_size, &rbsp_size, 0); if (!rbsp_buf) - return NULL; + return AVERROR(ENOMEM); ret = init_get_bits8(&gb, rbsp_buf, rbsp_size); if (ret < 0) goto end; - sps = av_mallocz(sizeof(*sps)); - if (!sps) - goto end; + memset(sps, 0, sizeof(*sps)); sps->profile_idc = get_bits(&gb, 8); sps->constraint_set_flags |= get_bits1(&gb) << 0; // constraint_set0_flag @@ -351,7 +371,7 @@ H264SequenceParameterSet *ff_avc_decode_sps(const uint8_t *buf, int buf_size) skip_bits1(&gb); // separate_colour_plane_flag } sps->bit_depth_luma = get_ue_golomb(&gb) + 8; - get_ue_golomb(&gb); // bit_depth_chroma_minus8 + sps->bit_depth_chroma = get_ue_golomb(&gb) + 8; skip_bits1(&gb); // qpprime_y_zero_transform_bypass_flag if (get_bits1(&gb)) { // seq_scaling_matrix_present_flag for (i = 0; i < ((sps->chroma_format_idc != 3) ? 8 : 12); i++) { @@ -372,6 +392,7 @@ H264SequenceParameterSet *ff_avc_decode_sps(const uint8_t *buf, int buf_size) } else { sps->chroma_format_idc = 1; sps->bit_depth_luma = 8; + sps->bit_depth_chroma = 8; } get_ue_golomb(&gb); // log2_max_frame_num_minus4 @@ -423,7 +444,8 @@ H264SequenceParameterSet *ff_avc_decode_sps(const uint8_t *buf, int buf_size) sps->sar.den = 1; } + ret = 0; end: av_free(rbsp_buf); - return sps; + return ret; } diff --git a/libavformat/avc.h b/libavformat/avc.h index a79bf9b2db5..9792b77913d 100644 --- a/libavformat/avc.h +++ b/libavformat/avc.h @@ -43,10 +43,11 @@ typedef struct { uint8_t constraint_set_flags; uint8_t chroma_format_idc; uint8_t bit_depth_luma; + uint8_t bit_depth_chroma; uint8_t frame_mbs_only_flag; AVRational sar; -} H264SequenceParameterSet; +} H264SPS; -H264SequenceParameterSet *ff_avc_decode_sps(const uint8_t *src, int src_len); +int ff_avc_decode_sps(H264SPS *sps, const uint8_t *buf, int buf_size); #endif /* AVFORMAT_AVC_H */ diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 6eb329f13f5..21c282a1007 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -170,14 +170,9 @@ * information will be in AVStream.time_base units, i.e. it has to be * multiplied by the timebase to convert them to seconds. * - * If AVPacket.buf is set on the returned packet, then the packet is - * allocated dynamically and the user may keep it indefinitely. - * Otherwise, if AVPacket.buf is NULL, the packet data is backed by a - * static storage somewhere inside the demuxer and the packet is only valid - * until the next av_read_frame() call or closing the file. If the caller - * requires a longer lifetime, av_packet_make_refcounted() will ensure that - * the data is reference counted, copying the data if necessary. - * In both cases, the packet must be freed with av_packet_unref() when it is no + * A packet returned by av_read_frame() is always reference-counted, + * i.e. AVPacket.buf is set and the user may keep it indefinitely. + * The packet must be freed with av_packet_unref() when it is no * longer needed. * * @section lavf_decoding_seek Seeking @@ -556,7 +551,8 @@ typedef struct AVOutputFormat { int (*write_packet)(struct AVFormatContext *, AVPacket *pkt); int (*write_trailer)(struct AVFormatContext *); /** - * Currently only used to set pixel format if not YUV420P. + * A format-specific function for interleavement. + * If unset, packets will be interleaved by dts. */ int (*interleave_packet)(struct AVFormatContext *, AVPacket *out, AVPacket *in, int flush); @@ -715,8 +711,7 @@ typedef struct AVInputFormat { * AVFMTCTX_NOHEADER is used and only in the calling thread (not in a * background thread). * @return 0 on success, < 0 on error. - * When returning an error, pkt must not have been allocated - * or must be freed before returning + * Upon returning an error, pkt must be unreferenced by the caller. */ int (*read_packet)(struct AVFormatContext *, AVPacket *pkt); @@ -1125,7 +1120,6 @@ typedef struct AVStream { * -1 -> probing finished * 0 -> no probing requested * rest -> perform probing with request_probe being the minimum score to accept. - * NOT PART OF PUBLIC API */ int request_probe; /** @@ -1171,7 +1165,6 @@ typedef struct AVStream { /** * Timestamp offset added to timestamps before muxing - * NOT PART OF PUBLIC API */ int64_t mux_ts_offset; @@ -1951,6 +1944,13 @@ typedef struct AVFormatContext { * - 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; } AVFormatContext; #if FF_API_FORMAT_GET_SET @@ -2390,13 +2390,12 @@ int av_find_best_stream(AVFormatContext *ic, * omit invalid data between valid frames so as to give the decoder the maximum * information possible for decoding. * - * If pkt->buf is NULL, then the packet is valid until the next - * av_read_frame() or until avformat_close_input(). Otherwise the packet - * is valid indefinitely. In both cases the packet must be freed with - * av_packet_unref when it is no longer needed. For video, the packet contains - * exactly one frame. For audio, it contains an integer number of frames if each - * frame has a known fixed size (e.g. PCM or ADPCM data). If the audio frames - * have a variable size (e.g. MPEG audio), then it contains one frame. + * On success, the returned packet is reference-counted (pkt->buf is set) and + * valid indefinitely. The packet must be freed with av_packet_unref() when + * it is no longer needed. For video, the packet contains exactly one frame. + * For audio, it contains an integer number of frames if each frame has + * a known fixed size (e.g. PCM or ADPCM data). If the audio frames have + * a variable size (e.g. MPEG audio), then it contains one frame. * * pkt->pts, pkt->dts and pkt->duration are always set to correct * values in AVStream.time_base units (and guessed if the format cannot @@ -2404,7 +2403,11 @@ int av_find_best_stream(AVFormatContext *ic, * has B-frames, so it is better to rely on pkt->dts if you do not * decompress the payload. * - * @return 0 if OK, < 0 on error or end of file + * @return 0 if OK, < 0 on error or end of file. On error, pkt will be blank + * (as if it came from av_packet_alloc()). + * + * @note pkt will be initialized, so it may be uninitialized, but it must not + * contain data that needs to be freed. */ int av_read_frame(AVFormatContext *s, AVPacket *pkt); @@ -2449,8 +2452,6 @@ int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, * @return >=0 on success, error code otherwise * * @note This is part of the new seek API which is still under construction. - * Thus do not use this yet. It may change at any time, do not expect - * ABI compatibility yet! */ int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags); @@ -2637,9 +2638,9 @@ int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt); * Write an uncoded frame to an output media file. * * The frame must be correctly interleaved according to the container - * specification; if not, then av_interleaved_write_frame() must be used. + * specification; if not, av_interleaved_write_uncoded_frame() must be used. * - * See av_interleaved_write_frame() for details. + * See av_interleaved_write_uncoded_frame() for details. */ int av_write_uncoded_frame(AVFormatContext *s, int stream_index, AVFrame *frame); diff --git a/libavformat/avidec.c b/libavformat/avidec.c index df78925d554..5fc3e01aa9b 100644 --- a/libavformat/avidec.c +++ b/libavformat/avidec.c @@ -23,7 +23,6 @@ #include "libavutil/avassert.h" #include "libavutil/avstring.h" -#include "libavutil/bswap.h" #include "libavutil/opt.h" #include "libavutil/dict.h" #include "libavutil/internal.h" @@ -61,7 +60,7 @@ typedef struct AVIStream { AVFormatContext *sub_ctx; AVPacket sub_pkt; - uint8_t *sub_buffer; + AVBufferRef *sub_buffer; int64_t seek_pos; } AVIStream; @@ -117,8 +116,8 @@ static const AVMetadataConv avi_metadata_conv[] = { static int avi_load_index(AVFormatContext *s); static int guess_ni_flag(AVFormatContext *s); -#define print_tag(str, tag, size) \ - av_log(NULL, AV_LOG_TRACE, "pos:%"PRIX64" %s: tag=%s size=0x%x\n", \ +#define print_tag(s, str, tag, size) \ + av_log(s, AV_LOG_TRACE, "pos:%"PRIX64" %s: tag=%s size=0x%x\n", \ avio_tell(pb), str, av_fourcc2str(tag), size) \ static inline int get_duration(AVIStream *ast, int len) @@ -306,8 +305,10 @@ static int avi_read_tag(AVFormatContext *s, AVStream *st, uint32_t tag, value = av_malloc(size + 1); if (!value) return AVERROR(ENOMEM); - if (avio_read(pb, value, size) != size) + if (avio_read(pb, value, size) != size) { + av_freep(&value); return AVERROR_INVALIDDATA; + } value[size] = 0; AV_WL32(key, tag); @@ -502,7 +503,7 @@ static int avi_read_header(AVFormatContext *s) tag = avio_rl32(pb); size = avio_rl32(pb); - print_tag("tag", tag, size); + print_tag(s, "tag", tag, size); switch (tag) { case MKTAG('L', 'I', 'S', 'T'): @@ -510,7 +511,7 @@ static int avi_read_header(AVFormatContext *s) /* Ignored, except at start of video packets. */ tag1 = avio_rl32(pb); - print_tag("list", tag1, 0); + print_tag(s, "list", tag1, 0); if (tag1 == MKTAG('m', 'o', 'v', 'i')) { avi->movi_list = avio_tell(pb) - 4; @@ -518,7 +519,7 @@ static int avi_read_header(AVFormatContext *s) avi->movi_end = avi->movi_list + size + (size & 1); else avi->movi_end = avi->fsize; - av_log(NULL, AV_LOG_TRACE, "movi end=%"PRIx64"\n", avi->movi_end); + av_log(s, AV_LOG_TRACE, "movi end=%"PRIx64"\n", avi->movi_end); goto end_of_header; } else if (tag1 == MKTAG('I', 'N', 'F', 'O')) ff_read_riff_info(s, size - 4); @@ -582,7 +583,7 @@ static int avi_read_header(AVFormatContext *s) tag1 = stream_index ? MKTAG('a', 'u', 'd', 's') : MKTAG('v', 'i', 'd', 's'); - print_tag("strh", tag1, -1); + print_tag(s, "strh", tag1, -1); if (tag1 == MKTAG('i', 'a', 'v', 's') || tag1 == MKTAG('i', 'v', 'a', 's')) { @@ -598,28 +599,19 @@ static int avi_read_header(AVFormatContext *s) handler != MKTAG('d', 'v', 's', 'l')) goto fail; + if (!CONFIG_DV_DEMUXER) + return AVERROR_DEMUXER_NOT_FOUND; + ast = s->streams[0]->priv_data; - av_freep(&s->streams[0]->codecpar->extradata); - av_freep(&s->streams[0]->codecpar); -#if FF_API_LAVF_AVCTX -FF_DISABLE_DEPRECATION_WARNINGS - av_freep(&s->streams[0]->codec); -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (s->streams[0]->info) - av_freep(&s->streams[0]->info->duration_error); - av_freep(&s->streams[0]->info); - if (s->streams[0]->internal) - av_freep(&s->streams[0]->internal->avctx); - av_freep(&s->streams[0]->internal); - av_freep(&s->streams[0]); - s->nb_streams = 0; - if (CONFIG_DV_DEMUXER) { - avi->dv_demux = avpriv_dv_init_demux(s); - if (!avi->dv_demux) - goto fail; - } else - goto fail; + st->priv_data = NULL; + ff_free_stream(s, st); + + avi->dv_demux = avpriv_dv_init_demux(s); + if (!avi->dv_demux) { + av_free(ast); + return AVERROR(ENOMEM); + } + s->streams[0]->priv_data = ast; avio_skip(pb, 3 * 4); ast->scale = avio_rl32(pb); @@ -769,10 +761,11 @@ FF_ENABLE_DEPRECATION_WARNINGS st->codecpar->extradata_size = size - 10 * 4; if (st->codecpar->extradata) { av_log(s, AV_LOG_WARNING, "New extradata in strf chunk, freeing previous one.\n"); - av_freep(&st->codecpar->extradata); } - if (ff_get_extradata(s, st->codecpar, pb, st->codecpar->extradata_size) < 0) - return AVERROR(ENOMEM); + ret = ff_get_extradata(s, st->codecpar, pb, + st->codecpar->extradata_size); + if (ret < 0) + return ret; } // FIXME: check if the encoder really did this correctly @@ -800,7 +793,7 @@ FF_ENABLE_DEPRECATION_WARNINGS ast->has_pal = 1; } - print_tag("video", tag1, 0); + print_tag(s, "video", tag1, 0); st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; st->codecpar->codec_tag = tag1; @@ -830,6 +823,9 @@ FF_ENABLE_DEPRECATION_WARNINGS st->need_parsing = AVSTREAM_PARSE_FULL; if (st->codecpar->codec_id == AV_CODEC_ID_RV40) st->need_parsing = AVSTREAM_PARSE_NONE; + if (st->codecpar->codec_id == AV_CODEC_ID_HEVC && + st->codecpar->codec_tag == MKTAG('H', '2', '6', '5')) + st->need_parsing = AVSTREAM_PARSE_FULL; if (st->codecpar->codec_tag == 0 && st->codecpar->height > 0 && st->codecpar->extradata_size < 1U << 30) { @@ -929,10 +925,9 @@ FF_ENABLE_DEPRECATION_WARNINGS if (size<(1<<30)) { if (st->codecpar->extradata) { av_log(s, AV_LOG_WARNING, "New extradata in strd chunk, freeing previous one.\n"); - av_freep(&st->codecpar->extradata); } - if (ff_get_extradata(s, st->codecpar, pb, size) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_get_extradata(s, st->codecpar, pb, size)) < 0) + return ret; } if (st->codecpar->extradata_size & 1) //FIXME check if the encoder really did this correctly @@ -1121,8 +1116,9 @@ static int read_gab2_sub(AVFormatContext *s, AVStream *st, AVPacket *pkt) time_base = ast->sub_ctx->streams[0]->time_base; avpriv_set_pts_info(st, 64, time_base.num, time_base.den); } - ast->sub_buffer = pkt->data; - memset(pkt, 0, sizeof(*pkt)); + ast->sub_buffer = pkt->buf; + pkt->buf = NULL; + av_packet_unref(pkt); return 1; error: @@ -1531,11 +1527,12 @@ static int avi_read_packet(AVFormatContext *s, AVPacket *pkt) if (!avi->non_interleaved && st->nb_index_entries>1 && avi->index_loaded>1) { int64_t dts= av_rescale_q(pkt->dts, st->time_base, AV_TIME_BASE_Q); - if (avi->dts_max - dts > 2*AV_TIME_BASE) { + if (avi->dts_max < dts) { + avi->dts_max = dts; + } else if (avi->dts_max - (uint64_t)dts > 2*AV_TIME_BASE) { avi->non_interleaved= 1; av_log(s, AV_LOG_INFO, "Switching to NI mode, due to poor interleaving\n"); - }else if (avi->dts_max < dts) - avi->dts_max = dts; + } } return 0; @@ -1913,7 +1910,7 @@ static int avi_read_close(AVFormatContext *s) av_freep(&ast->sub_ctx->pb); avformat_close_input(&ast->sub_ctx); } - av_freep(&ast->sub_buffer); + av_buffer_unref(&ast->sub_buffer); av_packet_unref(&ast->sub_pkt); } } diff --git a/libavformat/avienc.c b/libavformat/avienc.c index ac0f04c3547..297d5b8964a 100644 --- a/libavformat/avienc.c +++ b/libavformat/avienc.c @@ -31,7 +31,6 @@ #include "libavutil/avstring.h" #include "libavutil/avutil.h" #include "libavutil/internal.h" -#include "libavutil/intreadwrite.h" #include "libavutil/dict.h" #include "libavutil/avassert.h" #include "libavutil/timestamp.h" @@ -269,8 +268,8 @@ static int avi_write_header(AVFormatContext *s) int padding; if (s->nb_streams > AVI_MAX_STREAM_COUNT) { - av_log(s, AV_LOG_ERROR, "AVI does not support >%d streams\n", - AVI_MAX_STREAM_COUNT); + av_log(s, AV_LOG_ERROR, "AVI does not support " + ">"AV_STRINGIFY(AVI_MAX_STREAM_COUNT)" streams\n"); return AVERROR(EINVAL); } @@ -459,6 +458,14 @@ static int avi_write_header(AVFormatContext *s) && par->format != AV_PIX_FMT_NONE) av_log(s, AV_LOG_ERROR, "%s rawvideo cannot be written to avi, output file will be unreadable\n", av_get_pix_fmt_name(par->format)); + + if (par->format == AV_PIX_FMT_PAL8) { + if (par->bits_per_coded_sample < 0 || par->bits_per_coded_sample > 8) { + av_log(s, AV_LOG_ERROR, "PAL8 with %d bps is not allowed\n", par->bits_per_coded_sample); + return AVERROR(EINVAL); + } + } + break; case AVMEDIA_TYPE_AUDIO: flags = (avi->write_channel_mask == 0) ? FF_PUT_WAV_HEADER_SKIP_CHANNELMASK : 0; @@ -581,8 +588,6 @@ static int avi_write_header(AVFormatContext *s) avi->movi_list = ff_start_tag(pb, "LIST"); ffio_wfourcc(pb, "movi"); - avio_flush(pb); - return 0; } @@ -594,7 +599,6 @@ static void update_odml_entry(AVFormatContext *s, int stream_index, int64_t ix, int64_t pos; int au_byterate, au_ssize, au_scale; - avio_flush(pb); pos = avio_tell(pb); /* Updating one entry in the AVI OpenDML master index */ @@ -909,7 +913,7 @@ static int avi_write_trailer(AVFormatContext *s) AVIContext *avi = s->priv_data; AVIOContext *pb = s->pb; int res = 0; - int i, j, n, nb_frames; + int i, n, nb_frames; int64_t file_size; for (i = 0; i < s->nb_streams; i++) { @@ -962,10 +966,6 @@ static int avi_write_trailer(AVFormatContext *s) for (i = 0; i < s->nb_streams; i++) { AVIStream *avist = s->streams[i]->priv_data; - for (j = 0; j < avist->indexes.ents_allocated / AVI_INDEX_CLUSTER_SIZE; j++) - av_freep(&avist->indexes.cluster[j]); - av_freep(&avist->indexes.cluster); - avist->indexes.ents_allocated = avist->indexes.entry = 0; if (pb->seekable & AVIO_SEEKABLE_NORMAL) { avio_seek(pb, avist->frames_hdr_strm + 4, SEEK_SET); avio_wl32(pb, avist->max_size); @@ -975,6 +975,19 @@ static int avi_write_trailer(AVFormatContext *s) return res; } +static void avi_deinit(AVFormatContext *s) +{ + for (int i = 0; i < s->nb_streams; i++) { + AVIStream *avist = s->streams[i]->priv_data; + if (!avist) + continue; + for (int j = 0; j < avist->indexes.ents_allocated / AVI_INDEX_CLUSTER_SIZE; j++) + av_freep(&avist->indexes.cluster[j]); + av_freep(&avist->indexes.cluster); + avist->indexes.ents_allocated = avist->indexes.entry = 0; + } +} + #define OFFSET(x) offsetof(AVIContext, x) #define ENC AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { @@ -999,6 +1012,7 @@ AVOutputFormat ff_avi_muxer = { .audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_AC3, .video_codec = AV_CODEC_ID_MPEG4, .init = avi_init, + .deinit = avi_deinit, .write_header = avi_write_header, .write_packet = avi_write_packet, .write_trailer = avi_write_trailer, diff --git a/libavformat/avio.c b/libavformat/avio.c index 663789ec024..237966c3030 100644 --- a/libavformat/avio.c +++ b/libavformat/avio.c @@ -26,6 +26,7 @@ #include "libavutil/avassert.h" #include "os_support.h" #include "avformat.h" +#include "internal.h" #if CONFIG_NETWORK #include "network.h" #endif @@ -54,8 +55,8 @@ static void *urlcontext_child_next(void *obj, void *prev) #define E AV_OPT_FLAG_ENCODING_PARAM #define D AV_OPT_FLAG_DECODING_PARAM static const AVOption options[] = { - {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, - {"protocol_blacklist", "List of protocols that are not allowed to be used", OFFSET(protocol_blacklist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, + {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D }, + {"protocol_blacklist", "List of protocols that are not allowed to be used", OFFSET(protocol_blacklist), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D }, {"rw_timeout", "Timeout for IO operations (in microseconds)", offsetof(URLContext, rw_timeout), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_DECODING_PARAM }, { NULL } }; @@ -283,6 +284,9 @@ static const struct URLProtocol *url_find_protocol(const char *filename) } } av_freep(&protocols); + if (av_strstart(filename, "https:", NULL) || av_strstart(filename, "tls:", NULL)) + av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile FFmpeg with " + "openssl, gnutls or securetransport enabled.\n"); return NULL; } @@ -297,10 +301,6 @@ int ffurl_alloc(URLContext **puc, const char *filename, int flags, return url_alloc_for_protocol(puc, p, filename, flags, int_cb); *puc = NULL; - if (av_strstart(filename, "https:", NULL) || av_strstart(filename, "tls:", NULL)) - av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile FFmpeg with " - "openssl, gnutls " - "or securetransport enabled.\n"); return AVERROR_PROTOCOL_NOT_FOUND; } @@ -347,8 +347,7 @@ int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, if (!ret) return 0; fail: - ffurl_close(*puc); - *puc = NULL; + ffurl_closep(puc); return ret; } @@ -667,3 +666,11 @@ int ff_check_interrupt(AVIOInterruptCB *cb) return cb->callback(cb->opaque); return 0; } + +int ff_rename(const char *url_src, const char *url_dst, void *logctx) +{ + int ret = avpriv_io_move(url_src, url_dst); + if (ret < 0) + av_log(logctx, AV_LOG_ERROR, "failed to rename file %s to %s: %s\n", url_src, url_dst, av_err2str(ret)); + return ret; +} diff --git a/libavformat/avio.h b/libavformat/avio.h index dcb8dcdf93a..d022820a6ed 100644 --- a/libavformat/avio.h +++ b/libavformat/avio.h @@ -571,9 +571,29 @@ int64_t avio_size(AVIOContext *s); */ int avio_feof(AVIOContext *s); -/** @warning Writes up to 4 KiB per call */ +/** + * Writes a formatted string to the context. + * @return number of bytes written, < 0 on error. + */ int avio_printf(AVIOContext *s, const char *fmt, ...) av_printf_format(2, 3); +/** + * Write a NULL terminated array of strings to the context. + * 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[]); + +/** + * Write strings (const char *) to the context. + * This is a convenience macro around avio_print_string_array and it + * automatically creates the string array from the variable argument list. + * For simple string concatenations this function is more performant than using + * avio_printf since it does not need a temporary buffer. + */ +#define avio_print(s, ...) \ + avio_print_string_array(s, (const char*[]){__VA_ARGS__, NULL}) + /** * Force flushing of buffered data. * @@ -787,6 +807,13 @@ int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer); */ const char *avio_enum_protocols(void **opaque, int output); +/** + * Get AVClass by names of available protocols. + * + * @return A AVClass of input protocol name or NULL + */ +const AVClass *avio_protocol_get_class(const char *name); + /** * Pause and resume playing - only meaningful if using a network streaming * protocol (e.g. MMS). diff --git a/libavformat/avio_internal.h b/libavformat/avio_internal.h index 04c1ad5157b..c575df80352 100644 --- a/libavformat/avio_internal.h +++ b/libavformat/avio_internal.h @@ -86,6 +86,15 @@ int ffio_read_size(AVIOContext *s, unsigned char *buf, int size); /** @warning must be called before any I/O */ int ffio_set_buf_size(AVIOContext *s, int buf_size); +/** + * Reallocate a given buffer for AVIOContext. + * + * @param s the AVIOContext to realloc. + * @param buf_size required new buffer size. + * @return 0 on success, a negative AVERROR on failure. + */ +int ffio_realloc_buf(AVIOContext *s, int buf_size); + /** * Ensures that the requested seekback buffer size will be available * @@ -162,6 +171,13 @@ int ffio_open_whitelist(AVIOContext **s, const char *url, int flags, */ int ffio_close_null_buf(AVIOContext *s); +/** + * Reset a dynamic buffer. + * + * Resets everything, but keeps the allocated buffer for later use. + */ +void ffio_reset_dyn_buf(AVIOContext *s); + /** * Free a dynamic buffer. * diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 2d011027c9f..a48ceebaefe 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -42,15 +42,10 @@ */ #define SHORT_SEEK_THRESHOLD 4096 -typedef struct AVIOInternal { - URLContext *h; -} AVIOInternal; - static void *ff_avio_child_next(void *obj, void *prev) { AVIOContext *s = obj; - AVIOInternal *internal = s->opaque; - return prev ? NULL : internal->h; + return prev ? NULL : s->opaque; } static const AVClass *ff_avio_child_class_next(const AVClass *prev) @@ -62,7 +57,7 @@ static const AVClass *ff_avio_child_class_next(const AVClass *prev) #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 }, CHAR_MIN, CHAR_MAX, D }, + {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D }, { NULL }, }; @@ -255,6 +250,9 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence) if(!s) return AVERROR(EINVAL); + if ((whence & AVSEEK_SIZE)) + return s->seek ? s->seek(s->opaque, offset, AVSEEK_SIZE) : AVERROR(ENOSYS); + buffer_size = s->buf_end - s->buffer; // pos is the absolute position that the beginning of s->buffer corresponds to in the file pos = s->pos - (s->write_flag ? 0 : buffer_size); @@ -434,26 +432,6 @@ PUT_STR16(be, 1) #undef PUT_STR16 -int ff_get_v_length(uint64_t val) -{ - int i = 1; - - while (val >>= 7) - i++; - - return i; -} - -void ff_put_v(AVIOContext *bc, uint64_t val) -{ - int i = ff_get_v_length(val); - - while (--i > 0) - avio_w8(bc, 128 | (uint8_t)(val >> (7*i))); - - avio_w8(bc, val & 127); -} - void avio_wl64(AVIOContext *s, uint64_t val) { avio_wl32(s, (uint32_t)(val & 0xffffffff)); @@ -937,49 +915,8 @@ uint64_t ffio_read_varlen(AVIOContext *bc){ return val; } -static int io_read_packet(void *opaque, uint8_t *buf, int buf_size) -{ - AVIOInternal *internal = opaque; - return ffurl_read(internal->h, buf, buf_size); -} - -static int io_write_packet(void *opaque, uint8_t *buf, int buf_size) -{ - AVIOInternal *internal = opaque; - return ffurl_write(internal->h, buf, buf_size); -} - -static int64_t io_seek(void *opaque, int64_t offset, int whence) -{ - AVIOInternal *internal = opaque; - return ffurl_seek(internal->h, offset, whence); -} - -static int io_short_seek(void *opaque) -{ - AVIOInternal *internal = opaque; - return ffurl_get_short_seek(internal->h); -} - -static int io_read_pause(void *opaque, int pause) -{ - AVIOInternal *internal = opaque; - if (!internal->h->prot->url_read_pause) - return AVERROR(ENOSYS); - return internal->h->prot->url_read_pause(internal->h, pause); -} - -static int64_t io_read_seek(void *opaque, int stream_index, int64_t timestamp, int flags) -{ - AVIOInternal *internal = opaque; - if (!internal->h->prot->url_read_seek) - return AVERROR(ENOSYS); - return internal->h->prot->url_read_seek(internal->h, stream_index, timestamp, flags); -} - int ffio_fdopen(AVIOContext **s, URLContext *h) { - AVIOInternal *internal = NULL; uint8_t *buffer = NULL; int buffer_size, max_packet_size; @@ -993,14 +930,10 @@ int ffio_fdopen(AVIOContext **s, URLContext *h) if (!buffer) return AVERROR(ENOMEM); - internal = av_mallocz(sizeof(*internal)); - if (!internal) - goto fail; - - internal->h = h; - - *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, - internal, io_read_packet, io_write_packet, io_seek); + *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) goto fail; @@ -1020,30 +953,28 @@ int ffio_fdopen(AVIOContext **s, URLContext *h) (*s)->max_packet_size = max_packet_size; (*s)->min_packet_size = h->min_packet_size; if(h->prot) { - (*s)->read_pause = io_read_pause; - (*s)->read_seek = io_read_seek; + (*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; } - (*s)->short_seek_get = io_short_seek; + (*s)->short_seek_get = (int (*)(void *))ffurl_get_short_seek; (*s)->av_class = &ff_avio_class; return 0; fail: - av_freep(&internal); av_freep(&buffer); return AVERROR(ENOMEM); } URLContext* ffio_geturlcontext(AVIOContext *s) { - AVIOInternal *internal; if (!s) return NULL; - internal = s->opaque; - if (internal && s->read_packet == io_read_packet) - return internal->h; + if (s->opaque && s->read_packet == (int (*)(void *, uint8_t *, int))ffurl_read) + return s->opaque; else return NULL; } @@ -1093,6 +1024,37 @@ int ffio_set_buf_size(AVIOContext *s, int buf_size) return 0; } +int ffio_realloc_buf(AVIOContext *s, int buf_size) +{ + uint8_t *buffer; + int data_size; + + if (!s->buffer_size) + return ffio_set_buf_size(s, buf_size); + + if (buf_size <= s->buffer_size) + return 0; + + buffer = av_malloc(buf_size); + if (!buffer) + return AVERROR(ENOMEM); + + data_size = s->write_flag ? (s->buf_ptr - s->buffer) : (s->buf_end - s->buf_ptr); + if (data_size > 0) + memcpy(buffer, s->write_flag ? s->buffer : s->buf_ptr, data_size); + av_free(s->buffer); + s->buffer = buffer; + s->orig_buffer_size = buf_size; + s->buffer_size = buf_size; + s->buf_ptr = s->write_flag ? (s->buffer + data_size) : s->buffer; + if (s->write_flag) + s->buf_ptr_max = s->buffer + data_size; + + s->buf_end = s->write_flag ? (s->buffer + s->buffer_size) : (s->buf_ptr + data_size); + + return 0; +} + static int url_resetbuf(AVIOContext *s, int flags) { av_assert1(flags == AVIO_FLAG_WRITE || flags == AVIO_FLAG_READ); @@ -1163,6 +1125,8 @@ int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags, URLContext *h; int err; + *s = NULL; + err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL); if (err < 0) return err; @@ -1182,17 +1146,15 @@ int avio_open2(AVIOContext **s, const char *filename, int flags, int avio_close(AVIOContext *s) { - AVIOInternal *internal; URLContext *h; if (!s) return 0; avio_flush(s); - internal = s->opaque; - h = internal->h; + h = s->opaque; + s->opaque = NULL; - av_freep(&s->opaque); av_freep(&s->buffer); if (s->write_flag) av_log(s, AV_LOG_VERBOSE, "Statistics: %d seeks, %d writeouts\n", s->seek_count, s->writeout_count); @@ -1215,14 +1177,26 @@ int avio_closep(AVIOContext **s) int avio_printf(AVIOContext *s, const char *fmt, ...) { va_list ap; - char buf[4096]; /* update doc entry in avio.h if changed */ - int ret; + AVBPrint bp; + av_bprint_init(&bp, 0, INT_MAX); va_start(ap, fmt); - ret = vsnprintf(buf, sizeof(buf), fmt, ap); + av_vbprintf(&bp, fmt, ap); va_end(ap); - avio_write(s, buf, strlen(buf)); - return ret; + if (!av_bprint_is_complete(&bp)) { + av_bprint_finalize(&bp, NULL); + s->error = AVERROR(ENOMEM); + return AVERROR(ENOMEM); + } + avio_write(s, bp.str, bp.len); + av_bprint_finalize(&bp, NULL); + return bp.len; +} + +void avio_print_string_array(AVIOContext *s, const char *strings[]) +{ + for(; *strings; strings++) + avio_write(s, (const unsigned char *)*strings, strlen(*strings)); } int avio_pause(AVIOContext *s, int pause) @@ -1272,8 +1246,7 @@ int avio_read_to_bprint(AVIOContext *h, AVBPrint *pb, size_t max_size) int avio_accept(AVIOContext *s, AVIOContext **c) { int ret; - AVIOInternal *internal = s->opaque; - URLContext *sc = internal->h; + URLContext *sc = s->opaque; URLContext *cc = NULL; ret = ffurl_accept(sc, &cc); if (ret < 0) @@ -1283,8 +1256,7 @@ int avio_accept(AVIOContext *s, AVIOContext **c) int avio_handshake(AVIOContext *c) { - AVIOInternal *internal = c->opaque; - URLContext *cc = internal->h; + URLContext *cc = c->opaque; return ffurl_handshake(cc); } @@ -1303,7 +1275,7 @@ static int dyn_buf_write(void *opaque, uint8_t *buf, int buf_size) unsigned new_size, new_allocated_size; /* reallocate buffer if needed */ - new_size = d->pos + buf_size; + new_size = (unsigned)d->pos + buf_size; new_allocated_size = d->allocated_size; if (new_size < d->pos || new_size > INT_MAX/2) return -1; @@ -1401,15 +1373,31 @@ int avio_get_dyn_buf(AVIOContext *s, uint8_t **pbuffer) *pbuffer = NULL; return 0; } + d = s->opaque; + + if (!s->error && !d->size) { + *pbuffer = d->io_buffer; + return FFMAX(s->buf_ptr, s->buf_ptr_max) - s->buffer; + } avio_flush(s); - d = s->opaque; *pbuffer = d->buffer; return d->size; } +void ffio_reset_dyn_buf(AVIOContext *s) +{ + DynBuffer *d = s->opaque; + int max_packet_size = s->max_packet_size; + + ffio_init_context(s, d->io_buffer, d->io_buffer_size, 1, d, NULL, + s->write_packet, s->seek); + s->max_packet_size = max_packet_size; + d->pos = d->size = 0; +} + int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer) { DynBuffer *d; @@ -1442,12 +1430,15 @@ int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer) void ffio_free_dyn_buf(AVIOContext **s) { - uint8_t *tmp; + DynBuffer *d; + if (!*s) return; - avio_close_dyn_buf(*s, &tmp); - av_free(tmp); - *s = NULL; + + d = (*s)->opaque; + av_free(d->buffer); + av_free(d); + avio_context_free(s); } static int null_buf_write(void *opaque, uint8_t *buf, int buf_size) diff --git a/libavformat/avisynth.c b/libavformat/avisynth.c index 5dfe94ae0c1..2c08ace8dbb 100644 --- a/libavformat/avisynth.c +++ b/libavformat/avisynth.c @@ -1,5 +1,5 @@ /* - * AviSynth/AvxSynth support + * AviSynth(+) support * Copyright (c) 2012 AvxSynth Team * * This file is part of FFmpeg @@ -31,20 +31,19 @@ /* Enable function pointer definitions for runtime loading. */ #define AVSC_NO_DECLSPEC -/* Platform-specific directives for AviSynth vs AvxSynth. */ +/* Platform-specific directives. */ #ifdef _WIN32 #include "compat/w32dlfcn.h" #undef EXTERN_C - #include "compat/avisynth/avisynth_c.h" #define AVISYNTH_LIB "avisynth" - #define USING_AVISYNTH #else #include - #include "compat/avisynth/avxsynth_c.h" - #define AVISYNTH_NAME "libavxsynth" + #define AVISYNTH_NAME "libavisynth" #define AVISYNTH_LIB AVISYNTH_NAME SLIBSUF #endif +#include + typedef struct AviSynthLibrary { void *library; #define AVSC_DECLARE_FUNC(name) name ## _func name @@ -62,7 +61,6 @@ typedef struct AviSynthLibrary { AVSC_DECLARE_FUNC(avs_release_value); AVSC_DECLARE_FUNC(avs_release_video_frame); AVSC_DECLARE_FUNC(avs_take_clip); -#ifdef USING_AVISYNTH AVSC_DECLARE_FUNC(avs_bits_per_pixel); AVSC_DECLARE_FUNC(avs_get_height_p); AVSC_DECLARE_FUNC(avs_get_pitch_p); @@ -70,7 +68,6 @@ typedef struct AviSynthLibrary { AVSC_DECLARE_FUNC(avs_get_row_size_p); AVSC_DECLARE_FUNC(avs_is_planar_rgb); AVSC_DECLARE_FUNC(avs_is_planar_rgba); -#endif #undef AVSC_DECLARE_FUNC } AviSynthLibrary; @@ -97,14 +94,12 @@ static const int avs_planes_packed[1] = { 0 }; static const int avs_planes_grey[1] = { AVS_PLANAR_Y }; static const int avs_planes_yuv[3] = { AVS_PLANAR_Y, AVS_PLANAR_U, AVS_PLANAR_V }; -#ifdef USING_AVISYNTH static const int avs_planes_rgb[3] = { AVS_PLANAR_G, AVS_PLANAR_B, AVS_PLANAR_R }; static const int avs_planes_yuva[4] = { AVS_PLANAR_Y, AVS_PLANAR_U, AVS_PLANAR_V, AVS_PLANAR_A }; static const int avs_planes_rgba[4] = { AVS_PLANAR_G, AVS_PLANAR_B, AVS_PLANAR_R, AVS_PLANAR_A }; -#endif /* A conflict between C++ global objects, atexit, and dynamic loading requires * us to register our own atexit handler to prevent double freeing. */ @@ -142,7 +137,6 @@ static av_cold int avisynth_load_library(void) LOAD_AVS_FUNC(avs_release_value, 0); LOAD_AVS_FUNC(avs_release_video_frame, 0); LOAD_AVS_FUNC(avs_take_clip, 0); -#ifdef USING_AVISYNTH LOAD_AVS_FUNC(avs_bits_per_pixel, 1); LOAD_AVS_FUNC(avs_get_height_p, 1); LOAD_AVS_FUNC(avs_get_pitch_p, 1); @@ -150,7 +144,6 @@ static av_cold int avisynth_load_library(void) LOAD_AVS_FUNC(avs_get_row_size_p, 1); LOAD_AVS_FUNC(avs_is_planar_rgb, 1); LOAD_AVS_FUNC(avs_is_planar_rgba, 1); -#endif #undef LOAD_AVS_FUNC atexit(avisynth_atexit_handler); @@ -249,7 +242,6 @@ static int avisynth_create_stream_video(AVFormatContext *s, AVStream *st) avpriv_set_pts_info(st, 32, avs->vi->fps_denominator, avs->vi->fps_numerator); switch (avs->vi->pixel_type) { -#ifdef USING_AVISYNTH /* 10~16-bit YUV pix_fmts (AviSynth+) */ case AVS_CS_YUV444P10: st->codecpar->format = AV_PIX_FMT_YUV444P10; @@ -434,8 +426,7 @@ static int avisynth_create_stream_video(AVFormatContext *s, AVStream *st) case AVS_CS_BGR64: st->codecpar->format = AV_PIX_FMT_BGRA64; break; -#endif - /* AviSynth 2.5 and AvxSynth pix_fmts */ + /* AviSynth 2.5 pix_fmts */ case AVS_CS_BGR24: st->codecpar->format = AV_PIX_FMT_BGR24; break; @@ -461,7 +452,6 @@ static int avisynth_create_stream_video(AVFormatContext *s, AVStream *st) } switch (planar) { -#ifdef USING_AVISYNTH case 5: // Planar RGB + Alpha avs->n_planes = 4; avs->planes = avs_planes_rgba; @@ -474,7 +464,6 @@ static int avisynth_create_stream_video(AVFormatContext *s, AVStream *st) avs->n_planes = 3; avs->planes = avs_planes_rgb; break; -#endif case 2: // Y8 avs->n_planes = 1; avs->planes = avs_planes_grey; @@ -556,7 +545,7 @@ static int avisynth_open_file(AVFormatContext *s) AviSynthContext *avs = s->priv_data; AVS_Value arg, val; int ret; -#ifdef USING_AVISYNTH +#ifdef _WIN32 char filename_ansi[MAX_PATH * 4]; wchar_t filename_wc[MAX_PATH * 4]; #endif @@ -564,14 +553,14 @@ static int avisynth_open_file(AVFormatContext *s) if (ret = avisynth_context_create(s)) return ret; -#ifdef USING_AVISYNTH +#ifdef _WIN32 /* Convert UTF-8 to ANSI code page */ - MultiByteToWideChar(CP_UTF8, 0, s->filename, -1, filename_wc, MAX_PATH * 4); + MultiByteToWideChar(CP_UTF8, 0, s->url, -1, filename_wc, MAX_PATH * 4); WideCharToMultiByte(CP_THREAD_ACP, 0, filename_wc, -1, filename_ansi, MAX_PATH * 4, NULL, NULL); arg = avs_new_value_string(filename_ansi); #else - arg = avs_new_value_string(s->filename); + arg = avs_new_value_string(s->url); #endif val = avs_library.avs_invoke(avs->env, "Import", arg, 0); if (avs_is_error(val)) { @@ -588,11 +577,9 @@ static int avisynth_open_file(AVFormatContext *s) avs->clip = avs_library.avs_take_clip(val, avs->env); avs->vi = avs_library.avs_get_video_info(avs->clip); -#ifdef USING_AVISYNTH /* On Windows, FFmpeg supports AviSynth interface version 6 or higher. * This includes AviSynth 2.6 RC1 or higher, and AviSynth+ r1718 or higher, - * and excludes 2.5 and the 2.6 alphas. Since AvxSynth identifies itself - * as interface version 3 like 2.5.8, this needs to be special-cased. */ + * and excludes 2.5 and the 2.6 alphas. */ if (avs_library.avs_get_version(avs->clip) < 6) { av_log(s, AV_LOG_ERROR, @@ -600,7 +587,6 @@ static int avisynth_open_file(AVFormatContext *s) ret = AVERROR_UNKNOWN; goto fail; } -#endif /* Release the AVS_Value as it will go out of scope. */ avs_library.avs_release_value(val); @@ -640,7 +626,7 @@ static int avisynth_read_packet_video(AVFormatContext *s, AVPacket *pkt, AVS_VideoFrame *frame; unsigned char *dst_p; const unsigned char *src_p; - int n, i, plane, rowsize, planeheight, pitch, bits; + int n, i, plane, rowsize, planeheight, pitch, bits, ret; const char *error; int avsplus av_unused; @@ -652,23 +638,21 @@ static int avisynth_read_packet_video(AVFormatContext *s, AVPacket *pkt, if (discard) return 0; -#ifdef USING_AVISYNTH +#ifdef _WIN32 /* Detect whether we're using AviSynth 2.6 or AviSynth+ by * looking for whether avs_is_planar_rgb exists. */ if (GetProcAddress(avs_library.library, "avs_is_planar_rgb") == NULL) avsplus = 0; else avsplus = 1; - - /* avs_bits_per_pixel changed to AVSC_API with AviSynth 2.6, which - * requires going through avs_library, while AvxSynth has it under - * the older AVSC_INLINE type, so special-case this. */ - - bits = avs_library.avs_bits_per_pixel(avs->vi); #else - bits = avs_bits_per_pixel(avs->vi); + /* AviSynth+ is now the only variant of AviSynth we support + * on Linux and macOS. */ + avsplus = 1; #endif + bits = avs_library.avs_bits_per_pixel(avs->vi); + /* Without the cast to int64_t, calculation overflows at about 9k x 9k * resolution. */ pkt->size = (((int64_t)avs->vi->width * @@ -676,8 +660,8 @@ static int avisynth_read_packet_video(AVFormatContext *s, AVPacket *pkt, if (!pkt->size) return AVERROR_UNKNOWN; - if (av_new_packet(pkt, pkt->size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, pkt->size)) < 0) + return ret; pkt->pts = n; pkt->dts = n; @@ -696,19 +680,11 @@ static int avisynth_read_packet_video(AVFormatContext *s, AVPacket *pkt, dst_p = pkt->data; for (i = 0; i < avs->n_planes; i++) { plane = avs->planes[i]; -#ifdef USING_AVISYNTH src_p = avs_library.avs_get_read_ptr_p(frame, plane); pitch = avs_library.avs_get_pitch_p(frame, plane); rowsize = avs_library.avs_get_row_size_p(frame, plane); planeheight = avs_library.avs_get_height_p(frame, plane); -#else - src_p = avs_get_read_ptr_p(frame, plane); - pitch = avs_get_pitch_p(frame, plane); - - rowsize = avs_get_row_size_p(frame, plane); - planeheight = avs_get_height_p(frame, plane); -#endif /* Flip RGB video. */ if (avs_is_rgb24(avs->vi) || avs_is_rgb(avs->vi)) { @@ -716,14 +692,12 @@ static int avisynth_read_packet_video(AVFormatContext *s, AVPacket *pkt, pitch = -pitch; } -#ifdef USING_AVISYNTH /* Flip Planar RGB video */ if (avsplus && (avs_library.avs_is_planar_rgb(avs->vi) || avs_library.avs_is_planar_rgba(avs->vi))) { src_p = src_p + (planeheight - 1) * pitch; pitch = -pitch; } -#endif avs_library.avs_bit_blt(avs->env, dst_p, rowsize, src_p, pitch, rowsize, planeheight); @@ -739,7 +713,7 @@ static int avisynth_read_packet_audio(AVFormatContext *s, AVPacket *pkt, { AviSynthContext *avs = s->priv_data; AVRational fps, samplerate; - int samples; + int samples, ret; int64_t n; const char *error; @@ -782,8 +756,8 @@ static int avisynth_read_packet_audio(AVFormatContext *s, AVPacket *pkt, if (!pkt->size) return AVERROR_UNKNOWN; - if (av_new_packet(pkt, pkt->size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, pkt->size)) < 0) + return ret; pkt->pts = n; pkt->dts = n; diff --git a/libavformat/avs.c b/libavformat/avs.c index 47fa41017dc..54b2c3f2a97 100644 --- a/libavformat/avs.c +++ b/libavformat/avs.c @@ -114,7 +114,6 @@ avs_read_video_packet(AVFormatContext * s, AVPacket * pkt, pkt->data[palette_size + 3] = (size >> 8) & 0xFF; ret = avio_read(s->pb, pkt->data + palette_size + 4, size - 4) + 4; if (ret < size) { - av_packet_unref(pkt); return AVERROR(EIO); } @@ -224,11 +223,6 @@ static int avs_read_packet(AVFormatContext * s, AVPacket * pkt) } } -static int avs_read_close(AVFormatContext * s) -{ - return 0; -} - AVInputFormat ff_avs_demuxer = { .name = "avs", .long_name = NULL_IF_CONFIG_SMALL("Argonaut Games Creature Shock"), @@ -236,5 +230,4 @@ AVInputFormat ff_avs_demuxer = { .read_probe = avs_probe, .read_header = avs_read_header, .read_packet = avs_read_packet, - .read_close = avs_read_close, }; diff --git a/libavformat/bethsoftvid.c b/libavformat/bethsoftvid.c index 4aefb04f14e..47a9a693303 100644 --- a/libavformat/bethsoftvid.c +++ b/libavformat/bethsoftvid.c @@ -49,7 +49,8 @@ typedef struct BVID_DemuxContext int bethsoft_global_delay; int video_index; /**< video stream index */ int audio_index; /**< audio stream index */ - uint8_t *palette; + int has_palette; + uint8_t palette[BVID_PALETTE_SIZE]; int is_finished; @@ -146,9 +147,13 @@ static int read_frame(BVID_DemuxContext *vid, AVIOContext *pb, AVPacket *pkt, } do{ - vidbuf_start = av_fast_realloc(vidbuf_start, &vidbuf_capacity, vidbuf_nbytes + BUFFER_PADDING_SIZE); - if(!vidbuf_start) - return AVERROR(ENOMEM); + uint8_t *tmp = av_fast_realloc(vidbuf_start, &vidbuf_capacity, + vidbuf_nbytes + BUFFER_PADDING_SIZE); + if (!tmp) { + ret = AVERROR(ENOMEM); + goto fail; + } + vidbuf_start = tmp; code = avio_r8(pb); vidbuf_start[vidbuf_nbytes++] = code; @@ -188,7 +193,7 @@ static int read_frame(BVID_DemuxContext *vid, AVIOContext *pb, AVPacket *pkt, pkt->flags |= AV_PKT_FLAG_KEY; /* if there is a new palette available, add it to packet side data */ - if (vid->palette) { + if (vid->has_palette) { uint8_t *pdata = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, BVID_PALETTE_SIZE); if (!pdata) { @@ -197,8 +202,7 @@ static int read_frame(BVID_DemuxContext *vid, AVIOContext *pb, AVPacket *pkt, goto fail; } memcpy(pdata, vid->palette, BVID_PALETTE_SIZE); - - av_freep(&vid->palette); + vid->has_palette = 0; } vid->nframes--; // used to check if all the frames were read @@ -222,17 +226,14 @@ static int vid_read_packet(AVFormatContext *s, block_type = avio_r8(pb); switch(block_type){ case PALETTE_BLOCK: - if (vid->palette) { + if (vid->has_palette) { av_log(s, AV_LOG_WARNING, "discarding unused palette\n"); - av_freep(&vid->palette); + vid->has_palette = 0; } - vid->palette = av_malloc(BVID_PALETTE_SIZE); - if (!vid->palette) - return AVERROR(ENOMEM); if (avio_read(pb, vid->palette, BVID_PALETTE_SIZE) != BVID_PALETTE_SIZE) { - av_freep(&vid->palette); return AVERROR(EIO); } + vid->has_palette = 1; return vid_read_packet(s, pkt); case FIRST_AUDIO_BLOCK: @@ -284,13 +285,6 @@ static int vid_read_packet(AVFormatContext *s, } } -static int vid_read_close(AVFormatContext *s) -{ - BVID_DemuxContext *vid = s->priv_data; - av_freep(&vid->palette); - return 0; -} - AVInputFormat ff_bethsoftvid_demuxer = { .name = "bethsoftvid", .long_name = NULL_IF_CONFIG_SMALL("Bethesda Softworks VID"), @@ -298,5 +292,4 @@ AVInputFormat ff_bethsoftvid_demuxer = { .read_probe = vid_probe, .read_header = vid_read_header, .read_packet = vid_read_packet, - .read_close = vid_read_close, }; diff --git a/libavformat/bink.c b/libavformat/bink.c index 631b8c4d7df..08125ba8f14 100644 --- a/libavformat/bink.c +++ b/libavformat/bink.c @@ -56,6 +56,7 @@ typedef struct BinkDemuxContext { int64_t audio_pts[BINK_MAX_AUDIO_TRACKS]; uint32_t remain_packet_size; + int flags; int smush_size; } BinkDemuxContext; @@ -90,6 +91,7 @@ static int read_header(AVFormatContext *s) unsigned int i; uint32_t pos, next_pos; uint16_t flags; + int next_keyframe = 1; int keyframe; int ret; uint32_t signature; @@ -150,8 +152,8 @@ static int read_header(AVFormatContext *s) vst->codecpar->codec_id = AV_CODEC_ID_NONE; } - if (ff_get_extradata(s, vst->codecpar, pb, 4) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_get_extradata(s, vst->codecpar, pb, 4)) < 0) + return ret; bink->num_audio_tracks = avio_rl32(pb); @@ -190,8 +192,8 @@ static int read_header(AVFormatContext *s) ast->codecpar->channels = 1; ast->codecpar->channel_layout = AV_CH_LAYOUT_MONO; } - if (ff_alloc_extradata(ast->codecpar, 4)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(ast->codecpar, 4)) < 0) + return ret; AV_WL32(ast->codecpar->extradata, vst->codecpar->codec_tag); } @@ -203,12 +205,13 @@ static int read_header(AVFormatContext *s) next_pos = avio_rl32(pb); for (i = 0; i < vst->duration; i++) { pos = next_pos; + keyframe = next_keyframe; if (i == vst->duration - 1) { next_pos = bink->file_size; - keyframe = 0; + next_keyframe = 0; } else { next_pos = avio_rl32(pb); - keyframe = pos & 1; + next_keyframe = next_pos & 1; } pos &= ~1; next_pos &= ~1; @@ -254,6 +257,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) } bink->remain_packet_size = st->index_entries[index_entry].size; + bink->flags = st->index_entries[index_entry].flags; bink->current_track = 0; } @@ -290,7 +294,8 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) return ret; pkt->stream_index = 0; pkt->pts = bink->video_pts++; - pkt->flags |= AV_PKT_FLAG_KEY; + if (bink->flags & AVINDEX_KEYFRAME) + pkt->flags |= AV_PKT_FLAG_KEY; /* -1 instructs the next call to read_packet() to read the next frame */ bink->current_track = -1; @@ -302,13 +307,15 @@ static int read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, in { BinkDemuxContext *bink = s->priv_data; AVStream *vst = s->streams[0]; + int64_t ret; if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) return -1; /* seek to the first frame */ - if (avio_seek(s->pb, vst->index_entries[0].pos + bink->smush_size, SEEK_SET) < 0) - return -1; + ret = avio_seek(s->pb, vst->index_entries[0].pos + bink->smush_size, SEEK_SET); + if (ret < 0) + return ret; bink->video_pts = 0; memset(bink->audio_pts, 0, sizeof(bink->audio_pts)); diff --git a/libavformat/bintext.c b/libavformat/bintext.c index d4921749a07..7dab5f377d5 100644 --- a/libavformat/bintext.c +++ b/libavformat/bintext.c @@ -149,7 +149,7 @@ static int bin_probe(const AVProbeData *p) return AVPROBE_SCORE_EXTENSION + 1; predict_width(&par, p->buf_size, got_width); - if (par.width <= 0) + if (par.width < 8) return 0; calculate_height(&par, p->buf_size); if (par.height <= 0) @@ -177,14 +177,14 @@ static int bintext_read_header(AVFormatContext *s) { BinDemuxContext *bin = s->priv_data; AVIOContext *pb = s->pb; - + int ret; AVStream *st = init_stream(s); if (!st) return AVERROR(ENOMEM); st->codecpar->codec_id = AV_CODEC_ID_BINTEXT; - if (ff_alloc_extradata(st->codecpar, 2)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, 2)) < 0) + return ret; st->codecpar->extradata[0] = 16; st->codecpar->extradata[1] = 0; @@ -195,6 +195,8 @@ static int bintext_read_header(AVFormatContext *s) next_tag_read(s, &bin->fsize); if (!bin->width) { predict_width(st->codecpar, bin->fsize, got_width); + if (st->codecpar->width < 8) + return AVERROR_INVALIDDATA; calculate_height(st->codecpar, bin->fsize); } avio_seek(pb, 0, SEEK_SET); @@ -220,7 +222,7 @@ static int xbin_read_header(AVFormatContext *s) BinDemuxContext *bin = s->priv_data; AVIOContext *pb = s->pb; char fontheight, flags; - + int ret; AVStream *st = init_stream(s); if (!st) return AVERROR(ENOMEM); @@ -239,8 +241,9 @@ static int xbin_read_header(AVFormatContext *s) st->codecpar->extradata_size += fontheight * (flags & 0x10 ? 512 : 256); st->codecpar->codec_id = flags & 4 ? AV_CODEC_ID_XBIN : AV_CODEC_ID_BINTEXT; - if (ff_alloc_extradata(st->codecpar, st->codecpar->extradata_size)) - return AVERROR(ENOMEM); + ret = ff_alloc_extradata(st->codecpar, st->codecpar->extradata_size); + if (ret < 0) + return ret; st->codecpar->extradata[0] = fontheight; st->codecpar->extradata[1] = flags; if (avio_read(pb, st->codecpar->extradata + 2, st->codecpar->extradata_size - 2) < 0) @@ -262,6 +265,7 @@ static int adf_read_header(AVFormatContext *s) BinDemuxContext *bin = s->priv_data; AVIOContext *pb = s->pb; AVStream *st; + int ret; if (avio_r8(pb) != 1) return AVERROR_INVALIDDATA; @@ -271,8 +275,8 @@ static int adf_read_header(AVFormatContext *s) return AVERROR(ENOMEM); st->codecpar->codec_id = AV_CODEC_ID_BINTEXT; - if (ff_alloc_extradata(st->codecpar, 2 + 48 + 4096)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, 2 + 48 + 4096)) < 0) + return ret; st->codecpar->extradata[0] = 16; st->codecpar->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT; @@ -316,7 +320,7 @@ static int idf_read_header(AVFormatContext *s) BinDemuxContext *bin = s->priv_data; AVIOContext *pb = s->pb; AVStream *st; - int got_width = 0; + int got_width = 0, ret; if (!(pb->seekable & AVIO_SEEKABLE_NORMAL)) return AVERROR(EIO); @@ -326,8 +330,8 @@ static int idf_read_header(AVFormatContext *s) return AVERROR(ENOMEM); st->codecpar->codec_id = AV_CODEC_ID_IDF; - if (ff_alloc_extradata(st->codecpar, 2 + 48 + 4096)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, 2 + 48 + 4096)) < 0) + return ret; st->codecpar->extradata[0] = 16; st->codecpar->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT; diff --git a/libavformat/bit.c b/libavformat/bit.c index 0aacfc7c386..2dc7d4f3f70 100644 --- a/libavformat/bit.c +++ b/libavformat/bit.c @@ -94,8 +94,8 @@ static int read_packet(AVFormatContext *s, if(ret != 8 * packet_size * sizeof(uint16_t)) return AVERROR(EIO); - if (av_new_packet(pkt, packet_size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, packet_size)) < 0) + return ret; init_put_bits(&pbo, pkt->data, packet_size); for(j=0; j < packet_size; j++) diff --git a/libavformat/bmv.c b/libavformat/bmv.c index ac567c21eb6..9f03fba0585 100644 --- a/libavformat/bmv.c +++ b/libavformat/bmv.c @@ -96,8 +96,8 @@ static int bmv_read_packet(AVFormatContext *s, AVPacket *pkt) audio_size, c->size); return AVERROR_INVALIDDATA; } - if (av_new_packet(pkt, audio_size) < 0) - return AVERROR(ENOMEM); + if ((err = av_new_packet(pkt, audio_size)) < 0) + return err; memcpy(pkt->data, c->packet + 1, pkt->size); pkt->stream_index = 1; pkt->pts = c->audio_pos; @@ -108,8 +108,8 @@ static int bmv_read_packet(AVFormatContext *s, AVPacket *pkt) } else break; } - if (av_new_packet(pkt, c->size + 1) < 0) - return AVERROR(ENOMEM); + if ((err = av_new_packet(pkt, c->size + 1)) < 0) + return err; pkt->stream_index = 0; c->get_next = 1; memcpy(pkt->data, c->packet, pkt->size); diff --git a/libavformat/brstm.c b/libavformat/brstm.c index e8a1eaa0220..ca965ed7e19 100644 --- a/libavformat/brstm.c +++ b/libavformat/brstm.c @@ -403,8 +403,8 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) (32 + 4 + size) > (INT_MAX / par->channels) || (32 + 4 + size) * par->channels > INT_MAX - 8) return AVERROR_INVALIDDATA; - if (av_new_packet(pkt, 8 + (32 + 4 + size) * par->channels) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, 8 + (32 + 4 + size) * par->channels)) < 0) + return ret; dst = pkt->data; if (par->codec_id == AV_CODEC_ID_ADPCM_THP_LE) { bytestream_put_le32(&dst, size * par->channels); @@ -422,8 +422,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) dst += size; avio_skip(s->pb, skip); if (ret != size) { - av_packet_unref(pkt); - break; + return AVERROR(EIO); } } pkt->duration = samples; diff --git a/libavformat/c93.c b/libavformat/c93.c index 8aa80b5e0b1..256b9800caf 100644 --- a/libavformat/c93.c +++ b/libavformat/c93.c @@ -158,22 +158,19 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) ret = avio_read(pb, pkt->data + 1, datasize); if (ret < datasize) { - ret = AVERROR(EIO); - goto fail; + return AVERROR(EIO); } datasize = avio_rl16(pb); /* palette size */ if (datasize) { if (datasize != 768) { av_log(s, AV_LOG_ERROR, "invalid palette size %u\n", datasize); - ret = AVERROR_INVALIDDATA; - goto fail; + return AVERROR_INVALIDDATA; } pkt->data[0] |= C93_HAS_PALETTE; ret = avio_read(pb, pkt->data + pkt->size, datasize); if (ret < datasize) { - ret = AVERROR(EIO); - goto fail; + return AVERROR(EIO); } pkt->size += 768; } @@ -186,10 +183,6 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) pkt->data[0] |= C93_FIRST_FRAME; } return 0; - - fail: - av_packet_unref(pkt); - return ret; } AVInputFormat ff_c93_demuxer = { diff --git a/libavformat/cache.c b/libavformat/cache.c index 3425be9350e..1e19dafc6a5 100644 --- a/libavformat/cache.c +++ b/libavformat/cache.c @@ -310,7 +310,7 @@ static int cache_close(URLContext *h) av_log(h, AV_LOG_ERROR, "Could not delete %s.\n", c->filename); av_freep(&c->filename); } - ffurl_close(c->inner); + ffurl_closep(&c->inner); av_tree_enumerate(c->root, NULL, NULL, enu_free); av_tree_destroy(c->root); @@ -326,7 +326,7 @@ static const AVOption options[] = { }; static const AVClass cache_context_class = { - .class_name = "Cache", + .class_name = "cache", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT, diff --git a/libavformat/cafdec.c b/libavformat/cafdec.c index 86228595c98..d0f942f3e46 100644 --- a/libavformat/cafdec.c +++ b/libavformat/cafdec.c @@ -100,6 +100,7 @@ static int read_kuki_chunk(AVFormatContext *s, int64_t size) { AVIOContext *pb = s->pb; AVStream *st = s->streams[0]; + int ret; if (size < 0 || size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) return -1; @@ -134,9 +135,8 @@ static int read_kuki_chunk(AVFormatContext *s, int64_t size) return AVERROR_INVALIDDATA; } - av_freep(&st->codecpar->extradata); - if (ff_alloc_extradata(st->codecpar, ALAC_HEADER)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, ALAC_HEADER)) < 0) + return ret; /* For the old style cookie, we skip 12 bytes, then read 36 bytes. * The new style cookie only contains the last 24 bytes of what was @@ -174,10 +174,8 @@ static int read_kuki_chunk(AVFormatContext *s, int64_t size) return AVERROR_PATCHWELCOME; } avio_skip(pb, size); - } else { - av_freep(&st->codecpar->extradata); - if (ff_get_extradata(s, st->codecpar, pb, size) < 0) - return AVERROR(ENOMEM); + } else if ((ret = ff_get_extradata(s, st->codecpar, pb, size)) < 0) { + return ret; } return 0; diff --git a/libavformat/cafenc.c b/libavformat/cafenc.c index 0f7c4ebbb32..98d4d9212f0 100644 --- a/libavformat/cafenc.c +++ b/libavformat/cafenc.c @@ -203,7 +203,6 @@ static int caf_write_header(AVFormatContext *s) avio_wb64(pb, -1); //< mChunkSize avio_wb32(pb, 0); //< mEditCount - avio_flush(pb); return 0; } @@ -259,7 +258,6 @@ static int caf_write_trailer(AVFormatContext *s) avio_write(pb, caf->pkt_sizes, caf->size_entries_used); caf->size_buffer_size = 0; } - avio_flush(pb); } av_freep(&caf->pkt_sizes); return 0; diff --git a/libavformat/cdxl.c b/libavformat/cdxl.c index 9aacaddb40f..5718fc3e214 100644 --- a/libavformat/cdxl.c +++ b/libavformat/cdxl.c @@ -131,7 +131,8 @@ static int cdxl_read_packet(AVFormatContext *s, AVPacket *pkt) height = AV_RB16(&cdxl->header[16]); palette_size = AV_RB16(&cdxl->header[20]); audio_size = AV_RB16(&cdxl->header[22]); - if (FFALIGN(width, 16) * (uint64_t)height * cdxl->header[19] > INT_MAX) + if (cdxl->header[19] == 0 || + FFALIGN(width, 16) * (uint64_t)height * cdxl->header[19] > INT_MAX) return AVERROR_INVALIDDATA; if (format == 0x20) image_size = width * height * cdxl->header[19] / 8; @@ -201,12 +202,11 @@ static int cdxl_read_packet(AVFormatContext *s, AVPacket *pkt) avpriv_set_pts_info(st, 64, 1, cdxl->sample_rate); } - if (av_new_packet(pkt, video_size + CDXL_HEADER_SIZE) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, video_size + CDXL_HEADER_SIZE)) < 0) + return ret; memcpy(pkt->data, cdxl->header, CDXL_HEADER_SIZE); ret = avio_read(pb, pkt->data + CDXL_HEADER_SIZE, video_size); if (ret < 0) { - av_packet_unref(pkt); return ret; } av_shrink_packet(pkt, CDXL_HEADER_SIZE + ret); diff --git a/libavformat/chromaprint.c b/libavformat/chromaprint.c index f39c09ddb92..0cd7cdeb26e 100644 --- a/libavformat/chromaprint.c +++ b/libavformat/chromaprint.c @@ -73,7 +73,7 @@ static int write_header(AVFormatContext *s) if (cpr->silence_threshold != -1) { #if CPR_VERSION_INT >= AV_VERSION_INT(0, 7, 0) if (!chromaprint_set_option(cpr->ctx, "silence_threshold", cpr->silence_threshold)) { - av_log(s, AV_LOG_ERROR, "Failed to set silence threshold.\n"); + av_log(s, AV_LOG_ERROR, "Failed to set silence threshold. Setting silence_threshold requires -algorithm 3 option.\n"); goto fail; } #else @@ -114,14 +114,15 @@ static int write_header(AVFormatContext *s) static int write_packet(AVFormatContext *s, AVPacket *pkt) { ChromaprintMuxContext *cpr = s->priv_data; - return chromaprint_feed(cpr->ctx, pkt->data, pkt->size / 2) ? 0 : AVERROR(EINVAL); + return chromaprint_feed(cpr->ctx, (const int16_t *)pkt->data, pkt->size / 2) ? 0 : AVERROR(EINVAL); } static int write_trailer(AVFormatContext *s) { ChromaprintMuxContext *cpr = s->priv_data; AVIOContext *pb = s->pb; - void *fp = NULL, *enc_fp = NULL; + void *fp = NULL; + char *enc_fp = NULL; int size, enc_size, ret = AVERROR(EINVAL); if (!chromaprint_finish(cpr->ctx)) { @@ -129,14 +130,14 @@ static int write_trailer(AVFormatContext *s) goto fail; } - if (!chromaprint_get_raw_fingerprint(cpr->ctx, &fp, &size)) { + if (!chromaprint_get_raw_fingerprint(cpr->ctx, (uint32_t **)&fp, &size)) { av_log(s, AV_LOG_ERROR, "Failed to retrieve fingerprint\n"); goto fail; } switch (cpr->fp_format) { case FINGERPRINT_RAW: - avio_write(pb, fp, size); + avio_write(pb, fp, size * 4); //fp points to array of uint32_t break; case FINGERPRINT_COMPRESSED: case FINGERPRINT_BASE64: @@ -164,7 +165,7 @@ 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", "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"}, diff --git a/libavformat/cinedec.c b/libavformat/cinedec.c index d27ebbba7bf..0f2453cdf59 100644 --- a/libavformat/cinedec.c +++ b/libavformat/cinedec.c @@ -168,6 +168,10 @@ static int cine_read_header(AVFormatContext *avctx) avio_skip(pb, 616); // Binning .. bFlipH if (!avio_rl32(pb) ^ vflip) { st->codecpar->extradata = av_strdup("BottomUp"); + if (!st->codecpar->extradata) { + st->codecpar->extradata_size = 0; + return AVERROR(ENOMEM); + } st->codecpar->extradata_size = 9; } diff --git a/libavformat/concat.c b/libavformat/concat.c index 19c83c309ac..418405dd50b 100644 --- a/libavformat/concat.c +++ b/libavformat/concat.c @@ -38,6 +38,7 @@ struct concat_data { struct concat_nodes *nodes; ///< list of nodes to concat size_t length; ///< number of cat'ed nodes size_t current; ///< index of currently read node + uint64_t total_size; }; static av_cold int concat_close(URLContext *h) @@ -48,7 +49,7 @@ static av_cold int concat_close(URLContext *h) struct concat_nodes *nodes = data->nodes; for (i = 0; i != data->length; i++) - err |= ffurl_close(nodes[i].uc); + err |= ffurl_closep(&nodes[i].uc); av_freep(&data->nodes); @@ -59,7 +60,7 @@ static av_cold int concat_open(URLContext *h, const char *uri, int flags) { char *node_uri = NULL; int err = 0; - int64_t size; + int64_t size, total_size = 0; size_t len, i; URLContext *uc; struct concat_data *data = h->priv_data; @@ -74,7 +75,6 @@ static av_cold int concat_open(URLContext *h, const char *uri, int flags) if (uri[i] == *AV_CAT_SEPARATOR) { /* integer overflow */ if (++len == UINT_MAX / sizeof(*nodes)) { - av_freep(&h->priv_data); return AVERROR(ENAMETOOLONG); } } @@ -112,6 +112,7 @@ static av_cold int concat_open(URLContext *h, const char *uri, int flags) /* assembling */ nodes[i].uc = uc; nodes[i].size = size; + total_size += size; } av_free(node_uri); data->length = i; @@ -123,6 +124,7 @@ static av_cold int concat_open(URLContext *h, const char *uri, int flags) err = AVERROR(ENOMEM); } else data->nodes = nodes; + data->total_size = total_size; return err; } @@ -158,6 +160,8 @@ static int64_t concat_seek(URLContext *h, int64_t pos, int whence) struct concat_nodes *nodes = data->nodes; size_t i; + if ((whence & AVSEEK_SIZE)) + return data->total_size; switch (whence) { case SEEK_END: for (i = data->length - 1; i && pos < -nodes[i].size; i--) diff --git a/libavformat/concatdec.c b/libavformat/concatdec.c index b80294efbf4..4b56b61404f 100644 --- a/libavformat/concatdec.c +++ b/libavformat/concatdec.c @@ -171,10 +171,6 @@ static int copy_stream_props(AVStream *st, AVStream *source_st) if (st->codecpar->codec_id || !source_st->codecpar->codec_id) { if (st->codecpar->extradata_size < source_st->codecpar->extradata_size) { - if (st->codecpar->extradata) { - av_freep(&st->codecpar->extradata); - st->codecpar->extradata_size = 0; - } ret = ff_alloc_extradata(st->codecpar, source_st->codecpar->extradata_size); if (ret < 0) @@ -546,7 +542,6 @@ static int filter_packet(AVFormatContext *avf, ConcatStream *cs, AVPacket *pkt) if (ret < 0) { av_log(avf, AV_LOG_ERROR, "h264_mp4toannexb filter " "failed to send input packet\n"); - av_packet_unref(pkt); return ret; } @@ -596,7 +591,6 @@ static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt) if (ret < 0) return ret; if ((ret = match_streams(avf)) < 0) { - av_packet_unref(pkt); return ret; } if (packet_after_outpoint(cat, pkt)) { @@ -612,7 +606,7 @@ static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt) } break; } - if ((ret = filter_packet(avf, cs, pkt))) + if ((ret = filter_packet(avf, cs, pkt)) < 0) return ret; st = cat->avf->streams[pkt->stream_index]; @@ -632,17 +626,16 @@ static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt) av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base)); if (cat->cur_file->metadata) { - uint8_t* metadata; int metadata_len; char* packed_metadata = av_packet_pack_dictionary(cat->cur_file->metadata, &metadata_len); if (!packed_metadata) return AVERROR(ENOMEM); - if (!(metadata = av_packet_new_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, metadata_len))) { + ret = av_packet_add_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, + packed_metadata, metadata_len); + if (ret < 0) { av_freep(&packed_metadata); - return AVERROR(ENOMEM); + return ret; } - memcpy(metadata, packed_metadata, metadata_len); - av_freep(&packed_metadata); } if (cat->cur_file->duration == AV_NOPTS_VALUE && st->cur_dts != AV_NOPTS_VALUE) { @@ -653,7 +646,7 @@ static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt) } pkt->stream_index = cs->out_stream_index; - return ret; + return 0; } static void rescale_interval(AVRational tb_in, AVRational tb_out, diff --git a/libavformat/crypto.c b/libavformat/crypto.c index 9a48f2e6f51..31f9ac0ab98 100644 --- a/libavformat/crypto.c +++ b/libavformat/crypto.c @@ -385,8 +385,7 @@ static int crypto_close(URLContext *h) ret = ffurl_write(c->hd, out_buf, BLOCKSIZE); } - if (c->hd) - ffurl_close(c->hd); + ffurl_closep(&c->hd); av_freep(&c->aes_decrypt); av_freep(&c->aes_encrypt); av_freep(&c->write_buf); diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c index f82a750d6fa..ec2aadcee38 100644 --- a/libavformat/dashdec.c +++ b/libavformat/dashdec.c @@ -29,6 +29,8 @@ #include "dash.h" #define INITIAL_BUFFER_SIZE 32768 +#define MAX_MANIFEST_SIZE 50 * 1024 +#define DEFAULT_MANIFEST_SIZE 8 * 1024 struct fragment { int64_t url_offset; @@ -85,6 +87,7 @@ struct representation { enum AVMediaType type; char id[20]; + char *lang; int bandwidth; AVRational framerate; AVStream *assoc_stream; /* demuxer stream associated with this representation */ @@ -122,19 +125,6 @@ struct representation { typedef struct DASHContext { const AVClass *class; char *base_url; - char *adaptionset_contenttype_val; - char *adaptionset_par_val; - char *adaptionset_lang_val; - char *adaptionset_minbw_val; - char *adaptionset_maxbw_val; - char *adaptionset_minwidth_val; - char *adaptionset_maxwidth_val; - char *adaptionset_minheight_val; - char *adaptionset_maxheight_val; - char *adaptionset_minframerate_val; - char *adaptionset_maxframerate_val; - char *adaptionset_segmentalignment_val; - char *adaptionset_bitstreamswitching_val; int n_videos; struct representation **videos; @@ -157,6 +147,9 @@ typedef struct DASHContext { uint64_t period_duration; uint64_t period_start; + /* AdaptationSet Attribute */ + char *adaptionset_lang; + int is_live; AVIOInterruptCB *interrupt_callback; char *allowed_extensions; @@ -363,8 +356,7 @@ static void free_representation(struct representation *pls) free_fragment(&pls->init_section); av_freep(&pls->init_sec_buf); av_freep(&pls->pb.buffer); - if (pls->input) - ff_format_io_close(pls->parent, &pls->input); + ff_format_io_close(pls->parent, &pls->input); if (pls->ctx) { pls->ctx->pb = NULL; avformat_close_input(&pls->ctx); @@ -600,7 +592,7 @@ static struct fragment * get_Fragment(char *range) char *str_end_offset; char *str_offset = av_strtok(range, "-", &str_end_offset); seg->url_offset = strtoll(str_offset, NULL, 10); - seg->size = strtoll(str_end_offset, NULL, 10) - seg->url_offset; + seg->size = strtoll(str_end_offset, NULL, 10) - seg->url_offset + 1; } return seg; @@ -794,12 +786,22 @@ static int resolve_content_path(AVFormatContext *s, const char *url, int *max_ur continue; } text = xmlNodeGetContent(baseurl_nodes[i]); - if (text) { + if (text && !av_strstart(text, "/", NULL)) { memset(tmp_str, 0, strlen(tmp_str)); if (!ishttp(text) && isRootHttp) { av_strlcpy(tmp_str, root_url, size + 1); } start = (text[0] == token); + if (start && av_stristr(tmp_str, text)) { + char *p = tmp_str; + if (!av_strncasecmp(tmp_str, "http://", 7)) { + p += 7; + } else if (!av_strncasecmp(tmp_str, "https://", 8)) { + p += 8; + } + p = strchr(p, '/'); + memset(p + 1, 0, strlen(p)); + } av_strlcat(tmp_str, text + start, tmp_max_url_size); xmlNodeSetContent(baseurl_nodes[i], tmp_str); updated = 1; @@ -876,6 +878,16 @@ static int parse_manifest_representation(AVFormatContext *s, const char *url, ret = AVERROR(ENOMEM); goto end; } + if (c->adaptionset_lang) { + rep->lang = av_strdup(c->adaptionset_lang); + if (!rep->lang) { + av_log(s, AV_LOG_ERROR, "alloc language memory failure\n"); + av_freep(&rep); + ret = AVERROR(ENOMEM); + goto end; + } + } + rep->parent = s; representation_segmenttemplate_node = find_child_node_by_name(representation_node, "SegmentTemplate"); representation_baseurl_node = find_child_node_by_name(representation_node, "BaseURL"); representation_segmentlist_node = find_child_node_by_name(representation_node, "SegmentList"); @@ -948,7 +960,7 @@ static int parse_manifest_representation(AVFormatContext *s, const char *url, xmlFree(timescale_val); } if (startnumber_val) { - rep->first_seq_no = (int64_t) strtoll(startnumber_val, NULL, 10); + rep->start_number = rep->first_seq_no = (int64_t) strtoll(startnumber_val, NULL, 10); av_log(s, AV_LOG_TRACE, "rep->first_seq_no = [%"PRId64"]\n", rep->first_seq_no); xmlFree(startnumber_val); } @@ -1006,6 +1018,7 @@ static int parse_manifest_representation(AVFormatContext *s, const char *url, duration_val = get_val_from_nodes_tab(segmentlists_tab, 3, "duration"); timescale_val = get_val_from_nodes_tab(segmentlists_tab, 3, "timescale"); + startnumber_val = get_val_from_nodes_tab(segmentlists_tab, 3, "startNumber"); if (duration_val) { rep->fragment_duration = (int64_t) strtoll(duration_val, NULL, 10); av_log(s, AV_LOG_TRACE, "rep->fragment_duration = [%"PRId64"]\n", rep->fragment_duration); @@ -1016,6 +1029,12 @@ static int parse_manifest_representation(AVFormatContext *s, const char *url, av_log(s, AV_LOG_TRACE, "rep->fragment_timescale = [%"PRId64"]\n", rep->fragment_timescale); xmlFree(timescale_val); } + if (startnumber_val) { + rep->start_number = rep->first_seq_no = (int64_t) strtoll(startnumber_val, NULL, 10); + av_log(s, AV_LOG_TRACE, "rep->first_seq_no = [%"PRId64"]\n", rep->first_seq_no); + xmlFree(startnumber_val); + } + fragmenturl_node = xmlFirstElementChild(representation_segmentlist_node); while (fragmenturl_node) { ret = parse_manifest_segmenturlnode(s, rep, fragmenturl_node, @@ -1099,6 +1118,19 @@ static int parse_manifest_representation(AVFormatContext *s, const char *url, return ret; } +static int parse_manifest_adaptationset_attr(AVFormatContext *s, xmlNodePtr adaptionset_node) +{ + DASHContext *c = s->priv_data; + + if (!adaptionset_node) { + av_log(s, AV_LOG_WARNING, "Cannot get AdaptionSet\n"); + return AVERROR(EINVAL); + } + c->adaptionset_lang = xmlGetProp(adaptionset_node, "lang"); + + return 0; +} + static int parse_manifest_adaptationset(AVFormatContext *s, const char *url, xmlNodePtr adaptionset_node, xmlNodePtr mpd_baseurl_node, @@ -1114,19 +1146,10 @@ static int parse_manifest_adaptationset(AVFormatContext *s, const char *url, xmlNodePtr adaptionset_segmentlist_node = NULL; xmlNodePtr adaptionset_supplementalproperty_node = NULL; xmlNodePtr node = NULL; - c->adaptionset_contenttype_val = xmlGetProp(adaptionset_node, "contentType"); - c->adaptionset_par_val = xmlGetProp(adaptionset_node, "par"); - c->adaptionset_lang_val = xmlGetProp(adaptionset_node, "lang"); - c->adaptionset_minbw_val = xmlGetProp(adaptionset_node, "minBandwidth"); - c->adaptionset_maxbw_val = xmlGetProp(adaptionset_node, "maxBandwidth"); - c->adaptionset_minwidth_val = xmlGetProp(adaptionset_node, "minWidth"); - c->adaptionset_maxwidth_val = xmlGetProp(adaptionset_node, "maxWidth"); - c->adaptionset_minheight_val = xmlGetProp(adaptionset_node, "minHeight"); - c->adaptionset_maxheight_val = xmlGetProp(adaptionset_node, "maxHeight"); - c->adaptionset_minframerate_val = xmlGetProp(adaptionset_node, "minFrameRate"); - c->adaptionset_maxframerate_val = xmlGetProp(adaptionset_node, "maxFrameRate"); - c->adaptionset_segmentalignment_val = xmlGetProp(adaptionset_node, "segmentAlignment"); - c->adaptionset_bitstreamswitching_val = xmlGetProp(adaptionset_node, "bitstreamSwitching"); + + ret = parse_manifest_adaptationset_attr(s, adaptionset_node); + if (ret < 0) + return ret; node = xmlFirstElementChild(adaptionset_node); while (node) { @@ -1152,13 +1175,15 @@ static int parse_manifest_adaptationset(AVFormatContext *s, const char *url, adaptionset_baseurl_node, adaptionset_segmentlist_node, adaptionset_supplementalproperty_node); - if (ret < 0) { - return ret; - } + if (ret < 0) + goto err; } node = xmlNextElementSibling(node); } - return 0; + +err: + av_freep(&c->adaptionset_lang); + return ret; } static int parse_programinformation(AVFormatContext *s, xmlNodePtr node) @@ -1185,6 +1210,7 @@ static int parse_programinformation(AVFormatContext *s, xmlNodePtr node) } node = xmlNextElementSibling(node); xmlFree(val); + val = NULL; } return 0; } @@ -1196,7 +1222,7 @@ static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in) int close_in = 0; uint8_t *new_url = NULL; int64_t filesize = 0; - char *buffer = NULL; + AVBPrint buf; AVDictionary *opts = NULL; xmlDoc *doc = NULL; xmlNodePtr root_element = NULL; @@ -1230,24 +1256,23 @@ static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in) } filesize = avio_size(in); - if (filesize <= 0) { - filesize = 8 * 1024; + if (filesize > MAX_MANIFEST_SIZE) { + av_log(s, AV_LOG_ERROR, "Manifest too large: %"PRId64"\n", filesize); + return AVERROR_INVALIDDATA; } - buffer = av_mallocz(filesize); - if (!buffer) { - av_free(c->base_url); - return AVERROR(ENOMEM); - } + av_bprint_init(&buf, (filesize > 0) ? filesize + 1 : DEFAULT_MANIFEST_SIZE, AV_BPRINT_SIZE_UNLIMITED); - filesize = avio_read(in, buffer, filesize); - if (filesize <= 0) { - av_log(s, AV_LOG_ERROR, "Unable to read to offset '%s'\n", url); - ret = AVERROR_INVALIDDATA; + if ((ret = avio_read_to_bprint(in, &buf, MAX_MANIFEST_SIZE)) < 0 || + !avio_feof(in) || + (filesize = buf.len) == 0) { + av_log(s, AV_LOG_ERROR, "Unable to read to manifest '%s'\n", url); + if (ret == 0) + ret = AVERROR_INVALIDDATA; } else { LIBXML_TEST_VERSION - doc = xmlReadMemory(buffer, filesize, c->base_url, NULL, 0); + doc = xmlReadMemory(buf.str, filesize, c->base_url, NULL, 0); root_element = xmlDocGetRootElement(doc); node = root_element; @@ -1370,7 +1395,7 @@ static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in) } av_free(new_url); - av_free(buffer); + av_bprint_finalize(&buf, NULL); if (close_in) { avio_close(in); } @@ -1833,7 +1858,7 @@ static int save_avio_options(AVFormatContext *s) { DASHContext *c = s->priv_data; const char *opts[] = { - "headers", "user_agent", "cookies", "http_proxy", "referer", "rw_timeout", NULL }; + "headers", "user_agent", "cookies", "http_proxy", "referer", "rw_timeout", "icy", NULL }; const char **opt = opts; uint8_t *buf = NULL; int ret = 0; @@ -1916,8 +1941,8 @@ static int reopen_demux_for_component(AVFormatContext *s, struct representation goto fail; pls->ctx->flags = AVFMT_FLAG_CUSTOM_IO; - pls->ctx->probesize = 1024 * 4; - pls->ctx->max_analyze_duration = 4 * AV_TIME_BASE; + 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; ret = av_probe_input_buffer(&pls->pb, &in_fmt, "", NULL, 0, 0); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Error when loading first fragment, playlist %d\n", (int)pls->rep_idx); @@ -2130,6 +2155,10 @@ static int dash_read_header(AVFormatContext *s) av_dict_set_int(&rep->assoc_stream->metadata, "variant_bitrate", rep->bandwidth, 0); if (rep->id[0]) av_dict_set(&rep->assoc_stream->metadata, "id", rep->id, 0); + if (rep->lang) { + av_dict_set(&rep->assoc_stream->metadata, "language", rep->lang, 0); + av_freep(&rep->lang); + } } for (i = 0; i < c->n_subtitles; i++) { rep = c->subtitles[i]; @@ -2137,6 +2166,10 @@ static int dash_read_header(AVFormatContext *s) rep->assoc_stream = s->streams[rep->stream_index]; if (rep->id[0]) av_dict_set(&rep->assoc_stream->metadata, "id", rep->id, 0); + if (rep->lang) { + av_dict_set(&rep->assoc_stream->metadata, "language", rep->lang, 0); + av_freep(&rep->lang); + } } } @@ -2164,8 +2197,7 @@ static void recheck_discard_flags(AVFormatContext *s, struct representation **p, av_log(s, AV_LOG_INFO, "Now receiving stream_index %d\n", pls->stream_index); } else if (!needed && pls->ctx) { close_demux_for_component(pls); - if (pls->input) - ff_format_io_close(pls->parent, &pls->input); + ff_format_io_close(pls->parent, &pls->input); av_log(s, AV_LOG_INFO, "No longer receiving stream_index %d\n", pls->stream_index); } } @@ -2226,8 +2258,7 @@ static int dash_read_packet(AVFormatContext *s, AVPacket *pkt) if (cur->is_restart_needed) { cur->cur_seg_offset = 0; cur->init_sec_buf_read_offset = 0; - if (cur->input) - ff_format_io_close(cur->parent, &cur->input); + ff_format_io_close(cur->parent, &cur->input); ret = reopen_demux_for_component(s, cur); cur->is_restart_needed = 0; } @@ -2265,8 +2296,7 @@ static int dash_seek(AVFormatContext *s, struct representation *pls, int64_t see return av_seek_frame(pls->ctx, -1, seek_pos_msec * 1000, flags); } - if (pls->input) - ff_format_io_close(pls->parent, &pls->input); + ff_format_io_close(pls->parent, &pls->input); // find the nearest fragment if (pls->n_timelines > 0 && pls->fragment_timescale > 0) { @@ -2346,7 +2376,8 @@ static int dash_probe(const AVProbeData *p) if (av_stristr(p->buf, "dash:profile:isoff-on-demand:2011") || av_stristr(p->buf, "dash:profile:isoff-live:2011") || av_stristr(p->buf, "dash:profile:isoff-live:2012") || - av_stristr(p->buf, "dash:profile:isoff-main:2011")) { + av_stristr(p->buf, "dash:profile:isoff-main:2011") || + av_stristr(p->buf, "3GPP:PSS:profile:DASH1")) { return AVPROBE_SCORE_MAX; } if (av_stristr(p->buf, "dash:profile")) { @@ -2361,7 +2392,7 @@ static int dash_probe(const AVProbeData *p) static const AVOption dash_options[] = { {"allowed_extensions", "List of file extensions that dash is allowed to access", OFFSET(allowed_extensions), AV_OPT_TYPE_STRING, - {.str = "aac,m4a,m4s,m4v,mov,mp4,webm"}, + {.str = "aac,m4a,m4s,m4v,mov,mp4,webm,ts"}, INT_MIN, INT_MAX, FLAGS}, {NULL} }; diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 24d43c34ea0..00a37b175d9 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -31,6 +31,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" +#include "libavutil/parseutils.h" #include "libavutil/rational.h" #include "libavutil/time.h" #include "libavutil/time_internal.h" @@ -57,6 +58,17 @@ typedef enum { SEGMENT_TYPE_NB } SegmentType; +enum { + FRAG_TYPE_NONE = 0, + FRAG_TYPE_EVERY_FRAME, + FRAG_TYPE_DURATION, + FRAG_TYPE_PFRAMES, + FRAG_TYPE_NB +}; + +#define MPD_PROFILE_DASH 1 +#define MPD_PROFILE_DVB 2 + typedef struct Segment { char file[1024]; int64_t start_pos; @@ -68,25 +80,40 @@ typedef struct Segment { } Segment; typedef struct AdaptationSet { - char id[10]; + int id; + char *descriptor; + int64_t seg_duration; + int64_t frag_duration; + int frag_type; enum AVMediaType media_type; AVDictionary *metadata; AVRational min_frame_rate, max_frame_rate; int ambiguous_frame_rate; + int64_t max_frag_duration; + int max_width, max_height; + int nb_streams; + AVRational par; + int trick_idx; } AdaptationSet; typedef struct OutputStream { AVFormatContext *ctx; int ctx_inited, as_idx; AVIOContext *out; + AVCodecParserContext *parser; + AVCodecContext *parser_avctx; int packets_written; char initfile[1024]; int64_t init_start_pos, pos; int init_range_length; int nb_segments, segments_size, segment_index; + int64_t seg_duration; + int64_t frag_duration; + int64_t last_duration; Segment **segments; int64_t first_pts, start_pts, max_pts; int64_t last_dts, last_pts; + int last_flags; int bit_rate; SegmentType segment_type; /* segment type selected for this particular stream */ const char *format_name; @@ -101,8 +128,15 @@ typedef struct OutputStream { char full_path[1024]; char temp_path[1024]; double availability_time_offset; + AVProducerReferenceTime producer_reference_time; + char producer_reference_time_str[100]; int total_pkt_size; + int64_t total_pkt_duration; int muxer_overhead; + int frag_type; + int64_t gop_size; + AVRational sar; + int coding_dependency; } OutputStream; typedef struct DASHContext { @@ -116,6 +150,7 @@ typedef struct DASHContext { int min_seg_duration; #endif int64_t seg_duration; + int64_t frag_duration; int remove_at_exit; int use_template; int use_timeline; @@ -126,6 +161,7 @@ typedef struct DASHContext { int64_t total_duration; char availability_start_time[100]; time_t start_time_s; + int64_t presentation_time_offset; char dirname[1024]; const char *single_file_name; /* file names as specified in options */ const char *init_seg_name; @@ -133,6 +169,7 @@ typedef struct DASHContext { const char *utc_timing_url; const char *method; const char *user_agent; + AVDictionary *http_opts; int hls_playlist; int http_persistent; int master_playlist_created; @@ -141,14 +178,24 @@ typedef struct DASHContext { int streaming; int64_t timeout; int index_correction; - char *format_options_str; + AVDictionary *format_options; int global_sidx; SegmentType segment_type_option; /* segment type as specified in options */ int ignore_io_errors; int lhls; + int ldash; int master_publish_rate; int nr_of_streams_to_flush; int nr_of_streams_flushed; + int frag_type; + int write_prft; + int64_t max_gop_size; + int64_t max_segment_duration; + int profile; + int64_t target_latency; + int target_latency_refid; + AVRational min_playback_rate; + AVRational max_playback_rate; } DASHContext; static struct codec_string { @@ -437,6 +484,7 @@ static void set_http_options(AVDictionary **options, DASHContext *c) { if (c->method) av_dict_set(options, "method", c->method, 0); + av_dict_copy(options, c->http_opts, 0); if (c->user_agent) av_dict_set(options, "user_agent", c->user_agent, 0); if (c->http_persistent) @@ -539,9 +587,7 @@ static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, dashenc_io_close(s, &c->m3u8_out, temp_filename_hls); if (use_rename) - if (avpriv_io_move(temp_filename_hls, filename_hls) < 0) { - av_log(os->ctx, AV_LOG_WARNING, "renaming file %s to %s failed\n\n", temp_filename_hls, filename_hls); - } + ff_rename(temp_filename_hls, filename_hls, os->ctx); } static int flush_init_segment(AVFormatContext *s, OutputStream *os) @@ -568,8 +614,10 @@ static void dash_free(AVFormatContext *s) int i, j; if (c->as) { - for (i = 0; i < c->nb_as; i++) + for (i = 0; i < c->nb_as; i++) { av_dict_free(&c->as[i].metadata); + av_freep(&c->as[i].descriptor); + } av_freep(&c->as); c->nb_as = 0; } @@ -585,8 +633,9 @@ static void dash_free(AVFormatContext *s) avio_close(os->ctx->pb); } ff_format_io_close(s, &os->out); - if (os->ctx) - avformat_free_context(os->ctx); + avformat_free_context(os->ctx); + avcodec_free_context(&os->parser_avctx); + av_parser_close(os->parser); for (j = 0; j < os->nb_segments; j++) av_free(os->segments[j]); av_free(os->segments); @@ -611,12 +660,18 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont int timescale = c->use_timeline ? os->ctx->streams[0]->time_base.den : AV_TIME_BASE; avio_printf(out, "\t\t\t\tuse_timeline) { - avio_printf(out, "duration=\"%"PRId64"\" ", c->seg_duration); + avio_printf(out, "duration=\"%"PRId64"\" ", os->seg_duration); if (c->streaming && os->availability_time_offset) avio_printf(out, "availabilityTimeOffset=\"%.3f\" ", os->availability_time_offset); } - avio_printf(out, "initialization=\"%s\" media=\"%s\" startNumber=\"%d\">\n", os->init_seg_name, os->media_seg_name, c->use_timeline ? start_number : 1); + if (c->streaming && os->availability_time_offset && !final) + avio_printf(out, "availabilityTimeComplete=\"false\" "); + + avio_printf(out, "initialization=\"%s\" media=\"%s\" startNumber=\"%d\"", os->init_seg_name, os->media_seg_name, c->use_timeline ? start_number : 1); + if (c->presentation_time_offset) + avio_printf(out, " presentationTimeOffset=\"%"PRId64"\"", c->presentation_time_offset); + avio_printf(out, ">\n"); if (c->use_timeline) { int64_t cur_time = 0; avio_printf(out, "\t\t\t\t\t\n"); @@ -644,7 +699,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont avio_printf(out, "\t\t\t\t\n"); } else if (c->single_file) { avio_printf(out, "\t\t\t\t%s\n", os->initfile); - avio_printf(out, "\t\t\t\t\n", AV_TIME_BASE, c->last_duration, start_number); + avio_printf(out, "\t\t\t\t\n", AV_TIME_BASE, FFMIN(os->seg_duration, os->last_duration), start_number); avio_printf(out, "\t\t\t\t\t\n", os->init_start_pos, os->init_start_pos + os->init_range_length - 1); for (i = start_index; i < os->nb_segments; i++) { Segment *seg = os->segments[i]; @@ -655,7 +710,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont } avio_printf(out, "\t\t\t\t\n"); } else { - avio_printf(out, "\t\t\t\t\n", AV_TIME_BASE, c->last_duration, start_number); + avio_printf(out, "\t\t\t\t\n", AV_TIME_BASE, FFMIN(os->seg_duration, os->last_duration), start_number); avio_printf(out, "\t\t\t\t\t\n", os->initfile); for (i = start_index; i < os->nb_segments; i++) { Segment *seg = os->segments[i]; @@ -725,10 +780,9 @@ static void write_time(AVIOContext *out, int64_t time) avio_printf(out, "%d.%dS", seconds, fractions / (AV_TIME_BASE / 10)); } -static void format_date_now(char *buf, int size) +static void format_date(char *buf, int size, int64_t time_us) { struct tm *ptm, tmbuf; - int64_t time_us = av_gettime(); int64_t time_ms = time_us / 1000; const time_t time_s = time_ms / 1000; int millisec = time_ms - (time_s * 1000); @@ -752,20 +806,32 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind AVDictionaryEntry *lang, *role; int i; - avio_printf(out, "\t\tid, as->media_type == AVMEDIA_TYPE_VIDEO ? "video" : "audio"); if (as->media_type == AVMEDIA_TYPE_VIDEO && as->max_frame_rate.num && !as->ambiguous_frame_rate && av_cmp_q(as->min_frame_rate, as->max_frame_rate) < 0) avio_printf(out, " maxFrameRate=\"%d/%d\"", as->max_frame_rate.num, as->max_frame_rate.den); + else if (as->media_type == AVMEDIA_TYPE_VIDEO && as->max_frame_rate.num && !as->ambiguous_frame_rate && !av_cmp_q(as->min_frame_rate, as->max_frame_rate)) + avio_printf(out, " frameRate=\"%d/%d\"", as->max_frame_rate.num, as->max_frame_rate.den); + if (as->media_type == AVMEDIA_TYPE_VIDEO) { + avio_printf(out, " maxWidth=\"%d\" maxHeight=\"%d\"", as->max_width, as->max_height); + avio_printf(out, " par=\"%d:%d\"", as->par.num, as->par.den); + } lang = av_dict_get(as->metadata, "language", NULL, 0); if (lang) avio_printf(out, " lang=\"%s\"", lang->value); avio_printf(out, ">\n"); + if (!final && c->ldash && as->max_frag_duration && !(c->profile & MPD_PROFILE_DVB)) + avio_printf(out, "\t\t\t\n", as->max_frag_duration); + if (as->trick_idx >= 0) + avio_printf(out, "\t\t\t\n", as->id, as->trick_idx); role = av_dict_get(as->metadata, "role", NULL, 0); if (role) avio_printf(out, "\t\t\t\n", role->value); - + if (as->descriptor) + avio_printf(out, "\t\t\t%s\n", as->descriptor); for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; OutputStream *os = &c->streams[i]; char bandwidth_str[64] = {'\0'}; @@ -777,11 +843,22 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind os->bit_rate); if (as->media_type == AVMEDIA_TYPE_VIDEO) { - AVStream *st = s->streams[i]; avio_printf(out, "\t\t\tformat_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height); - if (st->avg_frame_rate.num) + if (st->codecpar->field_order == AV_FIELD_UNKNOWN) + avio_printf(out, " scanType=\"unknown\""); + else if (st->codecpar->field_order != AV_FIELD_PROGRESSIVE) + avio_printf(out, " scanType=\"interlaced\""); + avio_printf(out, " sar=\"%d:%d\"", os->sar.num, os->sar.den); + if (st->avg_frame_rate.num && av_cmp_q(as->min_frame_rate, as->max_frame_rate) < 0) avio_printf(out, " frameRate=\"%d/%d\"", st->avg_frame_rate.num, st->avg_frame_rate.den); + if (as->trick_idx >= 0) { + AdaptationSet *tas = &c->as[as->trick_idx]; + if (!as->ambiguous_frame_rate && !tas->ambiguous_frame_rate) + avio_printf(out, " maxPlayoutRate=\"%d\"", FFMAX((int)av_q2d(av_div_q(tas->min_frame_rate, as->min_frame_rate)), 1)); + } + if (!os->coding_dependency) + avio_printf(out, " codingDependency=\"false\""); avio_printf(out, ">\n"); } else { avio_printf(out, "\t\t\t\n", @@ -789,6 +866,15 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind avio_printf(out, "\t\t\t\t\n", s->streams[i]->codecpar->channels); } + if (!final && c->write_prft && os->producer_reference_time_str[0]) { + avio_printf(out, "\t\t\t\t\n", + i, os->producer_reference_time.flags ? "captured" : "encoder", os->producer_reference_time_str, c->presentation_time_offset); + avio_printf(out, "\t\t\t\t\t\n", c->utc_timing_url); + avio_printf(out, "\t\t\t\t\n"); + } + if (!final && c->ldash && os->gop_size && os->frag_type != FRAG_TYPE_NONE && !(c->profile & MPD_PROFILE_DVB) && + (os->frag_type != FRAG_TYPE_DURATION || os->frag_duration != os->seg_duration)) + avio_printf(out, "\t\t\t\t\n", os->gop_size); output_segment_list(os, out, s, i, final); avio_printf(out, "\t\t\t\n"); } @@ -800,8 +886,13 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind static int add_adaptation_set(AVFormatContext *s, AdaptationSet **as, enum AVMediaType type) { DASHContext *c = s->priv_data; + void *mem; - void *mem = av_realloc(c->as, sizeof(*c->as) * (c->nb_as + 1)); + if (c->profile & MPD_PROFILE_DVB && (c->nb_as + 1) > 16) { + av_log(s, AV_LOG_ERROR, "DVB-DASH profile allows a max of 16 Adaptation Sets\n"); + return AVERROR(EINVAL); + } + mem = av_realloc(c->as, sizeof(*c->as) * (c->nb_as + 1)); if (!mem) return AVERROR(ENOMEM); c->as = mem; @@ -810,6 +901,8 @@ static int add_adaptation_set(AVFormatContext *s, AdaptationSet **as, enum AVMed *as = &c->as[c->nb_as - 1]; memset(*as, 0, sizeof(**as)); (*as)->media_type = type; + (*as)->frag_type = -1; + (*as)->trick_idx = -1; return 0; } @@ -827,7 +920,12 @@ static int adaptation_set_add_stream(AVFormatContext *s, int as_idx, int i) av_log(s, AV_LOG_ERROR, "Stream %d is already assigned to an AdaptationSet\n", i); return AVERROR(EINVAL); } + if (c->profile & MPD_PROFILE_DVB && (as->nb_streams + 1) > 16) { + av_log(s, AV_LOG_ERROR, "DVB-DASH profile allows a max of 16 Representations per Adaptation Set\n"); + return AVERROR(EINVAL); + } os->as_idx = as_idx; + ++as->nb_streams; return 0; } @@ -836,7 +934,7 @@ static int parse_adaptation_sets(AVFormatContext *s) { DASHContext *c = s->priv_data; const char *p = c->adaptation_sets; - enum { new_set, parse_id, parsing_streams } state; + enum { new_set, parse_default, parsing_streams, parse_seg_duration, parse_frag_duration } state; AdaptationSet *as; int i, n, ret; @@ -845,32 +943,121 @@ static int parse_adaptation_sets(AVFormatContext *s) for (i = 0; i < s->nb_streams; i++) { if ((ret = add_adaptation_set(s, &as, s->streams[i]->codecpar->codec_type)) < 0) return ret; - snprintf(as->id, sizeof(as->id), "%d", i); + as->id = i; c->streams[i].as_idx = c->nb_as; + ++as->nb_streams; } goto end; } // syntax id=0,streams=0,1,2 id=1,streams=3,4 and so on + // option id=0,descriptor=descriptor_str,streams=0,1,2 and so on + // option id=0,seg_duration=2.5,frag_duration=0.5,streams=0,1,2 + // id=1,trick_id=0,seg_duration=10,frag_type=none,streams=3 and so on + // descriptor is useful to the scheme defined by ISO/IEC 23009-1:2014/Amd.2:2015 + // descriptor_str should be a self-closing xml tag. + // seg_duration and frag_duration have the same syntax as the global options of + // the same name, and the former have precedence over them if set. state = new_set; while (*p) { if (*p == ' ') { p++; continue; } else if (state == new_set && av_strstart(p, "id=", &p)) { + char id_str[10], *end_str; + + n = strcspn(p, ","); + snprintf(id_str, sizeof(id_str), "%.*s", n, p); + + i = strtol(id_str, &end_str, 10); + if (id_str == end_str || i < 0 || i > c->nb_as) { + av_log(s, AV_LOG_ERROR, "\"%s\" is not a valid value for an AdaptationSet id\n", id_str); + return AVERROR(EINVAL); + } if ((ret = add_adaptation_set(s, &as, AVMEDIA_TYPE_UNKNOWN)) < 0) return ret; + as->id = i; + + p += n; + if (*p) + p++; + state = parse_default; + } else if (state != new_set && av_strstart(p, "seg_duration=", &p)) { + state = parse_seg_duration; + } else if (state != new_set && av_strstart(p, "frag_duration=", &p)) { + state = parse_frag_duration; + } else if (state == parse_seg_duration || state == parse_frag_duration) { + char str[32]; + int64_t usecs = 0; n = strcspn(p, ","); - snprintf(as->id, sizeof(as->id), "%.*s", n, p); + snprintf(str, sizeof(str), "%.*s", n, p); + p += n; + if (*p) + p++; + + ret = av_parse_time(&usecs, str, 1); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Unable to parse option value \"%s\" as duration\n", str); + return ret; + } + + if (state == parse_seg_duration) + as->seg_duration = usecs; + else + as->frag_duration = usecs; + state = parse_default; + } else if (state != new_set && av_strstart(p, "frag_type=", &p)) { + char type_str[16]; + + n = strcspn(p, ","); + snprintf(type_str, sizeof(type_str), "%.*s", n, p); + p += n; + if (*p) + p++; + if (!strcmp(type_str, "duration")) + as->frag_type = FRAG_TYPE_DURATION; + else if (!strcmp(type_str, "pframes")) + as->frag_type = FRAG_TYPE_PFRAMES; + else if (!strcmp(type_str, "every_frame")) + as->frag_type = FRAG_TYPE_EVERY_FRAME; + else if (!strcmp(type_str, "none")) + as->frag_type = FRAG_TYPE_NONE; + else { + av_log(s, AV_LOG_ERROR, "Unable to parse option value \"%s\" as fragment type\n", type_str); + return ret; + } + state = parse_default; + } else if (state != new_set && av_strstart(p, "descriptor=", &p)) { + n = strcspn(p, ">") + 1; //followed by one comma, so plus 1 + if (n < strlen(p)) { + as->descriptor = av_strndup(p, n); + } else { + av_log(s, AV_LOG_ERROR, "Parse error, descriptor string should be a self-closing xml tag\n"); + return AVERROR(EINVAL); + } p += n; if (*p) p++; - state = parse_id; - } else if (state == parse_id && av_strstart(p, "streams=", &p)) { + state = parse_default; + } else if ((state != new_set) && av_strstart(p, "trick_id=", &p)) { + char trick_id_str[10], *end_str; + + n = strcspn(p, ","); + snprintf(trick_id_str, sizeof(trick_id_str), "%.*s", n, p); + p += n; + + as->trick_idx = strtol(trick_id_str, &end_str, 10); + if (trick_id_str == end_str || as->trick_idx < 0) + return AVERROR(EINVAL); + + if (*p) + p++; + state = parse_default; + } else if ((state != new_set) && av_strstart(p, "streams=", &p)) { //descriptor and durations are optional state = parsing_streams; } else if (state == parsing_streams) { AdaptationSet *as = &c->as[c->nb_as - 1]; @@ -928,6 +1115,22 @@ static int parse_adaptation_sets(AVFormatContext *s) return AVERROR(EINVAL); } } + + // check references for trick mode AdaptationSet + for (i = 0; i < c->nb_as; i++) { + as = &c->as[i]; + if (as->trick_idx < 0) + continue; + for (n = 0; n < c->nb_as; n++) { + if (c->as[n].id == as->trick_idx) + break; + } + if (n >= c->nb_as) { + av_log(s, AV_LOG_ERROR, "reference AdaptationSet id \"%d\" not found for trick mode AdaptationSet id \"%d\"\n", as->trick_idx, as->id); + return AVERROR(EINVAL); + } + } + return 0; } @@ -959,8 +1162,13 @@ static int write_manifest(AVFormatContext *s, int final) "\txmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n" "\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n" "\txsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd\"\n" - "\tprofiles=\"urn:mpeg:dash:profile:isoff-live:2011\"\n" - "\ttype=\"%s\"\n", final ? "static" : "dynamic"); + "\tprofiles=\""); + if (c->profile & MPD_PROFILE_DASH) + avio_printf(out, "%s%s", "urn:mpeg:dash:profile:isoff-live:2011", c->profile & MPD_PROFILE_DVB ? "," : "\"\n"); + if (c->profile & MPD_PROFILE_DVB) + avio_printf(out, "%s", "urn:dvb:dash:profile:dvb-dash:2014\"\n"); + avio_printf(out, "\ttype=\"%s\"\n", + final ? "static" : "dynamic"); if (final) { avio_printf(out, "\tmediaPresentationDuration=\""); write_time(out, c->total_duration); @@ -971,10 +1179,11 @@ static int write_manifest(AVFormatContext *s, int final) if (c->use_template && !c->use_timeline) update_period = 500; avio_printf(out, "\tminimumUpdatePeriod=\"PT%"PRId64"S\"\n", update_period); - avio_printf(out, "\tsuggestedPresentationDelay=\"PT%"PRId64"S\"\n", c->last_duration / AV_TIME_BASE); + if (!c->ldash) + avio_printf(out, "\tsuggestedPresentationDelay=\"PT%"PRId64"S\"\n", c->last_duration / AV_TIME_BASE); if (c->availability_start_time[0]) avio_printf(out, "\tavailabilityStartTime=\"%s\"\n", c->availability_start_time); - format_date_now(now_str, sizeof(now_str)); + format_date(now_str, sizeof(now_str), av_gettime()); if (now_str[0]) avio_printf(out, "\tpublishTime=\"%s\"\n", now_str); if (c->window_size && c->use_template) { @@ -983,8 +1192,11 @@ static int write_manifest(AVFormatContext *s, int final) avio_printf(out, "\"\n"); } } + avio_printf(out, "\tmaxSegmentDuration=\""); + write_time(out, c->max_segment_duration); + avio_printf(out, "\"\n"); avio_printf(out, "\tminBufferTime=\""); - write_time(out, c->last_duration * 2); + write_time(out, c->ldash && c->max_gop_size ? c->max_gop_size : c->last_duration * 2); avio_printf(out, "\">\n"); avio_printf(out, "\t\n"); if (title) { @@ -994,6 +1206,19 @@ static int write_manifest(AVFormatContext *s, int final) } avio_printf(out, "\t\n"); + avio_printf(out, "\t\n"); + if (!final && c->target_latency && c->target_latency_refid >= 0) { + avio_printf(out, "\t\ttarget_latency / 1000); + if (s->nb_streams > 1) + avio_printf(out, " referenceId=\"%d\"", c->target_latency_refid); + avio_printf(out, "/>\n"); + } + if (av_cmp_q(c->min_playback_rate, (AVRational) {1, 1}) || + av_cmp_q(c->max_playback_rate, (AVRational) {1, 1})) + avio_printf(out, "\t\t\n", + av_q2d(c->min_playback_rate), av_q2d(c->max_playback_rate)); + avio_printf(out, "\t\n"); + if (c->window_size && s->nb_streams > 0 && c->streams[0].nb_segments > 0 && !c->use_template) { OutputStream *os = &c->streams[0]; int start_index = FFMAX(os->nb_segments - c->window_size, 0); @@ -1019,7 +1244,7 @@ static int write_manifest(AVFormatContext *s, int final) dashenc_io_close(s, &c->mpd_out, temp_filename); if (use_rename) { - if ((ret = avpriv_io_move(temp_filename, s->url)) < 0) + if ((ret = ff_rename(temp_filename, s->url, s)) < 0) return ret; } @@ -1097,11 +1322,11 @@ static int write_manifest(AVFormatContext *s, int final) get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); ff_hls_write_stream_info(st, c->m3u8_out, stream_bitrate, playlist_file, agroup, - codec_str_ptr, NULL); + codec_str_ptr, NULL, NULL); } dashenc_io_close(s, &c->m3u8_out, temp_filename); if (use_rename) - if ((ret = avpriv_io_move(temp_filename, filename_hls)) < 0) + if ((ret = ff_rename(temp_filename, filename_hls, s)) < 0) return ret; c->master_playlist_created = 1; } @@ -1130,6 +1355,10 @@ static int dash_init(AVFormatContext *s) if (c->single_file) c->use_template = 0; + if (!c->profile) { + av_log(s, AV_LOG_ERROR, "At least one profile must be enabled.\n"); + return AVERROR(EINVAL); + } #if FF_API_DASH_MIN_SEG_DURATION if (c->min_seg_duration != 5000000) { av_log(s, AV_LOG_WARNING, "The min_seg_duration option is deprecated and will be removed. Please use the -seg_duration\n"); @@ -1152,6 +1381,16 @@ static int dash_init(AVFormatContext *s) c->lhls = 0; } + if (c->ldash && !c->streaming) { + av_log(s, AV_LOG_WARNING, "LDash option will be ignored as streaming is not enabled\n"); + c->ldash = 0; + } + + if (c->target_latency && !c->streaming) { + av_log(s, AV_LOG_WARNING, "Target latency option will be ignored as streaming is not enabled\n"); + c->target_latency = 0; + } + if (c->global_sidx && !c->single_file) { av_log(s, AV_LOG_WARNING, "Global SIDX option will be ignored as single_file is not enabled\n"); c->global_sidx = 0; @@ -1161,6 +1400,40 @@ static int dash_init(AVFormatContext *s) av_log(s, AV_LOG_WARNING, "Global SIDX option will be ignored as streaming is enabled\n"); c->global_sidx = 0; } + if (c->frag_type == FRAG_TYPE_NONE && c->streaming) { + av_log(s, AV_LOG_VERBOSE, "Changing frag_type from none to every_frame as streaming is enabled\n"); + c->frag_type = FRAG_TYPE_EVERY_FRAME; + } + + if (c->write_prft < 0) { + c->write_prft = c->ldash; + if (c->ldash) + av_log(s, AV_LOG_VERBOSE, "Enabling Producer Reference Time element for Low Latency mode\n"); + } + + if (c->write_prft && !c->utc_timing_url) { + av_log(s, AV_LOG_WARNING, "Producer Reference Time element option will be ignored as utc_timing_url is not set\n"); + c->write_prft = 0; + } + + if (c->write_prft && !c->streaming) { + av_log(s, AV_LOG_WARNING, "Producer Reference Time element option will be ignored as streaming is not enabled\n"); + c->write_prft = 0; + } + + if (c->ldash && !c->write_prft) { + av_log(s, AV_LOG_WARNING, "Low Latency mode enabled without Producer Reference Time element option! Resulting manifest may not be complaint\n"); + } + + if (c->target_latency && !c->write_prft) { + av_log(s, AV_LOG_WARNING, "Target latency option will be ignored as Producer Reference Time element will not be written\n"); + c->target_latency = 0; + } + + 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"); + c->min_playback_rate = c->max_playback_rate = (AVRational) {1, 1}; + } av_strlcpy(c->dirname, s->url, sizeof(c->dirname)); ptr = strrchr(c->dirname, '/'); @@ -1207,10 +1480,6 @@ static int dash_init(AVFormatContext *s) dict_copy_entry(&as->metadata, s->streams[i]->metadata, "language"); dict_copy_entry(&as->metadata, s->streams[i]->metadata, "role"); - ctx = avformat_alloc_context(); - if (!ctx) - return AVERROR(ENOMEM); - if (c->init_seg_name) { os->init_seg_name = av_strireplace(c->init_seg_name, "$ext$", os->extension_name); if (!os->init_seg_name) @@ -1243,10 +1512,13 @@ static int dash_init(AVFormatContext *s) } } + os->ctx = ctx = avformat_alloc_context(); + if (!ctx) + return AVERROR(ENOMEM); + ctx->oformat = av_guess_format(os->format_name, NULL, NULL); if (!ctx->oformat) return AVERROR_MUXER_NOT_FOUND; - os->ctx = ctx; ctx->interrupt_callback = s->interrupt_callback; ctx->opaque = s->opaque; ctx->io_close = s->io_close; @@ -1262,6 +1534,18 @@ static int dash_init(AVFormatContext *s) ctx->avoid_negative_ts = s->avoid_negative_ts; ctx->flags = s->flags; + os->parser = av_parser_init(st->codecpar->codec_id); + if (os->parser) { + os->parser_avctx = avcodec_alloc_context3(NULL); + if (!os->parser_avctx) + return AVERROR(ENOMEM); + ret = avcodec_parameters_to_context(os->parser_avctx, st->codecpar); + if (ret < 0) + return ret; + // We only want to parse frame headers + os->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; + } + if (c->single_file) { if (os->single_file_name) ff_dash_fill_tmpl_params(os->initfile, sizeof(os->initfile), os->single_file_name, i, 0, os->bit_rate, 0); @@ -1285,24 +1569,60 @@ static int dash_init(AVFormatContext *s) return ret; os->init_start_pos = 0; - if (c->format_options_str) { - ret = av_dict_parse_string(&opts, c->format_options_str, "=", ":", 0); - if (ret < 0) - return ret; + av_dict_copy(&opts, c->format_options, 0); + if (!as->seg_duration) + as->seg_duration = c->seg_duration; + if (!as->frag_duration) + as->frag_duration = c->frag_duration; + if (as->frag_type < 0) + as->frag_type = c->frag_type; + os->seg_duration = as->seg_duration; + os->frag_duration = as->frag_duration; + os->frag_type = as->frag_type; + + c->max_segment_duration = FFMAX(c->max_segment_duration, as->seg_duration); + + if (c->profile & MPD_PROFILE_DVB && (os->seg_duration > 15000000 || os->seg_duration < 960000)) { + av_log(s, AV_LOG_ERROR, "Segment duration %"PRId64" is outside the allowed range for DVB-DASH profile\n", os->seg_duration); + return AVERROR(EINVAL); + } + + if (os->frag_type == FRAG_TYPE_DURATION && !os->frag_duration) { + av_log(s, AV_LOG_WARNING, "frag_type set to duration for stream %d but no frag_duration set\n", i); + os->frag_type = c->streaming ? FRAG_TYPE_EVERY_FRAME : FRAG_TYPE_NONE; + } + if (os->frag_type == FRAG_TYPE_DURATION && os->frag_duration > os->seg_duration) { + av_log(s, AV_LOG_ERROR, "Fragment duration %"PRId64" is longer than Segment duration %"PRId64"\n", os->frag_duration, os->seg_duration); + return AVERROR(EINVAL); } + if (os->frag_type == FRAG_TYPE_PFRAMES && (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO || !os->parser)) { + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && !os->parser) + av_log(s, AV_LOG_WARNING, "frag_type set to P-Frame reordering, but no parser found for stream %d\n", i); + os->frag_type = c->streaming ? FRAG_TYPE_EVERY_FRAME : FRAG_TYPE_NONE; + } + if (os->frag_type != FRAG_TYPE_PFRAMES && as->trick_idx < 0) + // Set this now if a parser isn't used + os->coding_dependency = 1; if (os->segment_type == SEGMENT_TYPE_MP4) { if (c->streaming) - // frag_every_frame : Allows lower latency streaming // skip_sidx : Reduce bitrate overhead // skip_trailer : Avoids growing memory usage with time - av_dict_set(&opts, "movflags", "frag_every_frame+dash+delay_moov+skip_sidx+skip_trailer", 0); + av_dict_set(&opts, "movflags", "+dash+delay_moov+skip_sidx+skip_trailer", AV_DICT_APPEND); else { if (c->global_sidx) - av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov+global_sidx+skip_trailer", 0); + av_dict_set(&opts, "movflags", "+dash+delay_moov+global_sidx+skip_trailer", AV_DICT_APPEND); else - av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov+skip_trailer", 0); + av_dict_set(&opts, "movflags", "+dash+delay_moov+skip_trailer", AV_DICT_APPEND); } + if (os->frag_type == FRAG_TYPE_EVERY_FRAME) + av_dict_set(&opts, "movflags", "+frag_every_frame", AV_DICT_APPEND); + else + av_dict_set(&opts, "movflags", "+frag_custom", AV_DICT_APPEND); + if (os->frag_type == FRAG_TYPE_DURATION) + av_dict_set_int(&opts, "frag_duration", os->frag_duration, 0); + if (c->write_prft) + av_dict_set(&opts, "write_prft", "wallclock", 0); } else { av_dict_set_int(&opts, "cluster_time_limit", c->seg_duration / 1000, 0); av_dict_set_int(&opts, "cluster_size_limit", 5 * 1024 * 1024, 0); // set a large cluster size limit @@ -1326,6 +1646,7 @@ static int dash_init(AVFormatContext *s) s->avoid_negative_ts = ctx->avoid_negative_ts; if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { AVRational avg_frame_rate = s->streams[i]->avg_frame_rate; + AVRational par; if (avg_frame_rate.num > 0) { if (av_cmp_q(avg_frame_rate, as->min_frame_rate) < 0) as->min_frame_rate = avg_frame_rate; @@ -1334,6 +1655,27 @@ static int dash_init(AVFormatContext *s) } else { as->ambiguous_frame_rate = 1; } + + if (st->codecpar->width > as->max_width) + as->max_width = st->codecpar->width; + if (st->codecpar->height > as->max_height) + as->max_height = st->codecpar->height; + + if (st->sample_aspect_ratio.num) + os->sar = st->sample_aspect_ratio; + else + os->sar = (AVRational){1,1}; + av_reduce(&par.num, &par.den, + st->codecpar->width * (int64_t)os->sar.num, + st->codecpar->height * (int64_t)os->sar.den, + 1024 * 1024); + + if (as->par.num && av_cmp_q(par, as->par)) { + av_log(s, AV_LOG_ERROR, "Conflicting stream par values in Adaptation Set %d\n", os->as_idx); + return AVERROR(EINVAL); + } + as->par = par; + c->has_video = 1; } @@ -1352,8 +1694,11 @@ static int dash_init(AVFormatContext *s) av_log(s, AV_LOG_WARNING, "no video stream and no seg duration set\n"); return AVERROR(EINVAL); } + if (!c->has_video && c->frag_type == FRAG_TYPE_PFRAMES) + av_log(s, AV_LOG_WARNING, "no video stream and P-frame fragmentation set\n"); c->nr_of_streams_flushed = 0; + c->target_latency_refid = -1; return 0; } @@ -1386,7 +1731,7 @@ static int add_segment(OutputStream *os, const char *file, Segment *seg; if (os->nb_segments >= os->segments_size) { os->segments_size = (os->segments_size + 1) * 2; - if ((err = av_reallocp(&os->segments, sizeof(*os->segments) * + if ((err = av_reallocp_array(&os->segments, sizeof(*os->segments), os->segments_size)) < 0) { os->segments_size = 0; os->nb_segments = 0; @@ -1505,28 +1850,20 @@ static void dashenc_delete_file(AVFormatContext *s, char *filename) { static int dashenc_delete_segment_file(AVFormatContext *s, const char* file) { DASHContext *c = s->priv_data; - size_t dirname_len, file_len; - char filename[1024]; - - dirname_len = strlen(c->dirname); - if (dirname_len >= sizeof(filename)) { - av_log(s, AV_LOG_WARNING, "Cannot delete segments as the directory path is too long: %"PRIu64" characters: %s\n", - (uint64_t)dirname_len, c->dirname); - return AVERROR(ENAMETOOLONG); - } + AVBPrint buf; - memcpy(filename, c->dirname, dirname_len); + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); - file_len = strlen(file); - if ((dirname_len + file_len) >= sizeof(filename)) { - av_log(s, AV_LOG_WARNING, "Cannot delete segments as the path is too long: %"PRIu64" characters: %s%s\n", - (uint64_t)(dirname_len + file_len), c->dirname, file); - return AVERROR(ENAMETOOLONG); + av_bprintf(&buf, "%s%s", c->dirname, file); + if (!av_bprint_is_complete(&buf)) { + av_bprint_finalize(&buf, NULL); + av_log(s, AV_LOG_WARNING, "Out of memory for filename\n"); + return AVERROR(ENOMEM); } - memcpy(filename + dirname_len, file, file_len + 1); // include the terminating zero - dashenc_delete_file(s, filename); + dashenc_delete_file(s, buf.str); + av_bprint_finalize(&buf, NULL); return 0; } @@ -1563,7 +1900,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream) c->streams[stream].first_pts, s->streams[stream]->time_base, AV_TIME_BASE_Q); - next_exp_index = (pts_diff / c->seg_duration) + 1; + next_exp_index = (pts_diff / c->streams[stream].seg_duration) + 1; } } @@ -1571,6 +1908,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream) OutputStream *os = &c->streams[i]; AVStream *st = s->streams[i]; int range_length, index_length = 0; + int64_t duration; if (!os->packets_written) continue; @@ -1579,6 +1917,9 @@ static int dash_flush(AVFormatContext *s, int final, int stream) // Flush all audio streams as well, in sync with video keyframes, // but not the other video streams. if (stream >= 0 && i != stream) { + if (s->streams[stream]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO && + s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) + continue; if (s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) continue; // Make sure we don't flush audio streams multiple times, when @@ -1587,12 +1928,8 @@ static int dash_flush(AVFormatContext *s, int final, int stream) continue; } - if (!c->single_file) { - if (os->segment_type == SEGMENT_TYPE_MP4 && !os->written_len) - write_styp(os->ctx->pb); - } else { + if (c->single_file) snprintf(os->full_path, sizeof(os->full_path), "%s%s", c->dirname, os->initfile); - } ret = flush_dynbuf(c, os, &range_length); if (ret < 0) @@ -1605,24 +1942,24 @@ static int dash_flush(AVFormatContext *s, int final, int stream) dashenc_io_close(s, &os->out, os->temp_path); if (use_rename) { - ret = avpriv_io_move(os->temp_path, os->full_path); + ret = ff_rename(os->temp_path, os->full_path, os->ctx); if (ret < 0) break; } } - if (!os->muxer_overhead) + duration = av_rescale_q(os->max_pts - os->start_pts, st->time_base, AV_TIME_BASE_Q); + os->last_duration = FFMAX(os->last_duration, duration); + + if (!os->muxer_overhead && os->max_pts > os->start_pts) os->muxer_overhead = ((int64_t) (range_length - os->total_pkt_size) * - 8 * AV_TIME_BASE) / - av_rescale_q(os->max_pts - os->start_pts, - st->time_base, AV_TIME_BASE_Q); + 8 * AV_TIME_BASE) / duration; os->total_pkt_size = 0; + os->total_pkt_duration = 0; if (!os->bit_rate) { // calculate average bitrate of first segment - int64_t bitrate = (int64_t) range_length * 8 * AV_TIME_BASE / av_rescale_q(os->max_pts - os->start_pts, - st->time_base, - AV_TIME_BASE_Q); + int64_t bitrate = (int64_t) range_length * 8 * (c->use_timeline ? os->ctx->streams[0]->time_base.den : AV_TIME_BASE) / duration; if (bitrate >= 0) os->bit_rate = bitrate; } @@ -1677,11 +2014,38 @@ static int dash_flush(AVFormatContext *s, int final, int stream) return ret; } +static int dash_parse_prft(DASHContext *c, AVPacket *pkt) +{ + OutputStream *os = &c->streams[pkt->stream_index]; + AVProducerReferenceTime *prft; + int side_data_size; + + prft = (AVProducerReferenceTime *)av_packet_get_side_data(pkt, AV_PKT_DATA_PRFT, &side_data_size); + if (!prft || side_data_size != sizeof(AVProducerReferenceTime) || (prft->flags && prft->flags != 24)) { + // No encoder generated or user provided capture time AVProducerReferenceTime side data. Instead + // of letting the mov muxer generate one, do it here so we can also use it for the manifest. + prft = (AVProducerReferenceTime *)av_packet_new_side_data(pkt, AV_PKT_DATA_PRFT, + sizeof(AVProducerReferenceTime)); + if (!prft) + return AVERROR(ENOMEM); + prft->wallclock = av_gettime(); + prft->flags = 24; + } + if (os->first_pts == AV_NOPTS_VALUE) { + os->producer_reference_time = *prft; + if (c->target_latency_refid < 0) + c->target_latency_refid = pkt->stream_index; + } + + return 0; +} + static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) { DASHContext *c = s->priv_data; AVStream *st = s->streams[pkt->stream_index]; OutputStream *os = &c->streams[pkt->stream_index]; + AdaptationSet *as = &c->as[os->as_idx - 1]; int64_t seg_end_duration, elapsed_duration; int ret; @@ -1707,54 +2071,94 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) pkt->dts = 0; } - if (os->first_pts == AV_NOPTS_VALUE) + if (c->write_prft) { + ret = dash_parse_prft(c, pkt); + if (ret < 0) + return ret; + } + + if (os->first_pts == AV_NOPTS_VALUE) { os->first_pts = pkt->pts; + } os->last_pts = pkt->pts; if (!c->availability_start_time[0]) { int64_t start_time_us = av_gettime(); c->start_time_s = start_time_us / 1000000; - format_date_now(c->availability_start_time, - sizeof(c->availability_start_time)); + format_date(c->availability_start_time, + sizeof(c->availability_start_time), start_time_us); } - if (!os->availability_time_offset && pkt->duration) { - int64_t frame_duration = av_rescale_q(pkt->duration, st->time_base, - AV_TIME_BASE_Q); - os->availability_time_offset = ((double) c->seg_duration - + if (!os->packets_written) + os->availability_time_offset = 0; + + if (!os->availability_time_offset && + ((os->frag_type == FRAG_TYPE_DURATION && os->seg_duration != os->frag_duration) || + (os->frag_type == FRAG_TYPE_EVERY_FRAME && pkt->duration))) { + AdaptationSet *as = &c->as[os->as_idx - 1]; + int64_t frame_duration = 0; + + switch (os->frag_type) { + case FRAG_TYPE_DURATION: + frame_duration = os->frag_duration; + break; + case FRAG_TYPE_EVERY_FRAME: + frame_duration = av_rescale_q(pkt->duration, st->time_base, AV_TIME_BASE_Q); + break; + } + + os->availability_time_offset = ((double) os->seg_duration - frame_duration) / AV_TIME_BASE; + as->max_frag_duration = FFMAX(frame_duration, as->max_frag_duration); } if (c->use_template && !c->use_timeline) { elapsed_duration = pkt->pts - os->first_pts; - seg_end_duration = (int64_t) os->segment_index * c->seg_duration; + seg_end_duration = (int64_t) os->segment_index * os->seg_duration; } else { elapsed_duration = pkt->pts - os->start_pts; - seg_end_duration = c->seg_duration; + seg_end_duration = os->seg_duration; } - if ((!c->has_video || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && - pkt->flags & AV_PKT_FLAG_KEY && os->packets_written && - av_compare_ts(elapsed_duration, st->time_base, - seg_end_duration, AV_TIME_BASE_Q) >= 0) { - int64_t prev_duration = c->last_duration; + if (os->parser && + (os->frag_type == FRAG_TYPE_PFRAMES || + as->trick_idx >= 0)) { + // Parse the packets only in scenarios where it's needed + uint8_t *data; + int size; + av_parser_parse2(os->parser, os->parser_avctx, + &data, &size, pkt->data, pkt->size, + pkt->pts, pkt->dts, pkt->pos); - c->last_duration = av_rescale_q(pkt->pts - os->start_pts, - st->time_base, - AV_TIME_BASE_Q); - c->total_duration = av_rescale_q(pkt->pts - os->first_pts, - st->time_base, - AV_TIME_BASE_Q); + os->coding_dependency |= os->parser->pict_type != AV_PICTURE_TYPE_I; + } - if ((!c->use_timeline || !c->use_template) && prev_duration) { - if (c->last_duration < prev_duration*9/10 || - c->last_duration > prev_duration*11/10) { - av_log(s, AV_LOG_WARNING, - "Segment durations differ too much, enable use_timeline " - "and use_template, or keep a stricter keyframe interval\n"); + if (pkt->flags & AV_PKT_FLAG_KEY && os->packets_written && + av_compare_ts(elapsed_duration, st->time_base, + seg_end_duration, AV_TIME_BASE_Q) >= 0) { + if (!c->has_video || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + c->last_duration = av_rescale_q(pkt->pts - os->start_pts, + st->time_base, + AV_TIME_BASE_Q); + c->total_duration = av_rescale_q(pkt->pts - os->first_pts, + st->time_base, + AV_TIME_BASE_Q); + + if ((!c->use_timeline || !c->use_template) && os->last_duration) { + if (c->last_duration < os->last_duration*9/10 || + c->last_duration > os->last_duration*11/10) { + av_log(s, AV_LOG_WARNING, + "Segment durations differ too much, enable use_timeline " + "and use_template, or keep a stricter keyframe interval\n"); + } } } + if (c->write_prft && os->producer_reference_time.wallclock && !os->producer_reference_time_str[0]) + format_date(os->producer_reference_time_str, + sizeof(os->producer_reference_time_str), + os->producer_reference_time.wallclock); + if ((ret = dash_flush(s, 0, pkt->stream_index)) < 0) return ret; } @@ -1772,11 +2176,42 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) os->max_pts = pkt->pts + pkt->duration; else os->max_pts = FFMAX(os->max_pts, pkt->pts + pkt->duration); - os->packets_written++; - os->total_pkt_size += pkt->size; + + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && + os->frag_type == FRAG_TYPE_PFRAMES && + os->packets_written) { + av_assert0(os->parser); + if ((os->parser->pict_type == AV_PICTURE_TYPE_P && + st->codecpar->video_delay && + !(os->last_flags & AV_PKT_FLAG_KEY)) || + pkt->flags & AV_PKT_FLAG_KEY) { + ret = av_write_frame(os->ctx, NULL); + if (ret < 0) + return ret; + + if (!os->availability_time_offset) { + int64_t frag_duration = av_rescale_q(os->total_pkt_duration, st->time_base, + AV_TIME_BASE_Q); + os->availability_time_offset = ((double) os->seg_duration - + frag_duration) / AV_TIME_BASE; + as->max_frag_duration = FFMAX(frag_duration, as->max_frag_duration); + } + } + } + + if (pkt->flags & AV_PKT_FLAG_KEY && (os->packets_written || os->nb_segments) && !os->gop_size && as->trick_idx < 0) { + os->gop_size = os->last_duration + av_rescale_q(os->total_pkt_duration, st->time_base, AV_TIME_BASE_Q); + c->max_gop_size = FFMAX(c->max_gop_size, os->gop_size); + } + if ((ret = ff_write_chained(os->ctx, 0, pkt, s, 0)) < 0) return ret; + os->packets_written++; + os->total_pkt_size += pkt->size; + os->total_pkt_duration += pkt->duration; + os->last_flags = pkt->flags; + if (!os->init_range_length) flush_init_segment(s, os); @@ -1785,6 +2220,8 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) AVDictionary *opts = NULL; const char *proto = avio_find_protocol_name(s->url); int use_rename = proto && !strcmp(proto, "file"); + if (os->segment_type == SEGMENT_TYPE_MP4) + write_styp(os->ctx->pb); os->filename[0] = os->full_path[0] = os->temp_path[0] = '\0'; ff_dash_fill_tmpl_params(os->filename, sizeof(os->filename), os->media_seg_name, pkt->stream_index, @@ -1809,8 +2246,6 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) if (c->streaming && os->segment_type == SEGMENT_TYPE_MP4) { int len = 0; uint8_t *buf = NULL; - if (!os->written_len) - write_styp(os->ctx->pb); avio_flush(os->ctx->pb); len = avio_get_dyn_buf (os->ctx->pb, &buf); if (os->out) { @@ -1878,10 +2313,8 @@ static int dash_check_bitstream(struct AVFormatContext *s, const AVPacket *avpkt if (ret == 1) { AVStream *st = s->streams[avpkt->stream_index]; AVStream *ost = oc->streams[0]; - st->internal->bsfcs = ost->internal->bsfcs; - st->internal->nb_bsfcs = ost->internal->nb_bsfcs; - ost->internal->bsfcs = NULL; - ost->internal->nb_bsfcs = 0; + st->internal->bsfc = ost->internal->bsfc; + ost->internal->bsfc = NULL; } return ret; } @@ -1898,6 +2331,12 @@ static const AVOption options[] = { { "min_seg_duration", "minimum segment duration (in microseconds) (will be deprecated)", OFFSET(min_seg_duration), AV_OPT_TYPE_INT, { .i64 = 5000000 }, 0, INT_MAX, E }, #endif { "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 }, @@ -1913,7 +2352,7 @@ static const AVOption options[] = { { "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_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, 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}, { "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"}, @@ -1921,7 +2360,16 @@ static const AVOption options[] = { { "webm", "make segment file in WebM format", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_WEBM }, 0, UINT_MAX, E, "segment_type"}, { "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 }, + { "ldash", "Enable Low-latency dash. Constrains the value of a few elements", OFFSET(ldash), 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 }, { NULL }, }; diff --git a/libavformat/derf.c b/libavformat/derf.c new file mode 100644 index 00000000000..58bbf5b791e --- /dev/null +++ b/libavformat/derf.c @@ -0,0 +1,79 @@ +/* + * DERF demuxer + * Copyright (c) 2020 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 "avformat.h" +#include "internal.h" +#include "pcm.h" + +static int derf_probe(const AVProbeData *p) +{ + if (AV_RL32(p->buf) != MKTAG('D','E','R','F')) + return 0; + if (AV_RL32(p->buf+4) != 1 && AV_RL32(p->buf+4) != 2) + return 0; + + return AVPROBE_SCORE_MAX / 3 * 2; +} + +static int derf_read_header(AVFormatContext *s) +{ + unsigned data_size; + AVIOContext *pb = s->pb; + AVCodecParameters *par; + AVStream *st; + + avio_skip(pb, 4); + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + par = st->codecpar; + par->codec_type = AVMEDIA_TYPE_AUDIO; + par->codec_id = AV_CODEC_ID_DERF_DPCM; + par->format = AV_SAMPLE_FMT_S16; + par->channels = avio_rl32(pb); + if (par->channels != 1 && par->channels != 2) + return AVERROR_INVALIDDATA; + if (par->channels == 1) + par->channel_layout = AV_CH_LAYOUT_MONO; + else if (par->channels == 2) + par->channel_layout = AV_CH_LAYOUT_STEREO; + data_size = avio_rl32(pb); + st->duration = data_size / par->channels; + par->sample_rate = 22050; + par->block_align = 1; + + avpriv_set_pts_info(st, 64, 1, par->sample_rate); + + return 0; +} + +AVInputFormat ff_derf_demuxer = { + .name = "derf", + .long_name = NULL_IF_CONFIG_SMALL("Xilam DERF"), + .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 a5ecfd9b15d..bd4ef2dafeb 100644 --- a/libavformat/dfa.c +++ b/libavformat/dfa.c @@ -40,7 +40,7 @@ static int dfa_read_header(AVFormatContext *s) { AVIOContext *pb = s->pb; AVStream *st; - int frames; + int frames, ret; int version; uint32_t mspf; @@ -69,8 +69,8 @@ static int dfa_read_header(AVFormatContext *s) avio_skip(pb, 128 - 16); // padding st->duration = frames; - if (ff_alloc_extradata(st->codecpar, 2)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, 2)) < 0) + return ret; AV_WL16(st->codecpar->extradata, version); if (version == 0x100) st->sample_aspect_ratio = (AVRational){2, 1}; @@ -93,7 +93,6 @@ static int dfa_read_packet(AVFormatContext *s, AVPacket *pkt) if (!first) { ret = av_append_packet(pb, pkt, 12); if (ret < 0) { - av_packet_unref(pkt); return ret; } } else @@ -101,7 +100,6 @@ static int dfa_read_packet(AVFormatContext *s, AVPacket *pkt) frame_size = AV_RL32(pkt->data + pkt->size - 8); if (frame_size > INT_MAX - 4) { av_log(s, AV_LOG_ERROR, "Too large chunk size: %"PRIu32"\n", frame_size); - av_packet_unref(pkt); return AVERROR(EIO); } if (AV_RL32(pkt->data + pkt->size - 12) == MKTAG('E', 'O', 'F', 'R')) { @@ -115,7 +113,6 @@ static int dfa_read_packet(AVFormatContext *s, AVPacket *pkt) } ret = av_append_packet(pb, pkt, frame_size); if (ret < 0) { - av_packet_unref(pkt); return ret; } } diff --git a/libavformat/dhav.c b/libavformat/dhav.c index e6f61eeb666..5e9abdb6113 100644 --- a/libavformat/dhav.c +++ b/libavformat/dhav.c @@ -39,6 +39,8 @@ typedef struct DHAVContext { int audio_channels; int audio_codec; int sample_rate; + int64_t last_good_pos; + int64_t duration; int video_stream_index; int audio_stream_index; @@ -46,6 +48,7 @@ typedef struct DHAVContext { typedef struct DHAVStream { int64_t last_timestamp; + int64_t last_time; int64_t pts; } DHAVStream; @@ -65,61 +68,6 @@ static int dhav_probe(const AVProbeData *p) return 0; } -static int dhav_read_header(AVFormatContext *s) -{ - DHAVContext *dhav = s->priv_data; - uint8_t signature[5]; - - ffio_ensure_seekback(s->pb, 5); - avio_read(s->pb, signature, sizeof(signature)); - if (!memcmp(signature, "DAHUA", 5)) - avio_skip(s->pb, 0x400 - 5); - else - avio_seek(s->pb, -5, SEEK_CUR); - - s->ctx_flags |= AVFMTCTX_NOHEADER; - dhav->video_stream_index = -1; - dhav->audio_stream_index = -1; - - return 0; -} - -static int64_t get_pts(AVFormatContext *s, DHAVStream *st) -{ - DHAVContext *dhav = s->priv_data; - /* - int year, month, day, hour, min, sec; - struct tm timeinfo; - - sec = dhav->date & 0x3F; - min = (dhav->date >> 6) & 0x3F; - hour = (dhav->date >> 12) & 0x1F; - day = (dhav->date >> 17) & 0x1F; - month = (dhav->date >> 22) & 0x0F; - year = ((dhav->date >> 26) & 0x3F) + 2000; - - timeinfo.tm_year = year - 1900; - timeinfo.tm_mon = month - 1; - timeinfo.tm_mday = day; - timeinfo.tm_hour = hour; - timeinfo.tm_min = min; - timeinfo.tm_sec = sec;*/ - - if (st->last_timestamp == AV_NOPTS_VALUE) { - st->last_timestamp = dhav->timestamp; - } - - if (st->last_timestamp <= dhav->timestamp) { - st->pts += dhav->timestamp - st->last_timestamp; - } else { - st->pts += 65535 + dhav->timestamp - st->last_timestamp; - } - - st->last_timestamp = dhav->timestamp; - - return st->pts; -} - static const uint32_t sample_rates[] = { 8000, 4000, 8000, 11025, 16000, 20000, 22050, 32000, 44100, 48000, @@ -129,26 +77,26 @@ static const uint32_t sample_rates[] = { static int parse_ext(AVFormatContext *s, int length) { DHAVContext *dhav = s->priv_data; - int index; + int index, ret = 0; while (length > 0) { int type = avio_r8(s->pb); switch (type) { case 0x80: - avio_skip(s->pb, 1); + ret = avio_skip(s->pb, 1); dhav->width = 8 * avio_r8(s->pb); dhav->height = 8 * avio_r8(s->pb); length -= 4; break; case 0x81: - avio_skip(s->pb, 1); + ret = avio_skip(s->pb, 1); dhav->video_codec = avio_r8(s->pb); dhav->frame_rate = avio_r8(s->pb); length -= 4; break; case 0x82: - avio_skip(s->pb, 3); + ret = avio_skip(s->pb, 3); dhav->width = avio_rl16(s->pb); dhav->height = avio_rl16(s->pb); length -= 8; @@ -165,11 +113,11 @@ static int parse_ext(AVFormatContext *s, int length) length -= 4; break; case 0x88: - avio_skip(s->pb, 7); + ret = avio_skip(s->pb, 7); length -= 8; break; case 0x8c: - avio_skip(s->pb, 1); + ret = avio_skip(s->pb, 1); dhav->audio_channels = avio_r8(s->pb); dhav->audio_codec = avio_r8(s->pb); index = avio_r8(s->pb); @@ -178,7 +126,7 @@ static int parse_ext(AVFormatContext *s, int length) } else { dhav->sample_rate = 8000; } - avio_skip(s->pb, 3); + ret = avio_skip(s->pb, 3); length -= 8; break; case 0x91: @@ -188,7 +136,7 @@ static int parse_ext(AVFormatContext *s, int length) case 0x9a: case 0x9b: // sample aspect ratio case 0xb3: - avio_skip(s->pb, 7); + ret = avio_skip(s->pb, 7); length -= 8; break; case 0x84: @@ -199,14 +147,17 @@ static int parse_ext(AVFormatContext *s, int length) case 0xa0: case 0xb2: case 0xb4: - avio_skip(s->pb, 3); + ret = avio_skip(s->pb, 3); length -= 4; break; default: av_log(s, AV_LOG_INFO, "Unknown type: %X, skipping rest of header.\n", type); - avio_skip(s->pb, length - 1); + ret = avio_skip(s->pb, length - 1); length = 0; } + + if (ret < 0) + return ret; } return 0; @@ -215,33 +166,44 @@ static int parse_ext(AVFormatContext *s, int length) static int read_chunk(AVFormatContext *s) { DHAVContext *dhav = s->priv_data; - unsigned frame_length, ext_length; + int frame_length, ext_length; int64_t start, end; int ret; - start = avio_tell(s->pb); - if (avio_feof(s->pb)) return AVERROR_EOF; - if (avio_rl32(s->pb) != MKTAG('D','H','A','V')) - return AVERROR_INVALIDDATA; + if (avio_rl32(s->pb) != MKTAG('D','H','A','V')) { + dhav->last_good_pos += 0x8000; + avio_seek(s->pb, dhav->last_good_pos, SEEK_SET); + + while (avio_rl32(s->pb) != MKTAG('D','H','A','V')) { + if (avio_feof(s->pb)) + return AVERROR_EOF; + dhav->last_good_pos += 0x8000; + ret = avio_skip(s->pb, 0x8000 - 4); + if (ret < 0) + return ret; + } + } + start = avio_tell(s->pb) - 4; + dhav->last_good_pos = start; dhav->type = avio_r8(s->pb); dhav->subtype = avio_r8(s->pb); dhav->channel = avio_r8(s->pb); dhav->frame_subnumber = avio_r8(s->pb); dhav->frame_number = avio_rl32(s->pb); frame_length = avio_rl32(s->pb); + dhav->date = avio_rl32(s->pb); if (frame_length < 24) return AVERROR_INVALIDDATA; if (dhav->type == 0xf1) { - avio_skip(s->pb, frame_length - 16); - return 0; + ret = avio_skip(s->pb, frame_length - 20); + return ret < 0 ? ret : 0; } - dhav->date = avio_rl32(s->pb); dhav->timestamp = avio_rl16(s->pb); ext_length = avio_r8(s->pb); avio_skip(s->pb, 1); // checksum @@ -255,14 +217,127 @@ static int read_chunk(AVFormatContext *s) return frame_length - 8 - (end - start); } -static int dhav_read_packet(AVFormatContext *s, AVPacket *pkt) +static void get_timeinfo(unsigned date, struct tm *timeinfo) +{ + int year, month, day, hour, min, sec; + + sec = date & 0x3F; + min = (date >> 6) & 0x3F; + hour = (date >> 12) & 0x1F; + day = (date >> 17) & 0x1F; + month = (date >> 22) & 0x0F; + year = ((date >> 26) & 0x3F) + 2000; + + timeinfo->tm_year = year - 1900; + timeinfo->tm_mon = month - 1; + timeinfo->tm_mday = day; + timeinfo->tm_hour = hour; + timeinfo->tm_min = min; + timeinfo->tm_sec = sec; +} + +static int64_t get_duration(AVFormatContext *s) { DHAVContext *dhav = s->priv_data; - int64_t start; - int ret; + int64_t start_pos = avio_tell(s->pb); + int64_t start = 0, end = 0; + struct tm timeinfo; + + if (!s->pb->seekable) + return 0; + + avio_seek(s->pb, avio_size(s->pb) - 8, SEEK_SET); + if (avio_rl32(s->pb) == MKTAG('d','h','a','v')) { + int seek_back = avio_rl32(s->pb); + + avio_seek(s->pb, -seek_back, SEEK_CUR); + read_chunk(s); + get_timeinfo(dhav->date, &timeinfo); + end = av_timegm(&timeinfo) * 1000LL; + } else { + avio_seek(s->pb, start_pos, SEEK_SET); + return 0; + } + + avio_seek(s->pb, start_pos, SEEK_SET); - start = avio_tell(s->pb); + read_chunk(s); + get_timeinfo(dhav->date, &timeinfo); + start = av_timegm(&timeinfo) * 1000LL; + + avio_seek(s->pb, start_pos, SEEK_SET); + + return end - start; +} +static int dhav_read_header(AVFormatContext *s) +{ + DHAVContext *dhav = s->priv_data; + uint8_t signature[5]; + + ffio_ensure_seekback(s->pb, 5); + avio_read(s->pb, signature, sizeof(signature)); + if (!memcmp(signature, "DAHUA", 5)) { + avio_skip(s->pb, 0x400 - 5); + dhav->last_good_pos = avio_tell(s->pb); + } else { + if (!memcmp(signature, "DHAV", 4)) { + avio_seek(s->pb, -5, SEEK_CUR); + dhav->last_good_pos = avio_tell(s->pb); + } else if (s->pb->seekable) { + avio_seek(s->pb, avio_size(s->pb) - 8, SEEK_SET); + while (avio_rl32(s->pb) == MKTAG('d','h','a','v')) { + int seek_back; + + seek_back = avio_rl32(s->pb) + 8; + dhav->last_good_pos = avio_tell(s->pb); + avio_seek(s->pb, -seek_back, SEEK_CUR); + } + avio_seek(s->pb, dhav->last_good_pos, SEEK_SET); + } + } + + dhav->duration = get_duration(s); + dhav->last_good_pos = avio_tell(s->pb); + s->ctx_flags |= AVFMTCTX_NOHEADER; + dhav->video_stream_index = -1; + dhav->audio_stream_index = -1; + + return 0; +} + +static int64_t get_pts(AVFormatContext *s, int stream_index) +{ + DHAVStream *dst = s->streams[stream_index]->priv_data; + DHAVContext *dhav = s->priv_data; + struct tm timeinfo; + time_t t; + + get_timeinfo(dhav->date, &timeinfo); + + t = av_timegm(&timeinfo); + if (dst->last_time == t) { + int64_t diff = dhav->timestamp - dst->last_timestamp; + + if (diff < 0) + diff += 65535; + dst->pts += diff; + } else { + dst->pts = t * 1000LL; + } + + dst->last_time = t; + dst->last_timestamp = dhav->timestamp; + + return dst->pts; +} + +static int dhav_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + DHAVContext *dhav = s->priv_data; + int size, ret, stream_index; + +retry: while ((ret = read_chunk(s)) == 0) ; @@ -286,6 +361,7 @@ static int dhav_read_packet(AVFormatContext *s, AVPacket *pkt) case 0xc: st->codecpar->codec_id = AV_CODEC_ID_HEVC; break; default: avpriv_request_sample(s, "Unknown video codec %X\n", dhav->video_codec); } + st->duration = dhav->duration; st->codecpar->width = dhav->width; st->codecpar->height = dhav->height; st->avg_frame_rate.num = dhav->frame_rate; @@ -293,7 +369,7 @@ static int dhav_read_packet(AVFormatContext *s, AVPacket *pkt) st->priv_data = dst = av_mallocz(sizeof(DHAVStream)); if (!st->priv_data) return AVERROR(ENOMEM); - dst->last_timestamp = AV_NOPTS_VALUE; + dst->last_time = AV_NOPTS_VALUE; dhav->video_stream_index = st->index; avpriv_set_pts_info(st, 64, 1, 1000); @@ -318,30 +394,39 @@ static int dhav_read_packet(AVFormatContext *s, AVPacket *pkt) case 0x0d: st->codecpar->codec_id = AV_CODEC_ID_ADPCM_MS; break; default: avpriv_request_sample(s, "Unknown audio codec %X\n", dhav->audio_codec); } + st->duration = dhav->duration; st->codecpar->channels = dhav->audio_channels; st->codecpar->sample_rate = dhav->sample_rate; st->priv_data = dst = av_mallocz(sizeof(DHAVStream)); if (!st->priv_data) return AVERROR(ENOMEM); - dst->last_timestamp = AV_NOPTS_VALUE; + dst->last_time = AV_NOPTS_VALUE; dhav->audio_stream_index = st->index; avpriv_set_pts_info(st, 64, 1, 1000); } - ret = av_get_packet(s->pb, pkt, ret); + stream_index = dhav->type == 0xf0 ? dhav->audio_stream_index : dhav->video_stream_index; + if (stream_index < 0) { + avio_skip(s->pb, ret); + if (avio_rl32(s->pb) == MKTAG('d','h','a','v')) + avio_skip(s->pb, 4); + goto retry; + } + + size = ret; + ret = av_get_packet(s->pb, pkt, size); if (ret < 0) return ret; - pkt->stream_index = dhav->type == 0xf0 ? dhav->audio_stream_index : dhav->video_stream_index; + pkt->stream_index = stream_index; if (dhav->type != 0xfc) pkt->flags |= AV_PKT_FLAG_KEY; - if (pkt->stream_index >= 0) - pkt->pts = get_pts(s, s->streams[pkt->stream_index]->priv_data); pkt->duration = 1; - pkt->pos = start; - if (avio_rl32(s->pb) != MKTAG('d','h','a','v')) - return AVERROR_INVALIDDATA; - avio_skip(s->pb, 4); + if (pkt->stream_index >= 0) + pkt->pts = get_pts(s, pkt->stream_index); + pkt->pos = dhav->last_good_pos; + if (avio_rl32(s->pb) == MKTAG('d','h','a','v')) + avio_skip(s->pb, 4); return ret; } @@ -349,6 +434,7 @@ static int dhav_read_packet(AVFormatContext *s, AVPacket *pkt) static int dhav_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { + DHAVContext *dhav = s->priv_data; AVStream *st = s->streams[stream_index]; int index = av_index_search_timestamp(st, timestamp, flags); int64_t pts; @@ -365,8 +451,9 @@ static int dhav_read_seek(AVFormatContext *s, int stream_index, DHAVStream *dst = st->priv_data; dst->pts = pts; - dst->last_timestamp = AV_NOPTS_VALUE; + dst->last_time = AV_NOPTS_VALUE; } + dhav->last_good_pos = avio_tell(s->pb); return 0; } @@ -380,5 +467,5 @@ AVInputFormat ff_dhav_demuxer = { .read_packet = dhav_read_packet, .read_seek = dhav_read_seek, .extensions = "dav", - .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK, + .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK | AVFMT_TS_DISCONT | AVFMT_TS_NONSTRICT, }; diff --git a/libavformat/dsfdec.c b/libavformat/dsfdec.c index afb24634b28..c9740cf28f6 100644 --- a/libavformat/dsfdec.c +++ b/libavformat/dsfdec.c @@ -56,8 +56,8 @@ static void read_id3(AVFormatContext *s, uint64_t id3pos) ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, 0); if (id3v2_extra_meta) { - ff_id3v2_parse_apic(s, &id3v2_extra_meta); - ff_id3v2_parse_chapters(s, &id3v2_extra_meta); + ff_id3v2_parse_apic(s, id3v2_extra_meta); + ff_id3v2_parse_chapters(s, id3v2_extra_meta); } ff_id3v2_free_extra_meta(&id3v2_extra_meta); } @@ -130,6 +130,7 @@ static int dsf_read_header(AVFormatContext *s) } st->codecpar->block_align *= st->codecpar->channels; st->codecpar->bit_rate = st->codecpar->channels * st->codecpar->sample_rate * 8LL; + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); avio_skip(pb, 4); /* data chunk */ @@ -150,11 +151,11 @@ static int dsf_read_packet(AVFormatContext *s, AVPacket *pkt) AVIOContext *pb = s->pb; AVStream *st = s->streams[0]; int64_t pos = avio_tell(pb); + int ret; if (pos >= dsf->data_end) return AVERROR_EOF; - pkt->stream_index = 0; if (dsf->data_size > dsf->audio_size) { int last_packet = pos == (dsf->data_end - st->codecpar->block_align); @@ -168,8 +169,8 @@ static int dsf_read_packet(AVFormatContext *s, AVPacket *pkt) if (packet_size <= 0 || skip_size <= 0) return AVERROR_INVALIDDATA; - if (av_new_packet(pkt, packet_size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, packet_size)) < 0) + return ret; dst = pkt->data; for (ch = 0; ch < st->codecpar->channels; ch++) { ret = avio_read(pb, dst, packet_size / st->codecpar->channels); @@ -180,10 +181,22 @@ static int dsf_read_packet(AVFormatContext *s, AVPacket *pkt) avio_skip(pb, skip_size / st->codecpar->channels); } + pkt->pos = pos; + pkt->stream_index = 0; + pkt->pts = (pos - s->internal->data_offset) / st->codecpar->channels; + pkt->duration = packet_size / st->codecpar->channels; return 0; } } - return av_get_packet(pb, pkt, FFMIN(dsf->data_end - pos, st->codecpar->block_align)); + ret = av_get_packet(pb, pkt, FFMIN(dsf->data_end - pos, st->codecpar->block_align)); + if (ret < 0) + return ret; + + pkt->stream_index = 0; + pkt->pts = (pos - s->internal->data_offset) / st->codecpar->channels; + pkt->duration = st->codecpar->block_align / st->codecpar->channels; + + return 0; } AVInputFormat ff_dsf_demuxer = { diff --git a/libavformat/dsicin.c b/libavformat/dsicin.c index 244622ee393..b18f43b9a0e 100644 --- a/libavformat/dsicin.c +++ b/libavformat/dsicin.c @@ -200,7 +200,6 @@ static int cin_read_packet(AVFormatContext *s, AVPacket *pkt) ret = avio_read(pb, &pkt->data[4], pkt_size); if (ret < 0) { - av_packet_unref(pkt); return ret; } if (ret < pkt_size) diff --git a/libavformat/dss.c b/libavformat/dss.c index e5b0be9debc..05850491302 100644 --- a/libavformat/dss.c +++ b/libavformat/dss.c @@ -19,8 +19,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/attributes.h" -#include "libavutil/bswap.h" #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" @@ -52,7 +50,7 @@ typedef struct DSSDemuxContext { int counter; int swap; int dss_sp_swap_byte; - int8_t *dss_sp_buf; + int8_t dss_sp_buf[DSS_FRAME_SIZE + 1]; int packet_size; int dss_header_size; @@ -105,15 +103,11 @@ static int dss_read_metadata_string(AVFormatContext *s, unsigned int offset, ret = avio_read(s->pb, value, size); if (ret < size) { - ret = ret < 0 ? ret : AVERROR_EOF; - goto exit; + av_free(value); + return ret < 0 ? ret : AVERROR_EOF; } - ret = av_dict_set(&s->metadata, key, value, 0); - -exit: - av_free(value); - return ret; + return av_dict_set(&s->metadata, key, value, AV_DICT_DONT_STRDUP_VAL); } static int dss_read_header(AVFormatContext *s) @@ -174,10 +168,6 @@ static int dss_read_header(AVFormatContext *s) ctx->counter = 0; ctx->swap = 0; - ctx->dss_sp_buf = av_malloc(DSS_FRAME_SIZE + 1); - if (!ctx->dss_sp_buf) - return AVERROR(ENOMEM); - return 0; } @@ -261,14 +251,12 @@ static int dss_sp_read_packet(AVFormatContext *s, AVPacket *pkt) dss_sp_byte_swap(ctx, pkt->data, ctx->dss_sp_buf); if (ctx->dss_sp_swap_byte < 0) { - ret = AVERROR(EAGAIN); - goto error_eof; + return AVERROR(EAGAIN); } return pkt->size; error_eof: - av_packet_unref(pkt); return ret < 0 ? ret : AVERROR_EOF; } @@ -310,7 +298,6 @@ static int dss_723_1_read_packet(AVFormatContext *s, AVPacket *pkt) ret = avio_read(s->pb, pkt->data + offset, size2 - offset); if (ret < size2 - offset) { - av_packet_unref(pkt); return ret < 0 ? ret : AVERROR_EOF; } @@ -320,7 +307,6 @@ static int dss_723_1_read_packet(AVFormatContext *s, AVPacket *pkt) ret = avio_read(s->pb, pkt->data + offset, size - offset); if (ret < size - offset) { - av_packet_unref(pkt); return ret < 0 ? ret : AVERROR_EOF; } @@ -337,15 +323,6 @@ static int dss_read_packet(AVFormatContext *s, AVPacket *pkt) return dss_723_1_read_packet(s, pkt); } -static int dss_read_close(AVFormatContext *s) -{ - DSSDemuxContext *ctx = s->priv_data; - - av_freep(&ctx->dss_sp_buf); - - return 0; -} - static int dss_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { @@ -392,7 +369,6 @@ AVInputFormat ff_dss_demuxer = { .read_probe = dss_probe, .read_header = dss_read_header, .read_packet = dss_read_packet, - .read_close = dss_read_close, .read_seek = dss_read_seek, .extensions = "dss" }; diff --git a/libavformat/dtsdec.c b/libavformat/dtsdec.c index ab59a56dfce..0215ee18bde 100644 --- a/libavformat/dtsdec.c +++ b/libavformat/dtsdec.c @@ -37,6 +37,7 @@ static int dts_probe(const AVProbeData *p) int exss_markers = 0, exss_nextpos = 0; int sum, max, pos, ret, i; int64_t diff = 0; + int diffcount = 1; uint8_t hdr[DCA_CORE_FRAME_HEADER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE] = { 0 }; for (pos = FFMIN(4096, p->buf_size); pos < p->buf_size - 2; pos += 2) { @@ -47,8 +48,12 @@ static int dts_probe(const AVProbeData *p) bufp = buf = p->buf + pos; state = (state << 16) | bytestream_get_be16(&bufp); - if (pos >= 4) - diff += FFABS(((int16_t)AV_RL16(buf)) - (int16_t)AV_RL16(buf-4)); + if (pos >= 4) { + if (AV_RL16(buf) || AV_RL16(buf-4)) { + diff += FFABS(((int16_t)AV_RL16(buf)) - (int16_t)AV_RL16(buf-4)); + diffcount ++; + } + } /* extension substream (EXSS) */ if (state == DCA_SYNCWORD_SUBSTREAM) { @@ -121,7 +126,7 @@ static int dts_probe(const AVProbeData *p) if (markers[max] > 3 && p->buf_size / markers[max] < 32*1024 && markers[max] * 4 > sum * 3 && - diff / p->buf_size > 200) + diff / diffcount > 600) return AVPROBE_SCORE_EXTENSION + 1; return 0; diff --git a/libavformat/dump.c b/libavformat/dump.c index 1c446560710..06bafc272d8 100644 --- a/libavformat/dump.c +++ b/libavformat/dump.c @@ -27,6 +27,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/log.h" #include "libavutil/mastering_display_metadata.h" +#include "libavutil/dovi_meta.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" #include "libavutil/avstring.h" @@ -210,7 +211,7 @@ static void dump_paramchange(void *ctx, AVPacketSideData *sd) return; fail: - av_log(ctx, AV_LOG_INFO, "unknown param"); + av_log(ctx, AV_LOG_ERROR, "unknown param"); } /* replaygain side data*/ @@ -239,7 +240,7 @@ static void dump_replaygain(void *ctx, AVPacketSideData *sd) AVReplayGain *rg; if (sd->size < sizeof(*rg)) { - av_log(ctx, AV_LOG_INFO, "invalid data"); + av_log(ctx, AV_LOG_ERROR, "invalid data"); return; } rg = (AVReplayGain*)sd->data; @@ -255,7 +256,7 @@ static void dump_stereo3d(void *ctx, AVPacketSideData *sd) AVStereo3D *stereo; if (sd->size < sizeof(*stereo)) { - av_log(ctx, AV_LOG_INFO, "invalid data"); + av_log(ctx, AV_LOG_ERROR, "invalid data"); return; } @@ -272,7 +273,7 @@ static void dump_audioservicetype(void *ctx, AVPacketSideData *sd) enum AVAudioServiceType *ast = (enum AVAudioServiceType *)sd->data; if (sd->size < sizeof(*ast)) { - av_log(ctx, AV_LOG_INFO, "invalid data"); + av_log(ctx, AV_LOG_ERROR, "invalid data"); return; } @@ -315,15 +316,22 @@ static void dump_cpb(void *ctx, AVPacketSideData *sd) AVCPBProperties *cpb = (AVCPBProperties *)sd->data; if (sd->size < sizeof(*cpb)) { - av_log(ctx, AV_LOG_INFO, "invalid data"); + av_log(ctx, AV_LOG_ERROR, "invalid data"); return; } av_log(ctx, AV_LOG_INFO, - "bitrate max/min/avg: %d/%d/%d buffer size: %d vbv_delay: %"PRId64, +#if FF_API_UNSANITIZED_BITRATES + "bitrate max/min/avg: %d/%d/%d buffer size: %d ", +#else + "bitrate max/min/avg: %"PRId64"/%"PRId64"/%"PRId64" buffer size: %d ", +#endif cpb->max_bitrate, cpb->min_bitrate, cpb->avg_bitrate, - cpb->buffer_size, - cpb->vbv_delay); + cpb->buffer_size); + if (cpb->vbv_delay == UINT64_MAX) + av_log(ctx, AV_LOG_INFO, "vbv_delay: N/A"); + else + av_log(ctx, AV_LOG_INFO, "vbv_delay: %"PRIu64"", cpb->vbv_delay); } static void dump_mastering_display_metadata(void *ctx, AVPacketSideData* sd) { @@ -357,7 +365,7 @@ static void dump_spherical(void *ctx, AVCodecParameters *par, AVPacketSideData * double yaw, pitch, roll; if (sd->size < sizeof(*spherical)) { - av_log(ctx, AV_LOG_INFO, "invalid data"); + av_log(ctx, AV_LOG_ERROR, "invalid data"); return; } @@ -380,6 +388,20 @@ static void dump_spherical(void *ctx, AVCodecParameters *par, AVPacketSideData * } } +static void dump_dovi_conf(void *ctx, AVPacketSideData* sd) +{ + AVDOVIDecoderConfigurationRecord *dovi = (AVDOVIDecoderConfigurationRecord *)sd->data; + + av_log(ctx, AV_LOG_INFO, "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, + dovi->rpu_present_flag, + dovi->el_present_flag, + dovi->bl_present_flag, + dovi->dv_bl_signal_compatibility_id); +} + static void dump_sidedata(void *ctx, AVStream *st, const char *indent) { int i; @@ -439,6 +461,13 @@ static void dump_sidedata(void *ctx, AVStream *st, const char *indent) case AV_PKT_DATA_CONTENT_LIGHT_LEVEL: dump_content_light_metadata(ctx, &sd); break; + case AV_PKT_DATA_ICC_PROFILE: + av_log(ctx, AV_LOG_INFO, "ICC Profile"); + break; + case AV_PKT_DATA_DOVI_CONF: + av_log(ctx, AV_LOG_INFO, "DOVI configuration record: "); + dump_dovi_conf(ctx, &sd); + break; default: av_log(ctx, AV_LOG_INFO, "unknown side data type %d (%d bytes)", sd.type, sd.size); @@ -586,7 +615,7 @@ void av_dump_format(AVFormatContext *ic, int index, if (!is_output) { av_log(NULL, AV_LOG_INFO, " Duration: "); if (ic->duration != AV_NOPTS_VALUE) { - int hours, mins, secs, us; + int64_t hours, mins, secs, us; int64_t duration = ic->duration + (ic->duration <= INT64_MAX - 5000 ? 5000 : 0); secs = duration / AV_TIME_BASE; us = duration % AV_TIME_BASE; @@ -594,7 +623,7 @@ void av_dump_format(AVFormatContext *ic, int index, secs %= 60; hours = mins / 60; mins %= 60; - av_log(NULL, AV_LOG_INFO, "%02d:%02d:%02d.%02d", hours, mins, secs, + av_log(NULL, AV_LOG_INFO, "%02"PRId64":%02"PRId64":%02"PRId64".%02"PRId64"", hours, mins, secs, (100 * us) / AV_TIME_BASE); } else { av_log(NULL, AV_LOG_INFO, "N/A"); diff --git a/libavformat/dv.c b/libavformat/dv.c index eb44e0acb6a..e99422d4b59 100644 --- a/libavformat/dv.c +++ b/libavformat/dv.c @@ -495,16 +495,18 @@ static int dv_read_header(AVFormatContext *s) { unsigned state, marker_pos = 0; RawDVContext *c = s->priv_data; + int ret; c->dv_demux = avpriv_dv_init_demux(s); if (!c->dv_demux) - return -1; + return AVERROR(ENOMEM); state = avio_rb32(s->pb); while ((state & 0xffffff7f) != 0x1f07003f) { if (avio_feof(s->pb)) { av_log(s, AV_LOG_ERROR, "Cannot find DV header.\n"); - return -1; + ret = AVERROR_INVALIDDATA; + goto fail; } if (state == 0x003f0700 || state == 0xff3f0700) marker_pos = avio_tell(s->pb); @@ -518,8 +520,10 @@ static int dv_read_header(AVFormatContext *s) AV_WB32(c->buf, state); if (avio_read(s->pb, c->buf + 4, DV_PROFILE_BYTES - 4) != DV_PROFILE_BYTES - 4 || - avio_seek(s->pb, -DV_PROFILE_BYTES, SEEK_CUR) < 0) - return AVERROR(EIO); + avio_seek(s->pb, -DV_PROFILE_BYTES, SEEK_CUR) < 0) { + ret = AVERROR(EIO); + goto fail; + } c->dv_demux->sys = av_dv_frame_profile(c->dv_demux->sys, c->buf, @@ -527,7 +531,8 @@ static int dv_read_header(AVFormatContext *s) if (!c->dv_demux->sys) { av_log(s, AV_LOG_ERROR, "Can't determine profile of DV input stream.\n"); - return -1; + ret = AVERROR_INVALIDDATA; + goto fail; } s->bit_rate = av_rescale_q(c->dv_demux->sys->frame_size, @@ -538,6 +543,11 @@ static int dv_read_header(AVFormatContext *s) dv_read_timecode(s); return 0; + +fail: + av_freep(&c->dv_demux); + + return ret; } static int dv_read_packet(AVFormatContext *s, AVPacket *pkt) diff --git a/libavformat/dvenc.c b/libavformat/dvenc.c index 93c103b3165..c71e532771e 100644 --- a/libavformat/dvenc.c +++ b/libavformat/dvenc.c @@ -28,7 +28,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include -#include #include "avformat.h" #include "internal.h" @@ -48,8 +47,8 @@ struct DVMuxContext { AVClass *av_class; const AVDVProfile* sys; /* current DV profile, e.g.: 525/60, 625/50 */ int n_ast; /* number of stereo audio streams (up to 2) */ - AVStream *ast[2]; /* stereo audio streams */ - AVFifoBuffer *audio_data[2]; /* FIFO for storing excessive amounts of PCM */ + AVStream *ast[4]; /* stereo audio streams */ + AVFifoBuffer *audio_data[4]; /* FIFO for storing excessive amounts of PCM */ int frames; /* current frame number */ int64_t start_time; /* recording start time */ int has_audio; /* frame under construction has audio */ @@ -87,14 +86,12 @@ static int dv_audio_frame_size(const AVDVProfile* sys, int frame, int sample_rat sizeof(sys->audio_samples_dist[0]))]; } -static int dv_write_pack(enum dv_pack_type pack_id, DVMuxContext *c, uint8_t* buf, ...) +static int dv_write_pack(enum dv_pack_type pack_id, DVMuxContext *c, uint8_t* buf, int channel, int seq) { struct tm tc; time_t ct; uint32_t timecode; - va_list ap; int audio_type = 0; - int channel; buf[0] = (uint8_t)pack_id; switch (pack_id) { @@ -104,8 +101,6 @@ static int dv_write_pack(enum dv_pack_type pack_id, DVMuxContext *c, uint8_t* bu AV_WB32(buf + 1, timecode); break; case dv_audio_source: /* AAUX source pack */ - va_start(ap, buf); - channel = va_arg(ap, int); if (c->ast[channel]->codecpar->sample_rate == 44100) { audio_type = 1; } else if (c->ast[channel]->codecpar->sample_rate == 32000) @@ -118,17 +113,16 @@ static int dv_write_pack(enum dv_pack_type pack_id, DVMuxContext *c, uint8_t* bu buf[2] = (0 << 7) | /* multi-stereo */ (0 << 5) | /* #of audio channels per block: 0 -- 1 channel */ (0 << 4) | /* pair bit: 0 -- one pair of channels */ - !!va_arg(ap, int); /* audio mode */ + (seq >= c->sys->difseg_size/2); /* audio mode (1st or 2nd channel) */ buf[3] = (1 << 7) | /* res */ (1 << 6) | /* multi-language flag */ (c->sys->dsf << 5) | /* system: 60fields/50fields */ - (c->sys->n_difchan & 2); /* definition: 0 -- 25Mbps, 2 -- 50Mbps */ + (DV_PROFILE_IS_HD(c->sys) ? 0x3 : c->sys->video_stype ? 2 : 0); /* stype */ buf[4] = (1 << 7) | /* emphasis: 1 -- off */ (0 << 6) | /* emphasis time constant: 0 -- reserved */ (audio_type << 3) | /* frequency: 0 -- 48kHz, 1 -- 44,1kHz, 2 -- 32kHz */ 0; /* quantization: 0 -- 16-bit linear, 1 -- 12-bit nonlinear */ - va_end(ap); break; case dv_audio_control: buf[1] = (0 << 6) | /* copy protection: 0 -- unrestricted */ @@ -192,7 +186,7 @@ static void dv_inject_audio(DVMuxContext *c, int channel, uint8_t* frame_ptr) for (i = 0; i < c->sys->difseg_size; i++) { frame_ptr += 6 * 80; /* skip DIF segment header */ for (j = 0; j < 9; j++) { - dv_write_pack(dv_aaux_packs_dist[i][j], c, &frame_ptr[3], channel, i >= c->sys->difseg_size/2); + dv_write_pack(dv_aaux_packs_dist[i][j], c, &frame_ptr[3], channel, i); for (d = 8; d < 80; d+=2) { of = c->sys->audio_shuffle[i][j] + (d - 8)/2 * c->sys->audio_stride; if (of*2 >= size) @@ -210,27 +204,28 @@ static void dv_inject_metadata(DVMuxContext *c, uint8_t* frame) { int j, k; uint8_t* buf; + int seq = 0; - for (buf = frame; buf < frame + c->sys->frame_size; buf += 150 * 80) { + for (buf = frame; buf < frame + c->sys->frame_size; buf += 150 * 80, seq++) { /* DV subcode: 2nd and 3d DIFs */ for (j = 80; j < 80 * 3; j += 80) { for (k = 6; k < 6 * 8; k += 8) - dv_write_pack(dv_timecode, c, &buf[j+k]); + dv_write_pack(dv_timecode, c, &buf[j+k], 0, seq); if (((long)(buf-frame)/(c->sys->frame_size/(c->sys->difseg_size*c->sys->n_difchan))%c->sys->difseg_size) > 5) { /* FIXME: is this really needed ? */ - dv_write_pack(dv_video_recdate, c, &buf[j+14]); - dv_write_pack(dv_video_rectime, c, &buf[j+22]); - dv_write_pack(dv_video_recdate, c, &buf[j+38]); - dv_write_pack(dv_video_rectime, c, &buf[j+46]); + dv_write_pack(dv_video_recdate, c, &buf[j+14], 0, seq); + dv_write_pack(dv_video_rectime, c, &buf[j+22], 0, seq); + dv_write_pack(dv_video_recdate, c, &buf[j+38], 0, seq); + dv_write_pack(dv_video_rectime, c, &buf[j+46], 0, seq); } } /* DV VAUX: 4th, 5th and 6th 3DIFs */ for (j = 80*3 + 3; j < 80*6; j += 80) { - dv_write_pack(dv_video_recdate, c, &buf[j+5*2]); - dv_write_pack(dv_video_rectime, c, &buf[j+5*3]); - dv_write_pack(dv_video_recdate, c, &buf[j+5*11]); - dv_write_pack(dv_video_rectime, c, &buf[j+5*12]); + dv_write_pack(dv_video_recdate, c, &buf[j+5* 2], 0, seq); + dv_write_pack(dv_video_rectime, c, &buf[j+5* 3], 0, seq); + dv_write_pack(dv_video_recdate, c, &buf[j+5*11], 0, seq); + dv_write_pack(dv_video_rectime, c, &buf[j+5*12], 0, seq); } } } @@ -307,12 +302,9 @@ static DVMuxContext* dv_init_mux(AVFormatContext* s) int i; /* we support at most 1 video and 2 audio streams */ - if (s->nb_streams > 3) + if (s->nb_streams > 5) return NULL; - c->n_ast = 0; - c->ast[0] = c->ast[1] = NULL; - /* We have to sort out where audio and where video stream is */ for (i=0; inb_streams; i++) { switch (s->streams[i]->codecpar->codec_type) { @@ -355,8 +347,9 @@ static DVMuxContext* dv_init_mux(AVFormatContext* s) goto bail_out; } - if ((c->n_ast > 1) && (c->sys->n_difchan < 2)) { - /* only 1 stereo pair is allowed in 25Mbps mode */ + 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; } @@ -368,10 +361,6 @@ static DVMuxContext* dv_init_mux(AVFormatContext* s) for (i=0; i < c->n_ast; i++) { if (c->ast[i] && !(c->audio_data[i]=av_fifo_alloc_array(100, MAX_AUDIO_FRAME_SIZE))) { - while (i > 0) { - i--; - av_fifo_freep(&c->audio_data[i]); - } goto bail_out; } } @@ -382,13 +371,6 @@ static DVMuxContext* dv_init_mux(AVFormatContext* s) return NULL; } -static void dv_delete_mux(DVMuxContext *c) -{ - int i; - for (i=0; i < c->n_ast; i++) - av_fifo_freep(&c->audio_data[i]); -} - static int dv_write_header(AVFormatContext *s) { AVRational rate; @@ -436,10 +418,12 @@ static int dv_write_packet(struct AVFormatContext *s, AVPacket *pkt) * Currently we simply drop the last frame. I don't know whether this * is the best strategy of all */ -static int dv_write_trailer(struct AVFormatContext *s) +static void dv_deinit(AVFormatContext *s) { - dv_delete_mux(s->priv_data); - return 0; + DVMuxContext *c = s->priv_data; + + for (int i = 0; i < c->n_ast; i++) + av_fifo_freep(&c->audio_data[i]); } AVOutputFormat ff_dv_muxer = { @@ -451,5 +435,5 @@ AVOutputFormat ff_dv_muxer = { .video_codec = AV_CODEC_ID_DVVIDEO, .write_header = dv_write_header, .write_packet = dv_write_packet, - .write_trailer = dv_write_trailer, + .deinit = dv_deinit, }; diff --git a/libavformat/dxa.c b/libavformat/dxa.c index 298cda05d7d..27fa6afb6a0 100644 --- a/libavformat/dxa.c +++ b/libavformat/dxa.c @@ -179,8 +179,8 @@ static int dxa_read_packet(AVFormatContext *s, AVPacket *pkt) tag = AV_RL32(buf); switch (tag) { case MKTAG('N', 'U', 'L', 'L'): - if(av_new_packet(pkt, 4 + pal_size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, 4 + pal_size)) < 0) + return ret; pkt->stream_index = 0; if(pal_size) memcpy(pkt->data, pal, pal_size); memcpy(pkt->data + pal_size, buf, 4); @@ -204,12 +204,12 @@ static int dxa_read_packet(AVFormatContext *s, AVPacket *pkt) size); return AVERROR_INVALIDDATA; } - if(av_new_packet(pkt, size + DXA_EXTRA_SIZE + pal_size) < 0) - return AVERROR(ENOMEM); + ret = av_new_packet(pkt, size + DXA_EXTRA_SIZE + pal_size); + if (ret < 0) + return ret; memcpy(pkt->data + pal_size, buf, DXA_EXTRA_SIZE); ret = avio_read(s->pb, pkt->data + DXA_EXTRA_SIZE + pal_size, size); if(ret != size){ - av_packet_unref(pkt); return AVERROR(EIO); } if(pal_size) memcpy(pkt->data, pal, pal_size); diff --git a/libavformat/electronicarts.c b/libavformat/electronicarts.c index 6dbc3e350a4..2ee5e1b6fad 100644 --- a/libavformat/electronicarts.c +++ b/libavformat/electronicarts.c @@ -574,11 +574,12 @@ static int ea_read_packet(AVFormatContext *s, AVPacket *pkt) EaDemuxContext *ea = s->priv_data; AVIOContext *pb = s->pb; int partial_packet = 0; + int hit_end = 0; unsigned int chunk_type, chunk_size; int ret = 0, packet_read = 0, key = 0; int av_uninit(num_samples); - while (!packet_read || partial_packet) { + while ((!packet_read && !hit_end) || partial_packet) { chunk_type = avio_rl32(pb); chunk_size = ea->big_endian ? avio_rb32(pb) : avio_rl32(pb); if (chunk_size < 8) @@ -632,7 +633,6 @@ static int ea_read_packet(AVFormatContext *s, AVPacket *pkt) case AV_CODEC_ID_ADPCM_EA_R3: if (pkt->size < 4) { av_log(s, AV_LOG_ERROR, "Packet is too short\n"); - av_packet_unref(pkt); return AVERROR_INVALIDDATA; } if (ea->audio_codec == AV_CODEC_ID_ADPCM_EA_R3) @@ -676,7 +676,7 @@ static int ea_read_packet(AVFormatContext *s, AVPacket *pkt) } if (avio_feof(pb)) ret = AVERROR_EOF; - packet_read = 1; + hit_end = 1; break; case MVIh_TAG: @@ -735,8 +735,9 @@ static int ea_read_packet(AVFormatContext *s, AVPacket *pkt) } } - if (ret < 0 && partial_packet) - av_packet_unref(pkt); + if (ret >= 0 && hit_end && !packet_read) + return AVERROR(EAGAIN); + return ret; } diff --git a/libavformat/ffmetaenc.c b/libavformat/ffmetaenc.c index a9adbb1d19c..800fb1887c6 100644 --- a/libavformat/ffmetaenc.c +++ b/libavformat/ffmetaenc.c @@ -54,7 +54,6 @@ static int write_header(AVFormatContext *s) avio_write(s->pb, ID_STRING, sizeof(ID_STRING) - 1); avio_w8(s->pb, '1'); // version avio_w8(s->pb, '\n'); - avio_flush(s->pb); return 0; } diff --git a/libavformat/fifo.c b/libavformat/fifo.c index b403ba717b5..d11dc6626c7 100644 --- a/libavformat/fifo.c +++ b/libavformat/fifo.c @@ -36,7 +36,6 @@ typedef struct FifoContext { AVFormatContext *avf; char *format; - char *format_options_str; AVDictionary *format_options; int queue_size; @@ -490,16 +489,6 @@ static int fifo_init(AVFormatContext *avf) return AVERROR(EINVAL); } - if (fifo->format_options_str) { - ret = av_dict_parse_string(&fifo->format_options, fifo->format_options_str, - "=", ":", 0); - if (ret < 0) { - av_log(avf, AV_LOG_ERROR, "Could not parse format options list '%s'\n", - fifo->format_options_str); - return ret; - } - } - oformat = av_guess_format(fifo->format, avf->url, NULL); if (!oformat) { ret = AVERROR_MUXER_NOT_FOUND; @@ -547,7 +536,6 @@ static int fifo_write_packet(AVFormatContext *avf, AVPacket *pkt) int ret; if (pkt) { - av_init_packet(&msg.pkt); ret = av_packet_ref(&msg.pkt,pkt); if (ret < 0) return ret; @@ -604,7 +592,6 @@ static void fifo_deinit(AVFormatContext *avf) { FifoContext *fifo = avf->priv_data; - av_dict_free(&fifo->format_options); avformat_free_context(fifo->avf); av_thread_message_queue_free(&fifo->queue); if (fifo->overflow_flag_lock_initialized) @@ -619,8 +606,8 @@ static const AVOption options[] = { {"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_str), - AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, 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}, {"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}, diff --git a/libavformat/file.c b/libavformat/file.c index 08c7f8e6dd7..8303436be09 100644 --- a/libavformat/file.c +++ b/libavformat/file.c @@ -369,7 +369,7 @@ const URLProtocol ff_file_protocol = { .url_open_dir = file_open_dir, .url_read_dir = file_read_dir, .url_close_dir = file_close_dir, - .default_whitelist = "file,crypto" + .default_whitelist = "file,crypto,data" }; #endif /* CONFIG_FILE_PROTOCOL */ @@ -408,7 +408,7 @@ const URLProtocol ff_pipe_protocol = { .url_check = file_check, .priv_data_size = sizeof(FileContext), .priv_data_class = &pipe_class, - .default_whitelist = "crypto" + .default_whitelist = "crypto,data" }; #endif /* CONFIG_PIPE_PROTOCOL */ diff --git a/libavformat/filmstripenc.c b/libavformat/filmstripenc.c index 8ead696360e..83faf29556f 100644 --- a/libavformat/filmstripenc.c +++ b/libavformat/filmstripenc.c @@ -26,13 +26,10 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "rawenc.h" #define RAND_TAG MKBETAG('R','a','n','d') -typedef struct FilmstripMuxContext { - int nb_frames; -} FilmstripMuxContext; - static int write_header(AVFormatContext *s) { if (s->streams[0]->codecpar->format != AV_PIX_FMT_RGBA) { @@ -42,23 +39,14 @@ static int write_header(AVFormatContext *s) return 0; } -static int write_packet(AVFormatContext *s, AVPacket *pkt) -{ - FilmstripMuxContext *film = s->priv_data; - avio_write(s->pb, pkt->data, pkt->size); - film->nb_frames++; - return 0; -} - static int write_trailer(AVFormatContext *s) { - FilmstripMuxContext *film = s->priv_data; AVIOContext *pb = s->pb; AVStream *st = s->streams[0]; int i; avio_wb32(pb, RAND_TAG); - avio_wb32(pb, film->nb_frames); + avio_wb32(pb, st->nb_frames); avio_wb16(pb, 0); // packing method avio_wb16(pb, 0); // reserved avio_wb16(pb, st->codecpar->width); @@ -76,10 +64,9 @@ AVOutputFormat ff_filmstrip_muxer = { .name = "filmstrip", .long_name = NULL_IF_CONFIG_SMALL("Adobe Filmstrip"), .extensions = "flm", - .priv_data_size = sizeof(FilmstripMuxContext), .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_RAWVIDEO, .write_header = write_header, - .write_packet = write_packet, + .write_packet = ff_raw_write_packet, .write_trailer = write_trailer, }; diff --git a/libavformat/fitsdec.c b/libavformat/fitsdec.c index c7d1edec60d..e52ddc7e790 100644 --- a/libavformat/fitsdec.c +++ b/libavformat/fitsdec.c @@ -157,11 +157,11 @@ static int fits_read_packet(AVFormatContext *s, AVPacket *pkt) 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); if (pos < 0) return pos; - av_bprint_finalize(&avbuf, NULL); av_bprint_init(&avbuf, FITS_BLOCK_SIZE, AV_BPRINT_SIZE_UNLIMITED); avpriv_fits_header_init(&header, STATE_XTENSION); } @@ -183,7 +183,6 @@ static int fits_read_packet(AVFormatContext *s, AVPacket *pkt) ret = av_bprint_finalize(&avbuf, &buf); if (ret < 0) { - av_packet_unref(pkt); return ret; } @@ -192,7 +191,6 @@ static int fits_read_packet(AVFormatContext *s, AVPacket *pkt) av_freep(&buf); ret = avio_read(s->pb, pkt->data + pkt->size, size); if (ret < 0) { - av_packet_unref(pkt); return ret; } diff --git a/libavformat/flac_picture.c b/libavformat/flac_picture.c index 8317ab2fa64..53e24b28b74 100644 --- a/libavformat/flac_picture.c +++ b/libavformat/flac_picture.c @@ -19,51 +19,63 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/avassert.h" #include "libavutil/intreadwrite.h" +#include "libavcodec/bytestream.h" #include "libavcodec/png.h" #include "avformat.h" #include "flac_picture.h" #include "id3v2.h" #include "internal.h" -int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size) +#define MAX_TRUNC_PICTURE_SIZE (500 * 1024 * 1024) + +int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size, int truncate_workaround) { const CodecMime *mime = ff_id3v2_mime_tags; enum AVCodecID id = AV_CODEC_ID_NONE; AVBufferRef *data = NULL; uint8_t mimetype[64], *desc = NULL; - AVIOContext *pb = NULL; + GetByteContext g; AVStream *st; int width, height, ret = 0; - int len; unsigned int type; + uint32_t len, left, trunclen = 0; + + if (buf_size < 34) { + av_log(s, AV_LOG_ERROR, "Attached picture metadata block too short\n"); + if (s->error_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + return 0; + } - pb = avio_alloc_context(buf, buf_size, 0, NULL, NULL, NULL, NULL); - if (!pb) - return AVERROR(ENOMEM); + bytestream2_init(&g, buf, buf_size); /* read the picture type */ - type = avio_rb32(pb); + type = bytestream2_get_be32u(&g); if (type >= FF_ARRAY_ELEMS(ff_id3v2_picture_types)) { av_log(s, AV_LOG_ERROR, "Invalid picture type: %d.\n", type); if (s->error_recognition & AV_EF_EXPLODE) { - RETURN_ERROR(AVERROR_INVALIDDATA); + return AVERROR_INVALIDDATA; } type = 0; } /* picture mimetype */ - len = avio_rb32(pb); - if (len <= 0 || len >= 64 || - avio_read(pb, mimetype, FFMIN(len, sizeof(mimetype) - 1)) != len) { + len = bytestream2_get_be32u(&g); + if (len <= 0 || len >= sizeof(mimetype)) { av_log(s, AV_LOG_ERROR, "Could not read mimetype from an attached " "picture.\n"); if (s->error_recognition & AV_EF_EXPLODE) - ret = AVERROR_INVALIDDATA; - goto fail; + return AVERROR_INVALIDDATA; + return 0; + } + if (len + 24 > bytestream2_get_bytes_left(&g)) { + av_log(s, AV_LOG_ERROR, "Attached picture metadata block too short\n"); + if (s->error_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + return 0; } - av_assert0(len < sizeof(mimetype)); + bytestream2_get_bufferu(&g, mimetype, len); mimetype[len] = 0; while (mime->id != AV_CODEC_ID_NONE) { @@ -77,49 +89,73 @@ int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size) av_log(s, AV_LOG_ERROR, "Unknown attached picture mimetype: %s.\n", mimetype); if (s->error_recognition & AV_EF_EXPLODE) - ret = AVERROR_INVALIDDATA; - goto fail; + return AVERROR_INVALIDDATA; + return 0; } /* picture description */ - len = avio_rb32(pb); + len = bytestream2_get_be32u(&g); + if (len > bytestream2_get_bytes_left(&g) - 20) { + av_log(s, AV_LOG_ERROR, "Attached picture metadata block too short\n"); + if (s->error_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + return 0; + } if (len > 0) { if (!(desc = av_malloc(len + 1))) { - RETURN_ERROR(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); } - if (avio_read(pb, desc, len) != len) { - av_log(s, AV_LOG_ERROR, "Error reading attached picture description.\n"); - if (s->error_recognition & AV_EF_EXPLODE) - ret = AVERROR(EIO); - goto fail; - } + bytestream2_get_bufferu(&g, desc, len); desc[len] = 0; } /* picture metadata */ - width = avio_rb32(pb); - height = avio_rb32(pb); - avio_skip(pb, 8); + width = bytestream2_get_be32u(&g); + height = bytestream2_get_be32u(&g); + bytestream2_skipu(&g, 8); /* picture data */ - len = avio_rb32(pb); - if (len <= 0) { - av_log(s, AV_LOG_ERROR, "Invalid attached picture size: %d.\n", len); - if (s->error_recognition & AV_EF_EXPLODE) - ret = AVERROR_INVALIDDATA; - goto fail; + len = bytestream2_get_be32u(&g); + + left = bytestream2_get_bytes_left(&g); + if (len <= 0 || len > left) { + if (len > MAX_TRUNC_PICTURE_SIZE || len >= INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) { + av_log(s, AV_LOG_ERROR, "Attached picture metadata block too big %u\n", len); + if (s->error_recognition & AV_EF_EXPLODE) + ret = AVERROR_INVALIDDATA; + goto fail; + } + + // Workaround bug for flac muxers that writs truncated metadata picture block size if + // the picture size do not fit in 24 bits. lavf flacenc used to have the issue and based + // on existing broken files other unknown flac muxers seems to truncate also. + if (truncate_workaround && + s->strict_std_compliance <= FF_COMPLIANCE_NORMAL && + len > left && (len & 0xffffff) == left) { + av_log(s, AV_LOG_INFO, "Correcting truncated metadata picture size from %u to %u\n", left, len); + trunclen = len - left; + } else { + av_log(s, AV_LOG_ERROR, "Attached picture metadata block too short\n"); + if (s->error_recognition & AV_EF_EXPLODE) + ret = AVERROR_INVALIDDATA; + goto fail; + } } if (!(data = av_buffer_alloc(len + AV_INPUT_BUFFER_PADDING_SIZE))) { RETURN_ERROR(AVERROR(ENOMEM)); } - memset(data->data + len, 0, AV_INPUT_BUFFER_PADDING_SIZE); - if (avio_read(pb, data->data, len) != len) { - av_log(s, AV_LOG_ERROR, "Error reading attached picture data.\n"); - if (s->error_recognition & AV_EF_EXPLODE) - ret = AVERROR(EIO); - goto fail; + + if (trunclen == 0) { + bytestream2_get_bufferu(&g, data->data, len); + } else { + // If truncation was detected copy all data from block and read missing bytes + // not included in the block size + bytestream2_get_bufferu(&g, data->data, left); + if (avio_read(s->pb, data->data + len - trunclen, trunclen) < trunclen) + RETURN_ERROR(AVERROR_INVALIDDATA); } + memset(data->data + len, 0, AV_INPUT_BUFFER_PADDING_SIZE); if (AV_RB64(data->data) == PNGSIG) id = AV_CODEC_ID_PNG; @@ -145,14 +181,11 @@ int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size) if (desc) av_dict_set(&st->metadata, "title", desc, AV_DICT_DONT_STRDUP_VAL); - avio_context_free(&pb); - return 0; fail: av_buffer_unref(&data); av_freep(&desc); - avio_context_free(&pb); return ret; } diff --git a/libavformat/flac_picture.h b/libavformat/flac_picture.h index 4374b6f4f60..61fd0c88065 100644 --- a/libavformat/flac_picture.h +++ b/libavformat/flac_picture.h @@ -26,6 +26,6 @@ #define RETURN_ERROR(code) do { ret = (code); goto fail; } while (0) -int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size); +int ff_flac_parse_picture(AVFormatContext *s, uint8_t *buf, int buf_size, int truncate_workaround); #endif /* AVFORMAT_FLAC_PICTURE_H */ diff --git a/libavformat/flacdec.c b/libavformat/flacdec.c index 8394e474831..79c05f14bf7 100644 --- a/libavformat/flacdec.c +++ b/libavformat/flacdec.c @@ -146,7 +146,7 @@ static int flac_read_header(AVFormatContext *s) } av_freep(&buffer); } else if (metadata_type == FLAC_METADATA_TYPE_PICTURE) { - ret = ff_flac_parse_picture(s, buffer, metadata_size); + ret = ff_flac_parse_picture(s, buffer, metadata_size, 1); av_freep(&buffer); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Error parsing attached picture.\n"); @@ -259,7 +259,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) { - AVPacket pkt, out_pkt; + AVPacket pkt; AVStream *st = s->streams[stream_index]; AVCodecParserContext *parser; int ret; @@ -276,6 +276,9 @@ static av_unused int64_t flac_read_timestamp(AVFormatContext *s, int stream_inde parser->flags |= PARSER_FLAG_USE_CODEC_TS; for (;;){ + uint8_t *data; + int size; + ret = ff_raw_read_partial_packet(s, &pkt); if (ret < 0){ if (ret == AVERROR(EAGAIN)) @@ -285,14 +288,12 @@ static av_unused int64_t flac_read_timestamp(AVFormatContext *s, int stream_inde av_assert1(!pkt.size); } } - av_init_packet(&out_pkt); av_parser_parse2(parser, st->internal->avctx, - &out_pkt.data, &out_pkt.size, pkt.data, pkt.size, + &data, &size, pkt.data, pkt.size, pkt.pts, pkt.dts, *ppos); av_packet_unref(&pkt); - if (out_pkt.size){ - int size = out_pkt.size; + if (size) { if (parser->pts != AV_NOPTS_VALUE){ // seeking may not have started from beginning of a frame // calculate frame start position from next frame backwards diff --git a/libavformat/flacenc.c b/libavformat/flacenc.c index a07260f4268..b947a3b067b 100644 --- a/libavformat/flacenc.c +++ b/libavformat/flacenc.c @@ -29,7 +29,6 @@ #include "id3v2.h" #include "internal.h" #include "vorbiscomment.h" -#include "libavcodec/bytestream.h" typedef struct FlacMuxerContext { @@ -42,7 +41,8 @@ typedef struct FlacMuxerContext { AVPacketList *queue, *queue_end; /* updated streaminfo sent by the encoder at the end */ - uint8_t *streaminfo; + uint8_t streaminfo[FLAC_STREAMINFO_SIZE]; + int updated_streaminfo; unsigned attached_types; } FlacMuxerContext; @@ -61,25 +61,16 @@ static int flac_write_block_comment(AVIOContext *pb, AVDictionary **m, { const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; int64_t len; - uint8_t *p, *p0; ff_metadata_conv(m, ff_vorbiscomment_metadata_conv, NULL); len = ff_vorbiscomment_length(*m, vendor, NULL, 0); if (len >= ((1<<24) - 4)) return AVERROR(EINVAL); - p0 = av_malloc(len+4); - if (!p0) - return AVERROR(ENOMEM); - p = p0; - - bytestream_put_byte(&p, last_block ? 0x84 : 0x04); - bytestream_put_be24(&p, len); - ff_vorbiscomment_write(&p, m, vendor, NULL, 0); - avio_write(pb, p0, len+4); - av_freep(&p0); - p = NULL; + avio_w8(pb, last_block ? 0x84 : 0x04); + avio_wb24(pb, len); + ff_vorbiscomment_write(pb, *m, vendor, NULL, 0); return 0; } @@ -93,7 +84,7 @@ static int flac_write_picture(struct AVFormatContext *s, AVPacket *pkt) AVDictionaryEntry *e; const char *mimetype = NULL, *desc = ""; const AVStream *st = s->streams[pkt->stream_index]; - int i, mimelen, desclen, type = 0; + int i, mimelen, desclen, type = 0, blocklen; if (!pkt->data) return 0; @@ -140,8 +131,14 @@ static int flac_write_picture(struct AVFormatContext *s, AVPacket *pkt) desc = e->value; desclen = strlen(desc); + blocklen = 4 + 4 + mimelen + 4 + desclen + 4 + 4 + 4 + 4 + 4 + pkt->size; + if (blocklen >= 1<<24) { + av_log(s, AV_LOG_ERROR, "Picture block too big %d >= %d\n", blocklen, 1<<24); + return AVERROR(EINVAL); + } + avio_w8(pb, 0x06); - avio_wb24(pb, 4 + 4 + mimelen + 4 + desclen + 4 + 4 + 4 + 4 + 4 + pkt->size); + avio_wb24(pb, blocklen); avio_wb32(pb, type); @@ -288,12 +285,8 @@ static int flac_write_audio_packet(struct AVFormatContext *s, AVPacket *pkt) streaminfo = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &streaminfo_size); if (streaminfo && streaminfo_size == FLAC_STREAMINFO_SIZE) { - av_freep(&c->streaminfo); - - c->streaminfo = av_malloc(FLAC_STREAMINFO_SIZE); - if (!c->streaminfo) - return AVERROR(ENOMEM); memcpy(c->streaminfo, streaminfo, FLAC_STREAMINFO_SIZE); + c->updated_streaminfo = 1; } if (pkt->size) @@ -325,8 +318,6 @@ static int flac_write_trailer(struct AVFormatContext *s) AVIOContext *pb = s->pb; int64_t file_size; FlacMuxerContext *c = s->priv_data; - uint8_t *streaminfo = c->streaminfo ? c->streaminfo : - s->streams[c->audio_stream_idx]->codecpar->extradata; if (c->waiting_pics) { av_log(s, AV_LOG_WARNING, "No packets were sent for some of the " @@ -334,25 +325,29 @@ static int flac_write_trailer(struct AVFormatContext *s) flac_queue_flush(s); } - if (!c->write_header || !streaminfo) + if (!c->write_header || !c->updated_streaminfo) return 0; if (pb->seekable & AVIO_SEEKABLE_NORMAL) { /* rewrite the STREAMINFO header block data */ file_size = avio_tell(pb); avio_seek(pb, 8, SEEK_SET); - avio_write(pb, streaminfo, FLAC_STREAMINFO_SIZE); + avio_write(pb, c->streaminfo, FLAC_STREAMINFO_SIZE); avio_seek(pb, file_size, SEEK_SET); - avio_flush(pb); } else { av_log(s, AV_LOG_WARNING, "unable to rewrite FLAC header.\n"); } - av_freep(&c->streaminfo); - return 0; } +static void flac_deinit(struct AVFormatContext *s) +{ + FlacMuxerContext *c = s->priv_data; + + ff_packet_list_free(&c->queue, &c->queue_end); +} + static int flac_write_packet(struct AVFormatContext *s, AVPacket *pkt) { FlacMuxerContext *c = s->priv_data; @@ -425,6 +420,7 @@ AVOutputFormat ff_flac_muxer = { .write_header = flac_write_header, .write_packet = flac_write_packet, .write_trailer = flac_write_trailer, + .deinit = flac_deinit, .flags = AVFMT_NOTIMESTAMPS, .priv_class = &flac_muxer_class, }; diff --git a/libavformat/flacenc.h b/libavformat/flacenc.h index d5d53a5dcbd..b308d0d0214 100644 --- a/libavformat/flacenc.h +++ b/libavformat/flacenc.h @@ -26,7 +26,7 @@ #include "libavcodec/bytestream.h" #include "avformat.h" -int ff_flac_write_header(AVIOContext *pb, uint8_t *extradata, +int ff_flac_write_header(AVIOContext *pb, const uint8_t *extradata, int extradata_size, int last_block); int ff_flac_is_native_layout(uint64_t channel_layout); diff --git a/libavformat/flacenc_header.c b/libavformat/flacenc_header.c index 61833cc8b95..aba22a59d80 100644 --- a/libavformat/flacenc_header.c +++ b/libavformat/flacenc_header.c @@ -26,7 +26,7 @@ #include "avformat.h" #include "flacenc.h" -int ff_flac_write_header(AVIOContext *pb, uint8_t *extradata, +int ff_flac_write_header(AVIOContext *pb, const uint8_t *extradata, int extradata_size, int last_block) { uint8_t header[8] = { diff --git a/libavformat/flic.c b/libavformat/flic.c index d7844ce04f1..e65c157777d 100644 --- a/libavformat/flic.c +++ b/libavformat/flic.c @@ -89,7 +89,7 @@ static int flic_read_header(AVFormatContext *s) AVIOContext *pb = s->pb; unsigned char header[FLIC_HEADER_SIZE]; AVStream *st, *ast; - int speed; + int speed, ret; int magic_number; unsigned char preamble[FLIC_PREAMBLE_SIZE]; @@ -125,8 +125,8 @@ static int flic_read_header(AVFormatContext *s) } /* send over the whole 128-byte FLIC header */ - if (ff_alloc_extradata(st->codecpar, FLIC_HEADER_SIZE)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, FLIC_HEADER_SIZE)) < 0) + return ret; memcpy(st->codecpar->extradata, header, FLIC_HEADER_SIZE); /* peek at the preamble to detect TFTD videos - they seem to always start with an audio chunk */ @@ -175,9 +175,8 @@ static int flic_read_header(AVFormatContext *s) avio_seek(pb, 12, SEEK_SET); /* send over abbreviated FLIC header chunk */ - av_freep(&st->codecpar->extradata); - if (ff_alloc_extradata(st->codecpar, 12)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, 12)) < 0) + return ret; memcpy(st->codecpar->extradata, header, 12); } else if (magic_number == FLIC_FILE_MAGIC_1) { @@ -216,10 +215,9 @@ static int flic_read_packet(AVFormatContext *s, magic = AV_RL16(&preamble[4]); if (((magic == FLIC_CHUNK_MAGIC_1) || (magic == FLIC_CHUNK_MAGIC_2)) && size > FLIC_PREAMBLE_SIZE) { - if (av_new_packet(pkt, size)) { - ret = AVERROR(EIO); - break; - } + if ((ret = av_new_packet(pkt, size)) < 0) + return ret; + pkt->stream_index = flic->video_stream_index; pkt->pts = flic->frame_number++; pkt->pos = avio_tell(pb); @@ -227,15 +225,12 @@ static int flic_read_packet(AVFormatContext *s, ret = avio_read(pb, pkt->data + FLIC_PREAMBLE_SIZE, size - FLIC_PREAMBLE_SIZE); if (ret != size - FLIC_PREAMBLE_SIZE) { - av_packet_unref(pkt); ret = AVERROR(EIO); } packet_read = 1; } else if (magic == FLIC_TFTD_CHUNK_AUDIO) { - if (av_new_packet(pkt, size)) { - ret = AVERROR(EIO); - break; - } + if ((ret = av_new_packet(pkt, size)) < 0) + return ret; /* skip useless 10B sub-header (yes, it's not accounted for in the chunk header) */ avio_skip(pb, 10); @@ -245,8 +240,8 @@ static int flic_read_packet(AVFormatContext *s, ret = avio_read(pb, pkt->data, size); if (ret != size) { - av_packet_unref(pkt); ret = AVERROR(EIO); + break; } packet_read = 1; diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c index b531a39adc8..957acedf39b 100644 --- a/libavformat/flvdec.c +++ b/libavformat/flvdec.c @@ -32,7 +32,6 @@ #include "libavutil/mathematics.h" #include "libavutil/time_internal.h" #include "libavcodec/bytestream.h" -#include "libavcodec/mpeg4audio.h" #include "avformat.h" #include "internal.h" #include "avio_internal.h" @@ -796,12 +795,12 @@ static int flv_read_close(AVFormatContext *s) static int flv_get_extradata(AVFormatContext *s, AVStream *st, int size) { + int ret; if (!size) return 0; - av_freep(&st->codecpar->extradata); - if (ff_get_extradata(s, st->codecpar, s->pb, size) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_get_extradata(s, st->codecpar, s->pb, size)) < 0) + return ret; st->internal->need_context_update = 1; return 0; } @@ -1265,22 +1264,6 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) if (st->codecpar->codec_id == AV_CODEC_ID_AAC && t && !strcmp(t->value, "Omnia A/XE")) st->codecpar->extradata_size = 2; - if (st->codecpar->codec_id == AV_CODEC_ID_AAC && 0) { - MPEG4AudioConfig cfg; - - if (avpriv_mpeg4audio_get_config(&cfg, st->codecpar->extradata, - st->codecpar->extradata_size * 8, 1) >= 0) { - st->codecpar->channels = cfg.channels; - st->codecpar->channel_layout = 0; - if (cfg.ext_sample_rate) - st->codecpar->sample_rate = cfg.ext_sample_rate; - else - st->codecpar->sample_rate = cfg.sample_rate; - av_log(s, AV_LOG_TRACE, "mp4a config channels %d sample rate %d\n", - st->codecpar->channels, st->codecpar->sample_rate); - } - } - ret = FFERROR_REDO; goto leave; } @@ -1300,12 +1283,11 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) pkt->stream_index = st->index; pkt->pos = pos; if (flv->new_extradata[stream_type]) { - uint8_t *side = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, - flv->new_extradata_size[stream_type]); - if (side) { - memcpy(side, flv->new_extradata[stream_type], - flv->new_extradata_size[stream_type]); - av_freep(&flv->new_extradata[stream_type]); + int ret = av_packet_add_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, + flv->new_extradata[stream_type], + flv->new_extradata_size[stream_type]); + if (ret >= 0) { + flv->new_extradata[stream_type] = NULL; flv->new_extradata_size[stream_type] = 0; } } diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c index fb1dede7aed..5cf3ce8a1ab 100644 --- a/libavformat/flvenc.c +++ b/libavformat/flvenc.c @@ -580,7 +580,7 @@ static int shift_data(AVFormatContext *s) int n = 0; int64_t metadata_size = 0; FLVContext *flv = s->priv_data; - int64_t pos, pos_end = avio_tell(s->pb); + int64_t pos, pos_end = avio_tell(s->pb); /* Save the pre-shift size. */ uint8_t *buf, *read_buf[2]; int read_buf_id = 0; int read_size[2]; @@ -608,7 +608,6 @@ static int shift_data(AVFormatContext *s) avio_seek(s->pb, flv->metadata_totalsize_pos, SEEK_SET); avio_wb32(s->pb, flv->metadata_totalsize + 11 + metadata_size); - avio_seek(s->pb, pos_end, SEEK_SET); /* Shift the data: the AVIO context of the output can only be used for * writing, so we re-open the same output, but for reading. It also avoids @@ -621,9 +620,7 @@ static int shift_data(AVFormatContext *s) goto end; } - /* mark the end of the shift to up to the last data we wrote, and get ready - * for writing */ - pos_end = avio_tell(s->pb); + /* Get ready for writing. */ avio_seek(s->pb, flv->keyframes_info_offset + metadata_size, SEEK_SET); /* start reading at where the keyframe index information will be placed */ @@ -887,7 +884,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) unsigned ts; int size = pkt->size; uint8_t *data = NULL; - int flags = -1, flags_size, ret; + int flags = -1, flags_size, ret = 0; int64_t cur_offset = avio_tell(pb); if (par->codec_type == AVMEDIA_TYPE_AUDIO && !pkt->size) { @@ -908,14 +905,10 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) int side_size = 0; 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))) { - av_free(par->extradata); - par->extradata = av_mallocz(side_size + AV_INPUT_BUFFER_PADDING_SIZE); - if (!par->extradata) { - par->extradata_size = 0; - return AVERROR(ENOMEM); - } + ret = ff_alloc_extradata(par, side_size); + if (ret < 0) + return ret; memcpy(par->extradata, side, side_size); - par->extradata_size = side_size; flv_write_codec_header(s, par, pkt->dts); } } @@ -928,6 +921,12 @@ 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 (pkt->pts == AV_NOPTS_VALUE) { + av_log(s, AV_LOG_ERROR, "Packet is missing PTS\n"); + return AVERROR(EINVAL); + } + } ts = pkt->dts; @@ -970,10 +969,10 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) } 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) { - 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"); - return AVERROR_INVALIDDATA; + 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"); + return AVERROR_INVALIDDATA; } av_log(s, AV_LOG_WARNING, "aac bitstream error\n"); } @@ -990,7 +989,8 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) if (size + flags_size >= 1<<24) { av_log(s, AV_LOG_ERROR, "Too large packet with size %u >= %u\n", size + flags_size, 1<<24); - return AVERROR(EINVAL); + ret = AVERROR(EINVAL); + goto fail; } avio_wb24(pb, size + flags_size); @@ -1055,15 +1055,17 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) case AVMEDIA_TYPE_VIDEO: flv->videosize += (avio_tell(pb) - cur_offset); flv->lasttimestamp = flv->acurframeindex / flv->framerate; + flv->acurframeindex++; if (pkt->flags & AV_PKT_FLAG_KEY) { - double ts = flv->acurframeindex / flv->framerate; + double ts = flv->lasttimestamp; int64_t pos = cur_offset; - flv->lastkeyframetimestamp = flv->acurframeindex / flv->framerate; + flv->lastkeyframetimestamp = ts; flv->lastkeyframelocation = pos; - flv_append_keyframe_info(s, flv, ts, pos); + ret = flv_append_keyframe_info(s, flv, ts, pos); + if (ret < 0) + goto fail; } - flv->acurframeindex++; break; case AVMEDIA_TYPE_AUDIO: @@ -1075,10 +1077,10 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) break; } } - +fail: av_free(data); - return pb->error; + return ret; } static int flv_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt) diff --git a/libavformat/framehash.c b/libavformat/framehash.c index 3ae9092c61d..8d90793d7c7 100644 --- a/libavformat/framehash.c +++ b/libavformat/framehash.c @@ -45,7 +45,6 @@ int ff_framehash_write_header(AVFormatContext *s) avio_printf(s->pb, "#sar %d: %d/%d\n", i, st->sample_aspect_ratio.num, st->sample_aspect_ratio.den); break; } - avio_flush(s->pb); } return 0; } diff --git a/libavformat/fsb.c b/libavformat/fsb.c index faad6b16fda..fd3e484371f 100644 --- a/libavformat/fsb.c +++ b/libavformat/fsb.c @@ -41,6 +41,7 @@ static int fsb_read_header(AVFormatContext *s) int64_t offset; AVCodecParameters *par; AVStream *st = avformat_new_stream(s, NULL); + int ret; avio_skip(pb, 3); // "FSB" version = avio_r8(pb) - '0'; @@ -86,9 +87,9 @@ static int fsb_read_header(AVFormatContext *s) par->block_align = 8 * par->channels; if (par->channels > INT_MAX / 32) return AVERROR_INVALIDDATA; - ff_alloc_extradata(par, 32 * par->channels); - if (!par->extradata) - return AVERROR(ENOMEM); + ret = ff_alloc_extradata(par, 32 * par->channels); + if (ret < 0) + return ret; avio_seek(pb, 0x68, SEEK_SET); for (c = 0; c < par->channels; c++) { avio_read(pb, par->extradata + 32 * c, 32); @@ -130,18 +131,18 @@ static int fsb_read_header(AVFormatContext *s) switch (par->codec_id) { case AV_CODEC_ID_XMA2: - ff_alloc_extradata(par, 34); - if (!par->extradata) - return AVERROR(ENOMEM); + ret = ff_alloc_extradata(par, 34); + if (ret < 0) + return ret; memset(par->extradata, 0, 34); par->block_align = 2048; break; case AV_CODEC_ID_ADPCM_THP: if (par->channels > INT_MAX / 32) return AVERROR_INVALIDDATA; - ff_alloc_extradata(par, 32 * par->channels); - if (!par->extradata) - return AVERROR(ENOMEM); + ret = ff_alloc_extradata(par, 32 * par->channels); + if (ret < 0) + return ret; avio_seek(pb, 0x80, SEEK_SET); for (c = 0; c < par->channels; c++) { avio_read(pb, par->extradata + 32 * c, 32); diff --git a/libavformat/ftp.c b/libavformat/ftp.c index 3adc04ee1fb..caeea429209 100644 --- a/libavformat/ftp.c +++ b/libavformat/ftp.c @@ -18,12 +18,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include + #include "libavutil/avstring.h" #include "libavutil/internal.h" #include "libavutil/parseutils.h" #include "avformat.h" #include "internal.h" #include "url.h" +#include "urldecode.h" #include "libavutil/opt.h" #include "libavutil/bprint.h" @@ -36,7 +39,8 @@ typedef enum { DOWNLOADING, UPLOADING, LISTING_DIR, - DISCONNECTED + DISCONNECTED, + ENDOFFILE, } FTPState; typedef enum { @@ -69,6 +73,8 @@ typedef struct { size_t dir_buffer_size; size_t dir_buffer_offset; int utf8; + const char *option_user; /**< User to be used if none given in the URL */ + const char *option_password; /**< Password to be used if none given in the URL */ } FTPContext; #define OFFSET(x) offsetof(FTPContext, x) @@ -78,6 +84,8 @@ static const AVOption options[] = { {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, {"ftp-write-seekable", "control seekability of connection during encoding", OFFSET(write_seekable), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E }, {"ftp-anonymous-password", "password for anonymous login. E-mail address should be used.", OFFSET(anonymous_password), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, + {"ftp-user", "user for FTP login. Overridden by whatever is in the URL.", OFFSET(option_user), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, + {"ftp-password", "password for FTP login. Overridden by whatever is in the URL.", OFFSET(option_password), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, {NULL} }; @@ -224,7 +232,6 @@ static int ftp_send_command(FTPContext *s, const char *command, static void ftp_close_data_connection(FTPContext *s) { ffurl_closep(&s->conn_data); - s->position = 0; s->state = DISCONNECTED; } @@ -241,10 +248,14 @@ static int ftp_auth(FTPContext *s) static const int user_codes[] = {331, 230, 0}; static const int pass_codes[] = {230, 0}; + if (strpbrk(s->user, "\r\n")) + return AVERROR(EINVAL); snprintf(buf, sizeof(buf), "USER %s\r\n", s->user); err = ftp_send_command(s, buf, user_codes, NULL); if (err == 331) { if (s->password) { + if (strpbrk(s->password, "\r\n")) + return AVERROR(EINVAL); snprintf(buf, sizeof(buf), "PASS %s\r\n", s->password); err = ftp_send_command(s, buf, pass_codes, NULL); } else @@ -322,15 +333,15 @@ static int ftp_passive_mode(FTPContext *s) *end = '\0'; /* skip ip */ if (!av_strtok(start, ",", &end)) goto fail; - if (!av_strtok(end, ",", &end)) goto fail; - if (!av_strtok(end, ",", &end)) goto fail; - if (!av_strtok(end, ",", &end)) goto fail; + if (!av_strtok(NULL, ",", &end)) goto fail; + if (!av_strtok(NULL, ",", &end)) goto fail; + if (!av_strtok(NULL, ",", &end)) goto fail; /* parse port number */ - start = av_strtok(end, ",", &end); + start = av_strtok(NULL, ",", &end); if (!start) goto fail; s->server_data_port = atoi(start) * 256; - start = av_strtok(end, ",", &end); + start = av_strtok(NULL, ",", &end); if (!start) goto fail; s->server_data_port += atoi(start); ff_dlog(s, "Server data port: %d\n", s->server_data_port); @@ -652,9 +663,9 @@ static int ftp_abort(URLContext *h) static int ftp_connect(URLContext *h, const char *url) { - char proto[10], path[MAX_URL_SIZE], credencials[MAX_URL_SIZE], hostname[MAX_URL_SIZE]; + char proto[10], path[MAX_URL_SIZE], credentials[MAX_URL_SIZE], hostname[MAX_URL_SIZE]; const char *tok_user = NULL, *tok_pass = NULL; - char *end = NULL, *newpath = NULL; + char *newpath = NULL; int err; FTPContext *s = h->priv_data; @@ -665,20 +676,34 @@ static int ftp_connect(URLContext *h, const char *url) s->features = NULL; av_url_split(proto, sizeof(proto), - credencials, sizeof(credencials), + credentials, sizeof(credentials), hostname, sizeof(hostname), &s->server_control_port, path, sizeof(path), url); - tok_user = av_strtok(credencials, ":", &end); - tok_pass = av_strtok(end, ":", &end); - if (!tok_user) { - tok_user = "anonymous"; - tok_pass = av_x_if_null(s->anonymous_password, "nopassword"); + if (!*credentials) { + if (!s->option_user) { + tok_user = "anonymous"; + tok_pass = av_x_if_null(s->anonymous_password, "nopassword"); + } else { + tok_user = s->option_user; + tok_pass = s->option_password; + } + s->user = av_strdup(tok_user); + s->password = av_strdup(tok_pass); + } else { + char *pass = strchr(credentials, ':'); + if (pass) { + *pass++ = '\0'; + tok_pass = pass; + s->password = ff_urldecode(pass, 0); + } else { + tok_pass = s->option_password; + s->password = av_strdup(tok_pass); + } + s->user = ff_urldecode(credentials, 0); } - s->user = av_strdup(tok_user); - s->password = av_strdup(tok_pass); s->hostname = av_strdup(hostname); if (!s->hostname || !s->user || (tok_pass && !s->password)) { return AVERROR(ENOMEM); @@ -715,8 +740,7 @@ static int ftp_open(URLContext *h, const char *url, int flags) if (ftp_restart(s, 0) < 0) { h->is_streamed = 1; } else { - if (ftp_file_size(s) < 0 && flags & AVIO_FLAG_READ) - h->is_streamed = 1; + ftp_file_size(s); if (s->write_seekable != 1 && flags & AVIO_FLAG_WRITE) h->is_streamed = 1; } @@ -733,7 +757,7 @@ static int64_t ftp_seek(URLContext *h, int64_t pos, int whence) { FTPContext *s = h->priv_data; int err; - int64_t new_pos, fake_pos; + int64_t new_pos; ff_dlog(h, "ftp protocol seek %"PRId64" %d\n", pos, whence); @@ -763,11 +787,10 @@ static int64_t ftp_seek(URLContext *h, int64_t pos, int whence) return AVERROR(EINVAL); } - fake_pos = s->filesize != -1 ? FFMIN(new_pos, s->filesize) : new_pos; - if (fake_pos != s->position) { + if (new_pos != s->position) { if ((err = ftp_abort(h)) < 0) return err; - s->position = fake_pos; + s->position = new_pos; } return new_pos; } @@ -779,16 +802,13 @@ static int ftp_read(URLContext *h, unsigned char *buf, int size) ff_dlog(h, "ftp protocol read %d bytes\n", size); retry: + if (s->state == ENDOFFILE) + return AVERROR_EOF; if (s->state == DISCONNECTED) { - /* optimization */ - if (s->position >= s->filesize) - return AVERROR_EOF; if ((err = ftp_connect_data_connection(h)) < 0) return err; } if (s->state == READY) { - if (s->position >= s->filesize) - return AVERROR_EOF; if ((err = ftp_retrieve(s)) < 0) return err; } @@ -796,27 +816,28 @@ static int ftp_read(URLContext *h, unsigned char *buf, int size) read = ffurl_read(s->conn_data, buf, size); if (read >= 0) { s->position += read; - if (s->position >= s->filesize) { - /* server will terminate, but keep current position to avoid madness */ - /* save position to restart from it */ - int64_t pos = s->position; - if (ftp_abort(h) < 0) { - s->position = pos; - return AVERROR(EIO); - } - s->position = pos; - } + s->filesize = FFMAX(s->filesize, s->position); } - if (read <= 0 && s->position < s->filesize && !h->is_streamed) { + if (read == AVERROR_EOF) { + static const int retr_codes[] = {226, 250, 425, 426, 451, 0}; + char *response = NULL; + err = ftp_status(s, &response, retr_codes); + if (err == 226) { + ftp_close_data_connection(s); + av_freep(&response); + s->state = ENDOFFILE; + return AVERROR_EOF; + } + /* 250 is not allowed, any other status means some kind of error */ + av_log(h, AV_LOG_ERROR, "FTP transfer failed: %s\n", response ? response : (err < 0 ? av_err2str(err) : "?")); + av_freep(&response); + read = AVERROR(EIO); + } + if (read <= 0 && !h->is_streamed) { /* Server closed connection. Probably due to inactivity */ - int64_t pos = s->position; av_log(h, AV_LOG_INFO, "Reconnect to FTP server.\n"); if ((err = ftp_abort(h)) < 0) return err; - if ((err = ftp_seek(h, pos, SEEK_SET)) < 0) { - av_log(h, AV_LOG_ERROR, "Position cannot be restored.\n"); - return err; - } if (!retry_done) { retry_done = 1; goto retry; @@ -942,8 +963,10 @@ static int ftp_parse_entry_nlst(char *line, AVIODirEntry *next) static int ftp_parse_entry_mlsd(char *mlsd, AVIODirEntry *next) { char *fact, *value; + char *saveptr = NULL, *p = mlsd; ff_dlog(NULL, "%s\n", mlsd); - while(fact = av_strtok(mlsd, ";", &mlsd)) { + while(fact = av_strtok(p, ";", &saveptr)) { + p = NULL; if (fact[0] == ' ') { next->name = av_strdup(&fact[1]); continue; diff --git a/libavformat/fwse.c b/libavformat/fwse.c new file mode 100644 index 00000000000..00e2e13b116 --- /dev/null +++ b/libavformat/fwse.c @@ -0,0 +1,88 @@ +/* + * FWSE demuxer + * Copyright (c) 2020 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 "avformat.h" +#include "internal.h" +#include "pcm.h" + +static int fwse_probe(const AVProbeData *p) +{ + if (AV_RL32(p->buf) != MKTAG('F','W','S','E')) + return 0; + if (AV_RL32(p->buf+4) != 2 && AV_RL32(p->buf+4) != 3) + return 0; + if (AV_RL32(p->buf+16) != 1 && AV_RL32(p->buf+16) != 2) + return 0; + + return AVPROBE_SCORE_MAX / 4 * 3; +} + +static int fwse_read_header(AVFormatContext *s) +{ + unsigned start_offset, version; + AVIOContext *pb = s->pb; + AVCodecParameters *par; + AVStream *st; + + avio_skip(pb, 4); + version = avio_rl32(pb); + if (version != 2 && version != 3) + return AVERROR_INVALIDDATA; + avio_skip(pb, 4); + start_offset = avio_rl32(pb); + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + par = st->codecpar; + par->codec_type = AVMEDIA_TYPE_AUDIO; + par->codec_id = AV_CODEC_ID_ADPCM_IMA_MTF; + par->format = AV_SAMPLE_FMT_S16; + par->channels = avio_rl32(pb); + if (par->channels != 1 && par->channels != 2) + return AVERROR_INVALIDDATA; + if (par->channels == 1) + par->channel_layout = AV_CH_LAYOUT_MONO; + else if (par->channels == 2) + par->channel_layout = AV_CH_LAYOUT_STEREO; + st->duration = avio_rl32(pb); + par->sample_rate = avio_rl32(pb); + if (par->sample_rate <= 0 || par->sample_rate > INT_MAX) + return AVERROR_INVALIDDATA; + + par->block_align = 1; + avio_skip(pb, start_offset - avio_tell(pb)); + + avpriv_set_pts_info(st, 64, 1, par->sample_rate); + + return 0; +} + +AVInputFormat ff_fwse_demuxer = { + .name = "fwse", + .long_name = NULL_IF_CONFIG_SMALL("Capcom's MT Framework sound"), + .read_probe = fwse_probe, + .read_header = fwse_read_header, + .read_packet = ff_pcm_read_packet, + .extensions = "fwse", +}; diff --git a/libavformat/g723_1.c b/libavformat/g723_1.c index 27c8c3951be..3af48093478 100644 --- a/libavformat/g723_1.c +++ b/libavformat/g723_1.c @@ -69,7 +69,6 @@ static int g723_1_read_packet(AVFormatContext *s, AVPacket *pkt) ret = avio_read(s->pb, pkt->data + 1, size - 1); if (ret < size - 1) { - av_packet_unref(pkt); return ret < 0 ? ret : AVERROR_EOF; } diff --git a/libavformat/g729dec.c b/libavformat/g729dec.c index 7b67fc488ce..c58855cb998 100644 --- a/libavformat/g729dec.c +++ b/libavformat/g729dec.c @@ -61,8 +61,7 @@ static int g729_read_header(AVFormatContext *s) return AVERROR(EINVAL); } - avpriv_set_pts_info(st, st->codecpar->block_align << 3, 1, - st->codecpar->sample_rate); + avpriv_set_pts_info(st, 64, 80, st->codecpar->sample_rate); return 0; } @@ -76,6 +75,7 @@ static int g729_read_packet(AVFormatContext *s, AVPacket *pkt) pkt->stream_index = 0; pkt->dts = pkt->pts = pkt->pos / st->codecpar->block_align; + pkt->duration = 1; return 0; } diff --git a/libavformat/gdv.c b/libavformat/gdv.c index b698497a6a8..2ecbb535e7c 100644 --- a/libavformat/gdv.c +++ b/libavformat/gdv.c @@ -182,7 +182,6 @@ static int gdv_read_packet(AVFormatContext *ctx, AVPacket *pkt) pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); if (!pal) { - av_packet_unref(pkt); return AVERROR(ENOMEM); } memcpy(pal, gdv->pal, AVPALETTE_SIZE); diff --git a/libavformat/gopher.c b/libavformat/gopher.c index 3070b24caf2..8b6d14a1f77 100644 --- a/libavformat/gopher.c +++ b/libavformat/gopher.c @@ -68,10 +68,7 @@ static int gopher_connect(URLContext *h, const char *path) static int gopher_close(URLContext *h) { GopherContext *s = h->priv_data; - if (s->hd) { - ffurl_close(s->hd); - s->hd = NULL; - } + ffurl_closep(&s->hd); return 0; } diff --git a/libavformat/gsmdec.c b/libavformat/gsmdec.c index 1044cde3178..ec6b2e924ff 100644 --- a/libavformat/gsmdec.c +++ b/libavformat/gsmdec.c @@ -62,7 +62,6 @@ static int gsm_read_packet(AVFormatContext *s, AVPacket *pkt) ret = av_get_packet(s->pb, pkt, size); if (ret < GSM_BLOCK_SIZE) { - av_packet_unref(pkt); return ret < 0 ? ret : AVERROR(EIO); } pkt->duration = 1; diff --git a/libavformat/gxfenc.c b/libavformat/gxfenc.c index 3507c00b401..6d4df894f63 100644 --- a/libavformat/gxfenc.c +++ b/libavformat/gxfenc.c @@ -27,8 +27,8 @@ #include "avformat.h" #include "internal.h" #include "gxf.h" -#include "audiointerleave.h" +#define GXF_SAMPLES_PER_FRAME 32768 #define GXF_AUDIO_PACKET_SIZE 65536 #define GXF_TIMECODE(c, d, h, m, s, f) \ @@ -44,7 +44,7 @@ typedef struct GXFTimecode{ } GXFTimecode; typedef struct GXFStreamContext { - AudioInterleaveContext aic; + int64_t pkt_cnt; uint32_t track_type; uint32_t sample_size; uint32_t sample_rate; @@ -663,8 +663,6 @@ static int gxf_write_umf_packet(AVFormatContext *s) return updatePacketSize(pb, pos); } -static const int GXF_samples_per_frame[] = { 32768, 0 }; - static void gxf_init_timecode_track(GXFStreamContext *sc, GXFStreamContext *vsc) { if (!vsc) @@ -736,6 +734,9 @@ static int gxf_write_header(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "only mono tracks are allowed\n"); return -1; } + ret = ff_stream_add_bitstream_filter(st, "pcm_rechunk", "n="AV_STRINGIFY(GXF_SAMPLES_PER_FRAME)); + if (ret < 0) + return ret; sc->track_type = 2; sc->sample_rate = st->codecpar->sample_rate; avpriv_set_pts_info(st, 64, 1, sc->sample_rate); @@ -818,9 +819,6 @@ static int gxf_write_header(AVFormatContext *s) sc->order = s->nb_streams - st->index; } - if (ff_audio_interleave_init(s, GXF_samples_per_frame, (AVRational){ 1, 48000 }) < 0) - return -1; - if (tcr && vsc) gxf_init_timecode(s, &gxf->tc, tcr->value, vsc->fields); @@ -834,7 +832,6 @@ static int gxf_write_header(AVFormatContext *s) gxf->packet_count = 3; - avio_flush(pb); return 0; } @@ -854,8 +851,6 @@ static int gxf_write_trailer(AVFormatContext *s) int i; int ret; - ff_audio_interleave_close(s); - gxf_write_eos_packet(pb); end = avio_tell(pb); avio_seek(pb, 0, SEEK_SET); @@ -864,21 +859,24 @@ static int gxf_write_trailer(AVFormatContext *s) return ret; gxf_write_flt_packet(s); gxf_write_umf_packet(s); - avio_flush(pb); /* update duration in all map packets */ for (i = 1; i < gxf->map_offsets_nb; i++) { avio_seek(pb, gxf->map_offsets[i], SEEK_SET); if ((ret = gxf_write_map_packet(s, 1)) < 0) return ret; - avio_flush(pb); } avio_seek(pb, end, SEEK_SET); + return 0; +} + +static void gxf_deinit(AVFormatContext *s) +{ + GXFContext *gxf = s->priv_data; + av_freep(&gxf->flt_entries); av_freep(&gxf->map_offsets); - - return 0; } static int gxf_parse_mpeg_frame(GXFStreamContext *sc, const uint8_t *buf, int size) @@ -987,10 +985,11 @@ static int gxf_write_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -static int gxf_compare_field_nb(AVFormatContext *s, AVPacket *next, AVPacket *cur) +static int gxf_compare_field_nb(AVFormatContext *s, const AVPacket *next, + const AVPacket *cur) { GXFContext *gxf = s->priv_data; - AVPacket *pkt[2] = { cur, next }; + const AVPacket *pkt[2] = { cur, next }; int i, field_nb[2]; GXFStreamContext *sc[2]; @@ -1011,10 +1010,19 @@ static int gxf_compare_field_nb(AVFormatContext *s, AVPacket *next, AVPacket *cu static int gxf_interleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush) { - if (pkt && s->streams[pkt->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) - pkt->duration = 2; // enforce 2 fields - return ff_audio_rechunk_interleave(s, out, pkt, flush, - ff_interleave_packet_per_dts, gxf_compare_field_nb); + int ret; + if (pkt) { + AVStream *st = s->streams[pkt->stream_index]; + GXFStreamContext *sc = st->priv_data; + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) + pkt->pts = pkt->dts = sc->pkt_cnt * 2; // enforce 2 fields + else + pkt->pts = pkt->dts = sc->pkt_cnt * GXF_SAMPLES_PER_FRAME; + sc->pkt_cnt++; + if ((ret = ff_interleave_add_packet(s, pkt, gxf_compare_field_nb)) < 0) + return ret; + } + return ff_interleave_packet_per_dts(s, out, NULL, flush); } AVOutputFormat ff_gxf_muxer = { @@ -1027,5 +1035,6 @@ AVOutputFormat ff_gxf_muxer = { .write_header = gxf_write_header, .write_packet = gxf_write_packet, .write_trailer = gxf_write_trailer, + .deinit = gxf_deinit, .interleave_packet = gxf_interleave_packet, }; diff --git a/libavformat/hashenc.c b/libavformat/hashenc.c index 06fc085d18b..ce609f6efa1 100644 --- a/libavformat/hashenc.c +++ b/libavformat/hashenc.c @@ -29,61 +29,145 @@ struct HashContext { const AVClass *avclass; - struct AVHashContext *hash; + struct AVHashContext **hashes; char *hash_name; + int per_stream; int format_version; }; #define OFFSET(x) offsetof(struct HashContext, x) #define ENC AV_OPT_FLAG_ENCODING_PARAM -#if CONFIG_HASH_MUXER || CONFIG_FRAMEHASH_MUXER +#define HASH_OPT(defaulttype) \ + { "hash", "set hash to use", OFFSET(hash_name), AV_OPT_TYPE_STRING, {.str = defaulttype}, 0, 0, ENC } +#define FORMAT_VERSION_OPT \ + { "format_version", "file format version", OFFSET(format_version), AV_OPT_TYPE_INT, {.i64 = 2}, 1, 2, ENC } + +#if CONFIG_HASH_MUXER static const AVOption hash_options[] = { - { "hash", "set hash to use", OFFSET(hash_name), AV_OPT_TYPE_STRING, {.str = "sha256"}, 0, 0, ENC }, - { "format_version", "file format version", OFFSET(format_version), AV_OPT_TYPE_INT, {.i64 = 2}, 1, 2, ENC }, + HASH_OPT("sha256"), { NULL }, }; #endif -#if CONFIG_MD5_MUXER || CONFIG_FRAMEMD5_MUXER +#if CONFIG_FRAMEHASH_MUXER +static const AVOption framehash_options[] = { + HASH_OPT("sha256"), + FORMAT_VERSION_OPT, + { NULL }, +}; +#endif + +#if CONFIG_STREAMHASH_MUXER +static const AVOption streamhash_options[] = { + HASH_OPT("sha256"), + { NULL }, +}; +#endif + +#if CONFIG_MD5_MUXER static const AVOption md5_options[] = { - { "hash", "set hash to use", OFFSET(hash_name), AV_OPT_TYPE_STRING, {.str = "md5"}, 0, 0, ENC }, - { "format_version", "file format version", OFFSET(format_version), AV_OPT_TYPE_INT, {.i64 = 2}, 1, 2, ENC }, + HASH_OPT("md5"), + { NULL }, +}; +#endif + +#if CONFIG_FRAMEMD5_MUXER +static const AVOption framemd5_options[] = { + HASH_OPT("md5"), + FORMAT_VERSION_OPT, { NULL }, }; #endif #if CONFIG_HASH_MUXER || CONFIG_MD5_MUXER -static int hash_write_header(struct AVFormatContext *s) +static int hash_init(struct AVFormatContext *s) { + int res; struct HashContext *c = s->priv_data; - int res = av_hash_alloc(&c->hash, c->hash_name); + c->per_stream = 0; + c->hashes = av_mallocz_array(1, sizeof(*c->hashes)); + if (!c->hashes) + return AVERROR(ENOMEM); + res = av_hash_alloc(&c->hashes[0], c->hash_name); if (res < 0) return res; - av_hash_init(c->hash); + av_hash_init(c->hashes[0]); return 0; } +#endif + +#if CONFIG_STREAMHASH_MUXER +static int streamhash_init(struct AVFormatContext *s) +{ + int res, i; + struct HashContext *c = s->priv_data; + c->per_stream = 1; + c->hashes = av_mallocz_array(s->nb_streams, sizeof(*c->hashes)); + if (!c->hashes) + return AVERROR(ENOMEM); + for (i = 0; i < s->nb_streams; i++) { + res = av_hash_alloc(&c->hashes[i], c->hash_name); + if (res < 0) { + return res; + } + av_hash_init(c->hashes[i]); + } + return 0; +} +#endif + +#if CONFIG_HASH_MUXER || CONFIG_MD5_MUXER || CONFIG_STREAMHASH_MUXER +static char get_media_type_char(enum AVMediaType type) +{ + switch (type) { + case AVMEDIA_TYPE_VIDEO: return 'v'; + case AVMEDIA_TYPE_AUDIO: return 'a'; + case AVMEDIA_TYPE_DATA: return 'd'; + case AVMEDIA_TYPE_SUBTITLE: return 's'; + case AVMEDIA_TYPE_ATTACHMENT: return 't'; + default: return '?'; + } +} static int hash_write_packet(struct AVFormatContext *s, AVPacket *pkt) { struct HashContext *c = s->priv_data; - av_hash_update(c->hash, pkt->data, pkt->size); + av_hash_update(c->hashes[c->per_stream ? pkt->stream_index : 0], pkt->data, pkt->size); return 0; } static int hash_write_trailer(struct AVFormatContext *s) { struct HashContext *c = s->priv_data; - char buf[AV_HASH_MAX_SIZE*2+128]; - snprintf(buf, sizeof(buf) - 200, "%s=", av_hash_get_name(c->hash)); - - av_hash_final_hex(c->hash, buf + strlen(buf), sizeof(buf) - strlen(buf)); - av_strlcatf(buf, sizeof(buf), "\n"); - avio_write(s->pb, buf, strlen(buf)); - avio_flush(s->pb); + int num_hashes = c->per_stream ? s->nb_streams : 1; + for (int i = 0; i < num_hashes; i++) { + char buf[AV_HASH_MAX_SIZE*2+128]; + if (c->per_stream) { + AVStream *st = s->streams[i]; + snprintf(buf, sizeof(buf) - 200, "%d,%c,%s=", i, get_media_type_char(st->codecpar->codec_type), + av_hash_get_name(c->hashes[i])); + } else { + snprintf(buf, sizeof(buf) - 200, "%s=", av_hash_get_name(c->hashes[i])); + } + av_hash_final_hex(c->hashes[i], buf + strlen(buf), sizeof(buf) - strlen(buf)); + av_strlcatf(buf, sizeof(buf), "\n"); + avio_write(s->pb, buf, strlen(buf)); + } - av_hash_freep(&c->hash); return 0; } + +static void hash_free(struct AVFormatContext *s) +{ + struct HashContext *c = s->priv_data; + if (c->hashes) { + int num_hashes = c->per_stream ? s->nb_streams : 1; + for (int i = 0; i < num_hashes; i++) { + av_hash_freep(&c->hashes[i]); + } + } + av_freep(&c->hashes); +} #endif #if CONFIG_HASH_MUXER @@ -100,9 +184,10 @@ AVOutputFormat ff_hash_muxer = { .priv_data_size = sizeof(struct HashContext), .audio_codec = AV_CODEC_ID_PCM_S16LE, .video_codec = AV_CODEC_ID_RAWVIDEO, - .write_header = hash_write_header, + .init = hash_init, .write_packet = hash_write_packet, .write_trailer = hash_write_trailer, + .deinit = hash_free, .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_TS_NEGATIVE, .priv_class = &hashenc_class, @@ -123,15 +208,40 @@ AVOutputFormat ff_md5_muxer = { .priv_data_size = sizeof(struct HashContext), .audio_codec = AV_CODEC_ID_PCM_S16LE, .video_codec = AV_CODEC_ID_RAWVIDEO, - .write_header = hash_write_header, + .init = hash_init, .write_packet = hash_write_packet, .write_trailer = hash_write_trailer, + .deinit = hash_free, .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_TS_NEGATIVE, .priv_class = &md5enc_class, }; #endif +#if CONFIG_STREAMHASH_MUXER +static const AVClass streamhashenc_class = { + .class_name = "stream hash muxer", + .item_name = av_default_item_name, + .option = streamhash_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVOutputFormat ff_streamhash_muxer = { + .name = "streamhash", + .long_name = NULL_IF_CONFIG_SMALL("Per-stream hash testing"), + .priv_data_size = sizeof(struct HashContext), + .audio_codec = AV_CODEC_ID_PCM_S16LE, + .video_codec = AV_CODEC_ID_RAWVIDEO, + .init = streamhash_init, + .write_packet = hash_write_packet, + .write_trailer = hash_write_trailer, + .deinit = hash_free, + .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | + AVFMT_TS_NEGATIVE, + .priv_class = &streamhashenc_class, +}; +#endif + #if CONFIG_FRAMEHASH_MUXER || CONFIG_FRAMEMD5_MUXER static void framehash_print_extradata(struct AVFormatContext *s) { @@ -145,24 +255,35 @@ static void framehash_print_extradata(struct AVFormatContext *s) char buf[AV_HASH_MAX_SIZE*2+1]; avio_printf(s->pb, "#extradata %d, %31d, ", i, par->extradata_size); - av_hash_init(c->hash); - av_hash_update(c->hash, par->extradata, par->extradata_size); - av_hash_final_hex(c->hash, buf, sizeof(buf)); + av_hash_init(c->hashes[0]); + av_hash_update(c->hashes[0], par->extradata, par->extradata_size); + av_hash_final_hex(c->hashes[0], buf, sizeof(buf)); avio_write(s->pb, buf, strlen(buf)); avio_printf(s->pb, "\n"); } } } -static int framehash_write_header(struct AVFormatContext *s) +static int framehash_init(struct AVFormatContext *s) { + int res; struct HashContext *c = s->priv_data; - int res = av_hash_alloc(&c->hash, c->hash_name); + c->per_stream = 0; + c->hashes = av_mallocz_array(1, sizeof(*c->hashes)); + if (!c->hashes) + return AVERROR(ENOMEM); + res = av_hash_alloc(&c->hashes[0], c->hash_name); if (res < 0) return res; + return 0; +} + +static int framehash_write_header(struct AVFormatContext *s) +{ + struct HashContext *c = s->priv_data; avio_printf(s->pb, "#format: frame checksums\n"); avio_printf(s->pb, "#version: %d\n", c->format_version); - avio_printf(s->pb, "#hash: %s\n", av_hash_get_name(c->hash)); + avio_printf(s->pb, "#hash: %s\n", av_hash_get_name(c->hashes[0])); framehash_print_extradata(s); ff_framehash_write_header(s); avio_printf(s->pb, "#stream#, dts, pts, duration, size, hash\n"); @@ -174,44 +295,44 @@ static int framehash_write_packet(struct AVFormatContext *s, AVPacket *pkt) struct HashContext *c = s->priv_data; char buf[AV_HASH_MAX_SIZE*2+128]; int len; - av_hash_init(c->hash); - av_hash_update(c->hash, pkt->data, pkt->size); + av_hash_init(c->hashes[0]); + av_hash_update(c->hashes[0], pkt->data, pkt->size); snprintf(buf, sizeof(buf) - (AV_HASH_MAX_SIZE * 2 + 1), "%d, %10"PRId64", %10"PRId64", %8"PRId64", %8d, ", pkt->stream_index, pkt->dts, pkt->pts, pkt->duration, pkt->size); len = strlen(buf); - av_hash_final_hex(c->hash, buf + len, sizeof(buf) - len); + av_hash_final_hex(c->hashes[0], buf + len, sizeof(buf) - len); avio_write(s->pb, buf, strlen(buf)); if (c->format_version > 1 && pkt->side_data_elems) { int i, j; avio_printf(s->pb, ", S=%d", pkt->side_data_elems); for (i = 0; i < pkt->side_data_elems; i++) { - av_hash_init(c->hash); + av_hash_init(c->hashes[0]); if (HAVE_BIGENDIAN && pkt->side_data[i].type == AV_PKT_DATA_PALETTE) { for (j = 0; j < pkt->side_data[i].size; j += sizeof(uint32_t)) { uint32_t data = AV_RL32(pkt->side_data[i].data + j); - av_hash_update(c->hash, (uint8_t *)&data, sizeof(uint32_t)); + av_hash_update(c->hashes[0], (uint8_t *)&data, sizeof(uint32_t)); } } else - av_hash_update(c->hash, pkt->side_data[i].data, pkt->side_data[i].size); + av_hash_update(c->hashes[0], pkt->side_data[i].data, pkt->side_data[i].size); snprintf(buf, sizeof(buf) - (AV_HASH_MAX_SIZE * 2 + 1), ", %8d, ", pkt->side_data[i].size); len = strlen(buf); - av_hash_final_hex(c->hash, buf + len, sizeof(buf) - len); + av_hash_final_hex(c->hashes[0], buf + len, sizeof(buf) - len); avio_write(s->pb, buf, strlen(buf)); } } avio_printf(s->pb, "\n"); - avio_flush(s->pb); return 0; } -static int framehash_write_trailer(struct AVFormatContext *s) +static void framehash_free(struct AVFormatContext *s) { struct HashContext *c = s->priv_data; - av_hash_freep(&c->hash); - return 0; + if (c->hashes) + av_hash_freep(&c->hashes[0]); + av_freep(&c->hashes); } #endif @@ -219,7 +340,7 @@ static int framehash_write_trailer(struct AVFormatContext *s) static const AVClass framehash_class = { .class_name = "frame hash muxer", .item_name = av_default_item_name, - .option = hash_options, + .option = framehash_options, .version = LIBAVUTIL_VERSION_INT, }; @@ -229,9 +350,10 @@ AVOutputFormat ff_framehash_muxer = { .priv_data_size = sizeof(struct HashContext), .audio_codec = AV_CODEC_ID_PCM_S16LE, .video_codec = AV_CODEC_ID_RAWVIDEO, + .init = framehash_init, .write_header = framehash_write_header, .write_packet = framehash_write_packet, - .write_trailer = framehash_write_trailer, + .deinit = framehash_free, .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_TS_NEGATIVE, .priv_class = &framehash_class, @@ -242,7 +364,7 @@ AVOutputFormat ff_framehash_muxer = { static const AVClass framemd5_class = { .class_name = "frame MD5 muxer", .item_name = av_default_item_name, - .option = md5_options, + .option = framemd5_options, .version = LIBAVUTIL_VERSION_INT, }; @@ -252,9 +374,10 @@ AVOutputFormat ff_framemd5_muxer = { .priv_data_size = sizeof(struct HashContext), .audio_codec = AV_CODEC_ID_PCM_S16LE, .video_codec = AV_CODEC_ID_RAWVIDEO, + .init = framehash_init, .write_header = framehash_write_header, .write_packet = framehash_write_packet, - .write_trailer = framehash_write_trailer, + .deinit = framehash_free, .flags = AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT | AVFMT_TS_NEGATIVE, .priv_class = &framemd5_class, diff --git a/libavformat/hca.c b/libavformat/hca.c new file mode 100644 index 00000000000..8f55e07204d --- /dev/null +++ b/libavformat/hca.c @@ -0,0 +1,124 @@ +/* + * HCA demuxer + * Copyright (c) 2020 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 "libavcodec/bytestream.h" + +#include "avformat.h" +#include "internal.h" + +static int hca_probe(const AVProbeData *p) +{ + if (AV_RL32(p->buf) != MKTAG('H', 'C', 'A', 0)) + return 0; + + if (AV_RL32(p->buf + 8) != MKTAG('f', 'm', 't', 0)) + return 0; + + return AVPROBE_SCORE_MAX / 3; +} + +static int hca_read_header(AVFormatContext *s) +{ + AVCodecParameters *par; + GetByteContext gb; + AVIOContext *pb = s->pb; + AVStream *st; + uint32_t chunk; + uint16_t version; + uint32_t block_count; + uint16_t block_size; + int ret; + + avio_skip(pb, 4); + version = avio_rb16(pb); + + s->internal->data_offset = avio_rb16(pb); + if (s->internal->data_offset <= 8) + return AVERROR_INVALIDDATA; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + par = st->codecpar; + ret = ff_alloc_extradata(par, s->internal->data_offset); + if (ret < 0) + return ret; + + ret = avio_read(pb, par->extradata + 8, par->extradata_size - 8); + if (ret < par->extradata_size - 8) + return AVERROR(EIO); + AV_WL32(par->extradata, MKTAG('H', 'C', 'A', 0)); + AV_WB16(par->extradata + 4, version); + AV_WB16(par->extradata + 6, s->internal->data_offset); + + bytestream2_init(&gb, par->extradata + 8, par->extradata_size - 8); + + if (bytestream2_get_le32(&gb) != MKTAG('f', 'm', 't', 0)) + return AVERROR_INVALIDDATA; + + par->codec_type = AVMEDIA_TYPE_AUDIO; + par->codec_id = AV_CODEC_ID_HCA; + par->codec_tag = 0; + par->channels = bytestream2_get_byte(&gb); + par->sample_rate = bytestream2_get_be24(&gb); + block_count = bytestream2_get_be32(&gb); + bytestream2_skip(&gb, 4); + chunk = bytestream2_get_le32(&gb); + if (chunk == MKTAG('c', 'o', 'm', 'p')) { + block_size = bytestream2_get_be16(&gb); + } else if (chunk == MKTAG('d', 'e', 'c', 0)) { + block_size = bytestream2_get_be16(&gb); + } else { + return AVERROR_INVALIDDATA; + } + + if (block_size < 8) + return AVERROR_INVALIDDATA; + par->block_align = block_size; + st->duration = 1024 * block_count; + + avio_seek(pb, s->internal->data_offset, SEEK_SET); + avpriv_set_pts_info(st, 64, 1, par->sample_rate); + + return 0; +} + +static int hca_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVCodecParameters *par = s->streams[0]->codecpar; + int ret; + + ret = av_get_packet(s->pb, pkt, par->block_align); + pkt->duration = 1024; + return ret; +} + +AVInputFormat ff_hca_demuxer = { + .name = "hca", + .long_name = NULL_IF_CONFIG_SMALL("CRI HCA"), + .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 0d1736b6201..3e1e8da236c 100644 --- a/libavformat/hcom.c +++ b/libavformat/hcom.c @@ -38,7 +38,7 @@ static int hcom_probe(const AVProbeData *p) static int hcom_read_header(AVFormatContext *s) { AVStream *st; - unsigned data_size, rsrc_size, huffcount; + av_unused unsigned data_size, rsrc_size, huffcount; unsigned compresstype, divisor; unsigned dict_entries; int ret; diff --git a/libavformat/hdsenc.c b/libavformat/hdsenc.c index 026530ac369..353a45f6df2 100644 --- a/libavformat/hdsenc.c +++ b/libavformat/hdsenc.c @@ -146,8 +146,7 @@ static void hds_free(AVFormatContext *s) av_write_trailer(os->ctx); if (os->ctx) avio_context_free(&os->ctx->pb); - if (os->ctx) - avformat_free_context(os->ctx); + avformat_free_context(os->ctx); av_freep(&os->metadata); for (j = 0; j < os->nb_extra_packets; j++) av_freep(&os->extra_packets[j]); @@ -318,21 +317,18 @@ static int hds_write_header(AVFormatContext *s) ff_const59 AVOutputFormat *oformat; if (mkdir(s->url, 0777) == -1 && errno != EEXIST) { - ret = AVERROR(errno); av_log(s, AV_LOG_ERROR , "Failed to create directory %s\n", s->url); - goto fail; + return AVERROR(errno); } oformat = av_guess_format("flv", NULL, NULL); if (!oformat) { - ret = AVERROR_MUXER_NOT_FOUND; - goto fail; + return AVERROR_MUXER_NOT_FOUND; } c->streams = av_mallocz_array(s->nb_streams, sizeof(*c->streams)); if (!c->streams) { - ret = AVERROR(ENOMEM); - goto fail; + return AVERROR(ENOMEM); } for (i = 0; i < s->nb_streams; i++) { @@ -342,8 +338,7 @@ static int hds_write_header(AVFormatContext *s) if (!st->codecpar->bit_rate) { av_log(s, AV_LOG_ERROR, "No bit rate set for stream %d\n", i); - ret = AVERROR(EINVAL); - goto fail; + return AVERROR(EINVAL); } if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { if (os->has_video) { @@ -359,8 +354,7 @@ static int hds_write_header(AVFormatContext *s) os->has_audio = 1; } else { av_log(s, AV_LOG_ERROR, "Unsupported stream type in stream %d\n", i); - ret = AVERROR(EINVAL); - goto fail; + return AVERROR(EINVAL); } os->bitrate += s->streams[i]->codecpar->bit_rate; @@ -368,8 +362,7 @@ static int hds_write_header(AVFormatContext *s) os->first_stream = i; ctx = avformat_alloc_context(); if (!ctx) { - ret = AVERROR(ENOMEM); - goto fail; + return AVERROR(ENOMEM); } os->ctx = ctx; ctx->oformat = oformat; @@ -380,8 +373,7 @@ static int hds_write_header(AVFormatContext *s) AVIO_FLAG_WRITE, os, NULL, hds_write, NULL); if (!ctx->pb) { - ret = AVERROR(ENOMEM); - goto fail; + return AVERROR(ENOMEM); } } else { ctx = os->ctx; @@ -389,8 +381,7 @@ static int hds_write_header(AVFormatContext *s) s->streams[i]->id = c->nb_streams; if (!(st = avformat_new_stream(ctx, NULL))) { - ret = AVERROR(ENOMEM); - goto fail; + return AVERROR(ENOMEM); } avcodec_parameters_copy(st->codecpar, s->streams[i]->codecpar); st->codecpar->codec_tag = 0; @@ -404,7 +395,7 @@ static int hds_write_header(AVFormatContext *s) OutputStream *os = &c->streams[i]; int j; if ((ret = avformat_write_header(os->ctx, NULL)) < 0) { - goto fail; + return ret; } os->ctx_inited = 1; avio_flush(os->ctx->pb); @@ -415,7 +406,7 @@ static int hds_write_header(AVFormatContext *s) "%s/stream%d_temp", s->url, i); ret = init_file(s, os, 0); if (ret < 0) - goto fail; + return ret; if (!os->has_video && c->min_frag_duration <= 0) { av_log(s, AV_LOG_WARNING, @@ -426,9 +417,6 @@ static int hds_write_header(AVFormatContext *s) } ret = write_manifest(s, 0); -fail: - if (ret) - hds_free(s); return ret; } @@ -558,7 +546,6 @@ static int hds_write_trailer(AVFormatContext *s) rmdir(s->url); } - hds_free(s); return 0; } @@ -589,5 +576,6 @@ AVOutputFormat ff_hds_muxer = { .write_header = hds_write_header, .write_packet = hds_write_packet, .write_trailer = hds_write_trailer, + .deinit = hds_free, .priv_class = &hds_class, }; diff --git a/libavformat/hevc.c b/libavformat/hevc.c index c7c4be34416..f621cb2f198 100644 --- a/libavformat/hevc.c +++ b/libavformat/hevc.c @@ -25,6 +25,7 @@ #include "libavutil/intreadwrite.h" #include "avc.h" #include "avio.h" +#include "avio_internal.h" #include "hevc.h" #define MAX_SPATIAL_SEGMENTATION 4096 // max. value of u(12) field @@ -1054,9 +1055,14 @@ int ff_hevc_annexb2mp4_buf(const uint8_t *buf_in, uint8_t **buf_out, return ret; ret = ff_hevc_annexb2mp4(pb, buf_in, *size, filter_ps, ps_count); + if (ret < 0) { + ffio_free_dyn_buf(&pb); + return ret; + } + *size = avio_close_dyn_buf(pb, buf_out); - return ret; + return 0; } int ff_isom_write_hvcc(AVIOContext *pb, const uint8_t *data, diff --git a/libavformat/hevc.h b/libavformat/hevc.h index 796eaf40b16..0f56325c1cd 100644 --- a/libavformat/hevc.h +++ b/libavformat/hevc.h @@ -60,19 +60,20 @@ int ff_hevc_annexb2mp4(AVIOContext *pb, const uint8_t *buf_in, * If filter_ps is non-zero, any HEVC parameter sets found in the input will be * discarded, and *ps_count will be set to the number of discarded PS NAL units. * - * On output, *size holds the size (in bytes) of the output data buffer. + * On success, *size holds the size (in bytes) of the output data buffer. * * @param buf_in address of the buffer holding the input data * @param size address of the variable holding the size (in bytes) of the input - * buffer (on input) and of the output buffer (on output) - * @param buf_out address of the variable holding the address of the output - * buffer + * buffer (on input) and of the output buffer (on success) + * @param buf_out on success, address of the variable holding the address of + * the output buffer * @param filter_ps whether to write parameter set NAL units to the output (0) * or to discard them (non-zero) * @param ps_count address of the variable where the number of discarded * parameter set NAL units shall be written, may be NULL - * @return the amount (in bytes) of data written in case of success, a negative - * value corresponding to an AVERROR code in case of failure + * @return 0 in case of success, a negative value corresponding to an AVERROR + * code in case of failure + * @note *buf_out will be treated as uninitialized on input and won't be freed. */ int ff_hevc_annexb2mp4_buf(const uint8_t *buf_in, uint8_t **buf_out, int *size, int filter_ps, int *ps_count); diff --git a/libavformat/hls.c b/libavformat/hls.c index 8c12fcef77b..3c7e197ce77 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -116,7 +116,10 @@ struct playlist { int n_segments; struct segment **segments; int needed; + int broken; int cur_seq_no; + int last_seq_no; + int m3u8_hold_counters; int64_t cur_seg_offset; int64_t last_load_time; @@ -197,6 +200,7 @@ typedef struct HLSContext { struct rendition **renditions; int cur_seq_no; + int m3u8_hold_counters; int live_start_index; int first_packet; int64_t first_timestamp; @@ -207,6 +211,7 @@ typedef struct HLSContext { int max_reload; int http_persistent; int http_multiple; + int http_seekable; AVIOContext *playlist_pb; } HLSContext; @@ -253,11 +258,9 @@ static void free_playlist_list(HLSContext *c) av_freep(&pls->init_sec_buf); av_packet_unref(&pls->pkt); av_freep(&pls->pb.buffer); - if (pls->input) - ff_format_io_close(c->ctx, &pls->input); + ff_format_io_close(c->ctx, &pls->input); pls->input_read_done = 0; - if (pls->input_next) - ff_format_io_close(c->ctx, &pls->input_next); + ff_format_io_close(c->ctx, &pls->input_next); pls->input_next_requested = 0; if (pls->ctx) { pls->ctx->pb = NULL; @@ -308,6 +311,8 @@ static struct playlist *new_playlist(HLSContext *c, const char *url, return NULL; reset_packet(&pls->pkt); ff_make_absolute_url(pls->url, sizeof(pls->url), base, url); + if (!pls->url[0]) + return NULL; pls->seek_timestamp = AV_NOPTS_VALUE; pls->is_id3_timestamped = -1; @@ -400,8 +405,7 @@ static struct segment *new_init_section(struct playlist *pls, const char *url_base) { struct segment *sec; - char *ptr; - char tmp_str[MAX_URL_SIZE]; + char tmp_str[MAX_URL_SIZE], *ptr = tmp_str; if (!info->uri[0]) return NULL; @@ -410,8 +414,16 @@ static struct segment *new_init_section(struct playlist *pls, if (!sec) return NULL; - ff_make_absolute_url(tmp_str, sizeof(tmp_str), url_base, info->uri); - sec->url = av_strdup(tmp_str); + if (!av_strncasecmp(info->uri, "data:", 5)) { + ptr = info->uri; + } else { + ff_make_absolute_url(tmp_str, sizeof(tmp_str), url_base, info->uri); + if (!tmp_str[0]) { + av_free(sec); + return NULL; + } + } + sec->url = av_strdup(ptr); if (!sec->url) { av_free(sec); return NULL; @@ -477,20 +489,20 @@ static struct rendition *new_rendition(HLSContext *c, struct rendition_info *inf return NULL; if (type == AVMEDIA_TYPE_UNKNOWN) { - av_log(c, AV_LOG_WARNING, "Can't support the type: %s\n", info->type); + av_log(c->ctx, AV_LOG_WARNING, "Can't support the type: %s\n", info->type); return NULL; } /* URI is mandatory for subtitles as per spec */ if (type == AVMEDIA_TYPE_SUBTITLE && !info->uri[0]) { - av_log(c, AV_LOG_ERROR, "The URI tag is REQUIRED for subtitle.\n"); + av_log(c->ctx, AV_LOG_ERROR, "The URI tag is REQUIRED for subtitle.\n"); return NULL; } /* TODO: handle subtitles (each segment has to parsed separately) */ if (c->ctx->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) if (type == AVMEDIA_TYPE_SUBTITLE) { - av_log(c, AV_LOG_WARNING, "Can't support the subtitle(uri: %s)\n", info->uri); + av_log(c->ctx, AV_LOG_WARNING, "Can't support the subtitle(uri: %s)\n", info->uri); return NULL; } @@ -595,7 +607,7 @@ static int ensure_playlist(HLSContext *c, struct playlist **pls, const char *url } static int open_url_keepalive(AVFormatContext *s, AVIOContext **pb, - const char *url) + const char *url, AVDictionary **options) { #if !CONFIG_HTTP_PROTOCOL return AVERROR_PROTOCOL_NOT_FOUND; @@ -604,7 +616,7 @@ static int open_url_keepalive(AVFormatContext *s, AVIOContext **pb, URLContext *uc = ffio_geturlcontext(*pb); av_assert0(uc); (*pb)->eof_reached = 0; - ret = ff_http_do_new_request(uc, url); + ret = ff_http_do_new_request2(uc, url, options); if (ret < 0) { ff_format_io_close(s, pb); } @@ -621,12 +633,12 @@ static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url, int ret; int is_http = 0; - av_dict_copy(&tmp, opts, 0); - av_dict_copy(&tmp, opts2, 0); - if (av_strstart(url, "crypto", NULL)) { if (url[6] == '+' || url[6] == ':') proto_name = avio_find_protocol_name(url + 7); + } else if (av_strstart(url, "data", NULL)) { + if (url[4] == '+' || url[4] == ':') + proto_name = avio_find_protocol_name(url + 5); } if (!proto_name) @@ -646,6 +658,8 @@ static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url, } } else if (av_strstart(proto_name, "http", NULL)) { is_http = 1; + } else if (av_strstart(proto_name, "data", NULL)) { + ; } else return AVERROR_INVALIDDATA; @@ -653,17 +667,23 @@ static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url, ; else if (av_strstart(url, "crypto", NULL) && !strncmp(proto_name, url + 7, strlen(proto_name)) && url[7 + strlen(proto_name)] == ':') ; + else if (av_strstart(url, "data", NULL) && !strncmp(proto_name, url + 5, strlen(proto_name)) && url[5 + strlen(proto_name)] == ':') + ; else if (strcmp(proto_name, "file") || !strncmp(url, "file,", 5)) return AVERROR_INVALIDDATA; + av_dict_copy(&tmp, opts, 0); + av_dict_copy(&tmp, opts2, 0); + if (is_http && c->http_persistent && *pb) { - ret = open_url_keepalive(c->ctx, pb, url); + ret = open_url_keepalive(c->ctx, pb, url, &tmp); if (ret == AVERROR_EXIT) { + av_dict_free(&tmp); return ret; } else if (ret < 0) { if (ret != AVERROR_EOF) av_log(s, AV_LOG_WARNING, - "keepalive request failed for '%s' when opening url, retrying with new connection: %s\n", + "keepalive request failed for '%s' with error: '%s' when opening url, retrying with new connection\n", url, av_err2str(ret)); ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp); } @@ -714,13 +734,13 @@ static int parse_playlist(HLSContext *c, const char *url, if (is_http && !in && c->http_persistent && c->playlist_pb) { in = c->playlist_pb; - ret = open_url_keepalive(c->ctx, &c->playlist_pb, url); + ret = open_url_keepalive(c->ctx, &c->playlist_pb, url, NULL); if (ret == AVERROR_EXIT) { return ret; } else if (ret < 0) { if (ret != AVERROR_EOF) av_log(c->ctx, AV_LOG_WARNING, - "keepalive request failed for '%s' when parsing playlist, retrying with new connection: %s\n", + "keepalive request failed for '%s' with error: '%s' when parsing playlist\n", url, av_err2str(ret)); in = NULL; } @@ -827,6 +847,11 @@ static int parse_playlist(HLSContext *c, const char *url, if (key_type != KEY_NONE) { ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, key); + if (!tmp_str[0]) { + av_free(cur_init_section); + ret = AVERROR_INVALIDDATA; + goto fail; + } cur_init_section->key = av_strdup(tmp_str); if (!cur_init_section->key) { av_free(cur_init_section); @@ -861,20 +886,14 @@ static int parse_playlist(HLSContext *c, const char *url, } if (is_segment) { struct segment *seg; - if (!pls) { - if (!new_variant(c, 0, url, NULL)) { - ret = AVERROR(ENOMEM); - goto fail; - } - pls = c->playlists[c->n_playlists - 1]; - } + ret = ensure_playlist(c, &pls, url); + if (ret < 0) + goto fail; seg = av_malloc(sizeof(struct segment)); if (!seg) { ret = AVERROR(ENOMEM); goto fail; } - seg->duration = duration; - seg->key_type = key_type; if (has_iv) { memcpy(seg->iv, iv, sizeof(iv)); } else { @@ -885,6 +904,11 @@ static int parse_playlist(HLSContext *c, const char *url, if (key_type != KEY_NONE) { ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, key); + if (!tmp_str[0]) { + ret = AVERROR_INVALIDDATA; + av_free(seg); + goto fail; + } seg->key = av_strdup(tmp_str); if (!seg->key) { av_free(seg); @@ -896,6 +920,13 @@ static int parse_playlist(HLSContext *c, const char *url, } ff_make_absolute_url(tmp_str, sizeof(tmp_str), url, line); + if (!tmp_str[0]) { + ret = AVERROR_INVALIDDATA; + if (seg->key) + av_free(seg->key); + av_free(seg); + goto fail; + } seg->url = av_strdup(tmp_str); if (!seg->url) { av_free(seg->key); @@ -904,6 +935,13 @@ static int parse_playlist(HLSContext *c, const char *url, goto fail; } + if (duration < 0.001 * AV_TIME_BASE) { + av_log(c->ctx, AV_LOG_WARNING, "Cannot get correct #EXTINF value of segment %s," + " set to default value to 1ms.\n", seg->url); + duration = 0.001 * AV_TIME_BASE; + } + seg->duration = duration; + seg->key_type = key_type; dynarray_add(&pls->segments, &pls->n_segments, seg); is_segment = 0; @@ -994,7 +1032,7 @@ static void parse_id3(AVFormatContext *s, AVIOContext *pb, ff_id3v2_read_dict(pb, metadata, ID3v2_DEFAULT_MAGIC, extra_meta); for (meta = *extra_meta; meta; meta = meta->next) { if (!strcmp(meta->tag, "PRIV")) { - ID3v2ExtraMetaPRIV *priv = meta->data; + ID3v2ExtraMetaPRIV *priv = &meta->data.priv; if (priv->datasize == 8 && !strcmp(priv->owner, id3_priv_owner_ts)) { /* 33-bit MPEG timestamp */ int64_t ts = AV_RB64(priv->data); @@ -1005,7 +1043,7 @@ static void parse_id3(AVFormatContext *s, AVIOContext *pb, av_log(s, AV_LOG_ERROR, "Invalid HLS ID3 audio timestamp %"PRId64"\n", ts); } } else if (!strcmp(meta->tag, "APIC") && apic) - *apic = meta->data; + *apic = &meta->data.apic; } } @@ -1060,18 +1098,18 @@ static void handle_id3(AVIOContext *pb, struct playlist *pls) /* get picture attachment and set text metadata */ if (pls->ctx->nb_streams) - ff_id3v2_parse_apic(pls->ctx, &extra_meta); + ff_id3v2_parse_apic(pls->ctx, extra_meta); else /* demuxer not yet opened, defer picture attachment */ pls->id3_deferred_extra = extra_meta; - ff_id3v2_parse_priv_dict(&metadata, &extra_meta); + ff_id3v2_parse_priv_dict(&metadata, extra_meta); av_dict_copy(&pls->ctx->metadata, metadata, 0); pls->id3_initial = metadata; } else { if (!pls->id3_changed && id3_has_changed_values(pls, metadata, apic)) { - avpriv_report_missing_feature(pls->ctx, "Changing ID3 metadata in HLS audio elementary stream"); + avpriv_report_missing_feature(pls->parent, "Changing ID3 metadata in HLS audio elementary stream"); pls->id3_changed = 1; } av_dict_free(&metadata); @@ -1122,7 +1160,7 @@ static void intercept_id3(struct playlist *pls, uint8_t *buf, int remaining = taglen - tag_got_bytes; if (taglen > maxsize) { - av_log(pls->ctx, AV_LOG_ERROR, "Too large HLS ID3 tag (%d > %"PRId64" bytes)\n", + av_log(pls->parent, AV_LOG_ERROR, "Too large HLS ID3 tag (%d > %"PRId64" bytes)\n", taglen, maxsize); break; } @@ -1143,14 +1181,14 @@ static void intercept_id3(struct playlist *pls, uint8_t *buf, /* strip the intercepted bytes */ *len -= tag_got_bytes; memmove(buf, buf + tag_got_bytes, *len); - av_log(pls->ctx, AV_LOG_DEBUG, "Stripped %d HLS ID3 bytes\n", tag_got_bytes); + av_log(pls->parent, AV_LOG_DEBUG, "Stripped %d HLS ID3 bytes\n", tag_got_bytes); if (remaining > 0) { /* read the rest of the tag in */ if (read_from_url(pls, seg, pls->id3_buf + id3_buf_pos, remaining) != remaining) break; id3_buf_pos += remaining; - av_log(pls->ctx, AV_LOG_DEBUG, "Stripped additional %d HLS ID3 bytes\n", remaining); + av_log(pls->parent, AV_LOG_DEBUG, "Stripped additional %d HLS ID3 bytes\n", remaining); } } else { @@ -1431,6 +1469,17 @@ static int read_data(void *opaque, uint8_t *buf, int buf_size) v->start_seq_no - v->cur_seq_no); v->cur_seq_no = v->start_seq_no; } + if (v->cur_seq_no > v->last_seq_no) { + v->last_seq_no = v->cur_seq_no; + v->m3u8_hold_counters = 0; + } else if (v->last_seq_no == v->cur_seq_no) { + v->m3u8_hold_counters++; + if (v->m3u8_hold_counters >= c->m3u8_hold_counters) { + return AVERROR_EOF; + } + } else { + av_log(v->parent, AV_LOG_WARNING, "maybe the m3u8 list sequence have been wraped.\n"); + } if (v->cur_seq_no >= v->start_seq_no + v->n_segments) { if (v->finished) return AVERROR_EOF; @@ -1453,6 +1502,7 @@ static int read_data(void *opaque, uint8_t *buf, int buf_size) if (c->http_multiple == 1 && v->input_next_requested) { FFSWAP(AVIOContext *, v->input, v->input_next); + v->cur_seg_offset = 0; v->input_next_requested = 0; ret = 0; } else { @@ -1486,7 +1536,7 @@ static int read_data(void *opaque, uint8_t *buf, int buf_size) if (ret < 0) { if (ff_check_interrupt(c->interrupt_callback)) return AVERROR_EXIT; - av_log(v->parent, AV_LOG_WARNING, "Failed to open segment %d of playlist %d\n", + av_log(v->parent, AV_LOG_WARNING, "Failed to open next segment %d of playlist %d\n", v->cur_seq_no + 1, v->index); } else { @@ -1651,7 +1701,7 @@ static int save_avio_options(AVFormatContext *s) { HLSContext *c = s->priv_data; static const char * const opts[] = { - "headers", "http_proxy", "user_agent", "cookies", "referer", "rw_timeout", NULL }; + "headers", "http_proxy", "user_agent", "cookies", "referer", "rw_timeout", "icy", NULL }; const char * const * opt = opts; uint8_t *buf; int ret = 0; @@ -1800,8 +1850,10 @@ static int hls_read_header(AVFormatContext *s) if ((ret = save_avio_options(s)) < 0) goto fail; - /* Some HLS servers don't like being sent the range header */ - av_dict_set(&c->avio_opts, "seekable", "0", 0); + /* XXX: Some HLS servers don't like being sent the range header, + in this case, need to setting http_seekable = 0 to disable + the range header */ + av_dict_set_int(&c->avio_opts, "seekable", c->http_seekable, 0); if ((ret = parse_playlist(c, s->url, NULL, s->pb)) < 0) goto fail; @@ -1816,15 +1868,22 @@ static int hls_read_header(AVFormatContext *s) if (c->n_playlists > 1 || c->playlists[0]->n_segments == 0) { for (i = 0; i < c->n_playlists; i++) { struct playlist *pls = c->playlists[i]; - if ((ret = parse_playlist(c, pls->url, pls, NULL)) < 0) + pls->m3u8_hold_counters = 0; + if ((ret = parse_playlist(c, pls->url, pls, NULL)) < 0) { + av_log(s, AV_LOG_WARNING, "parse_playlist error %s [%s]\n", av_err2str(ret), pls->url); + pls->broken = 1; + if (c->n_playlists > 1) + continue; goto fail; + } } } - if (c->variants[0]->playlists[0]->n_segments == 0) { - av_log(s, AV_LOG_WARNING, "Empty segment\n"); - ret = AVERROR_EOF; - goto fail; + for (i = 0; i < c->n_variants; i++) { + if (c->variants[i]->playlists[0]->n_segments == 0) { + av_log(s, AV_LOG_WARNING, "Empty segment [%s]\n", c->variants[i]->playlists[0]->url); + c->variants[i]->playlists[0]->broken = 1; + } } /* If this isn't a live stream, calculate the total duration of the @@ -1873,6 +1932,7 @@ static int hls_read_header(AVFormatContext *s) /* Open the demuxer for each playlist */ for (i = 0; i < c->n_playlists; i++) { struct playlist *pls = c->playlists[i]; + char *url; ff_const59 AVInputFormat *in_fmt = NULL; if (!(pls->ctx = avformat_alloc_context())) { @@ -1908,9 +1968,11 @@ static int hls_read_header(AVFormatContext *s) } ffio_init_context(&pls->pb, pls->read_buffer, INITIAL_BUFFER_SIZE, 0, pls, read_data, NULL, NULL); - pls->pb.seekable = 0; - ret = av_probe_input_buffer(&pls->pb, &in_fmt, pls->segments[0]->url, - NULL, 0, 0); + 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; + url = av_strdup(pls->segments[0]->url); + ret = av_probe_input_buffer(&pls->pb, &in_fmt, url, NULL, 0, 0); + av_free(url); if (ret < 0) { /* Free the ctx - it isn't initialized properly at this point, * so avformat_close_input shouldn't be called. If @@ -1933,11 +1995,10 @@ static int hls_read_header(AVFormatContext *s) goto fail; if (pls->id3_deferred_extra && pls->ctx->nb_streams == 1) { - ff_id3v2_parse_apic(pls->ctx, &pls->id3_deferred_extra); + ff_id3v2_parse_apic(pls->ctx, pls->id3_deferred_extra); avformat_queue_attached_pictures(pls->ctx); - ff_id3v2_parse_priv(pls->ctx, &pls->id3_deferred_extra); + ff_id3v2_parse_priv(pls->ctx, pls->id3_deferred_extra); ff_id3v2_free_extra_meta(&pls->id3_deferred_extra); - pls->id3_deferred_extra = NULL; } if (pls->is_id3_timestamped == -1) @@ -1994,6 +2055,9 @@ static int recheck_discard_flags(AVFormatContext *s, int first) cur_needed = playlist_needed(c->playlists[i]); + if (pls->broken) { + continue; + } if (cur_needed && !pls->needed) { pls->needed = 1; changed = 1; @@ -2007,11 +2071,9 @@ static int recheck_discard_flags(AVFormatContext *s, int first) } av_log(s, AV_LOG_INFO, "Now receiving playlist %d, segment %d\n", i, pls->cur_seq_no); } else if (first && !cur_needed && pls->needed) { - if (pls->input) - ff_format_io_close(pls->parent, &pls->input); + ff_format_io_close(pls->parent, &pls->input); pls->input_read_done = 0; - if (pls->input_next) - ff_format_io_close(pls->parent, &pls->input_next); + ff_format_io_close(pls->parent, &pls->input_next); pls->input_next_requested = 0; pls->needed = 0; changed = 1; @@ -2120,7 +2182,6 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt) } } av_packet_unref(&pls->pkt); - reset_packet(&pls->pkt); } } /* Check if this stream has the packet with the lowest dts */ @@ -2149,7 +2210,6 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt) ret = update_streams_from_subdemuxer(s, pls); if (ret < 0) { av_packet_unref(&pls->pkt); - reset_packet(&pls->pkt); return ret; } @@ -2174,16 +2234,14 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt) av_log(s, AV_LOG_ERROR, "stream index inconsistency: index %d, %d main streams, %d subdemuxer streams\n", pls->pkt.stream_index, pls->n_main_streams, pls->ctx->nb_streams); av_packet_unref(&pls->pkt); - reset_packet(&pls->pkt); return AVERROR_BUG; } ist = pls->ctx->streams[pls->pkt.stream_index]; st = pls->main_streams[pls->pkt.stream_index]; - *pkt = pls->pkt; + av_packet_move_ref(pkt, &pls->pkt); pkt->stream_index = st->index; - reset_packet(&c->playlists[minplaylist]->pkt); if (pkt->dts != AV_NOPTS_VALUE) c->cur_timestamp = av_rescale_q(pkt->dts, @@ -2195,7 +2253,6 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt) if (ist->codecpar->codec_id != st->codecpar->codec_id) { ret = set_stream_info_from_input_stream(st, pls, ist); if (ret < 0) { - av_packet_unref(pkt); return ret; } } @@ -2255,14 +2312,11 @@ static int hls_read_seek(AVFormatContext *s, int stream_index, for (i = 0; i < c->n_playlists; i++) { /* Reset reading */ struct playlist *pls = c->playlists[i]; - if (pls->input) - ff_format_io_close(pls->parent, &pls->input); + ff_format_io_close(pls->parent, &pls->input); pls->input_read_done = 0; - if (pls->input_next) - ff_format_io_close(pls->parent, &pls->input_next); + ff_format_io_close(pls->parent, &pls->input_next); pls->input_next_requested = 0; av_packet_unref(&pls->pkt); - reset_packet(&pls->pkt); pls->pb.eof_reached = 0; /* Clear any buffered data */ pls->pb.buf_end = pls->pb.buf_ptr = pls->pb.buffer; @@ -2315,10 +2369,14 @@ static const AVOption hls_options[] = { INT_MIN, INT_MAX, FLAGS}, {"max_reload", "Maximum number of times a insufficient list is attempted to be reloaded", OFFSET(max_reload), AV_OPT_TYPE_INT, {.i64 = 1000}, 0, INT_MAX, FLAGS}, + {"m3u8_hold_counters", "The maximum number of times to load m3u8 when it refreshes without new segments", + OFFSET(m3u8_hold_counters), AV_OPT_TYPE_INT, {.i64 = 1000}, 0, INT_MAX, FLAGS}, {"http_persistent", "Use persistent HTTP connections", OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, {"http_multiple", "Use multiple HTTP connections for fetching segments", OFFSET(http_multiple), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, FLAGS}, + {"http_seekable", "Use HTTP partial requests, 0 = disable, 1 = enable, -1 = auto", + OFFSET(http_seekable), AV_OPT_TYPE_BOOL, { .i64 = -1}, -1, 1, FLAGS}, {NULL} }; @@ -2334,7 +2392,7 @@ AVInputFormat ff_hls_demuxer = { .long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"), .priv_class = &hls_class, .priv_data_size = sizeof(HLSContext), - .flags = AVFMT_NOGENSEARCH, + .flags = AVFMT_NOGENSEARCH | AVFMT_TS_DISCONT, .read_probe = hls_probe, .read_header = hls_read_header, .read_packet = hls_read_packet, diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 2ade6723f9d..71fa3db060b 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -41,6 +41,7 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "libavutil/log.h" +#include "libavutil/time.h" #include "libavutil/time_internal.h" #include "avformat.h" @@ -53,9 +54,11 @@ #include "os_support.h" typedef enum { - HLS_START_SEQUENCE_AS_START_NUMBER = 0, - HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH = 1, - HLS_START_SEQUENCE_AS_FORMATTED_DATETIME = 2, // YYYYMMDDhhmmss + HLS_START_SEQUENCE_AS_START_NUMBER = 0, + HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH = 1, + HLS_START_SEQUENCE_AS_FORMATTED_DATETIME = 2, // YYYYMMDDhhmmss + HLS_START_SEQUENCE_AS_MICROSECONDS_SINCE_EPOCH = 3, + HLS_START_SEQUENCE_LAST, // unused } StartSequenceSourceType; typedef enum { @@ -118,6 +121,8 @@ typedef struct VariantStream { AVIOContext *out; int packets_written; int init_range_length; + uint8_t *temp_buffer; + uint8_t *init_buffer; AVFormatContext *avf; AVFormatContext *vtt_avf; @@ -125,6 +130,7 @@ typedef struct VariantStream { int has_video; int has_subtitle; int new_start; + int start_pts_from_audio; double dpp; // duration per packet int64_t start_pts; int64_t end_pts; @@ -154,23 +160,30 @@ typedef struct VariantStream { char *fmp4_init_filename; char *base_output_dirname; + int encrypt_started; + + char key_file[LINE_BUFFER_SIZE + 1]; + char key_uri[LINE_BUFFER_SIZE + 1]; + char key_string[KEYSIZE*2 + 1]; + char iv_string[KEYSIZE*2 + 1]; + AVStream **streams; char codec_attr[128]; CodecAttributeStatus attr_status; unsigned int nb_streams; int m3u8_created; /* status of media play-list creation */ int is_default; /* default status of audio group */ - char *language; /* audio lauguage name */ - char *agroup; /* audio group name */ - char *ccgroup; /* closed caption group name */ - char *baseurl; - char *varname; // variant name + const char *language; /* audio lauguage name */ + const char *agroup; /* audio group name */ + const char *sgroup; /* subtitle group name */ + const char *ccgroup; /* closed caption group name */ + const char *varname; /* variant name */ } VariantStream; typedef struct ClosedCaptionsStream { - char *ccgroup; /* closed caption group name */ - char *instreamid; /* closed captions INSTREAM-ID */ - char *language; /* closed captions langauge */ + const char *ccgroup; /* closed caption group name */ + const char *instreamid; /* closed captions INSTREAM-ID */ + const char *language; /* closed captions langauge */ } ClosedCaptionsStream; typedef struct HLSContext { @@ -190,6 +203,7 @@ typedef struct HLSContext { char *segment_filename; char *fmp4_init_filename; int segment_type; + int resend_init_file; ///< resend init file into disk after refresh m3u8 int use_localtime; ///< flag to expand filename with localtime int use_localtime_mkdir;///< flag to mkdir dirname in timebased filename @@ -198,7 +212,6 @@ typedef struct HLSContext { int64_t max_seg_size; // every segment file max size char *baseurl; - char *format_options_str; char *vtt_format_options_str; char *subtitle_filename; AVDictionary *format_options; @@ -243,7 +256,8 @@ typedef struct HLSContext { } HLSContext; static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, - AVDictionary **options) { + AVDictionary **options) +{ HLSContext *hls = s->priv_data; int http_base_proto = filename ? ff_is_http_proto(filename) : 0; int err = AVERROR_MUXER_NOT_FOUND; @@ -262,11 +276,13 @@ static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, return err; } -static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename) { +static int hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename) +{ HLSContext *hls = s->priv_data; int http_base_proto = filename ? ff_is_http_proto(filename) : 0; + int ret = 0; if (!*pb) - return; + return ret; if (!http_base_proto || !hls->http_persistent || hls->key_info_file || hls->encrypt) { ff_format_io_close(s, pb); #if CONFIG_HTTP_PROTOCOL @@ -275,8 +291,10 @@ static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename av_assert0(http_url_context); avio_flush(*pb); ffurl_shutdown(http_url_context, AVIO_FLAG_WRITE); + ret = ff_http_get_shutdown_status(http_url_context); #endif } + return ret; } static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSContext *c) @@ -286,7 +304,6 @@ static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSCont if (c->method) { av_dict_set(options, "method", c->method, 0); } else if (http_base_proto) { - av_log(c, AV_LOG_WARNING, "No HTTP method set, hls muxer defaulting to method PUT.\n"); av_dict_set(options, "method", "PUT", 0); } if (c->user_agent) @@ -299,7 +316,8 @@ static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSCont av_dict_set(options, "headers", c->headers, 0); } -static void write_codec_attr(AVStream *st, VariantStream *vs) { +static void write_codec_attr(AVStream *st, VariantStream *vs) +{ int codec_strlen = strlen(vs->codec_attr); char attr[32]; @@ -447,7 +465,6 @@ static void write_styp(AVIOContext *pb) static int flush_dynbuf(VariantStream *vs, int *range_length) { AVFormatContext *ctx = vs->avf; - uint8_t *buffer; if (!ctx->pb) { return AVERROR(EINVAL); @@ -455,32 +472,67 @@ static int flush_dynbuf(VariantStream *vs, int *range_length) // flush av_write_frame(ctx, NULL); - avio_flush(ctx->pb); // write out to file - *range_length = avio_close_dyn_buf(ctx->pb, &buffer); + *range_length = avio_close_dyn_buf(ctx->pb, &vs->temp_buffer); ctx->pb = NULL; - avio_write(vs->out, buffer, *range_length); - av_free(buffer); + avio_write(vs->out, vs->temp_buffer, *range_length); + avio_flush(vs->out); // re-open buffer return avio_open_dyn_buf(&ctx->pb); } +static void reflush_dynbuf(VariantStream *vs, int *range_length) +{ + // re-open buffer + avio_write(vs->out, vs->temp_buffer, *range_length); +} + +#if HAVE_DOS_PATHS +#define SEPARATOR '\\' +#else +#define SEPARATOR '/' +#endif + +static int hls_delete_file(HLSContext *hls, AVFormatContext *avf, + const char *path, const char *proto) +{ + if (hls->method || (proto && !av_strcasecmp(proto, "http"))) { + AVDictionary *opt = NULL; + AVIOContext *out = NULL; + int ret; + av_dict_set(&opt, "method", "DELETE", 0); + ret = avf->io_open(avf, &out, path, AVIO_FLAG_WRITE, &opt); + av_dict_free(&opt); + if (ret < 0) + return hls->ignore_io_errors ? 1 : ret; + ff_format_io_close(avf, &out); + } else if (unlink(path) < 0) { + av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n", + path, strerror(errno)); + } + return 0; +} + static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls, - VariantStream *vs) { + VariantStream *vs) +{ HLSSegment *segment, *previous_segment = NULL; float playlist_duration = 0.0f; - int ret = 0, path_size, sub_path_size; + int ret = 0; int segment_cnt = 0; - char *dirname = NULL, *p, *sub_path; - char *path = NULL; - char *vtt_dirname = NULL; - AVDictionary *options = NULL; - AVIOContext *out = NULL; + AVBPrint path; + const char *dirname = NULL; + char *dirname_r = NULL; + char *dirname_repl = NULL; + const char *vtt_dirname = NULL; + char *vtt_dirname_r = NULL; const char *proto = NULL; + av_bprint_init(&path, 0, AV_BPRINT_SIZE_UNLIMITED); + segment = vs->segments; while (segment) { playlist_duration += segment->duration; @@ -505,114 +557,71 @@ static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls, } if (segment && !hls->use_localtime_mkdir) { - if (hls->segment_filename) { - dirname = av_strdup(hls->segment_filename); - } else { - dirname = av_strdup(vs->avf->url); - } - if (!dirname) { - ret = AVERROR(ENOMEM); - goto fail; - } - p = (char *)av_basename(dirname); - *p = '\0'; - + dirname_r = hls->segment_filename ? av_strdup(hls->segment_filename): av_strdup(vs->avf->url); + dirname = av_dirname(dirname_r); } /* if %v is present in the file's directory * all segment belongs to the same variant, so do it only once before the loop*/ if (dirname && av_stristr(dirname, "%v")) { - char * r_dirname = dirname; if (!vs->varname) { - if (replace_int_data_in_filename(&r_dirname, dirname, 'v', segment->var_stream_idx) < 1) { + if (replace_int_data_in_filename(&dirname_repl, dirname, 'v', segment->var_stream_idx) < 1) { ret = AVERROR(EINVAL); goto fail; } } else { - if (replace_str_data_in_filename(&r_dirname, dirname, 'v', vs->varname) < 1) { + if (replace_str_data_in_filename(&dirname_repl, dirname, 'v', vs->varname) < 1) { ret = AVERROR(EINVAL); goto fail; } } - av_free(dirname); - dirname = r_dirname; + dirname = dirname_repl; } while (segment) { av_log(hls, AV_LOG_DEBUG, "deleting old segment %s\n", - segment->filename); - path_size = (hls->use_localtime_mkdir ? 0 : strlen(dirname)) + strlen(segment->filename) + 1; - path = av_malloc(path_size); - if (!path) { + segment->filename); + if (!hls->use_localtime_mkdir) // segment->filename contains basename only + av_bprintf(&path, "%s%c", dirname, SEPARATOR); + av_bprintf(&path, "%s", segment->filename); + + if (!av_bprint_is_complete(&path)) { ret = AVERROR(ENOMEM); goto fail; } - if (hls->use_localtime_mkdir) - av_strlcpy(path, segment->filename, path_size); - else { // segment->filename contains basename only - av_strlcpy(path, dirname, path_size); - av_strlcat(path, segment->filename, path_size); - } - proto = avio_find_protocol_name(s->url); - if (hls->method || (proto && !av_strcasecmp(proto, "http"))) { - av_dict_set(&options, "method", "DELETE", 0); - if ((ret = vs->avf->io_open(vs->avf, &out, path, AVIO_FLAG_WRITE, &options)) < 0) { - if (hls->ignore_io_errors) - ret = 0; - goto fail; - } - ff_format_io_close(vs->avf, &out); - } else if (unlink(path) < 0) { - av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n", - path, strerror(errno)); - } + if (ret = hls_delete_file(hls, vs->avf, path.str, proto)) + goto fail; if ((segment->sub_filename[0] != '\0')) { - vtt_dirname = av_strdup(vs->vtt_avf->url); - if (!vtt_dirname) { - ret = AVERROR(ENOMEM); - goto fail; - } - p = (char *)av_basename(vtt_dirname); - *p = '\0'; - sub_path_size = strlen(segment->sub_filename) + 1 + strlen(vtt_dirname); - sub_path = av_malloc(sub_path_size); - if (!sub_path) { + vtt_dirname_r = av_strdup(vs->vtt_avf->url); + vtt_dirname = av_dirname(vtt_dirname_r); + + av_bprint_clear(&path); + av_bprintf(&path, "%s%c%s", vtt_dirname, SEPARATOR, + segment->sub_filename); + av_freep(&vtt_dirname_r); + + if (!av_bprint_is_complete(&path)) { ret = AVERROR(ENOMEM); goto fail; } - av_strlcpy(sub_path, vtt_dirname, sub_path_size); - av_strlcat(sub_path, segment->sub_filename, sub_path_size); - - if (hls->method || (proto && !av_strcasecmp(proto, "http"))) { - av_dict_set(&options, "method", "DELETE", 0); - if ((ret = vs->vtt_avf->io_open(vs->vtt_avf, &out, sub_path, AVIO_FLAG_WRITE, &options)) < 0) { - if (hls->ignore_io_errors) - ret = 0; - av_free(sub_path); - goto fail; - } - ff_format_io_close(vs->vtt_avf, &out); - } else if (unlink(sub_path) < 0) { - av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n", - sub_path, strerror(errno)); - } - av_free(sub_path); + if (ret = hls_delete_file(hls, vs->vtt_avf, path.str, proto)) + goto fail; } - av_freep(&path); + av_bprint_clear(&path); previous_segment = segment; segment = previous_segment->next; - av_free(previous_segment); + av_freep(&previous_segment); } fail: - av_free(path); - av_free(dirname); - av_free(vtt_dirname); + av_bprint_finalize(&path, NULL); + av_freep(&dirname_r); + av_freep(&dirname_repl); return ret; } @@ -638,13 +647,14 @@ static int do_encrypt(AVFormatContext *s, VariantStream *vs) int len; AVIOContext *pb; uint8_t key[KEYSIZE]; + char * key_basename_source = (hls->master_m3u8_url) ? hls->master_m3u8_url : s->url; - len = strlen(s->url) + 4 + 1; + len = strlen(key_basename_source) + 4 + 1; hls->key_basename = av_mallocz(len); if (!hls->key_basename) return AVERROR(ENOMEM); - av_strlcpy(hls->key_basename, s->url, len); + av_strlcpy(hls->key_basename, key_basename_source, len); av_strlcat(hls->key_basename, ".key", len); if (hls->key_url) { @@ -680,6 +690,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) { av_log(s, AV_LOG_ERROR, "Cannot generate a strong random key\n"); @@ -690,7 +701,10 @@ static int do_encrypt(AVFormatContext *s, VariantStream *vs) } ff_data_to_hex(hls->key_string, key, sizeof(key), 0); - if ((ret = s->io_open(s, &pb, hls->key_file, AVIO_FLAG_WRITE, NULL)) < 0) + set_http_options(s, &options, hls); + ret = s->io_open(s, &pb, hls->key_file, AVIO_FLAG_WRITE, &options); + av_dict_free(&options); + if (ret < 0) return ret; avio_seek(pb, 0, SEEK_CUR); avio_write(pb, key, KEYSIZE); @@ -700,54 +714,61 @@ static int do_encrypt(AVFormatContext *s, VariantStream *vs) } -static int hls_encryption_start(AVFormatContext *s) +static int hls_encryption_start(AVFormatContext *s, VariantStream *vs) { HLSContext *hls = s->priv_data; int ret; AVIOContext *pb; uint8_t key[KEYSIZE]; + AVDictionary *options = NULL; - if ((ret = s->io_open(s, &pb, hls->key_info_file, AVIO_FLAG_READ, NULL)) < 0) { + set_http_options(s, &options, hls); + ret = s->io_open(s, &pb, hls->key_info_file, AVIO_FLAG_READ, &options); + av_dict_free(&options); + if (ret < 0) { av_log(hls, AV_LOG_ERROR, - "error opening key info file %s\n", hls->key_info_file); + "error opening key info file %s\n", hls->key_info_file); return ret; } - ff_get_line(pb, hls->key_uri, sizeof(hls->key_uri)); - hls->key_uri[strcspn(hls->key_uri, "\r\n")] = '\0'; + ff_get_line(pb, vs->key_uri, sizeof(vs->key_uri)); + vs->key_uri[strcspn(vs->key_uri, "\r\n")] = '\0'; - ff_get_line(pb, hls->key_file, sizeof(hls->key_file)); - hls->key_file[strcspn(hls->key_file, "\r\n")] = '\0'; + ff_get_line(pb, vs->key_file, sizeof(vs->key_file)); + vs->key_file[strcspn(vs->key_file, "\r\n")] = '\0'; - ff_get_line(pb, hls->iv_string, sizeof(hls->iv_string)); - hls->iv_string[strcspn(hls->iv_string, "\r\n")] = '\0'; + ff_get_line(pb, vs->iv_string, sizeof(vs->iv_string)); + vs->iv_string[strcspn(vs->iv_string, "\r\n")] = '\0'; ff_format_io_close(s, &pb); - if (!*hls->key_uri) { + if (!*vs->key_uri) { av_log(hls, AV_LOG_ERROR, "no key URI specified in key info file\n"); return AVERROR(EINVAL); } - if (!*hls->key_file) { + if (!*vs->key_file) { av_log(hls, AV_LOG_ERROR, "no key file specified in key info file\n"); return AVERROR(EINVAL); } - if ((ret = s->io_open(s, &pb, hls->key_file, AVIO_FLAG_READ, NULL)) < 0) { - av_log(hls, AV_LOG_ERROR, "error opening key file %s\n", hls->key_file); + set_http_options(s, &options, hls); + ret = s->io_open(s, &pb, vs->key_file, AVIO_FLAG_READ, &options); + av_dict_free(&options); + if (ret < 0) { + av_log(hls, AV_LOG_ERROR, "error opening key file %s\n", vs->key_file); return ret; } ret = avio_read(pb, key, sizeof(key)); ff_format_io_close(s, &pb); if (ret != sizeof(key)) { - av_log(hls, AV_LOG_ERROR, "error reading key file %s\n", hls->key_file); + av_log(hls, AV_LOG_ERROR, "error reading key file %s\n", vs->key_file); if (ret >= 0 || ret == AVERROR_EOF) ret = AVERROR(EINVAL); return ret; } - ff_data_to_hex(hls->key_string, key, sizeof(key), 0); + ff_data_to_hex(vs->key_string, key, sizeof(key), 0); return 0; } @@ -759,6 +780,7 @@ static int hls_mux_init(AVFormatContext *s, VariantStream *vs) AVFormatContext *oc; AVFormatContext *vtt_oc = NULL; int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0); + int remaining_options; int i, ret; ret = avformat_alloc_output_context2(&vs->avf, vs->oformat, NULL, NULL); @@ -770,20 +792,19 @@ static int hls_mux_init(AVFormatContext *s, VariantStream *vs) if (!oc->url) return AVERROR(ENOMEM); - oc->oformat = vs->oformat; - oc->interrupt_callback = s->interrupt_callback; - oc->max_delay = s->max_delay; - oc->opaque = s->opaque; - oc->io_open = s->io_open; - oc->io_close = s->io_close; + oc->interrupt_callback = s->interrupt_callback; + oc->max_delay = s->max_delay; + oc->opaque = s->opaque; + oc->io_open = s->io_open; + oc->io_close = s->io_close; + oc->strict_std_compliance = s->strict_std_compliance; av_dict_copy(&oc->metadata, s->metadata, 0); - if(vs->vtt_oformat) { + if (vs->vtt_oformat) { ret = avformat_alloc_output_context2(&vs->vtt_avf, vs->vtt_oformat, NULL, NULL); if (ret < 0) return ret; vtt_oc = vs->vtt_avf; - vtt_oc->oformat = vs->vtt_oformat; av_dict_copy(&vtt_oc->metadata, s->metadata, 0); } @@ -811,11 +832,10 @@ static int hls_mux_init(AVFormatContext *s, VariantStream *vs) av_dict_copy(&st->metadata, vs->streams[i]->metadata, 0); } - vs->packets_written = 1; vs->start_pos = 0; vs->new_start = 1; - if (hls->segment_type == SEGMENT_TYPE_FMP4) { + if (hls->segment_type == SEGMENT_TYPE_FMP4 && hls->max_seg_size > 0) { if (hls->http_persistent > 0) { //TODO: Support fragment fmp4 for http persistent in HLS muxer. av_log(s, AV_LOG_WARNING, "http persistent mode is currently unsupported for fragment mp4 in the HLS muxer.\n"); @@ -824,47 +844,46 @@ static int hls_mux_init(AVFormatContext *s, VariantStream *vs) av_log(s, AV_LOG_WARNING, "Multi-file byterange mode is currently unsupported in the HLS muxer.\n"); return AVERROR_PATCHWELCOME; } + } - vs->packets_written = 0; - vs->init_range_length = 0; - set_http_options(s, &options, hls); - if ((ret = avio_open_dyn_buf(&oc->pb)) < 0) - return ret; + if ((ret = avio_open_dyn_buf(&oc->pb)) < 0) + return ret; + if (hls->segment_type == SEGMENT_TYPE_FMP4) { + set_http_options(s, &options, hls); if (byterange_mode) { ret = hlsenc_io_open(s, &vs->out, vs->basename, &options); } else { ret = hlsenc_io_open(s, &vs->out, vs->base_output_dirname, &options); } av_dict_free(&options); - if (ret < 0) { - av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", vs->fmp4_init_filename); - return ret; - } - - if (hls->format_options_str) { - ret = av_dict_parse_string(&hls->format_options, hls->format_options_str, "=", ":", 0); - if (ret < 0) { - av_log(s, AV_LOG_ERROR, "Could not parse format options list '%s'\n", - hls->format_options_str); - return ret; - } - } + } + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", vs->fmp4_init_filename); + return ret; + } - av_dict_copy(&options, hls->format_options, 0); + av_dict_copy(&options, hls->format_options, 0); + if (hls->segment_type == SEGMENT_TYPE_FMP4) { av_dict_set(&options, "fflags", "-autobsf", 0); av_dict_set(&options, "movflags", "+frag_custom+dash+delay_moov", AV_DICT_APPEND); - ret = avformat_init_output(oc, &options); - if (ret < 0) - return ret; - if (av_dict_count(options)) { - av_log(s, AV_LOG_ERROR, "Some of the provided format options in '%s' are not recognized\n", hls->format_options_str); - av_dict_free(&options); - return AVERROR(EINVAL); - } - avio_flush(oc->pb); - av_dict_free(&options); + } else { + /* We only require one PAT/PMT per segment. */ + char period[21]; + snprintf(period, sizeof(period), "%d", (INT_MAX / 2) - 1); + av_dict_set(&options, "sdt_period", period, AV_DICT_DONT_OVERWRITE); + av_dict_set(&options, "pat_period", period, AV_DICT_DONT_OVERWRITE); } + ret = avformat_init_output(oc, &options); + remaining_options = av_dict_count(options); + av_dict_free(&options); + if (ret < 0) + return ret; + if (remaining_options) { + av_log(s, AV_LOG_ERROR, "Some of the provided format options are not recognized\n"); + return AVERROR(EINVAL); + } + avio_flush(oc->pb); return 0; } @@ -886,7 +905,6 @@ static int sls_flags_filename_process(struct AVFormatContext *s, HLSContext *hls strlen(vs->current_segment_final_filename_fmt)) { char * new_url = av_strdup(vs->current_segment_final_filename_fmt); if (!new_url) { - av_free(en); return AVERROR(ENOMEM); } ff_format_set_url(vs->avf, new_url); @@ -895,10 +913,9 @@ static int sls_flags_filename_process(struct AVFormatContext *s, HLSContext *hls if (replace_int_data_in_filename(&filename, vs->avf->url, 's', pos + size) < 1) { av_log(hls, AV_LOG_ERROR, "Invalid second level segment filename template '%s', " - "you can try to remove second_level_segment_size flag\n", + "you can try to remove second_level_segment_size flag\n", vs->avf->url); - av_free(filename); - av_free(en); + av_freep(&filename); return AVERROR(EINVAL); } ff_format_set_url(vs->avf, filename); @@ -906,13 +923,12 @@ static int sls_flags_filename_process(struct AVFormatContext *s, HLSContext *hls if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_DURATION) { char *filename = NULL; if (replace_int_data_in_filename(&filename, vs->avf->url, - 't', (int64_t)round(duration * HLS_MICROSECOND_UNIT)) < 1) { + '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_time flag\n", vs->avf->url); - av_free(filename); - av_free(en); + av_freep(&filename); return AVERROR(EINVAL); } ff_format_set_url(vs->avf, filename); @@ -926,14 +942,14 @@ static int sls_flag_check_duration_size_index(HLSContext *hls) int ret = 0; if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_DURATION) { - av_log(hls, AV_LOG_ERROR, - "second_level_segment_duration hls_flag requires strftime to be true\n"); - ret = AVERROR(EINVAL); + av_log(hls, AV_LOG_ERROR, + "second_level_segment_duration hls_flag requires strftime to be true\n"); + ret = AVERROR(EINVAL); } if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_SIZE) { - av_log(hls, AV_LOG_ERROR, - "second_level_segment_size hls_flag requires strfime to be true\n"); - ret = AVERROR(EINVAL); + av_log(hls, AV_LOG_ERROR, + "second_level_segment_size hls_flag requires strfime to be true\n"); + ret = AVERROR(EINVAL); } if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_INDEX) { av_log(hls, AV_LOG_ERROR, @@ -951,14 +967,14 @@ static int sls_flag_check_duration_size(HLSContext *hls, VariantStream *vs) int ret = 0; if ((hls->flags & HLS_SECOND_LEVEL_SEGMENT_DURATION) && !segment_renaming_ok) { - av_log(hls, AV_LOG_ERROR, - "second_level_segment_duration hls_flag works only with file protocol segment names\n"); - ret = AVERROR(EINVAL); + av_log(hls, AV_LOG_ERROR, + "second_level_segment_duration hls_flag works only with file protocol segment names\n"); + ret = AVERROR(EINVAL); } if ((hls->flags & HLS_SECOND_LEVEL_SEGMENT_SIZE) && !segment_renaming_ok) { - av_log(hls, AV_LOG_ERROR, - "second_level_segment_size hls_flag works only with file protocol segment names\n"); - ret = AVERROR(EINVAL); + av_log(hls, AV_LOG_ERROR, + "second_level_segment_size hls_flag works only with file protocol segment names\n"); + ret = AVERROR(EINVAL); } return ret; @@ -984,7 +1000,7 @@ static int sls_flag_use_localtime_filename(AVFormatContext *oc, HLSContext *c, V av_log(c, AV_LOG_ERROR, "Invalid second level segment filename template '%s', " "you can try to remove second_level_segment_index flag\n", oc->url); - av_free(filename); + av_freep(&filename); return AVERROR(EINVAL); } ff_format_set_url(oc, filename); @@ -998,7 +1014,7 @@ static int sls_flag_use_localtime_filename(AVFormatContext *oc, HLSContext *c, V av_log(c, AV_LOG_ERROR, "Invalid second level segment filename template '%s', " "you can try to remove second_level_segment_size flag\n", oc->url); - av_free(filename); + av_freep(&filename); return AVERROR(EINVAL); } ff_format_set_url(oc, filename); @@ -1009,7 +1025,7 @@ static int sls_flag_use_localtime_filename(AVFormatContext *oc, HLSContext *c, V av_log(c, AV_LOG_ERROR, "Invalid second level segment filename template '%s', " "you can try to remove second_level_segment_time flag\n", oc->url); - av_free(filename); + av_freep(&filename); return AVERROR(EINVAL); } ff_format_set_url(oc, filename); @@ -1034,6 +1050,7 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, en->var_stream_idx = vs->var_stream_idx; ret = sls_flags_filename_process(s, hls, vs, en, duration, pos, size); if (ret < 0) { + av_freep(&en); return ret; } @@ -1048,7 +1065,7 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, } av_strlcpy(en->filename, filename, sizeof(en->filename)); - if(vs->has_subtitle) + if (vs->has_subtitle) av_strlcpy(en->sub_filename, av_basename(vs->vtt_avf->url), sizeof(en->sub_filename)); else en->sub_filename[0] = '\0'; @@ -1067,8 +1084,8 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, } if (hls->key_info_file || hls->encrypt) { - av_strlcpy(en->key_uri, hls->key_uri, sizeof(en->key_uri)); - av_strlcpy(en->iv_string, hls->iv_string, sizeof(en->iv_string)); + av_strlcpy(en->key_uri, vs->key_uri, sizeof(en->key_uri)); + av_strlcpy(en->iv_string, vs->iv_string, sizeof(en->iv_string)); } if (!vs->segments) @@ -1097,7 +1114,7 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, if ((ret = hls_delete_old_segments(s, hls, vs)) < 0) return ret; } else - av_free(en); + av_freep(&en); } else vs->nb_entries++; @@ -1156,9 +1173,9 @@ static int parse_playlist(AVFormatContext *s, const char *url, VariantStream *vs ptr += strlen("URI=\""); end = av_stristr(ptr, ","); if (end) { - av_strlcpy(hls->key_uri, ptr, end - ptr); + av_strlcpy(vs->key_uri, ptr, end - ptr); } else { - av_strlcpy(hls->key_uri, ptr, sizeof(hls->key_uri)); + av_strlcpy(vs->key_uri, ptr, sizeof(vs->key_uri)); } } @@ -1167,9 +1184,9 @@ static int parse_playlist(AVFormatContext *s, const char *url, VariantStream *vs ptr += strlen("IV=0x"); end = av_stristr(ptr, ","); if (end) { - av_strlcpy(hls->iv_string, ptr, end - ptr); + av_strlcpy(vs->iv_string, ptr, end - ptr); } else { - av_strlcpy(hls->iv_string, ptr, sizeof(hls->iv_string)); + av_strlcpy(vs->iv_string, ptr, sizeof(vs->iv_string)); } } @@ -1186,6 +1203,7 @@ static int parse_playlist(AVFormatContext *s, const char *url, VariantStream *vs is_segment = 0; new_start_pos = avio_tell(vs->avf->pb); vs->size = new_start_pos - vs->start_pos; + vs->initial_prog_date_time -= vs->duration; // this is a previously existing segment ret = hls_append_segment(s, hls, vs, vs->duration, vs->start_pos, vs->size); if (ret < 0) goto fail; @@ -1203,10 +1221,10 @@ static void hls_free_segments(HLSSegment *p) { HLSSegment *en; - while(p) { + while (p) { en = p; p = p->next; - av_free(en); + av_freep(&en); } } @@ -1225,25 +1243,28 @@ static int hls_rename_temp_file(AVFormatContext *s, AVFormatContext *oc) return ret; } -static int get_relative_url(const char *master_url, const char *media_url, - char *rel_url, int rel_url_buf_size) +static const char* get_relative_url(const char *master_url, const char *media_url) { - char *p = NULL; - int base_len = -1; - p = strrchr(master_url, '/') ? strrchr(master_url, '/') :\ - strrchr(master_url, '\\'); + const char *p = strrchr(master_url, '/'); + size_t base_len = 0; + + if (!p) p = strrchr(master_url, '\\'); + if (p) { - base_len = FFABS(p - master_url); + base_len = p - master_url; if (av_strncasecmp(master_url, media_url, base_len)) { av_log(NULL, AV_LOG_WARNING, "Unable to find relative url\n"); - return AVERROR(EINVAL); + return NULL; } + } else { + return media_url; } - av_strlcpy(rel_url, &(media_url[base_len + 1]), rel_url_buf_size); - return 0; + + return media_url + base_len + 1; } -static int64_t get_stream_bit_rate(AVStream *stream) { +static int64_t get_stream_bit_rate(AVStream *stream) +{ AVCPBProperties *props = (AVCPBProperties*)av_stream_get_side_data( stream, AV_PKT_DATA_CPB_PROPERTIES, @@ -1266,8 +1287,11 @@ static int create_master_playlist(AVFormatContext *s, AVStream *vid_st, *aud_st; AVDictionary *options = NULL; unsigned int i, j; - int m3u8_name_size, ret, bandwidth; - char *m3u8_rel_name = NULL, *ccgroup; + int ret, bandwidth; + const char *m3u8_rel_name = NULL; + const char *vtt_m3u8_rel_name = NULL; + const char *ccgroup; + const char *sgroup = NULL; ClosedCaptionsStream *ccs; const char *proto = avio_find_protocol_name(hls->master_m3u8_url); int is_file_proto = proto && !strcmp(proto, "file"); @@ -1292,7 +1316,7 @@ static int create_master_playlist(AVFormatContext *s, ret = hlsenc_io_open(s, &hls->m3u8_out, temp_filename, &options); av_dict_free(&options); if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Failed to open master play list file '%s'\n", + av_log(s, AV_LOG_ERROR, "Failed to open master play list file '%s'\n", temp_filename); goto fail; } @@ -1316,40 +1340,22 @@ static int create_master_playlist(AVFormatContext *s, if (vs->has_video || vs->has_subtitle || !vs->agroup) continue; - m3u8_name_size = strlen(vs->m3u8_name) + 1; - m3u8_rel_name = av_malloc(m3u8_name_size); + m3u8_rel_name = get_relative_url(hls->master_m3u8_url, vs->m3u8_name); if (!m3u8_rel_name) { - ret = AVERROR(ENOMEM); - goto fail; - } - av_strlcpy(m3u8_rel_name, vs->m3u8_name, m3u8_name_size); - ret = get_relative_url(hls->master_m3u8_url, vs->m3u8_name, - m3u8_rel_name, m3u8_name_size); - if (ret < 0) { av_log(s, AV_LOG_ERROR, "Unable to find relative URL\n"); goto fail; } ff_hls_write_audio_rendition(hls->m3u8_out, vs->agroup, m3u8_rel_name, vs->language, i, hls->has_default_key ? vs->is_default : 1); - - av_freep(&m3u8_rel_name); } /* For variant streams with video add #EXT-X-STREAM-INF tag with attributes*/ for (i = 0; i < hls->nb_varstreams; i++) { vs = &(hls->var_streams[i]); - m3u8_name_size = strlen(vs->m3u8_name) + 1; - m3u8_rel_name = av_malloc(m3u8_name_size); + m3u8_rel_name = get_relative_url(hls->master_m3u8_url, vs->m3u8_name); if (!m3u8_rel_name) { - ret = AVERROR(ENOMEM); - goto fail; - } - av_strlcpy(m3u8_rel_name, vs->m3u8_name, m3u8_name_size); - ret = get_relative_url(hls->master_m3u8_url, vs->m3u8_name, - m3u8_rel_name, m3u8_name_size); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Unable to find relative URL\n"); + av_log(s, AV_LOG_ERROR, "Unable to find relative URL\n"); goto fail; } @@ -1363,7 +1369,7 @@ static int create_master_playlist(AVFormatContext *s, } if (!vid_st && !aud_st) { - av_log(NULL, AV_LOG_WARNING, "Media stream not found\n"); + av_log(s, AV_LOG_WARNING, "Media stream not found\n"); continue; } @@ -1404,26 +1410,34 @@ static int create_master_playlist(AVFormatContext *s, } } if (j == hls->nb_ccstreams) - av_log(NULL, AV_LOG_WARNING, "mapping ccgroup %s not found\n", + av_log(s, AV_LOG_WARNING, "mapping ccgroup %s not found\n", vs->ccgroup); } + if (vid_st && vs->sgroup) { + sgroup = vs->sgroup; + vtt_m3u8_rel_name = get_relative_url(hls->master_m3u8_url, vs->vtt_m3u8_name); + if (!vtt_m3u8_rel_name) { + av_log(s, AV_LOG_WARNING, "Unable to find relative subtitle URL\n"); + break; + } + + ff_hls_write_subtitle_rendition(hls->m3u8_out, sgroup, vtt_m3u8_rel_name, vs->language, i, hls->has_default_key ? vs->is_default : 1); + } + if (!hls->has_default_key || !hls->has_video_m3u8) { ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, m3u8_rel_name, - aud_st ? vs->agroup : NULL, vs->codec_attr, ccgroup); + aud_st ? vs->agroup : NULL, vs->codec_attr, ccgroup, sgroup); } else { if (vid_st) { ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, m3u8_rel_name, - aud_st ? vs->agroup : NULL, vs->codec_attr, ccgroup); + aud_st ? vs->agroup : NULL, vs->codec_attr, ccgroup, sgroup); } } - - av_freep(&m3u8_rel_name); } fail: - if(ret >=0) + if (ret >=0) hls->master_m3u8_created = 1; - av_freep(&m3u8_rel_name); hlsenc_io_close(s, &hls->m3u8_out, temp_filename); if (use_temp_file) ff_rename(temp_filename, hls->master_m3u8_url, s); @@ -1435,7 +1449,6 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) { HLSContext *hls = s->priv_data; HLSSegment *en; - AVFormatContext *oc = vs->avf; int target_duration = 0; int ret = 0; char temp_filename[MAX_URL_SIZE]; @@ -1471,7 +1484,7 @@ 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->segment_type == SEGMENT_TYPE_FMP4) ? &hls->m3u8_out : &oc->pb, temp_filename, &options)) < 0) { + if ((ret = hlsenc_io_open(s, byterange_mode ? &hls->m3u8_out : &vs->out, temp_filename, &options)) < 0) { if (hls->ignore_io_errors) ret = 0; goto fail; @@ -1483,35 +1496,35 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) } vs->discontinuity_set = 0; - ff_hls_write_playlist_header((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, hls->version, hls->allowcache, + ff_hls_write_playlist_header(byterange_mode ? hls->m3u8_out : vs->out, hls->version, hls->allowcache, target_duration, sequence, hls->pl_type, hls->flags & HLS_I_FRAMES_ONLY); - if((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && vs->discontinuity_set==0 ){ - avio_printf((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, "#EXT-X-DISCONTINUITY\n"); + if ((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && vs->discontinuity_set==0) { + avio_printf(byterange_mode ? hls->m3u8_out : vs->out, "#EXT-X-DISCONTINUITY\n"); vs->discontinuity_set = 1; } if (vs->has_video && (hls->flags & HLS_INDEPENDENT_SEGMENTS)) { - avio_printf((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, "#EXT-X-INDEPENDENT-SEGMENTS\n"); + avio_printf(byterange_mode ? hls->m3u8_out : vs->out, "#EXT-X-INDEPENDENT-SEGMENTS\n"); } for (en = vs->segments; en; en = en->next) { if ((hls->encrypt || hls->key_info_file) && (!key_uri || strcmp(en->key_uri, key_uri) || av_strcasecmp(en->iv_string, iv_string))) { - avio_printf((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri); + avio_printf(byterange_mode ? hls->m3u8_out : vs->out, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri); if (*en->iv_string) - avio_printf((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, ",IV=0x%s", en->iv_string); - avio_printf((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, "\n"); + avio_printf(byterange_mode ? hls->m3u8_out : vs->out, ",IV=0x%s", en->iv_string); + avio_printf(byterange_mode ? hls->m3u8_out : vs->out, "\n"); key_uri = en->key_uri; iv_string = en->iv_string; } if ((hls->segment_type == SEGMENT_TYPE_FMP4) && (en == vs->segments)) { - ff_hls_write_init_file((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, (hls->flags & HLS_SINGLE_FILE) ? en->filename : vs->fmp4_init_filename, + ff_hls_write_init_file(byterange_mode ? hls->m3u8_out : vs->out, (hls->flags & HLS_SINGLE_FILE) ? en->filename : vs->fmp4_init_filename, hls->flags & HLS_SINGLE_FILE, vs->init_range_length, 0); } - ret = ff_hls_write_file_entry((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb, en->discont, byterange_mode, + ret = ff_hls_write_file_entry(byterange_mode ? hls->m3u8_out : vs->out, en->discont, byterange_mode, en->duration, hls->flags & HLS_ROUND_DURATIONS, - en->size, en->pos, vs->baseurl, + en->size, en->pos, hls->baseurl, en->filename, prog_date_time_p, en->keyframe_size, en->keyframe_pos, hls->flags & HLS_I_FRAMES_ONLY); if (ret < 0) { av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n"); @@ -1519,7 +1532,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) } if (last && (hls->flags & HLS_OMIT_ENDLIST)==0) - ff_hls_write_end_list((byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? hls->m3u8_out : oc->pb); + ff_hls_write_end_list(byterange_mode ? hls->m3u8_out : vs->out); if (vs->vtt_m3u8_name) { snprintf(temp_vtt_filename, sizeof(temp_vtt_filename), use_temp_file ? "%s.tmp" : "%s", vs->vtt_m3u8_name); @@ -1533,7 +1546,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) for (en = vs->segments; en; en = en->next) { ret = ff_hls_write_file_entry(hls->sub_m3u8_out, 0, byterange_mode, en->duration, 0, en->size, en->pos, - vs->baseurl, en->sub_filename, NULL, 0, 0, 0); + hls->baseurl, en->sub_filename, NULL, 0, 0, 0); if (ret < 0) { av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n"); } @@ -1546,7 +1559,10 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) fail: av_dict_free(&options); - hlsenc_io_close(s, (byterange_mode || hls->segment_type == SEGMENT_TYPE_FMP4) ? &hls->m3u8_out : &oc->pb, temp_filename); + ret = hlsenc_io_close(s, byterange_mode ? &hls->m3u8_out : &vs->out, temp_filename); + if (ret < 0) { + return ret; + } hlsenc_io_close(s, &hls->sub_m3u8_out, vs->vtt_m3u8_name); if (use_temp_file) { ff_rename(temp_filename, vs->m3u8_name, s); @@ -1568,7 +1584,7 @@ static int hls_start(AVFormatContext *s, VariantStream *vs) AVDictionary *options = NULL; const char *proto = NULL; int use_temp_file = 0; - char *filename, iv_string[KEYSIZE*2 + 1]; + char iv_string[KEYSIZE*2 + 1]; int err = 0; if (c->flags & HLS_SINGLE_FILE) { @@ -1590,7 +1606,7 @@ static int hls_start(AVFormatContext *s, VariantStream *vs) #else vs->basename, 'd', vs->sequence) < 1) { #endif - av_free(filename); + av_freep(&filename); av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s', you can try to use -strftime 1 with it\n", vs->basename); return AVERROR(EINVAL); } @@ -1619,16 +1635,15 @@ static int hls_start(AVFormatContext *s, VariantStream *vs) if (c->use_localtime_mkdir) { const char *dir; char *fn_copy = av_strdup(oc->url); - if (!fn_copy) { + if (!fn_copy) return AVERROR(ENOMEM); - } dir = av_dirname(fn_copy); if (ff_mkdir_p(dir) == -1 && errno != EEXIST) { av_log(oc, AV_LOG_ERROR, "Could not create directory %s with use_localtime_mkdir\n", dir); - av_free(fn_copy); + av_freep(&fn_copy); return AVERROR(errno); } - av_free(fn_copy); + av_freep(&fn_copy); } } else { char *filename = NULL; @@ -1638,13 +1653,13 @@ static int hls_start(AVFormatContext *s, VariantStream *vs) #else vs->basename, 'd', vs->sequence) < 1) { #endif - av_free(filename); + av_freep(&filename); av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s' you can try to use -strftime 1 with it\n", vs->basename); return AVERROR(EINVAL); } ff_format_set_url(oc, filename); } - if( vs->vtt_basename) { + if (vs->vtt_basename) { char *filename = NULL; if (replace_int_data_in_filename(&filename, #if FF_API_HLS_WRAP @@ -1652,16 +1667,13 @@ static int hls_start(AVFormatContext *s, VariantStream *vs) #else vs->vtt_basename, 'd', vs->sequence) < 1) { #endif - av_free(filename); + av_freep(&filename); av_log(vtt_oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", vs->vtt_basename); return AVERROR(EINVAL); } ff_format_set_url(vtt_oc, filename); } } - vs->number++; - - set_http_options(s, &options, c); proto = avio_find_protocol_name(oc->url); use_temp_file = proto && !strcmp(proto, "file") && (c->flags & HLS_TEMP_FILE); @@ -1684,40 +1696,40 @@ static int hls_start(AVFormatContext *s, VariantStream *vs) " ignoring -hls_enc\n"); } - if (!c->encrypt_started || (c->flags & HLS_PERIODIC_REKEY)) { + if (!vs->encrypt_started || (c->flags & HLS_PERIODIC_REKEY)) { if (c->key_info_file) { - if ((err = hls_encryption_start(s)) < 0) + if ((err = hls_encryption_start(s, vs)) < 0) goto fail; } else { - if ((err = do_encrypt(s, vs)) < 0) - goto fail; + if (!c->encrypt_started) { + if ((err = do_encrypt(s, vs)) < 0) + goto fail; + c->encrypt_started = 1; + } + av_strlcpy(vs->key_uri, c->key_uri, sizeof(vs->key_uri)); + av_strlcpy(vs->key_string, c->key_string, sizeof(vs->key_string)); + av_strlcpy(vs->iv_string, c->iv_string, sizeof(vs->iv_string)); } - c->encrypt_started = 1; + vs->encrypt_started = 1; } - if ((err = av_dict_set(&options, "encryption_key", c->key_string, 0)) - < 0) - goto fail; - err = av_strlcpy(iv_string, c->iv_string, sizeof(iv_string)); - if (!err) + err = av_strlcpy(iv_string, vs->iv_string, sizeof(iv_string)); + if (!err) { snprintf(iv_string, sizeof(iv_string), "%032"PRIx64, vs->sequence); - if ((err = av_dict_set(&options, "encryption_iv", iv_string, 0)) < 0) - goto fail; - - filename = av_asprintf("crypto:%s", oc->url); - if (!filename) { - err = AVERROR(ENOMEM); - goto fail; + memset(vs->iv_string, 0, sizeof(vs->iv_string)); + memcpy(vs->iv_string, iv_string, sizeof(iv_string)); } - err = hlsenc_io_open(s, &oc->pb, filename, &options); - av_free(filename); - av_dict_free(&options); - if (err < 0) - return err; - } else if (c->segment_type != SEGMENT_TYPE_FMP4) { - if ((err = hlsenc_io_open(s, &oc->pb, oc->url, &options)) < 0) { - if (c->ignore_io_errors) - err = 0; - goto fail; + } + if (c->segment_type != SEGMENT_TYPE_FMP4) { + if (oc->oformat->priv_class && oc->priv_data) { + av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0); + } + if (c->flags & HLS_SINGLE_FILE) { + set_http_options(s, &options, c); + if ((err = hlsenc_io_open(s, &vs->out, oc->url, &options)) < 0) { + if (c->ignore_io_errors) + err = 0; + goto fail; + } } } if (vs->vtt_basename) { @@ -1730,19 +1742,6 @@ static int hls_start(AVFormatContext *s, VariantStream *vs) } av_dict_free(&options); - if (c->segment_type != SEGMENT_TYPE_FMP4) { - /* We only require one PAT/PMT per segment. */ - if (oc->oformat->priv_class && oc->priv_data) { - char period[21]; - - snprintf(period, sizeof(period), "%d", (INT_MAX / 2) - 1); - - av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0); - av_opt_set(oc->priv_data, "sdt_period", period, 0); - av_opt_set(oc->priv_data, "pat_period", period, 0); - } - } - if (vs->vtt_basename) { err = avformat_write_header(vtt_oc,NULL); if (err < 0) @@ -1797,17 +1796,12 @@ static int validate_name(int nb_vs, const char *fn) char *fn_dup = NULL; int ret = 0; - if (!fn) { - ret = AVERROR(EINVAL); - goto fail; - } + if (!fn) + return AVERROR(EINVAL); fn_dup = av_strdup(fn); - if (!fn_dup) { - ret = AVERROR(ENOMEM); - goto fail; - } - + if (!fn_dup) + return AVERROR(ENOMEM); filename = av_basename(fn); subdir_name = av_dirname(fn_dup); @@ -1837,14 +1831,12 @@ static int format_name(const char *buf, char **s, int index, const char *varname int ret = 0; orig_buf_dup = av_strdup(buf); - if (!orig_buf_dup) { - ret = AVERROR(ENOMEM); - goto fail; - } + if (!orig_buf_dup) + return AVERROR(ENOMEM); if (!av_stristr(buf, "%v")) { *s = orig_buf_dup; - return ret; + return 0; } if (!varname) { @@ -1865,11 +1857,6 @@ static int format_name(const char *buf, char **s, int index, const char *varname /* if %v is present in the file's directory, create sub-directory */ if (av_stristr(dir, "%v") && proto && !strcmp(proto, "file")) { mod_buf_dup = av_strdup(*s); - if (!mod_buf_dup) { - ret = AVERROR(ENOMEM); - goto fail; - } - dir = av_dirname(mod_buf_dup); if (ff_mkdir_p(dir) == -1 && errno != EEXIST) { ret = AVERROR(errno); @@ -1885,7 +1872,7 @@ static int format_name(const char *buf, char **s, int index, const char *varname static int get_nth_codec_stream_index(AVFormatContext *s, enum AVMediaType codec_type, - int stream_id) + int64_t stream_id) { unsigned int stream_index, cnt; if (stream_id < 0 || stream_id > s->nb_streams - 1) @@ -1907,7 +1894,7 @@ static int parse_variant_stream_mapstring(AVFormatContext *s) VariantStream *vs; int stream_index, i, j; enum AVMediaType codec_type; - int nb_varstreams, nb_streams; + int nb_varstreams = 0, nb_streams; char *p, *q, *saveptr1, *saveptr2, *varstr, *keyval; const char *val; @@ -1924,18 +1911,23 @@ static int parse_variant_stream_mapstring(AVFormatContext *s) * practical usage) * * agroup: is key to specify audio group. A string can be given as value. + * sgroup: is key to specify subtitle group. A string can be given as value. */ p = av_strdup(hls->var_stream_map); + if (!p) + return AVERROR(ENOMEM); + q = p; - while(av_strtok(q, " \t", &saveptr1)) { + while (av_strtok(q, " \t", &saveptr1)) { q = NULL; - hls->nb_varstreams++; + nb_varstreams++; } av_freep(&p); - hls->var_streams = av_mallocz(sizeof(*hls->var_streams) * hls->nb_varstreams); + hls->var_streams = av_mallocz(sizeof(*hls->var_streams) * nb_varstreams); if (!hls->var_streams) return AVERROR(ENOMEM); + hls->nb_varstreams = nb_varstreams; p = hls->var_stream_map; nb_varstreams = 0; @@ -1951,10 +1943,13 @@ static int parse_variant_stream_mapstring(AVFormatContext *s) return AVERROR(EINVAL); q = varstr; - while (q < varstr + strlen(varstr)) { + while (1) { if (!av_strncasecmp(q, "a:", 2) || !av_strncasecmp(q, "v:", 2) || !av_strncasecmp(q, "s:", 2)) vs->nb_streams++; + q = strchr(q, ','); + if (!q) + break; q++; } vs->streams = av_mallocz(sizeof(AVStream *) * vs->nb_streams); @@ -1963,11 +1958,11 @@ static int parse_variant_stream_mapstring(AVFormatContext *s) nb_streams = 0; while (keyval = av_strtok(varstr, ",", &saveptr2)) { + int64_t num; + char *end; varstr = NULL; if (av_strstart(keyval, "language:", &val)) { - vs->language = av_strdup(val); - if (!vs->language) - return AVERROR(ENOMEM); + vs->language = val; continue; } else if (av_strstart(keyval, "default:", &val)) { vs->is_default = (!av_strncasecmp(val, "YES", strlen("YES")) || @@ -1975,19 +1970,16 @@ static int parse_variant_stream_mapstring(AVFormatContext *s) hls->has_default_key = 1; continue; } else if (av_strstart(keyval, "name:", &val)) { - vs->varname = av_strdup(val); - if (!vs->varname) - return AVERROR(ENOMEM); + vs->varname = val; continue; } else if (av_strstart(keyval, "agroup:", &val)) { - vs->agroup = av_strdup(val); - if (!vs->agroup) - return AVERROR(ENOMEM); + vs->agroup = val; + continue; + } else if (av_strstart(keyval, "sgroup:", &val)) { + vs->sgroup = val; continue; } else if (av_strstart(keyval, "ccgroup:", &val)) { - vs->ccgroup = av_strdup(val); - if (!vs->ccgroup) - return AVERROR(ENOMEM); + vs->ccgroup = val; continue; } else if (av_strstart(keyval, "v:", &val)) { codec_type = AVMEDIA_TYPE_VIDEO; @@ -2001,21 +1993,23 @@ static int parse_variant_stream_mapstring(AVFormatContext *s) return AVERROR(EINVAL); } - stream_index = -1; - if (av_isdigit(*val)) - stream_index = get_nth_codec_stream_index (s, codec_type, - atoi(val)); + num = strtoll(val, &end, 10); + if (!av_isdigit(*val) || *end != '\0') { + av_log(s, AV_LOG_ERROR, "Invalid stream number: '%s'\n", val); + return AVERROR(EINVAL); + } + stream_index = get_nth_codec_stream_index(s, codec_type, num); if (stream_index >= 0 && nb_streams < vs->nb_streams) { - for(i = 0; nb_streams > 0 && i < nb_streams; i++) { + for (i = 0; nb_streams > 0 && i < nb_streams; i++) { if (vs->streams[i] == s->streams[stream_index]) { av_log(s, AV_LOG_ERROR, "Same elementary stream found more than once inside " "variant definition #%d\n", nb_varstreams - 1); return AVERROR(EINVAL); } } - for(j = 0; nb_varstreams > 1 && j < nb_varstreams - 1; j++) { - for(i = 0; i < hls->var_streams[j].nb_streams; i++) { + for (j = 0; nb_varstreams > 1 && j < nb_varstreams - 1; j++) { + for (i = 0; i < hls->var_streams[j].nb_streams; i++) { if (hls->var_streams[j].streams[i] == s->streams[stream_index]) { av_log(s, AV_LOG_ERROR, "Same elementary stream found more than once " "in two different variant definitions #%d and #%d\n", @@ -2040,23 +2034,27 @@ static int parse_variant_stream_mapstring(AVFormatContext *s) static int parse_cc_stream_mapstring(AVFormatContext *s) { HLSContext *hls = s->priv_data; - int nb_ccstreams; + int nb_ccstreams = 0; char *p, *q, *ccstr, *keyval; char *saveptr1 = NULL, *saveptr2 = NULL; const char *val; ClosedCaptionsStream *ccs; p = av_strdup(hls->cc_stream_map); + if(!p) + return AVERROR(ENOMEM); + q = p; - while(av_strtok(q, " \t", &saveptr1)) { + while (av_strtok(q, " \t", &saveptr1)) { q = NULL; - hls->nb_ccstreams++; + nb_ccstreams++; } av_freep(&p); - hls->cc_streams = av_mallocz(sizeof(*hls->cc_streams) * hls->nb_ccstreams); + hls->cc_streams = av_mallocz(sizeof(*hls->cc_streams) * nb_ccstreams); if (!hls->cc_streams) return AVERROR(ENOMEM); + hls->nb_ccstreams = nb_ccstreams; p = hls->cc_stream_map; nb_ccstreams = 0; @@ -2072,17 +2070,11 @@ static int parse_cc_stream_mapstring(AVFormatContext *s) ccstr = NULL; if (av_strstart(keyval, "ccgroup:", &val)) { - ccs->ccgroup = av_strdup(val); - if (!ccs->ccgroup) - return AVERROR(ENOMEM); + ccs->ccgroup = val; } else if (av_strstart(keyval, "instreamid:", &val)) { - ccs->instreamid = av_strdup(val); - if (!ccs->instreamid) - return AVERROR(ENOMEM); + ccs->instreamid = val; } else if (av_strstart(keyval, "language:", &val)) { - ccs->language = av_strdup(val); - if (!ccs->language) - return AVERROR(ENOMEM); + ccs->language = val; } else { av_log(s, AV_LOG_ERROR, "Invalid keyval %s\n", keyval); return AVERROR(EINVAL); @@ -2095,19 +2087,19 @@ static int parse_cc_stream_mapstring(AVFormatContext *s) } if (av_strstart(ccs->instreamid, "CC", &val)) { - if(atoi(val) < 1 || atoi(val) > 4) { + if (atoi(val) < 1 || atoi(val) > 4) { av_log(s, AV_LOG_ERROR, "Invalid instream ID CC index %d in %s, range 1-4\n", atoi(val), ccs->instreamid); return AVERROR(EINVAL); } } else if (av_strstart(ccs->instreamid, "SERVICE", &val)) { - if(atoi(val) < 1 || atoi(val) > 63) { + if (atoi(val) < 1 || atoi(val) > 63) { av_log(s, AV_LOG_ERROR, "Invalid instream ID SERVICE index %d in %s, range 1-63 \n", atoi(val), ccs->instreamid); return AVERROR(EINVAL); } } else { - av_log(s, AV_LOG_ERROR, "Invalid instream ID %s, supported are CCn or SERIVICEn\n", + av_log(s, AV_LOG_ERROR, "Invalid instream ID %s, supported are CCn or SERVICEn\n", ccs->instreamid); return AVERROR(EINVAL); } @@ -2116,7 +2108,8 @@ static int parse_cc_stream_mapstring(AVFormatContext *s) return 0; } -static int update_variant_stream_info(AVFormatContext *s) { +static int update_variant_stream_info(AVFormatContext *s) +{ HLSContext *hls = s->priv_data; unsigned int i; int ret = 0; @@ -2131,11 +2124,10 @@ static int update_variant_stream_info(AVFormatContext *s) { return parse_variant_stream_mapstring(s); } else { //By default, a single variant stream with all the codec streams is created - hls->nb_varstreams = 1; - hls->var_streams = av_mallocz(sizeof(*hls->var_streams) * - hls->nb_varstreams); + hls->var_streams = av_mallocz(sizeof(*hls->var_streams)); if (!hls->var_streams) return AVERROR(ENOMEM); + hls->nb_varstreams = 1; hls->var_streams[0].var_stream_idx = 0; hls->var_streams[0].nb_streams = s->nb_streams; @@ -2145,11 +2137,8 @@ static int update_variant_stream_info(AVFormatContext *s) { return AVERROR(ENOMEM); //by default, the first available ccgroup is mapped to the variant stream - if (hls->nb_ccstreams) { - hls->var_streams[0].ccgroup = av_strdup(hls->cc_streams[0].ccgroup); - if (!hls->var_streams[0].ccgroup) - return AVERROR(ENOMEM); - } + if (hls->nb_ccstreams) + hls->var_streams[0].ccgroup = hls->cc_streams[0].ccgroup; for (i = 0; i < s->nb_streams; i++) hls->var_streams[0].streams[i] = s->streams[i]; @@ -2157,18 +2146,16 @@ static int update_variant_stream_info(AVFormatContext *s) { return 0; } -static int update_master_pl_info(AVFormatContext *s) { +static int update_master_pl_info(AVFormatContext *s) +{ HLSContext *hls = s->priv_data; const char *dir; char *fn1= NULL, *fn2 = NULL; int ret = 0; fn1 = av_strdup(s->url); - if (!fn1) { - ret = AVERROR(ENOMEM); - goto fail; - } - + if (!fn1) + return AVERROR(ENOMEM); dir = av_dirname(fn1); /** @@ -2256,6 +2243,23 @@ static int hls_write_header(AVFormatContext *s) return ret; } +static int hls_init_file_resend(AVFormatContext *s, VariantStream *vs) +{ + HLSContext *hls = s->priv_data; + AVDictionary *options = NULL; + int ret = 0; + + set_http_options(s, &options, hls); + ret = hlsenc_io_open(s, &vs->out, vs->base_output_dirname, &options); + av_dict_free(&options); + if (ret < 0) + return ret; + avio_write(vs->out, vs->init_buffer, vs->init_range_length); + hlsenc_io_close(s, &vs->out, hls->fmp4_init_filename); + + return ret; +} + static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) { HLSContext *hls = s->priv_data; @@ -2268,16 +2272,14 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) int range_length = 0; const char *proto = NULL; int use_temp_file = 0; - uint8_t *buffer = NULL; VariantStream *vs = NULL; - AVDictionary *options = NULL; char *old_filename = NULL; for (i = 0; i < hls->nb_varstreams; i++) { vs = &hls->var_streams[i]; for (j = 0; j < vs->nb_streams; j++) { if (vs->streams[j] == st) { - if( st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE ) { + if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) { oc = vs->vtt_avf; stream_index = 0; } else { @@ -2301,17 +2303,23 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) if (vs->sequence - vs->nb_entries > hls->start_sequence && hls->init_time > 0) { /* reset end_pts, hls->recording_time at end of the init hls list */ - int init_list_dur = hls->init_time * vs->nb_entries * AV_TIME_BASE; - int after_init_list_dur = (vs->sequence - hls->start_sequence - vs->nb_entries ) * (hls->time * AV_TIME_BASE); + int64_t init_list_dur = hls->init_time * vs->nb_entries * AV_TIME_BASE; + int64_t after_init_list_dur = (vs->sequence - hls->start_sequence - vs->nb_entries) * (hls->time * AV_TIME_BASE); hls->recording_time = hls->time * AV_TIME_BASE; end_pts = init_list_dur + after_init_list_dur ; } if (vs->start_pts == AV_NOPTS_VALUE) { vs->start_pts = pkt->pts; + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) + vs->start_pts_from_audio = 1; + } + if (vs->start_pts_from_audio && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && vs->start_pts > pkt->pts) { + vs->start_pts = pkt->pts; + vs->start_pts_from_audio = 0; } - if (vs->has_video) { + if (vs->has_video) { can_split = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && ((pkt->flags & AV_PKT_FLAG_KEY) || (hls->flags & HLS_SPLIT_BY_TIME)); is_ref_pkt = (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && (pkt->stream_index == vs->reference_stream_index); @@ -2343,36 +2351,26 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) int64_t new_start_pos; int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0); - av_write_frame(vs->avf, NULL); /* Flush any buffered data */ - - new_start_pos = avio_tell(vs->avf->pb); - - if (hls->segment_type != SEGMENT_TYPE_FMP4) { - avio_flush(oc->pb); - vs->size = new_start_pos - vs->start_pos; - } else { - vs->size = new_start_pos; - } - + av_write_frame(oc, NULL); /* Flush any buffered data */ + new_start_pos = avio_tell(oc->pb); + vs->size = new_start_pos - vs->start_pos; + avio_flush(oc->pb); if (hls->segment_type == SEGMENT_TYPE_FMP4) { if (!vs->init_range_length) { - avio_flush(oc->pb); - range_length = avio_close_dyn_buf(oc->pb, &buffer); - avio_write(vs->out, buffer, range_length); - av_free(buffer); + range_length = avio_close_dyn_buf(oc->pb, &vs->init_buffer); + if (range_length <= 0) + return AVERROR(EINVAL); + avio_write(vs->out, vs->init_buffer, range_length); + if (!hls->resend_init_file) + av_freep(&vs->init_buffer); vs->init_range_length = range_length; avio_open_dyn_buf(&oc->pb); vs->packets_written = 0; vs->start_pos = range_length; if (!byterange_mode) { - ff_format_io_close(s, &vs->out); hlsenc_io_close(s, &vs->out, vs->base_output_dirname); } } - } else { - if (!byterange_mode) { - hlsenc_io_close(s, &oc->pb, oc->url); - } } if (!byterange_mode) { if (vs->vtt_avf) { @@ -2380,91 +2378,131 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) } } - if (oc->url[0]) { - proto = avio_find_protocol_name(oc->url); - use_temp_file = proto && !strcmp(proto, "file") && (hls->flags & HLS_TEMP_FILE); - } + if (hls->flags & HLS_SINGLE_FILE) { + ret = flush_dynbuf(vs, &range_length); + av_freep(&vs->temp_buffer); + if (ret < 0) { + return ret; + } + vs->size = range_length; + } else { + if (oc->url[0]) { + proto = avio_find_protocol_name(oc->url); + use_temp_file = proto && !strcmp(proto, "file") + && (hls->flags & HLS_TEMP_FILE); + } + + if ((hls->max_seg_size > 0 && (vs->size >= hls->max_seg_size)) || !byterange_mode) { + AVDictionary *options = NULL; + char *filename = NULL; + if (hls->key_info_file || hls->encrypt) { + av_dict_set(&options, "encryption_key", vs->key_string, 0); + av_dict_set(&options, "encryption_iv", vs->iv_string, 0); + filename = av_asprintf("crypto:%s", oc->url); + } else { + filename = av_asprintf("%s", oc->url); + } + if (!filename) { + av_dict_free(&options); + return AVERROR(ENOMEM); + } - // look to rename the asset name - if (use_temp_file) { - if (!(hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size <= 0)) - if ((vs->avf->oformat->priv_class && vs->avf->priv_data) && hls->segment_type != SEGMENT_TYPE_FMP4) - av_opt_set(vs->avf->priv_data, "mpegts_flags", "resend_headers", 0); - } + // look to rename the asset name + if (use_temp_file) + av_dict_set(&options, "mpegts_flags", "resend_headers", 0); - if (hls->segment_type == SEGMENT_TYPE_FMP4) { - if (hls->flags & HLS_SINGLE_FILE) { - ret = flush_dynbuf(vs, &range_length); - if (ret < 0) { - return ret; - } - vs->size = range_length; - } else { set_http_options(s, &options, hls); - ret = hlsenc_io_open(s, &vs->out, vs->avf->url, &options); + + ret = hlsenc_io_open(s, &vs->out, filename, &options); if (ret < 0) { av_log(s, hls->ignore_io_errors ? AV_LOG_WARNING : AV_LOG_ERROR, - "Failed to open file '%s'\n", vs->avf->url); + "Failed to open file '%s'\n", filename); + av_freep(&filename); + av_dict_free(&options); return hls->ignore_io_errors ? 0 : ret; } - write_styp(vs->out); + if (hls->segment_type == SEGMENT_TYPE_FMP4) { + write_styp(vs->out); + } ret = flush_dynbuf(vs, &range_length); if (ret < 0) { + av_freep(&filename); + av_dict_free(&options); return ret; } - ff_format_io_close(s, &vs->out); + ret = hlsenc_io_close(s, &vs->out, filename); + if (ret < 0) { + av_log(s, AV_LOG_WARNING, "upload segment failed," + " 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); + } + av_dict_free(&options); + av_freep(&vs->temp_buffer); + av_freep(&filename); } - } - if (use_temp_file && !(hls->flags & HLS_SINGLE_FILE)) { - hls_rename_temp_file(s, oc); + if (use_temp_file) + hls_rename_temp_file(s, oc); } - old_filename = av_strdup(vs->avf->url); + old_filename = av_strdup(oc->url); if (!old_filename) { return AVERROR(ENOMEM); } if (vs->start_pos || hls->segment_type != SEGMENT_TYPE_FMP4) { - ret = hls_append_segment(s, hls, vs, vs->duration, vs->start_pos, vs->size); + double cur_duration = (double)(pkt->pts - vs->end_pts) * st->time_base.num / st->time_base.den; + ret = hls_append_segment(s, hls, vs, cur_duration, vs->start_pos, vs->size); vs->end_pts = pkt->pts; vs->duration = 0; if (ret < 0) { - av_free(old_filename); + av_freep(&old_filename); return ret; } } - if (hls->segment_type != SEGMENT_TYPE_FMP4) { - vs->start_pos = new_start_pos; - } else { - vs->start_pos += vs->size; - } // if we're building a VOD playlist, skip writing the manifest multiple times, and just wait until the end if (hls->pl_type != PLAYLIST_TYPE_VOD) { if ((ret = hls_window(s, 0, vs)) < 0) { + av_log(s, AV_LOG_WARNING, "upload playlist failed, will retry with a new http session.\n"); + ff_format_io_close(s, &vs->out); + if ((ret = hls_window(s, 0, vs)) < 0) { + av_freep(&old_filename); + return ret; + } + } + } + + if (hls->resend_init_file && hls->segment_type == SEGMENT_TYPE_FMP4) { + ret = hls_init_file_resend(s, vs); + if (ret < 0) { + av_freep(&old_filename); return ret; } } if (hls->flags & HLS_SINGLE_FILE) { - vs->number++; + vs->start_pos += vs->size; } else if (hls->max_seg_size > 0) { - if (vs->start_pos >= hls->max_seg_size) { + vs->start_pos = new_start_pos; + if (vs->size >= hls->max_seg_size) { vs->sequence++; sls_flag_file_rename(hls, vs, old_filename); ret = hls_start(s, vs); vs->start_pos = 0; /* When split segment by byte, the duration is short than hls_time, * so it is not enough one segment duration as hls_time, */ - vs->number--; } - vs->number++; } else { + vs->start_pos = new_start_pos; sls_flag_file_rename(hls, vs, old_filename); ret = hls_start(s, vs); } - av_free(old_filename); + vs->number++; + av_freep(&old_filename); if (ret < 0) { return ret; @@ -2475,13 +2513,12 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) vs->packets_written++; if (oc->pb) { ret = ff_write_chained(oc, stream_index, pkt, s, 0); + vs->video_keyframe_size += pkt->size; if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && (pkt->flags & AV_PKT_FLAG_KEY)) { - vs->video_keyframe_size = avio_tell(oc->pb) - vs->video_lastpos; - vs->video_keyframe_pos = vs->start_pos; + vs->video_keyframe_size = avio_tell(oc->pb); } else { - vs->video_lastpos = avio_tell(oc->pb); + vs->video_keyframe_pos = avio_tell(vs->out); } - if (hls->ignore_io_errors) ret = 0; } @@ -2489,34 +2526,37 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -static void hls_free_variant_streams(struct HLSContext *hls) +static void hls_deinit(AVFormatContext *s) { + HLSContext *hls = s->priv_data; int i = 0; - AVFormatContext *vtt_oc = NULL; VariantStream *vs = NULL; for (i = 0; i < hls->nb_varstreams; i++) { vs = &hls->var_streams[i]; - vtt_oc = vs->vtt_avf; av_freep(&vs->basename); av_freep(&vs->base_output_dirname); av_freep(&vs->fmp4_init_filename); - if (vtt_oc) { - av_freep(&vs->vtt_basename); - av_freep(&vs->vtt_m3u8_name); - } + av_freep(&vs->vtt_basename); + av_freep(&vs->vtt_m3u8_name); + avformat_free_context(vs->vtt_avf); + avformat_free_context(vs->avf); + if (hls->resend_init_file) + av_freep(&vs->init_buffer); hls_free_segments(vs->segments); hls_free_segments(vs->old_segments); av_freep(&vs->m3u8_name); av_freep(&vs->streams); - av_freep(&vs->agroup); - av_freep(&vs->language); - av_freep(&vs->ccgroup); - av_freep(&vs->baseurl); - av_freep(&vs->varname); } + + ff_format_io_close(s, &hls->m3u8_out); + ff_format_io_close(s, &hls->sub_m3u8_out); + av_freep(&hls->key_basename); + av_freep(&hls->var_streams); + av_freep(&hls->cc_streams); + av_freep(&hls->master_m3u8_url); } static int hls_write_trailer(struct AVFormatContext *s) @@ -2530,29 +2570,41 @@ static int hls_write_trailer(struct AVFormatContext *s) int i; int ret = 0; VariantStream *vs = NULL; + AVDictionary *options = NULL; + int range_length, byterange_mode; for (i = 0; i < hls->nb_varstreams; i++) { + char *filename = NULL; vs = &hls->var_streams[i]; - oc = vs->avf; vtt_oc = vs->vtt_avf; - old_filename = av_strdup(vs->avf->url); + old_filename = av_strdup(oc->url); use_temp_file = 0; if (!old_filename) { return AVERROR(ENOMEM); } - if ( hls->segment_type == SEGMENT_TYPE_FMP4) { + if (hls->key_info_file || hls->encrypt) { + av_dict_set(&options, "encryption_key", vs->key_string, 0); + av_dict_set(&options, "encryption_iv", vs->iv_string, 0); + filename = av_asprintf("crypto:%s", oc->url); + } else { + filename = av_asprintf("%s", oc->url); + } + if (!filename) { + av_freep(&old_filename); + return AVERROR(ENOMEM); + } + + if (hls->segment_type == SEGMENT_TYPE_FMP4) { int range_length = 0; if (!vs->init_range_length) { uint8_t *buffer = NULL; - int range_length, byterange_mode; - av_write_frame(vs->avf, NULL); /* Flush any buffered data */ - avio_flush(oc->pb); + av_write_frame(oc, NULL); /* Flush any buffered data */ range_length = avio_close_dyn_buf(oc->pb, &buffer); avio_write(vs->out, buffer, range_length); - av_free(buffer); + av_freep(&buffer); vs->init_range_length = range_length; avio_open_dyn_buf(&oc->pb); vs->packets_written = 0; @@ -2563,51 +2615,62 @@ static int hls_write_trailer(struct AVFormatContext *s) hlsenc_io_close(s, &vs->out, vs->base_output_dirname); } } - if (!(hls->flags & HLS_SINGLE_FILE)) { - ret = hlsenc_io_open(s, &vs->out, vs->avf->url, NULL); - if (ret < 0) { - av_log(s, AV_LOG_ERROR, "Failed to open file '%s'\n", vs->avf->url); - goto failed; - } - write_styp(vs->out); - } - ret = flush_dynbuf(vs, &range_length); + } + if (!(hls->flags & HLS_SINGLE_FILE)) { + set_http_options(s, &options, hls); + ret = hlsenc_io_open(s, &vs->out, filename, &options); if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Failed to open file '%s'\n", oc->url); goto failed; } - vs->size = range_length; + if (hls->segment_type == SEGMENT_TYPE_FMP4) + write_styp(vs->out); + } + ret = flush_dynbuf(vs, &range_length); + if (ret < 0) + goto failed; + + vs->size = range_length; + hlsenc_io_close(s, &vs->out, filename); + ret = hlsenc_io_close(s, &vs->out, filename); + if (ret < 0) { + av_log(s, AV_LOG_WARNING, "upload segment failed, will retry with a new http session.\n"); ff_format_io_close(s, &vs->out); + ret = hlsenc_io_open(s, &vs->out, filename, &options); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Failed to open file '%s'\n", oc->url); + goto failed; + } + reflush_dynbuf(vs, &range_length); + ret = hlsenc_io_close(s, &vs->out, filename); + if (ret < 0) + av_log(s, AV_LOG_WARNING, "Failed to upload file '%s' at the end.\n", oc->url); } failed: + av_freep(&vs->temp_buffer); + av_dict_free(&options); + av_freep(&filename); av_write_trailer(oc); - if (oc->url[0]) { proto = avio_find_protocol_name(oc->url); use_temp_file = proto && !strcmp(proto, "file") && (hls->flags & HLS_TEMP_FILE); } - if (oc->pb) { - if (hls->segment_type != SEGMENT_TYPE_FMP4) { - vs->size = avio_tell(vs->avf->pb) - vs->start_pos; - hlsenc_io_close(s, &vs->avf->pb, vs->avf->url); - } - - // rename that segment from .tmp to the real one - if (use_temp_file && !(hls->flags & HLS_SINGLE_FILE)) { - hls_rename_temp_file(s, oc); - av_free(old_filename); - old_filename = av_strdup(vs->avf->url); + // rename that segment from .tmp to the real one + if (use_temp_file && !(hls->flags & HLS_SINGLE_FILE)) { + hls_rename_temp_file(s, oc); + av_freep(&old_filename); + old_filename = av_strdup(oc->url); - if (!old_filename) { - return AVERROR(ENOMEM); - } + if (!old_filename) { + return AVERROR(ENOMEM); } - - /* after av_write_trailer, then duration + 1 duration per packet */ - hls_append_segment(s, hls, vs, vs->duration + vs->dpp, vs->start_pos, vs->size); } + /* after av_write_trailer, then duration + 1 duration per packet */ + hls_append_segment(s, hls, vs, vs->duration + vs->dpp, vs->start_pos, vs->size); + sls_flag_file_rename(hls, vs, old_filename); if (vtt_oc) { @@ -2615,30 +2678,18 @@ static int hls_write_trailer(struct AVFormatContext *s) av_write_trailer(vtt_oc); vs->size = avio_tell(vs->vtt_avf->pb) - vs->start_pos; ff_format_io_close(s, &vtt_oc->pb); - avformat_free_context(vtt_oc); } - hls_window(s, 1, vs); - avformat_free_context(oc); + ret = hls_window(s, 1, vs); + if (ret < 0) { + av_log(s, AV_LOG_WARNING, "upload playlist failed, will retry with a new http session.\n"); + ff_format_io_close(s, &vs->out); + hls_window(s, 1, vs); + } + ffio_free_dyn_buf(&oc->pb); - vs->avf = NULL; av_free(old_filename); } - hls_free_variant_streams(hls); - - for (i = 0; i < hls->nb_ccstreams; i++) { - ClosedCaptionsStream *ccs = &hls->cc_streams[i]; - av_freep(&ccs->ccgroup); - av_freep(&ccs->instreamid); - av_freep(&ccs->language); - } - - ff_format_io_close(s, &hls->m3u8_out); - ff_format_io_close(s, &hls->sub_m3u8_out); - av_freep(&hls->key_basename); - av_freep(&hls->var_streams); - av_freep(&hls->cc_streams); - av_freep(&hls->master_m3u8_url); return 0; } @@ -2649,50 +2700,54 @@ static int hls_init(AVFormatContext *s) int i = 0; int j = 0; HLSContext *hls = s->priv_data; - const char *pattern = "%d.ts"; + const char *pattern; VariantStream *vs = NULL; - int basename_size = 0; - const char *pattern_localtime_fmt = get_default_pattern_localtime_fmt(s); - const char *vtt_pattern = "%d.vtt"; + const char *vtt_pattern = hls->flags & HLS_SINGLE_FILE ? ".vtt" : "%d.vtt"; char *p = NULL; - int vtt_basename_size = 0; + int http_base_proto = ff_is_http_proto(s->url); int fmp4_init_filename_len = strlen(hls->fmp4_init_filename) + 1; + if (hls->use_localtime) { + pattern = get_default_pattern_localtime_fmt(s); + } else { + pattern = hls->segment_type == SEGMENT_TYPE_FMP4 ? "%d.m4s" : "%d.ts"; + if (hls->flags & HLS_SINGLE_FILE) + pattern += 2; + } + hls->has_default_key = 0; hls->has_video_m3u8 = 0; ret = update_variant_stream_info(s); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Variant stream info update failed with status %x\n", ret); - goto fail; + return ret; } - //TODO: Updates needed to encryption functionality with periodic re-key when more than one variant streams are present - if (hls->nb_varstreams > 1 && hls->flags & HLS_PERIODIC_REKEY) { - ret = AVERROR(EINVAL); - av_log(s, AV_LOG_ERROR, "Periodic re-key not supported when more than one variant streams are present\n"); - goto fail; + + if (!hls->method && http_base_proto) { + av_log(hls, AV_LOG_WARNING, "No HTTP method set, hls muxer defaulting to method PUT.\n"); } ret = validate_name(hls->nb_varstreams, s->url); if (ret < 0) - goto fail; + return ret; if (hls->segment_filename) { ret = validate_name(hls->nb_varstreams, hls->segment_filename); if (ret < 0) - goto fail; + return ret; } if (av_strcasecmp(hls->fmp4_init_filename, "init.mp4")) { ret = validate_name(hls->nb_varstreams, hls->fmp4_init_filename); if (ret < 0) - goto fail; + return ret; } if (hls->subtitle_filename) { ret = validate_name(hls->nb_varstreams, hls->subtitle_filename); if (ret < 0) - goto fail; + return ret; } if (hls->master_pl_name) { @@ -2700,23 +2755,23 @@ static int hls_init(AVFormatContext *s) if (ret < 0) { av_log(s, AV_LOG_ERROR, "Master stream info update failed with status %x\n", ret); - goto fail; + return ret; } } - if (hls->segment_type == SEGMENT_TYPE_FMP4) { - pattern = "%d.m4s"; - } if ((hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) || + (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_MICROSECONDS_SINCE_EPOCH) || (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_FORMATTED_DATETIME)) { - time_t t = time(NULL); // we will need it in either case - if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) { + time_t t = time(NULL); + if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_MICROSECONDS_SINCE_EPOCH) { + hls->start_sequence = av_gettime(); + } else if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) { hls->start_sequence = (int64_t)t; } else if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_FORMATTED_DATETIME) { char b[15]; struct tm *p, tmbuf; if (!(p = localtime_r(&t, &tmbuf))) - return AVERROR(ENOMEM); + return AVERROR(errno); if (!strftime(b, sizeof(b), "%Y%m%d%H%M%S", p)) return AVERROR(ENOMEM); hls->start_sequence = strtoll(b, NULL, 10); @@ -2725,38 +2780,32 @@ static int hls_init(AVFormatContext *s) } hls->recording_time = (hls->init_time ? hls->init_time : hls->time) * AV_TIME_BASE; + + if (hls->flags & HLS_SPLIT_BY_TIME && hls->flags & HLS_INDEPENDENT_SEGMENTS) { + // Independent segments cannot be guaranteed when splitting by time + hls->flags &= ~HLS_INDEPENDENT_SEGMENTS; + av_log(s, AV_LOG_WARNING, + "'split_by_time' and 'independent_segments' cannot be " + "enabled together. Disabling 'independent_segments' flag\n"); + } + for (i = 0; i < hls->nb_varstreams; i++) { vs = &hls->var_streams[i]; ret = format_name(s->url, &vs->m3u8_name, i, vs->varname); if (ret < 0) - goto fail; + return ret; - vs->sequence = hls->start_sequence; - vs->start_pts = AV_NOPTS_VALUE; - vs->end_pts = AV_NOPTS_VALUE; + vs->sequence = hls->start_sequence; + vs->start_pts = AV_NOPTS_VALUE; + vs->end_pts = AV_NOPTS_VALUE; vs->current_segment_final_filename_fmt[0] = '\0'; - if (hls->flags & HLS_SPLIT_BY_TIME && hls->flags & HLS_INDEPENDENT_SEGMENTS) { - // Independent segments cannot be guaranteed when splitting by time - hls->flags &= ~HLS_INDEPENDENT_SEGMENTS; - av_log(s, AV_LOG_WARNING, - "'split_by_time' and 'independent_segments' cannot be enabled together. " - "Disabling 'independent_segments' flag\n"); - } - if (hls->flags & HLS_PROGRAM_DATE_TIME) { time_t now0; time(&now0); vs->initial_prog_date_time = now0; } - if (hls->format_options_str) { - ret = av_dict_parse_string(&hls->format_options, hls->format_options_str, "=", ":", 0); - if (ret < 0) { - av_log(s, AV_LOG_ERROR, "Could not parse format options list '%s'\n", hls->format_options_str); - goto fail; - } - } for (j = 0; j < vs->nb_streams; j++) { vs->has_video += vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO; @@ -2775,55 +2824,24 @@ static int hls_init(AVFormatContext *s) } else { vs->oformat = av_guess_format("mpegts", NULL, NULL); } + if (!vs->oformat) + return AVERROR_MUXER_NOT_FOUND; - if (!vs->oformat) { - ret = AVERROR_MUXER_NOT_FOUND; - goto fail; - } - - if (vs->has_subtitle) { - vs->vtt_oformat = av_guess_format("webvtt", NULL, NULL); - if (!vs->oformat) { - ret = AVERROR_MUXER_NOT_FOUND; - goto fail; - } - } if (hls->segment_filename) { ret = format_name(hls->segment_filename, &vs->basename, i, vs->varname); if (ret < 0) - goto fail; - basename_size = strlen(vs->basename) + 1; + return ret; } else { - if (hls->flags & HLS_SINGLE_FILE) { - if (hls->segment_type == SEGMENT_TYPE_FMP4) { - pattern = ".m4s"; - } else { - pattern = ".ts"; - } - } - - if (hls->use_localtime) { - basename_size = strlen(vs->m3u8_name) + strlen(pattern_localtime_fmt) + 1; - } else { - basename_size = strlen(vs->m3u8_name) + strlen(pattern) + 1; - } - - vs->basename = av_malloc(basename_size); - if (!vs->basename) { - ret = AVERROR(ENOMEM); - goto fail; - } + p = strrchr(vs->m3u8_name, '.'); + if (p) + *p = '\0'; - av_strlcpy(vs->basename, vs->m3u8_name, basename_size); + vs->basename = av_asprintf("%s%s", vs->m3u8_name, pattern); + if (!vs->basename) + return AVERROR(ENOMEM); - p = strrchr(vs->basename, '.'); if (p) - *p = '\0'; - if (hls->use_localtime) { - av_strlcat(vs->basename, pattern_localtime_fmt, basename_size); - } else { - av_strlcat(vs->basename, pattern, basename_size); - } + *p = '.'; } if (hls->segment_type == SEGMENT_TYPE_FMP4) { @@ -2831,106 +2849,73 @@ static int hls_init(AVFormatContext *s) fmp4_init_filename_len += strlen(POSTFIX_PATTERN); if (hls->flags & HLS_SINGLE_FILE) { vs->fmp4_init_filename = av_strdup(vs->basename); - if (!vs->fmp4_init_filename) { - ret = AVERROR(ENOMEM); - goto fail; - } + if (!vs->fmp4_init_filename) + return AVERROR(ENOMEM); } else { vs->fmp4_init_filename = av_malloc(fmp4_init_filename_len); - if (!vs->fmp4_init_filename ) { - ret = AVERROR(ENOMEM); - goto fail; - } + if (!vs->fmp4_init_filename) + return AVERROR(ENOMEM); av_strlcpy(vs->fmp4_init_filename, hls->fmp4_init_filename, fmp4_init_filename_len); if (hls->nb_varstreams > 1) { if (av_stristr(vs->fmp4_init_filename, "%v")) { av_freep(&vs->fmp4_init_filename); - format_name(hls->fmp4_init_filename, &vs->fmp4_init_filename, i, vs->varname); + ret = format_name(hls->fmp4_init_filename, + &vs->fmp4_init_filename, i, vs->varname); } else { ret = append_postfix(vs->fmp4_init_filename, fmp4_init_filename_len, i); } if (ret < 0) - goto fail; - } - - fmp4_init_filename_len = strlen(vs->m3u8_name) + - strlen(vs->fmp4_init_filename) + 1; - - vs->base_output_dirname = av_malloc(fmp4_init_filename_len); - if (!vs->base_output_dirname) { - ret = AVERROR(ENOMEM); - goto fail; + return ret; } - av_strlcpy(vs->base_output_dirname, vs->m3u8_name, - fmp4_init_filename_len); - p = strrchr(vs->base_output_dirname, '/'); + p = strrchr(vs->m3u8_name, '/'); if (p) { - *(p + 1) = '\0'; - av_strlcat(vs->base_output_dirname, vs->fmp4_init_filename, - fmp4_init_filename_len); + char tmp = *(++p); + *p = '\0'; + vs->base_output_dirname = av_asprintf("%s%s", vs->m3u8_name, + vs->fmp4_init_filename); + *p = tmp; } else { - av_strlcpy(vs->base_output_dirname, vs->fmp4_init_filename, - fmp4_init_filename_len); + vs->base_output_dirname = av_strdup(vs->fmp4_init_filename); } + if (!vs->base_output_dirname) + return AVERROR(ENOMEM); } } - if (!hls->use_localtime) { - ret = sls_flag_check_duration_size_index(hls); - if (ret < 0) { - goto fail; - } - } else { - ret = sls_flag_check_duration_size(hls, vs); - if (ret < 0) { - goto fail; - } - } - if (vs->has_subtitle) { + ret = hls->use_localtime ? sls_flag_check_duration_size(hls, vs) : sls_flag_check_duration_size_index(hls); + if (ret < 0) + return ret; - if (hls->flags & HLS_SINGLE_FILE) - vtt_pattern = ".vtt"; - vtt_basename_size = strlen(vs->m3u8_name) + strlen(vtt_pattern) + 1; + if (vs->has_subtitle) { + vs->vtt_oformat = av_guess_format("webvtt", NULL, NULL); + if (!vs->vtt_oformat) + return AVERROR_MUXER_NOT_FOUND; - vs->vtt_basename = av_malloc(vtt_basename_size); - if (!vs->vtt_basename) { - ret = AVERROR(ENOMEM); - goto fail; - } - vs->vtt_m3u8_name = av_malloc(vtt_basename_size); - if (!vs->vtt_m3u8_name ) { - ret = AVERROR(ENOMEM); - goto fail; - } - av_strlcpy(vs->vtt_basename, vs->m3u8_name, vtt_basename_size); - p = strrchr(vs->vtt_basename, '.'); + p = strrchr(vs->m3u8_name, '.'); if (p) *p = '\0'; - if ( hls->subtitle_filename ) { - av_freep(&vs->vtt_m3u8_name); + vs->vtt_basename = av_asprintf("%s%s", vs->m3u8_name, vtt_pattern); + if (!vs->vtt_basename) + return AVERROR(ENOMEM); + + if (hls->subtitle_filename) { ret = format_name(hls->subtitle_filename, &vs->vtt_m3u8_name, i, vs->varname); if (ret < 0) - goto fail; + return ret; } else { - strcpy(vs->vtt_m3u8_name, vs->vtt_basename); - av_strlcat(vs->vtt_m3u8_name, "_vtt.m3u8", vtt_basename_size); - } - av_strlcat(vs->vtt_basename, vtt_pattern, vtt_basename_size); - } - - if (hls->baseurl) { - vs->baseurl = av_strdup(hls->baseurl); - if (!vs->baseurl) { - ret = AVERROR(ENOMEM); - goto fail; + vs->vtt_m3u8_name = av_asprintf("%s_vtt.m3u8", vs->m3u8_name); + if (!vs->vtt_m3u8_name) + return AVERROR(ENOMEM); } + if (p) + *p = '.'; } if ((ret = hls_mux_init(s, vs)) < 0) - goto fail; + return ret; if (hls->flags & HLS_APPEND_LIST) { parse_playlist(s, vs->m3u8_name, vs); @@ -2944,39 +2929,8 @@ static int hls_init(AVFormatContext *s) } if ((ret = hls_start(s, vs)) < 0) - goto fail; - } - -fail: - if (ret < 0) { - av_freep(&hls->key_basename); - for (i = 0; i < hls->nb_varstreams && hls->var_streams; i++) { - vs = &hls->var_streams[i]; - av_freep(&vs->basename); - av_freep(&vs->vtt_basename); - av_freep(&vs->fmp4_init_filename); - av_freep(&vs->m3u8_name); - av_freep(&vs->vtt_m3u8_name); - av_freep(&vs->streams); - av_freep(&vs->language); - av_freep(&vs->agroup); - av_freep(&vs->ccgroup); - av_freep(&vs->baseurl); - av_freep(&vs->varname); - if (vs->avf) - avformat_free_context(vs->avf); - if (vs->vtt_avf) - avformat_free_context(vs->vtt_avf); - } - for (i = 0; i < hls->nb_ccstreams; i++) { - ClosedCaptionsStream *ccs = &hls->cc_streams[i]; - av_freep(&ccs->ccgroup); - av_freep(&ccs->instreamid); - av_freep(&ccs->language); - } - av_freep(&hls->var_streams); - av_freep(&hls->cc_streams); - av_freep(&hls->master_m3u8_url); + return ret; + vs->number++; } return ret; @@ -2990,7 +2944,7 @@ static const AVOption options[] = { {"hls_init_time", "set segment length in seconds at init list", OFFSET(init_time), AV_OPT_TYPE_FLOAT, {.dbl = 0}, 0, FLT_MAX, E}, {"hls_list_size", "set maximum number of playlist entries", OFFSET(max_nb_segments), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E}, {"hls_delete_threshold", "set number of unreferenced segments to keep before deleting", OFFSET(hls_delete_threshold), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, E}, - {"hls_ts_options","set hls mpegts list of options for the container format used for hls", OFFSET(format_options_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, + {"hls_ts_options","set hls mpegts list of options for the container format used for hls", OFFSET(format_options), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, E}, {"hls_vtt_options","set hls vtt list of options for the container format used for hls", OFFSET(vtt_format_options_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, #if FF_API_HLS_WRAP {"hls_wrap", "set number after which the index wraps (will be deprecated)", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E}, @@ -3009,6 +2963,7 @@ static const AVOption options[] = { {"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_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"}, @@ -3026,7 +2981,7 @@ static const AVOption options[] = { {"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"}, #if FF_API_HLS_USE_LOCALTIME - {"use_localtime", "set filename expansion with strftime at segment creation(will be deprecated )", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, + {"use_localtime", "set filename expansion with strftime at segment creation(will be deprecated)", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, #endif {"strftime", "set filename expansion with strftime at segment creation", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, #if FF_API_HLS_USE_LOCALTIME @@ -3037,9 +2992,10 @@ static const AVOption options[] = { {"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" }, {"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_AS_FORMATTED_DATETIME, 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, "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" }, {"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}, @@ -3074,5 +3030,6 @@ AVOutputFormat ff_hls_muxer = { .write_header = hls_write_header, .write_packet = hls_write_packet, .write_trailer = hls_write_trailer, + .deinit = hls_deinit, .priv_class = &hls_class, }; diff --git a/libavformat/hlsplaylist.c b/libavformat/hlsplaylist.c index e8b566789c9..0e1dcc087f0 100644 --- a/libavformat/hlsplaylist.c +++ b/libavformat/hlsplaylist.c @@ -28,15 +28,18 @@ #include "avformat.h" #include "hlsplaylist.h" -void ff_hls_write_playlist_version(AVIOContext *out, int version) { +void ff_hls_write_playlist_version(AVIOContext *out, int version) +{ if (!out) return; avio_printf(out, "#EXTM3U\n"); avio_printf(out, "#EXT-X-VERSION:%d\n", version); } -void ff_hls_write_audio_rendition(AVIOContext *out, char *agroup, - char *filename, char *language, int name_id, int is_default) { +void ff_hls_write_audio_rendition(AVIOContext *out, const char *agroup, + const char *filename, const char *language, + int name_id, int is_default) +{ if (!out || !agroup || !filename) return; @@ -48,10 +51,26 @@ void ff_hls_write_audio_rendition(AVIOContext *out, char *agroup, avio_printf(out, "URI=\"%s\"\n", filename); } -void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, - int bandwidth, char *filename, char *agroup, - char *codecs, char *ccgroup) { +void ff_hls_write_subtitle_rendition(AVIOContext *out, const char *sgroup, + const char *filename, const char *language, + int name_id, int is_default) +{ + if (!out || !filename) + return; + + avio_printf(out, "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"%s\"", sgroup); + avio_printf(out, ",NAME=\"subtitle_%d\",DEFAULT=%s,", name_id, is_default ? "YES" : "NO"); + if (language) { + avio_printf(out, "LANGUAGE=\"%s\",", language); + } + avio_printf(out, "URI=\"%s\"\n", filename); +} +void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, int bandwidth, + const char *filename, const char *agroup, + const char *codecs, const char *ccgroup, + const char *sgroup) +{ if (!out || !filename) return; @@ -65,18 +84,21 @@ void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, if (st && st->codecpar->width > 0 && st->codecpar->height > 0) avio_printf(out, ",RESOLUTION=%dx%d", st->codecpar->width, st->codecpar->height); - if (codecs && strlen(codecs) > 0) + if (codecs && codecs[0]) avio_printf(out, ",CODECS=\"%s\"", codecs); - if (agroup && strlen(agroup) > 0) + if (agroup && agroup[0]) avio_printf(out, ",AUDIO=\"group_%s\"", agroup); - if (ccgroup && strlen(ccgroup) > 0) + if (ccgroup && ccgroup[0]) avio_printf(out, ",CLOSED-CAPTIONS=\"%s\"", ccgroup); + if (sgroup && sgroup[0]) + avio_printf(out, ",SUBTITLES=\"%s\"", sgroup); avio_printf(out, "\n%s\n\n", filename); } void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache, int target_duration, int64_t sequence, - uint32_t playlist_type, int iframe_mode) { + uint32_t playlist_type, int iframe_mode) +{ if (!out) return; ff_hls_write_playlist_version(out, version); @@ -97,8 +119,9 @@ void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache, } } -void ff_hls_write_init_file(AVIOContext *out, char *filename, - int byterange_mode, int64_t size, int64_t pos) { +void ff_hls_write_init_file(AVIOContext *out, const char *filename, + int byterange_mode, int64_t size, int64_t pos) +{ avio_printf(out, "#EXT-X-MAP:URI=\"%s\"", filename); if (byterange_mode) { avio_printf(out, ",BYTERANGE=\"%"PRId64"@%"PRId64"\"", size, pos); @@ -107,12 +130,14 @@ void ff_hls_write_init_file(AVIOContext *out, char *filename, } int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, - int byterange_mode, - double duration, int round_duration, - int64_t size, int64_t pos, //Used only if HLS_SINGLE_FILE flag is set - char *baseurl, //Ignored if NULL - char *filename, double *prog_date_time, - int64_t video_keyframe_size, int64_t video_keyframe_pos, int iframe_mode) { + int byterange_mode, double duration, + int round_duration, int64_t size, + int64_t pos /* Used only if HLS_SINGLE_FILE flag is set */, + const char *baseurl /* Ignored if NULL */, + const char *filename, double *prog_date_time, + int64_t video_keyframe_size, int64_t video_keyframe_pos, + int iframe_mode) +{ if (!out || !filename) return AVERROR(EINVAL); @@ -161,7 +186,8 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, return 0; } -void ff_hls_write_end_list (AVIOContext *out) { +void ff_hls_write_end_list(AVIOContext *out) +{ if (!out) return; avio_printf(out, "#EXT-X-ENDLIST\n"); diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h index 8d2d83b661e..29487da3edf 100644 --- a/libavformat/hlsplaylist.h +++ b/libavformat/hlsplaylist.h @@ -37,23 +37,29 @@ typedef enum { } PlaylistType; void ff_hls_write_playlist_version(AVIOContext *out, int version); -void ff_hls_write_audio_rendition(AVIOContext *out, char *agroup, - char *filename, char *language, int name_id, int is_default); -void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, - int bandwidth, char *filename, char *agroup, - char *codecs, char *ccgroup); +void ff_hls_write_audio_rendition(AVIOContext *out, const char *agroup, + const char *filename, const char *language, + int name_id, int is_default); +void ff_hls_write_subtitle_rendition(AVIOContext *out, const char *sgroup, + const char *filename, const char *language, + int name_id, int is_default); +void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, int bandwidth, + const char *filename, const char *agroup, + const char *codecs, const char *ccgroup, + const char *sgroup); void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache, int target_duration, int64_t sequence, uint32_t playlist_type, int iframe_mode); -void ff_hls_write_init_file(AVIOContext *out, char *filename, +void ff_hls_write_init_file(AVIOContext *out, const char *filename, int byterange_mode, int64_t size, int64_t pos); int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, - int byterange_mode, - double duration, int round_duration, - int64_t size, int64_t pos, //Used only if HLS_SINGLE_FILE flag is set - char *baseurl, //Ignored if NULL - char *filename, double *prog_date_time, - int64_t video_keyframe_size, int64_t video_keyframe_pos, int iframe_mode); + int byterange_mode, double duration, + int round_duration, int64_t size, + int64_t pos /* Used only if HLS_SINGLE_FILE flag is set */, + const char *baseurl /* Ignored if NULL */, + const char *filename, double *prog_date_time, + int64_t video_keyframe_size, int64_t video_keyframe_pos, + int iframe_mode); void ff_hls_write_end_list (AVIOContext *out); #endif /* AVFORMAT_HLSPLAYLIST_H_ */ diff --git a/libavformat/hlsproto.c b/libavformat/hlsproto.c index e5673e5e035..de45f771d66 100644 --- a/libavformat/hlsproto.c +++ b/libavformat/hlsproto.c @@ -178,7 +178,7 @@ static int hls_close(URLContext *h) free_segment_list(s); free_variant_list(s); - ffurl_close(s->seg_hd); + ffurl_closep(&s->seg_hd); return 0; } @@ -260,8 +260,7 @@ static int hls_read(URLContext *h, uint8_t *buf, int size) return ret; } if (s->seg_hd) { - ffurl_close(s->seg_hd); - s->seg_hd = NULL; + ffurl_closep(&s->seg_hd); s->cur_seq_no++; } reload_interval = s->n_segments > 0 ? diff --git a/libavformat/hnm.c b/libavformat/hnm.c index 40ef5c06ee1..f06add5cf83 100644 --- a/libavformat/hnm.c +++ b/libavformat/hnm.c @@ -37,19 +37,9 @@ #define HNM4_CHUNK_ID_SD 17491 typedef struct Hnm4DemuxContext { - uint8_t version; - uint16_t width; - uint16_t height; - uint32_t filesize; uint32_t frames; - uint32_t taboffset; - uint16_t bits; - uint16_t channels; - uint32_t framesize; uint32_t currentframe; - int64_t pts; uint32_t superchunk_remaining; - AVPacket vpkt; } Hnm4DemuxContext; static int hnm_probe(const AVProbeData *p) @@ -69,54 +59,37 @@ static int hnm_read_header(AVFormatContext *s) { Hnm4DemuxContext *hnm = s->priv_data; AVIOContext *pb = s->pb; + unsigned width, height; AVStream *vst; - - /* default context members */ - hnm->pts = 0; - av_init_packet(&hnm->vpkt); - hnm->vpkt.data = NULL; - hnm->vpkt.size = 0; - - hnm->superchunk_remaining = 0; + int ret; avio_skip(pb, 8); - hnm->width = avio_rl16(pb); - hnm->height = avio_rl16(pb); - hnm->filesize = avio_rl32(pb); + width = avio_rl16(pb); + height = avio_rl16(pb); + avio_rl32(pb); // filesize hnm->frames = avio_rl32(pb); - hnm->taboffset = avio_rl32(pb); - hnm->bits = avio_rl16(pb); - hnm->channels = avio_rl16(pb); - hnm->framesize = avio_rl32(pb); - avio_skip(pb, 32); + avio_skip(pb, 44); - hnm->currentframe = 0; - - if (hnm->width < 256 || hnm->width > 640 || - hnm->height < 150 || hnm->height > 480) { + if (width < 256 || width > 640 || + height < 150 || height > 480) { av_log(s, AV_LOG_ERROR, - "invalid resolution: %ux%u\n", hnm->width, hnm->height); + "invalid resolution: %ux%u\n", width, height); return AVERROR_INVALIDDATA; } - // TODO: find a better way to detect HNM4A - if (hnm->width == 640) - hnm->version = 0x4a; - else - hnm->version = 0x40; - if (!(vst = avformat_new_stream(s, NULL))) return AVERROR(ENOMEM); vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; vst->codecpar->codec_id = AV_CODEC_ID_HNM4_VIDEO; vst->codecpar->codec_tag = 0; - vst->codecpar->width = hnm->width; - vst->codecpar->height = hnm->height; - vst->codecpar->extradata = av_mallocz(1); + vst->codecpar->width = width; + vst->codecpar->height = height; + if ((ret = ff_alloc_extradata(vst->codecpar, 1)) < 0) + return ret; - vst->codecpar->extradata_size = 1; - memcpy(vst->codecpar->extradata, &hnm->version, 1); + // TODO: find a better way to detect HNM4A + vst->codecpar->extradata[0] = width == 640 ? 0x4a : 0x40; vst->start_time = 0; @@ -185,16 +158,6 @@ static int hnm_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -static int hnm_read_close(AVFormatContext *s) -{ - Hnm4DemuxContext *hnm = s->priv_data; - - if (hnm->vpkt.size > 0) - av_packet_unref(&hnm->vpkt); - - return 0; -} - AVInputFormat ff_hnm_demuxer = { .name = "hnm", .long_name = NULL_IF_CONFIG_SMALL("Cryo HNM v4"), @@ -202,6 +165,5 @@ AVInputFormat ff_hnm_demuxer = { .read_probe = hnm_probe, .read_header = hnm_read_header, .read_packet = hnm_read_packet, - .read_close = hnm_read_close, .flags = AVFMT_NO_BYTE_SEEK | AVFMT_NOGENSEARCH | AVFMT_NOBINSEARCH }; diff --git a/libavformat/http.c b/libavformat/http.c index 579debcd355..6c39da1a8b8 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -27,6 +27,7 @@ #include "libavutil/avassert.h" #include "libavutil/avstring.h" +#include "libavutil/bprint.h" #include "libavutil/opt.h" #include "libavutil/time.h" #include "libavutil/parseutils.h" @@ -45,7 +46,7 @@ /* The IO buffer size is unrelated to the max URL size in itself, but needs * to be large enough to fit the full request headers (including long * path names). */ -#define BUFFER_SIZE MAX_URL_SIZE +#define BUFFER_SIZE (MAX_URL_SIZE + HTTP_HEADERS_SIZE) #define MAX_REDIRECTS 8 #define HTTP_SINGLE 1 #define HTTP_MUTLI 2 @@ -190,9 +191,10 @@ void ff_http_init_auth_state(URLContext *dest, const URLContext *src) static int http_open_cnx_internal(URLContext *h, AVDictionary **options) { const char *path, *proxy_path, *lower_proto = "tcp", *local_path; + char *hashmark; char hostname[1024], hoststr[1024], proto[10]; char auth[1024], proxyauth[1024] = ""; - char path1[MAX_URL_SIZE]; + char path1[MAX_URL_SIZE], sanitized_path[MAX_URL_SIZE]; char buf[1024], urlbuf[MAX_URL_SIZE]; int port, use_proxy, err, location_changed = 0; HTTPContext *s = h->priv_data; @@ -215,10 +217,18 @@ static int http_open_cnx_internal(URLContext *h, AVDictionary **options) if (port < 0) port = 80; - if (path1[0] == '\0') + hashmark = strchr(path1, '#'); + if (hashmark) + *hashmark = '\0'; + + if (path1[0] == '\0') { path = "/"; - else + } else if (path1[0] == '?') { + snprintf(sanitized_path, sizeof(sanitized_path), "/%s", path1); + path = sanitized_path; + } else { path = path1; + } local_path = path; if (use_proxy) { /* Reassemble the request URL without auth string - we don't @@ -304,8 +314,27 @@ static int http_open_cnx(URLContext *h, AVDictionary **options) return location_changed; return ff_http_averror(s->http_code, AVERROR(EIO)); } +int ff_http_get_shutdown_status(URLContext *h) +{ + int ret = 0; + HTTPContext *s = h->priv_data; + + /* flush the receive buffer when it is write only mode */ + char buf[1024]; + int read_ret; + read_ret = ffurl_read(s->hd, buf, sizeof(buf)); + if (read_ret < 0) { + ret = read_ret; + } + + return ret; +} + +int ff_http_do_new_request(URLContext *h, const char *uri) { + return ff_http_do_new_request2(h, uri, NULL); +} -int ff_http_do_new_request(URLContext *h, const char *uri) +int ff_http_do_new_request2(URLContext *h, const char *uri, AVDictionary **opts) { HTTPContext *s = h->priv_data; AVDictionary *options = NULL; @@ -350,6 +379,9 @@ int ff_http_do_new_request(URLContext *h, const char *uri) if (!s->location) return AVERROR(ENOMEM); + if ((ret = av_opt_set_dict(s, opts)) < 0) + return ret; + av_log(s, AV_LOG_INFO, "Opening \'%s\' for %s\n", uri, h->flags & AVIO_FLAG_WRITE ? "writing" : "reading"); ret = http_open_cnx(h, &options); av_dict_free(&options); @@ -754,6 +786,7 @@ static int parse_set_cookie_expiry_time(const char *exp_str, struct tm *buf) static int parse_set_cookie(const char *set_cookie, AVDictionary **dict) { char *param, *next_param, *cstr, *back; + char *saveptr = NULL; if (!set_cookie[0]) return 0; @@ -771,8 +804,9 @@ static int parse_set_cookie(const char *set_cookie, AVDictionary **dict) } next_param = cstr; - while ((param = av_strtok(next_param, ";", &next_param))) { + while ((param = av_strtok(next_param, ";", &saveptr))) { char *name, *value; + next_param = NULL; param += strspn(param, WHITESPACES); if ((name = av_strtok(param, "=", &value))) { if (av_dict_set(dict, name, value, 0) < 0) { @@ -1032,6 +1066,7 @@ static int get_cookies(HTTPContext *s, char **cookies, const char *path, // Set-Cookie fields will result in multiple values delimited by a newline int ret = 0; char *cookie, *set_cookies, *next; + char *saveptr = NULL; // destroy any cookies in the dictionary. av_dict_free(&s->cookie_dict); @@ -1044,10 +1079,11 @@ static int get_cookies(HTTPContext *s, char **cookies, const char *path, return AVERROR(ENOMEM); *cookies = NULL; - while ((cookie = av_strtok(next, "\n", &next)) && !ret) { + while ((cookie = av_strtok(next, "\n", &saveptr)) && !ret) { AVDictionary *cookie_params = NULL; AVDictionaryEntry *cookie_entry, *e; + next = NULL; // store the cookie in a dict in case it is updated in the response if (parse_cookie(s, cookie, &s->cookie_dict)) av_log(s, AV_LOG_WARNING, "Unable to parse '%s'\n", cookie); @@ -1147,19 +1183,50 @@ static int http_read_header(URLContext *h, int *new_location) return err; } +/** + * Escape unsafe characters in path in order to pass them safely to the HTTP + * request. Insipred by the algorithm in GNU wget: + * - escape "%" characters not followed by two hex digits + * - escape all "unsafe" characters except which are also "reserved" + * - pass through everything else + */ +static void bprint_escaped_path(AVBPrint *bp, const char *path) +{ +#define NEEDS_ESCAPE(ch) \ + ((ch) <= ' ' || (ch) >= '\x7f' || \ + (ch) == '"' || (ch) == '%' || (ch) == '<' || (ch) == '>' || (ch) == '\\' || \ + (ch) == '^' || (ch) == '`' || (ch) == '{' || (ch) == '}' || (ch) == '|') + while (*path) { + char buf[1024]; + char *q = buf; + while (*path && q - buf < sizeof(buf) - 4) { + if (path[0] == '%' && av_isxdigit(path[1]) && av_isxdigit(path[2])) { + *q++ = *path++; + *q++ = *path++; + *q++ = *path++; + } else if (NEEDS_ESCAPE(*path)) { + q += snprintf(q, 4, "%%%02X", (uint8_t)*path++); + } else { + *q++ = *path++; + } + } + av_bprint_append_data(bp, buf, q - buf); + } +} + static int http_connect(URLContext *h, const char *path, const char *local_path, const char *hoststr, const char *auth, const char *proxyauth, int *new_location) { HTTPContext *s = h->priv_data; int post, err; - char headers[HTTP_HEADERS_SIZE] = ""; + AVBPrint request; char *authstr = NULL, *proxyauthstr = NULL; uint64_t off = s->off; - int len = 0; const char *method; int send_expect_100 = 0; - int ret; + + av_bprint_init_for_buffer(&request, s->buffer, sizeof(s->buffer)); /* send http header */ post = h->flags & AVIO_FLAG_WRITE; @@ -1202,95 +1269,74 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, s->user_agent = av_strdup(s->user_agent_deprecated); } #endif + + av_bprintf(&request, "%s ", method); + bprint_escaped_path(&request, path); + av_bprintf(&request, " HTTP/1.1\r\n"); + + if (post && s->chunked_post) + av_bprintf(&request, "Transfer-Encoding: chunked\r\n"); /* set default headers if needed */ if (!has_header(s->headers, "\r\nUser-Agent: ")) - len += av_strlcatf(headers + len, sizeof(headers) - len, - "User-Agent: %s\r\n", s->user_agent); + av_bprintf(&request, "User-Agent: %s\r\n", s->user_agent); if (s->referer) { /* set default headers if needed */ if (!has_header(s->headers, "\r\nReferer: ")) - len += av_strlcatf(headers + len, sizeof(headers) - len, - "Referer: %s\r\n", s->referer); + av_bprintf(&request, "Referer: %s\r\n", s->referer); } if (!has_header(s->headers, "\r\nAccept: ")) - len += av_strlcpy(headers + len, "Accept: */*\r\n", - sizeof(headers) - len); + av_bprintf(&request, "Accept: */*\r\n"); // Note: we send this on purpose even when s->off is 0 when we're probing, // since it allows us to detect more reliably if a (non-conforming) // server supports seeking by analysing the reply headers. if (!has_header(s->headers, "\r\nRange: ") && !post && (s->off > 0 || s->end_off || s->seekable == -1)) { - len += av_strlcatf(headers + len, sizeof(headers) - len, - "Range: bytes=%"PRIu64"-", s->off); + av_bprintf(&request, "Range: bytes=%"PRIu64"-", s->off); if (s->end_off) - len += av_strlcatf(headers + len, sizeof(headers) - len, - "%"PRId64, s->end_off - 1); - len += av_strlcpy(headers + len, "\r\n", - sizeof(headers) - len); + av_bprintf(&request, "%"PRId64, s->end_off - 1); + av_bprintf(&request, "\r\n"); } if (send_expect_100 && !has_header(s->headers, "\r\nExpect: ")) - len += av_strlcatf(headers + len, sizeof(headers) - len, - "Expect: 100-continue\r\n"); - - if (!has_header(s->headers, "\r\nConnection: ")) { - if (s->multiple_requests) - len += av_strlcpy(headers + len, "Connection: keep-alive\r\n", - sizeof(headers) - len); - else - len += av_strlcpy(headers + len, "Connection: close\r\n", - sizeof(headers) - len); - } + av_bprintf(&request, "Expect: 100-continue\r\n"); + + if (!has_header(s->headers, "\r\nConnection: ")) + av_bprintf(&request, "Connection: %s\r\n", s->multiple_requests ? "keep-alive" : "close"); if (!has_header(s->headers, "\r\nHost: ")) - len += av_strlcatf(headers + len, sizeof(headers) - len, - "Host: %s\r\n", hoststr); + av_bprintf(&request, "Host: %s\r\n", hoststr); if (!has_header(s->headers, "\r\nContent-Length: ") && s->post_data) - len += av_strlcatf(headers + len, sizeof(headers) - len, - "Content-Length: %d\r\n", s->post_datalen); + av_bprintf(&request, "Content-Length: %d\r\n", s->post_datalen); if (!has_header(s->headers, "\r\nContent-Type: ") && s->content_type) - len += av_strlcatf(headers + len, sizeof(headers) - len, - "Content-Type: %s\r\n", s->content_type); + av_bprintf(&request, "Content-Type: %s\r\n", s->content_type); if (!has_header(s->headers, "\r\nCookie: ") && s->cookies) { char *cookies = NULL; if (!get_cookies(s, &cookies, path, hoststr) && cookies) { - len += av_strlcatf(headers + len, sizeof(headers) - len, - "Cookie: %s\r\n", cookies); + av_bprintf(&request, "Cookie: %s\r\n", cookies); av_free(cookies); } } if (!has_header(s->headers, "\r\nIcy-MetaData: ") && s->icy) - len += av_strlcatf(headers + len, sizeof(headers) - len, - "Icy-MetaData: %d\r\n", 1); + av_bprintf(&request, "Icy-MetaData: 1\r\n"); /* now add in custom headers */ if (s->headers) - av_strlcpy(headers + len, s->headers, sizeof(headers) - len); + av_bprintf(&request, "%s", s->headers); - ret = snprintf(s->buffer, sizeof(s->buffer), - "%s %s HTTP/1.1\r\n" - "%s" - "%s" - "%s" - "%s%s" - "\r\n", - method, - path, - post && s->chunked_post ? "Transfer-Encoding: chunked\r\n" : "", - headers, - authstr ? authstr : "", - proxyauthstr ? "Proxy-" : "", proxyauthstr ? proxyauthstr : ""); + if (authstr) + av_bprintf(&request, "%s", authstr); + if (proxyauthstr) + av_bprintf(&request, "Proxy-%s", proxyauthstr); + av_bprintf(&request, "\r\n"); - av_log(h, AV_LOG_DEBUG, "request: %s\n", s->buffer); + av_log(h, AV_LOG_DEBUG, "request: %s\n", request.str); - if (strlen(headers) + 1 == sizeof(headers) || - ret >= sizeof(s->buffer)) { + if (!av_bprint_is_complete(&request)) { av_log(h, AV_LOG_ERROR, "overlong headers\n"); err = AVERROR(EINVAL); goto done; } - - if ((err = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0) + if ((err = ffurl_write(s->hd, request.str, request.len)) < 0) goto done; if (s->post_data) @@ -1769,7 +1815,7 @@ const URLProtocol ff_http_protocol = { .priv_data_size = sizeof(HTTPContext), .priv_data_class = &http_context_class, .flags = URL_PROTOCOL_FLAG_NETWORK, - .default_whitelist = "http,https,tls,rtp,tcp,udp,crypto,httpproxy" + .default_whitelist = "http,https,tls,rtp,tcp,udp,crypto,httpproxy,data" }; #endif /* CONFIG_HTTP_PROTOCOL */ diff --git a/libavformat/http.h b/libavformat/http.h index 7d02713e310..5557ce9b580 100644 --- a/libavformat/http.h +++ b/libavformat/http.h @@ -37,6 +37,15 @@ */ void ff_http_init_auth_state(URLContext *dest, const URLContext *src); +/** + * Get the HTTP shutdown response status, be used after http_shutdown. + * + * @param h pointer to the resource + * @return a negative value if an error condition occurred, 0 + * otherwise + */ +int ff_http_get_shutdown_status(URLContext *h); + /** * Send a new HTTP request, reusing the old connection. * @@ -47,6 +56,19 @@ void ff_http_init_auth_state(URLContext *dest, const URLContext *src); */ int ff_http_do_new_request(URLContext *h, const char *uri); +/** + * Send a new HTTP request, reusing the old connection. + * + * @param h pointer to the resource + * @param uri uri used to perform the request + * @param options A dictionary filled with HTTP options. On return + * this parameter will be destroyed and replaced with a dict containing options + * that were not found. May be NULL. + * @return a negative value if an error condition occurred, 0 + * otherwise + */ +int ff_http_do_new_request2(URLContext *h, const char *uri, AVDictionary **options); + int ff_http_averror(int status_code, int default_averror); #endif /* AVFORMAT_HTTP_H */ diff --git a/libavformat/httpauth.c b/libavformat/httpauth.c index 2d42ab2190c..4f79c78edc0 100644 --- a/libavformat/httpauth.c +++ b/libavformat/httpauth.c @@ -255,7 +255,7 @@ char *ff_http_auth_create_response(HTTPAuthState *state, const char *auth, if (state->auth_type == HTTP_AUTH_BASIC) { int auth_b64_len, len; - char *ptr, *decoded_auth = ff_urldecode(auth); + char *ptr, *decoded_auth = ff_urldecode(auth, 0); if (!decoded_auth) return NULL; @@ -275,7 +275,7 @@ char *ff_http_auth_create_response(HTTPAuthState *state, const char *auth, av_strlcat(ptr, "\r\n", len - (ptr - authstr)); av_free(decoded_auth); } else if (state->auth_type == HTTP_AUTH_DIGEST) { - char *username = ff_urldecode(auth), *password; + char *username = ff_urldecode(auth, 0), *password; if (!username) return NULL; diff --git a/libavformat/icecast.c b/libavformat/icecast.c index d2198b78ece..38af16b99e2 100644 --- a/libavformat/icecast.c +++ b/libavformat/icecast.c @@ -75,8 +75,7 @@ static void cat_header(AVBPrint *bp, const char key[], const char value[]) static int icecast_close(URLContext *h) { IcecastContext *s = h->priv_data; - if (s->hd) - ffurl_close(s->hd); + ffurl_closep(&s->hd); return 0; } @@ -89,7 +88,7 @@ static int icecast_open(URLContext *h, const char *uri, int flags) // URI part variables char h_url[1024], host[1024], auth[1024], path[1024]; - char *headers = NULL, *user = NULL; + char *headers, *user = NULL; int port, ret; AVBPrint bp; @@ -105,15 +104,16 @@ static int icecast_open(URLContext *h, const char *uri, int flags) cat_header(&bp, "Ice-Genre", s->genre); cat_header(&bp, "Ice-Public", s->public ? "1" : "0"); if (!av_bprint_is_complete(&bp)) { - ret = AVERROR(ENOMEM); - goto cleanup; + av_bprint_finalize(&bp, NULL); + return AVERROR(ENOMEM); } - av_bprint_finalize(&bp, &headers); + if ((ret = av_bprint_finalize(&bp, &headers)) < 0) + return ret; // Set options av_dict_set(&opt_dict, "method", s->legacy_icecast ? "SOURCE" : "PUT", 0); av_dict_set(&opt_dict, "auth_type", "basic", 0); - av_dict_set(&opt_dict, "headers", headers, 0); + av_dict_set(&opt_dict, "headers", headers, AV_DICT_DONT_STRDUP_VAL); av_dict_set(&opt_dict, "chunked_post", "0", 0); av_dict_set(&opt_dict, "send_expect_100", s->legacy_icecast ? "-1" : "1", 0); if (NOT_EMPTY(s->content_type)) @@ -169,7 +169,6 @@ static int icecast_open(URLContext *h, const char *uri, int flags) cleanup: av_freep(&user); - av_freep(&headers); av_dict_free(&opt_dict); return ret; diff --git a/libavformat/icodec.c b/libavformat/icodec.c index 98684e5e742..b47fa98f800 100644 --- a/libavformat/icodec.c +++ b/libavformat/icodec.c @@ -185,7 +185,6 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) bytestream_put_le32(&buf, 0); if ((ret = avio_read(pb, buf, image->size)) != image->size) { - av_packet_unref(pkt); return ret < 0 ? ret : AVERROR_INVALIDDATA; } diff --git a/libavformat/icoenc.c b/libavformat/icoenc.c index e641f7bdc71..a7df8b72bc6 100644 --- a/libavformat/icoenc.c +++ b/libavformat/icoenc.c @@ -106,8 +106,6 @@ static int ico_write_header(AVFormatContext *s) if (!ico->images) return AVERROR(ENOMEM); - avio_flush(pb); - return 0; } @@ -183,11 +181,16 @@ static int ico_write_trailer(AVFormatContext *s) avio_wl32(pb, ico->images[i].offset); } - av_freep(&ico->images); - return 0; } +static void ico_deinit(AVFormatContext *s) +{ + IcoMuxContext *ico = s->priv_data; + + av_freep(&ico->images); +} + AVOutputFormat ff_ico_muxer = { .name = "ico", .long_name = NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"), @@ -199,5 +202,6 @@ AVOutputFormat ff_ico_muxer = { .write_header = ico_write_header, .write_packet = ico_write_packet, .write_trailer = ico_write_trailer, + .deinit = ico_deinit, .flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/id3v1.c b/libavformat/id3v1.c index 19be42121d3..eb66098f516 100644 --- a/libavformat/id3v1.c +++ b/libavformat/id3v1.c @@ -92,7 +92,7 @@ const char * const ff_id3v1_genre_str[ID3v1_GENRE_MAX + 1] = { [64] = "Native American", [65] = "Cabaret", [66] = "New Wave", - [67] = "Psychadelic", /* sic, the misspelling is used in the specification */ + [67] = "Psychedelic", [68] = "Rave", [69] = "Showtunes", [70] = "Trailer", @@ -110,7 +110,7 @@ const char * const ff_id3v1_genre_str[ID3v1_GENRE_MAX + 1] = { [82] = "National Folk", [83] = "Swing", [84] = "Fast Fusion", - [85] = "Bebob", + [85] = "Bebop", [86] = "Latin", [87] = "Revival", [88] = "Celtic", @@ -148,20 +148,20 @@ const char * const ff_id3v1_genre_str[ID3v1_GENRE_MAX + 1] = { [120] = "Duet", [121] = "Punk Rock", [122] = "Drum Solo", - [123] = "A capella", + [123] = "A Cappella", [124] = "Euro-House", [125] = "Dance Hall", [126] = "Goa", [127] = "Drum & Bass", [128] = "Club-House", - [129] = "Hardcore", + [129] = "Hardcore Techno", [130] = "Terror", [131] = "Indie", [132] = "BritPop", [133] = "Negerpunk", [134] = "Polsk Punk", [135] = "Beat", - [136] = "Christian Gangsta", + [136] = "Christian Gangsta Rap", [137] = "Heavy Metal", [138] = "Black Metal", [139] = "Crossover", @@ -171,8 +171,52 @@ const char * const ff_id3v1_genre_str[ID3v1_GENRE_MAX + 1] = { [143] = "Salsa", [144] = "Thrash Metal", [145] = "Anime", - [146] = "JPop", - [147] = "SynthPop", + [146] = "Jpop", + [147] = "Synthpop", + [148] = "Abstract", + [149] = "Art Rock", + [150] = "Baroque", + [151] = "Bhangra", + [152] = "Big Beat", + [153] = "Breakbeat", + [154] = "Chillout", + [155] = "Downtempo", + [156] = "Dub", + [157] = "EBM", + [158] = "Eclectic", + [159] = "Electro", + [160] = "Electroclash", + [161] = "Emo", + [162] = "Experimental", + [163] = "Garage", + [164] = "Global", + [165] = "IDM", + [166] = "Illbient", + [167] = "Industro-Goth", + [168] = "Jam Band", + [169] = "Krautrock", + [170] = "Leftfield", + [171] = "Lounge", + [172] = "Math Rock", + [173] = "New Romantic", + [174] = "Nu-Breakz", + [175] = "Post-Punk", + [176] = "Post-Rock", + [177] = "Psytrance", + [178] = "Shoegaze", + [179] = "Space Rock", + [180] = "Trop Rock", + [181] = "World Music", + [182] = "Neoclassical", + [183] = "Audiobook", + [184] = "Audio Theatre", + [185] = "Neue Deutsche Welle", + [186] = "Podcast", + [187] = "Indie Rock", + [188] = "G-Funk", + [189] = "Dubstep", + [190] = "Garage Rock", + [191] = "Psybient" }; static void get_string(AVFormatContext *s, const char *key, diff --git a/libavformat/id3v1.h b/libavformat/id3v1.h index d5dca35873a..b3ad16df6c2 100644 --- a/libavformat/id3v1.h +++ b/libavformat/id3v1.h @@ -26,7 +26,7 @@ #define ID3v1_TAG_SIZE 128 -#define ID3v1_GENRE_MAX 147 +#define ID3v1_GENRE_MAX 191 /** * ID3v1 genres diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c index b43ab1745f2..cecd9b9f6d6 100644 --- a/libavformat/id3v2.c +++ b/libavformat/id3v2.c @@ -225,7 +225,6 @@ static void free_geobtag(void *obj) av_freep(&geob->file_name); av_freep(&geob->description); av_freep(&geob->data); - av_free(geob); } /** @@ -361,8 +360,8 @@ static void read_uslt(AVFormatContext *s, AVIOContext *pb, int taglen, { uint8_t lang[4]; uint8_t *descriptor = NULL; // 'Content descriptor' - uint8_t *text = NULL; - char *key = NULL; + uint8_t *text; + char *key; int encoding; int ok = 0; @@ -387,18 +386,19 @@ static void read_uslt(AVFormatContext *s, AVIOContext *pb, int taglen, key = av_asprintf("lyrics-%s%s%s", descriptor[0] ? (char *)descriptor : "", descriptor[0] ? "-" : "", lang); - if (!key) + if (!key) { + av_free(text); goto error; + } - av_dict_set(metadata, key, text, 0); + av_dict_set(metadata, key, text, + AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); ok = 1; error: if (!ok) av_log(s, AV_LOG_ERROR, "Error reading lyrics, skipped\n"); av_free(descriptor); - av_free(text); - av_free(key); } /** @@ -458,20 +458,15 @@ static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen, if (taglen < 1) return; - geob_data = av_mallocz(sizeof(ID3v2ExtraMetaGEOB)); - if (!geob_data) { - av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n", - sizeof(ID3v2ExtraMetaGEOB)); - return; - } - new_extra = av_mallocz(sizeof(ID3v2ExtraMeta)); if (!new_extra) { av_log(s, AV_LOG_ERROR, "Failed to alloc %"SIZE_SPECIFIER" bytes\n", sizeof(ID3v2ExtraMeta)); - goto fail; + return; } + geob_data = &new_extra->data.geob; + /* read encoding type byte */ encoding = avio_r8(pb); taglen--; @@ -510,7 +505,6 @@ static void read_geobtag(AVFormatContext *s, AVIOContext *pb, int taglen, /* add data to the list */ new_extra->tag = "GEOB"; - new_extra->data = geob_data; new_extra->next = *extra_meta; *extra_meta = new_extra; @@ -576,7 +570,6 @@ static void free_apic(void *obj) ID3v2ExtraMetaAPIC *apic = obj; av_buffer_unref(&apic->buf); av_freep(&apic->description); - av_freep(&apic); } static void rstrip_spaces(char *buf) @@ -602,10 +595,11 @@ static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen, goto fail; new_extra = av_mallocz(sizeof(*new_extra)); - apic = av_mallocz(sizeof(*apic)); - if (!new_extra || !apic) + if (!new_extra) goto fail; + apic = &new_extra->data.apic; + enc = avio_r8(pb); taglen--; @@ -657,7 +651,6 @@ static void read_apic(AVFormatContext *s, AVIOContext *pb, int taglen, memset(apic->buf->data + taglen, 0, AV_INPUT_BUFFER_PADDING_SIZE); new_extra->tag = "APIC"; - new_extra->data = apic; new_extra->next = *extra_meta; *extra_meta = new_extra; @@ -679,7 +672,6 @@ static void free_chapter(void *obj) ID3v2ExtraMetaCHAP *chap = obj; av_freep(&chap->element_id); av_dict_free(&chap->meta); - av_freep(&chap); } static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, const char *ttag, ID3v2ExtraMeta **extra_meta, int isv34) @@ -690,10 +682,10 @@ static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, const cha ID3v2ExtraMetaCHAP *chap = NULL; new_extra = av_mallocz(sizeof(*new_extra)); - chap = av_mallocz(sizeof(*chap)); + if (!new_extra) + return; - if (!new_extra || !chap) - goto fail; + chap = &new_extra->data.chap; if (decode_str(s, pb, 0, &chap->element_id, &len) < 0) goto fail; @@ -726,15 +718,13 @@ static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, const cha ff_metadata_conv(&chap->meta, NULL, ff_id3v2_4_metadata_conv); new_extra->tag = "CHAP"; - new_extra->data = chap; new_extra->next = *extra_meta; *extra_meta = new_extra; return; fail: - if (chap) - free_chapter(chap); + free_chapter(chap); av_freep(&new_extra); } @@ -743,7 +733,6 @@ static void free_priv(void *obj) ID3v2ExtraMetaPRIV *priv = obj; av_freep(&priv->owner); av_freep(&priv->data); - av_freep(&priv); } static void read_priv(AVFormatContext *s, AVIOContext *pb, int taglen, @@ -753,10 +742,10 @@ static void read_priv(AVFormatContext *s, AVIOContext *pb, int taglen, ID3v2ExtraMetaPRIV *priv; meta = av_mallocz(sizeof(*meta)); - priv = av_mallocz(sizeof(*priv)); + if (!meta) + return; - if (!meta || !priv) - goto fail; + priv = &meta->data.priv; if (decode_str(s, pb, ID3v2_ENCODING_ISO8859, &priv->owner, &taglen) < 0) goto fail; @@ -771,15 +760,13 @@ static void read_priv(AVFormatContext *s, AVIOContext *pb, int taglen, goto fail; meta->tag = "PRIV"; - meta->data = priv; meta->next = *extra_meta; *extra_meta = meta; return; fail: - if (priv) - free_priv(priv); + free_priv(priv); av_freep(&meta); } @@ -1131,7 +1118,7 @@ void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta) while (current) { if ((extra_func = get_extra_meta_func(current->tag, 1))) - extra_func->free(current->data); + extra_func->free(¤t->data); next = current->next; av_freep(¤t); current = next; @@ -1140,17 +1127,17 @@ void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta) *extra_meta = NULL; } -int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta) +int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta *extra_meta) { ID3v2ExtraMeta *cur; - for (cur = *extra_meta; cur; cur = cur->next) { + for (cur = extra_meta; cur; cur = cur->next) { ID3v2ExtraMetaAPIC *apic; AVStream *st; if (strcmp(cur->tag, "APIC")) continue; - apic = cur->data; + apic = &cur->data.apic; if (!(st = avformat_new_stream(s, NULL))) return AVERROR(ENOMEM); @@ -1180,7 +1167,7 @@ int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta) return 0; } -int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta **extra_meta) +int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta *extra_meta) { int ret = 0; ID3v2ExtraMeta *cur; @@ -1191,12 +1178,12 @@ int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta **extra_meta) // since extra_meta is a linked list where elements are prepended, // we need to reverse the order of chapters - for (cur = *extra_meta; cur; cur = cur->next) { + for (cur = extra_meta; cur; cur = cur->next) { ID3v2ExtraMetaCHAP *chap; if (strcmp(cur->tag, "CHAP")) continue; - chap = cur->data; + chap = &cur->data.chap; if ((ret = av_dynarray_add_nofree(&chapters, &num_chapters, chap)) < 0) goto end; @@ -1231,14 +1218,14 @@ int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta **extra_meta) return ret; } -int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta **extra_meta) +int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta *extra_meta) { ID3v2ExtraMeta *cur; int dict_flags = AV_DICT_DONT_OVERWRITE | AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL; - for (cur = *extra_meta; cur; cur = cur->next) { + for (cur = extra_meta; cur; cur = cur->next) { if (!strcmp(cur->tag, "PRIV")) { - ID3v2ExtraMetaPRIV *priv = cur->data; + ID3v2ExtraMetaPRIV *priv = &cur->data.priv; AVBPrint bprint; char *escaped, *key; int i, ret; @@ -1263,8 +1250,6 @@ int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta **extra_met } if ((ret = av_dict_set(metadata, key, escaped, dict_flags)) < 0) { - av_free(key); - av_free(escaped); return ret; } } @@ -1273,7 +1258,7 @@ int ff_id3v2_parse_priv_dict(AVDictionary **metadata, ID3v2ExtraMeta **extra_met return 0; } -int ff_id3v2_parse_priv(AVFormatContext *s, ID3v2ExtraMeta **extra_meta) +int ff_id3v2_parse_priv(AVFormatContext *s, ID3v2ExtraMeta *extra_meta) { return ff_id3v2_parse_priv_dict(&s->metadata, extra_meta); } diff --git a/libavformat/id3v2.h b/libavformat/id3v2.h index 9de0bee3743..a41fb271a42 100644 --- a/libavformat/id3v2.h +++ b/libavformat/id3v2.h @@ -54,12 +54,6 @@ typedef struct ID3v2EncContext { int len; ///< size of the tag written so far } ID3v2EncContext; -typedef struct ID3v2ExtraMeta { - const char *tag; - void *data; - struct ID3v2ExtraMeta *next; -} ID3v2ExtraMeta; - typedef struct ID3v2ExtraMetaGEOB { uint32_t datasize; uint8_t *mime_type; @@ -87,6 +81,17 @@ typedef struct ID3v2ExtraMetaCHAP { AVDictionary *meta; } ID3v2ExtraMetaCHAP; +typedef struct ID3v2ExtraMeta { + const char *tag; + struct ID3v2ExtraMeta *next; + union { + ID3v2ExtraMetaAPIC apic; + ID3v2ExtraMetaCHAP chap; + ID3v2ExtraMetaGEOB geob; + ID3v2ExtraMetaPRIV priv; + } data; +} ID3v2ExtraMeta; + /** * Detect ID3v2 Header. * @param buf must be ID3v2_HEADER_SIZE byte long @@ -162,25 +167,25 @@ void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta); * Create a stream for each APIC (attached picture) extracted from the * ID3v2 header. */ -int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta **extra_meta); +int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta *extra_meta); /** * Create chapters for all CHAP tags found in the ID3v2 header. */ -int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta **extra_meta); +int ff_id3v2_parse_chapters(AVFormatContext *s, ID3v2ExtraMeta *extra_meta); /** * Parse PRIV tags into a dictionary. The PRIV owner is the metadata key. The * PRIV data is the value, with non-printable characters escaped. */ -int ff_id3v2_parse_priv_dict(AVDictionary **d, ID3v2ExtraMeta **extra_meta); +int ff_id3v2_parse_priv_dict(AVDictionary **d, ID3v2ExtraMeta *extra_meta); /** * Add metadata for all PRIV tags in the ID3v2 header. The PRIV owner is the * metadata key. The PRIV data is the value, with non-printable characters * escaped. */ -int ff_id3v2_parse_priv(AVFormatContext *s, ID3v2ExtraMeta **extra_meta); +int ff_id3v2_parse_priv(AVFormatContext *s, ID3v2ExtraMeta *extra_meta); extern const AVMetadataConv ff_id3v2_34_metadata_conv[]; extern const AVMetadataConv ff_id3v2_4_metadata_conv[]; diff --git a/libavformat/id3v2enc.c b/libavformat/id3v2enc.c index c13b93a7d86..5d821ea4db2 100644 --- a/libavformat/id3v2enc.c +++ b/libavformat/id3v2enc.c @@ -65,11 +65,11 @@ static void id3v2_encode_string(AVIOContext *pb, const uint8_t *str, static int id3v2_put_ttag(ID3v2EncContext *id3, AVIOContext *avioc, const char *str1, const char *str2, uint32_t tag, enum ID3v2Encoding enc) { - int len; + int len, ret; uint8_t *pb; AVIOContext *dyn_buf; - if (avio_open_dyn_buf(&dyn_buf) < 0) - return AVERROR(ENOMEM); + if ((ret = avio_open_dyn_buf(&dyn_buf)) < 0) + return ret; /* check if the strings are ASCII-only and use UTF16 only if * they're not */ @@ -81,7 +81,7 @@ static int id3v2_put_ttag(ID3v2EncContext *id3, AVIOContext *avioc, const char * id3v2_encode_string(dyn_buf, str1, enc); if (str2) id3v2_encode_string(dyn_buf, str2, enc); - len = avio_close_dyn_buf(dyn_buf, &pb); + len = avio_get_dyn_buf(dyn_buf, &pb); avio_wb32(avioc, tag); /* ID3v2.3 frame size is not sync-safe */ @@ -92,7 +92,7 @@ static int id3v2_put_ttag(ID3v2EncContext *id3, AVIOContext *avioc, const char * avio_wb16(avioc, 0); avio_write(avioc, pb, len); - av_freep(&pb); + ffio_free_dyn_buf(&dyn_buf); return len + ID3v2_HEADER_SIZE; } @@ -103,7 +103,7 @@ static int id3v2_put_ttag(ID3v2EncContext *id3, AVIOContext *avioc, const char * */ static int id3v2_put_priv(ID3v2EncContext *id3, AVIOContext *avioc, const char *key, const char *data) { - int len; + int len, ret; uint8_t *pb; AVIOContext *dyn_buf; @@ -111,8 +111,8 @@ static int id3v2_put_priv(ID3v2EncContext *id3, AVIOContext *avioc, const char * return 0; } - if (avio_open_dyn_buf(&dyn_buf) < 0) - return AVERROR(ENOMEM); + if ((ret = avio_open_dyn_buf(&dyn_buf)) < 0) + return ret; // owner + null byte. avio_write(dyn_buf, key, strlen(key) + 1); @@ -134,7 +134,7 @@ static int id3v2_put_priv(ID3v2EncContext *id3, AVIOContext *avioc, const char * } } - len = avio_close_dyn_buf(dyn_buf, &pb); + len = avio_get_dyn_buf(dyn_buf, &pb); avio_wb32(avioc, MKBETAG('P', 'R', 'I', 'V')); if (id3->version == 3) @@ -144,7 +144,7 @@ static int id3v2_put_priv(ID3v2EncContext *id3, AVIOContext *avioc, const char * avio_wb16(avioc, 0); avio_write(avioc, pb, len); - av_free(pb); + ffio_free_dyn_buf(&dyn_buf); return len + ID3v2_HEADER_SIZE; } @@ -257,8 +257,8 @@ static int write_metadata(AVIOContext *pb, AVDictionary **metadata, static int write_ctoc(AVFormatContext *s, ID3v2EncContext *id3, int enc) { - uint8_t *dyn_buf = NULL; - AVIOContext *dyn_bc = NULL; + uint8_t *dyn_buf; + AVIOContext *dyn_bc; char name[123]; int len, ret; @@ -266,27 +266,24 @@ static int write_ctoc(AVFormatContext *s, ID3v2EncContext *id3, int enc) return 0; if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0) - goto fail; + return ret; - id3->len += avio_put_str(dyn_bc, "toc"); + avio_put_str(dyn_bc, "toc"); avio_w8(dyn_bc, 0x03); avio_w8(dyn_bc, s->nb_chapters); for (int i = 0; i < s->nb_chapters; i++) { snprintf(name, 122, "ch%d", i); - id3->len += avio_put_str(dyn_bc, name); + avio_put_str(dyn_bc, name); } - len = avio_close_dyn_buf(dyn_bc, &dyn_buf); - id3->len += 16 + ID3v2_HEADER_SIZE; + len = avio_get_dyn_buf(dyn_bc, &dyn_buf); + id3->len += len + ID3v2_HEADER_SIZE; avio_wb32(s->pb, MKBETAG('C', 'T', 'O', 'C')); avio_wb32(s->pb, len); avio_wb16(s->pb, 0); avio_write(s->pb, dyn_buf, len); -fail: - if (dyn_bc && !dyn_buf) - avio_close_dyn_buf(dyn_bc, &dyn_buf); - av_freep(&dyn_buf); + ffio_free_dyn_buf(&dyn_bc); return ret; } @@ -295,13 +292,13 @@ static int write_chapter(AVFormatContext *s, ID3v2EncContext *id3, int id, int e { const AVRational time_base = {1, 1000}; AVChapter *ch = s->chapters[id]; - uint8_t *dyn_buf = NULL; - AVIOContext *dyn_bc = NULL; + uint8_t *dyn_buf; + AVIOContext *dyn_bc; char name[123]; int len, start, end, ret; if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0) - goto fail; + return ret; start = av_rescale_q(ch->start, ch->time_base, time_base); end = av_rescale_q(ch->end, ch->time_base, time_base); @@ -316,7 +313,7 @@ static int write_chapter(AVFormatContext *s, ID3v2EncContext *id3, int id, int e if ((ret = write_metadata(dyn_bc, &ch->metadata, id3, enc)) < 0) goto fail; - len = avio_close_dyn_buf(dyn_bc, &dyn_buf); + len = avio_get_dyn_buf(dyn_bc, &dyn_buf); id3->len += 16 + ID3v2_HEADER_SIZE; avio_wb32(s->pb, MKBETAG('C', 'H', 'A', 'P')); @@ -325,9 +322,7 @@ static int write_chapter(AVFormatContext *s, ID3v2EncContext *id3, int id, int e avio_write(s->pb, dyn_buf, len); fail: - if (dyn_bc && !dyn_buf) - avio_close_dyn_buf(dyn_bc, &dyn_buf); - av_freep(&dyn_buf); + ffio_free_dyn_buf(&dyn_bc); return ret; } @@ -364,7 +359,7 @@ int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt) const char *mimetype = NULL, *desc = ""; int enc = id3->version == 3 ? ID3v2_ENCODING_UTF16BOM : ID3v2_ENCODING_UTF8; - int i, len, type = 0; + int i, len, type = 0, ret; /* get the mimetype*/ while (mime->id != AV_CODEC_ID_NONE) { @@ -398,15 +393,15 @@ int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt) enc = ID3v2_ENCODING_ISO8859; /* start writing */ - if (avio_open_dyn_buf(&dyn_buf) < 0) - return AVERROR(ENOMEM); + if ((ret = avio_open_dyn_buf(&dyn_buf)) < 0) + return ret; avio_w8(dyn_buf, enc); avio_put_str(dyn_buf, mimetype); avio_w8(dyn_buf, type); id3v2_encode_string(dyn_buf, desc, enc); avio_write(dyn_buf, pkt->data, pkt->size); - len = avio_close_dyn_buf(dyn_buf, &buf); + len = avio_get_dyn_buf(dyn_buf, &buf); avio_wb32(s->pb, MKBETAG('A', 'P', 'I', 'C')); if (id3->version == 3) @@ -415,7 +410,7 @@ int ff_id3v2_write_apic(AVFormatContext *s, ID3v2EncContext *id3, AVPacket *pkt) id3v2_put_size(s->pb, len); avio_wb16(s->pb, 0); avio_write(s->pb, buf, len); - av_freep(&buf); + ffio_free_dyn_buf(&dyn_buf); id3->len += len + ID3v2_HEADER_SIZE; diff --git a/libavformat/idcin.c b/libavformat/idcin.c index 0b1058171b4..5a6a15aa81d 100644 --- a/libavformat/idcin.c +++ b/libavformat/idcin.c @@ -313,7 +313,6 @@ static int idcin_read_packet(AVFormatContext *s, return ret; else if (ret != chunk_size) { av_log(s, AV_LOG_ERROR, "incomplete packet\n"); - av_packet_unref(pkt); return AVERROR(EIO); } if (command == 1) { @@ -322,7 +321,6 @@ static int idcin_read_packet(AVFormatContext *s, pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); if (!pal) { - av_packet_unref(pkt); return AVERROR(ENOMEM); } memcpy(pal, palette, AVPALETTE_SIZE); diff --git a/libavformat/idroqdec.c b/libavformat/idroqdec.c index 1db4cce6f0b..519f31d61a0 100644 --- a/libavformat/idroqdec.c +++ b/libavformat/idroqdec.c @@ -205,8 +205,9 @@ static int roq_read_packet(AVFormatContext *s, } /* load up the packet */ - if (av_new_packet(pkt, chunk_size + RoQ_CHUNK_PREAMBLE_SIZE)) - return AVERROR(EIO); + ret = av_new_packet(pkt, chunk_size + RoQ_CHUNK_PREAMBLE_SIZE); + if (ret < 0) + return ret; /* copy over preamble */ memcpy(pkt->data, preamble, RoQ_CHUNK_PREAMBLE_SIZE); @@ -223,8 +224,7 @@ static int roq_read_packet(AVFormatContext *s, ret = avio_read(pb, pkt->data + RoQ_CHUNK_PREAMBLE_SIZE, chunk_size); if (ret != chunk_size) { - av_packet_unref(pkt); - ret = AVERROR(EIO); + return AVERROR(EIO); } packet_read = 1; diff --git a/libavformat/idroqenc.c b/libavformat/idroqenc.c index 8122efef835..261f21939cc 100644 --- a/libavformat/idroqenc.c +++ b/libavformat/idroqenc.c @@ -55,7 +55,6 @@ static int roq_write_header(struct AVFormatContext *s) } avio_write(s->pb, header, 8); - avio_flush(s->pb); return 0; } diff --git a/libavformat/iff.c b/libavformat/iff.c index f4d806b7b8d..7feb121cd02 100644 --- a/libavformat/iff.c +++ b/libavformat/iff.c @@ -312,8 +312,8 @@ static int parse_dsd_prop(AVFormatContext *s, AVStream *st, uint64_t eof) id3v2_extra_meta = NULL; ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta, size); if (id3v2_extra_meta) { - if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0 || - (ret = ff_id3v2_parse_chapters(s, &id3v2_extra_meta)) < 0) { + if ((ret = ff_id3v2_parse_apic(s, id3v2_extra_meta)) < 0 || + (ret = ff_id3v2_parse_chapters(s, id3v2_extra_meta)) < 0) { ff_id3v2_free_extra_meta(&id3v2_extra_meta); return ret; } @@ -525,12 +525,15 @@ static int iff_read_header(AVFormatContext *s) data_size); return AVERROR_INVALIDDATA; } - st->codecpar->extradata_size = data_size + IFF_EXTRA_VIDEO_SIZE; - st->codecpar->extradata = av_malloc(data_size + IFF_EXTRA_VIDEO_SIZE + AV_INPUT_BUFFER_PADDING_SIZE); - if (!st->codecpar->extradata) - return AVERROR(ENOMEM); - if (avio_read(pb, st->codecpar->extradata + IFF_EXTRA_VIDEO_SIZE, data_size) < 0) + res = ff_alloc_extradata(st->codecpar, + data_size + IFF_EXTRA_VIDEO_SIZE); + if (res < 0) + return res; + if (avio_read(pb, st->codecpar->extradata + IFF_EXTRA_VIDEO_SIZE, data_size) < 0) { + av_freep(&st->codecpar->extradata); + st->codecpar->extradata_size = 0; return AVERROR(EIO); + } break; case ID_BMHD: @@ -768,10 +771,9 @@ static int iff_read_header(AVFormatContext *s) iff->transparency = transparency; if (!st->codecpar->extradata) { - st->codecpar->extradata_size = IFF_EXTRA_VIDEO_SIZE; - st->codecpar->extradata = av_malloc(IFF_EXTRA_VIDEO_SIZE + AV_INPUT_BUFFER_PADDING_SIZE); - if (!st->codecpar->extradata) - return AVERROR(ENOMEM); + int ret = ff_alloc_extradata(st->codecpar, IFF_EXTRA_VIDEO_SIZE); + if (ret < 0) + return ret; } av_assert0(st->codecpar->extradata_size >= IFF_EXTRA_VIDEO_SIZE); buf = st->codecpar->extradata; diff --git a/libavformat/ilbc.c b/libavformat/ilbc.c index ba11953b59c..188c0f091a7 100644 --- a/libavformat/ilbc.c +++ b/libavformat/ilbc.c @@ -21,6 +21,7 @@ #include "avformat.h" #include "internal.h" +#include "rawenc.h" static const char mode20_header[] = "#!iLBC20\n"; static const char mode30_header[] = "#!iLBC30\n"; @@ -49,13 +50,6 @@ static int ilbc_write_header(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "Unsupported mode\n"); return AVERROR(EINVAL); } - avio_flush(pb); - return 0; -} - -static int ilbc_write_packet(AVFormatContext *s, AVPacket *pkt) -{ - avio_write(s->pb, pkt->data, pkt->size); return 0; } @@ -112,7 +106,6 @@ static int ilbc_read_packet(AVFormatContext *s, pkt->pos = avio_tell(s->pb); pkt->duration = par->block_align == 38 ? 160 : 240; if ((ret = avio_read(s->pb, pkt->data, par->block_align)) != par->block_align) { - av_packet_unref(pkt); return ret < 0 ? ret : AVERROR(EIO); } @@ -128,6 +121,7 @@ AVInputFormat ff_ilbc_demuxer = { .flags = AVFMT_GENERIC_INDEX, }; +#if CONFIG_ILBC_MUXER AVOutputFormat ff_ilbc_muxer = { .name = "ilbc", .long_name = NULL_IF_CONFIG_SMALL("iLBC storage"), @@ -135,6 +129,7 @@ AVOutputFormat ff_ilbc_muxer = { .extensions = "lbc", .audio_codec = AV_CODEC_ID_ILBC, .write_header = ilbc_write_header, - .write_packet = ilbc_write_packet, + .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; +#endif diff --git a/libavformat/img2.c b/libavformat/img2.c index 16bc9d2abd8..d243d6c1253 100644 --- a/libavformat/img2.c +++ b/libavformat/img2.c @@ -40,6 +40,7 @@ const IdStrMap ff_img_tags[] = { { AV_CODEC_ID_PGMYUV, "pgmyuv" }, { AV_CODEC_ID_PBM, "pbm" }, { AV_CODEC_ID_PAM, "pam" }, + { AV_CODEC_ID_PFM, "pfm" }, { AV_CODEC_ID_ALIAS_PIX, "pix" }, { AV_CODEC_ID_DDS, "dds" }, { AV_CODEC_ID_MPEG1VIDEO, "mpg1-img" }, diff --git a/libavformat/img2.h b/libavformat/img2.h index 0e5b374a6b2..5fd8ff77fc2 100644 --- a/libavformat/img2.h +++ b/libavformat/img2.h @@ -61,6 +61,7 @@ typedef struct VideoDemuxData { int start_number_range; int frame_size; int ts_from_file; + int export_path_metadata; /**< enabled when set to 1. */ } VideoDemuxData; typedef struct IdStrMap { diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c index f8b4a655a53..ee7ceed08f4 100644 --- a/libavformat/img2dec.c +++ b/libavformat/img2dec.c @@ -374,6 +374,31 @@ int ff_img_read_header(AVFormatContext *s1) return 0; } +/** + * Add this frame's source path and basename to packet's sidedata + * as a dictionary, so it can be used by filters like 'drawtext'. + */ +static int add_filename_as_pkt_side_data(char *filename, AVPacket *pkt) { + int metadata_len, ret; + AVDictionary *d = NULL; + char *packed_metadata = NULL; + + av_dict_set(&d, "lavf.image2dec.source_path", filename, 0); + av_dict_set(&d, "lavf.image2dec.source_basename", av_basename(filename), 0); + + packed_metadata = av_packet_pack_dictionary(d, &metadata_len); + av_dict_free(&d); + if (!packed_metadata) + return AVERROR(ENOMEM); + ret = av_packet_add_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, + packed_metadata, metadata_len); + if (ret < 0) { + av_freep(&packed_metadata); + return ret; + } + return 0; +} + int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt) { VideoDemuxData *s = s1->priv_data; @@ -486,6 +511,17 @@ int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt) if (s->is_pipe) pkt->pos = avio_tell(f[0]); + /* + * export_path_metadata must be explicitly enabled via + * command line options for path metadata to be exported + * as packet side_data. + */ + if (!s->is_pipe && s->export_path_metadata == 1) { + res = add_filename_as_pkt_side_data(filename, pkt); + if (res < 0) + goto fail; + } + pkt->size = 0; for (i = 0; i < 3; i++) { if (f[i]) { @@ -504,7 +540,6 @@ int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt) } if (ret[0] <= 0 || ret[1] < 0 || ret[2] < 0) { - av_packet_unref(pkt); if (ret[0] < 0) { res = ret[0]; } else if (ret[1] < 0) { @@ -585,6 +620,7 @@ const AVOption ff_img_options[] = { { "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" }, + { "export_path_metadata", "enable metadata containing input path information", OFFSET(export_path_metadata), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, DEC }, \ COMMON_OPTIONS }; diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c index bec4bf81dde..b303d382398 100644 --- a/libavformat/img2enc.c +++ b/libavformat/img2enc.c @@ -23,6 +23,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/avassert.h" #include "libavutil/avstring.h" +#include "libavutil/dict.h" #include "libavutil/log.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" @@ -35,7 +36,6 @@ typedef struct VideoMuxData { const AVClass *class; /**< Class for private options. */ int img_number; - int is_pipe; int split_planes; /**< use independent file for each Y, U, V plane */ char path[1024]; char tmp[4][1024]; @@ -45,6 +45,7 @@ typedef struct VideoMuxData { int frame_pts; const char *muxer; int use_rename; + AVDictionary *protocol_opts; } VideoMuxData; static int write_header(AVFormatContext *s) @@ -55,12 +56,6 @@ static int write_header(AVFormatContext *s) av_strlcpy(img->path, s->url, sizeof(img->path)); - /* find format */ - if (s->oformat->flags & AVFMT_NOFILE) - img->is_pipe = 0; - else - img->is_pipe = 1; - if (st->codecpar->codec_id == AV_CODEC_ID_GIF) { img->muxer = "gif"; } else if (st->codecpar->codec_id == AV_CODEC_ID_FITS) { @@ -78,60 +73,116 @@ static int write_header(AVFormatContext *s) return 0; } +static int write_muxed_file(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) +{ + VideoMuxData *img = s->priv_data; + AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar; + AVStream *st; + AVPacket pkt2; + AVFormatContext *fmt = NULL; + int ret; + + /* URL is not used directly as we are overriding the IO context later. */ + ret = avformat_alloc_output_context2(&fmt, NULL, img->muxer, s->url); + if (ret < 0) + return ret; + st = avformat_new_stream(fmt, NULL); + if (!st) { + ret = AVERROR(ENOMEM); + goto out; + } + st->id = pkt->stream_index; + + fmt->pb = pb; + + ret = av_packet_ref(&pkt2, pkt); + if (ret < 0) + goto out; + pkt2.stream_index = 0; + + if ((ret = avcodec_parameters_copy(st->codecpar, par)) < 0 || + (ret = avformat_write_header(fmt, NULL)) < 0 || + (ret = av_interleaved_write_frame(fmt, &pkt2)) < 0 || + (ret = av_write_trailer(fmt))) {} + + av_packet_unref(&pkt2); +out: + avformat_free_context(fmt); + return ret; +} + +static int write_packet_pipe(AVFormatContext *s, AVPacket *pkt) +{ + VideoMuxData *img = s->priv_data; + if (img->muxer) { + int ret = write_muxed_file(s, s->pb, pkt); + if (ret < 0) + return ret; + } else { + avio_write(s->pb, pkt->data, pkt->size); + } + img->img_number++; + return 0; +} + static int write_packet(AVFormatContext *s, AVPacket *pkt) { VideoMuxData *img = s->priv_data; - AVIOContext *pb[4]; + AVIOContext *pb[4] = {0}; char filename[1024]; AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(par->format); - int i; + int ret, i; int nb_renames = 0; + AVDictionary *options = NULL; - if (!img->is_pipe) { - if (img->update) { - av_strlcpy(filename, img->path, sizeof(filename)); - } else if (img->use_strftime) { - time_t now0; - struct tm *tm, tmpbuf; - time(&now0); - tm = localtime_r(&now0, &tmpbuf); - if (!strftime(filename, sizeof(filename), img->path, tm)) { - av_log(s, AV_LOG_ERROR, "Could not get frame filename with strftime\n"); - return AVERROR(EINVAL); - } - } else if (img->frame_pts) { - if (av_get_frame_filename2(filename, sizeof(filename), img->path, pkt->pts, AV_FRAME_FILENAME_FLAGS_MULTIPLE) < 0) { - av_log(s, AV_LOG_ERROR, "Cannot write filename by pts of the frames."); - return AVERROR(EINVAL); - } - } else if (av_get_frame_filename2(filename, sizeof(filename), img->path, - img->img_number, - AV_FRAME_FILENAME_FLAGS_MULTIPLE) < 0 && - img->img_number > 1) { - av_log(s, AV_LOG_ERROR, - "Could not get frame filename number %d from pattern '%s'. " - "Use '-frames:v 1' for a single image, or '-update' option, or use a pattern such as %%03d within the filename.\n", - img->img_number, img->path); + if (img->update) { + av_strlcpy(filename, img->path, sizeof(filename)); + } else if (img->use_strftime) { + time_t now0; + struct tm *tm, tmpbuf; + time(&now0); + tm = localtime_r(&now0, &tmpbuf); + if (!strftime(filename, sizeof(filename), img->path, tm)) { + av_log(s, AV_LOG_ERROR, "Could not get frame filename with strftime\n"); return AVERROR(EINVAL); } - for (i = 0; i < 4; i++) { - snprintf(img->tmp[i], sizeof(img->tmp[i]), "%s.tmp", filename); - av_strlcpy(img->target[i], filename, sizeof(img->target[i])); - if (s->io_open(s, &pb[i], img->use_rename ? img->tmp[i] : filename, AVIO_FLAG_WRITE, NULL) < 0) { - av_log(s, AV_LOG_ERROR, "Could not open file : %s\n", img->use_rename ? img->tmp[i] : filename); - return AVERROR(EIO); - } - - if (!img->split_planes || i+1 >= desc->nb_components) - break; - filename[strlen(filename) - 1] = "UVAx"[i]; + } else if (img->frame_pts) { + if (av_get_frame_filename2(filename, sizeof(filename), img->path, pkt->pts, AV_FRAME_FILENAME_FLAGS_MULTIPLE) < 0) { + av_log(s, AV_LOG_ERROR, "Cannot write filename by pts of the frames."); + return AVERROR(EINVAL); } - if (img->use_rename) - nb_renames = i + 1; - } else { - pb[0] = s->pb; + } else if (av_get_frame_filename2(filename, sizeof(filename), img->path, + img->img_number, + AV_FRAME_FILENAME_FLAGS_MULTIPLE) < 0 && + img->img_number > 1) { + av_log(s, AV_LOG_ERROR, + "Could not get frame filename number %d from pattern '%s'. " + "Use '-frames:v 1' for a single image, or '-update' option, or use a pattern such as %%03d within the filename.\n", + img->img_number, img->path); + return AVERROR(EINVAL); } + for (i = 0; i < 4; i++) { + av_dict_copy(&options, img->protocol_opts, 0); + snprintf(img->tmp[i], sizeof(img->tmp[i]), "%s.tmp", filename); + av_strlcpy(img->target[i], filename, sizeof(img->target[i])); + if (s->io_open(s, &pb[i], img->use_rename ? img->tmp[i] : filename, AVIO_FLAG_WRITE, &options) < 0) { + av_log(s, AV_LOG_ERROR, "Could not open file : %s\n", img->use_rename ? img->tmp[i] : filename); + ret = AVERROR(EIO); + goto fail; + } + if (options) { + av_log(s, AV_LOG_ERROR, "Could not recognize some protocol options\n"); + ret = AVERROR(EINVAL); + goto fail; + } + + if (!img->split_planes || i+1 >= desc->nb_components) + break; + filename[strlen(filename) - 1] = "UVAx"[i]; + } + if (img->use_rename) + nb_renames = i + 1; if (img->split_planes) { int ysize = par->width * par->height; @@ -150,50 +201,29 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt) ff_format_io_close(s, &pb[3]); } } else if (img->muxer) { - int ret; - AVStream *st; - AVPacket pkt2 = {0}; - AVFormatContext *fmt = NULL; - - av_assert0(!img->split_planes); - - ret = avformat_alloc_output_context2(&fmt, NULL, img->muxer, s->url); + ret = write_muxed_file(s, pb[0], pkt); if (ret < 0) - return ret; - st = avformat_new_stream(fmt, NULL); - if (!st) { - avformat_free_context(fmt); - return AVERROR(ENOMEM); - } - st->id = pkt->stream_index; - - fmt->pb = pb[0]; - if ((ret = av_packet_ref(&pkt2, pkt)) < 0 || - (ret = avcodec_parameters_copy(st->codecpar, s->streams[0]->codecpar)) < 0 || - (ret = avformat_write_header(fmt, NULL)) < 0 || - (ret = av_interleaved_write_frame(fmt, &pkt2)) < 0 || - (ret = av_write_trailer(fmt)) < 0) { - av_packet_unref(&pkt2); - avformat_free_context(fmt); - return ret; - } - av_packet_unref(&pkt2); - avformat_free_context(fmt); + goto fail; } else { avio_write(pb[0], pkt->data, pkt->size); } avio_flush(pb[0]); - if (!img->is_pipe) { - ff_format_io_close(s, &pb[0]); - for (i = 0; i < nb_renames; i++) { - int ret = ff_rename(img->tmp[i], img->target[i], s); - if (ret < 0) - return ret; - } + ff_format_io_close(s, &pb[0]); + for (i = 0; i < nb_renames; i++) { + int ret = ff_rename(img->tmp[i], img->target[i], s); + if (ret < 0) + return ret; } img->img_number++; return 0; + +fail: + av_dict_free(&options); + for (i = 0; i < FF_ARRAY_ELEMS(pb); i++) + if (pb[i]) + ff_format_io_close(s, &pb[i]); + return ret; } static int query_codec(enum AVCodecID id, int std_compliance) @@ -215,6 +245,7 @@ static const AVOption muxoptions[] = { { "strftime", "use strftime for filename", OFFSET(use_strftime), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, ENC }, { "frame_pts", "use current frame pts for filename", OFFSET(frame_pts), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, ENC }, { "atomic_writing", "write files atomically (using temporary files and renames)", OFFSET(use_rename), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, ENC }, + { "protocol_opts", "specify protocol options for the opened files", OFFSET(protocol_opts), AV_OPT_TYPE_DICT, {0}, 0, 0, ENC }, { NULL }, }; @@ -248,7 +279,7 @@ AVOutputFormat ff_image2pipe_muxer = { .priv_data_size = sizeof(VideoMuxData), .video_codec = AV_CODEC_ID_MJPEG, .write_header = write_header, - .write_packet = write_packet, + .write_packet = write_packet_pipe, .query_codec = query_codec, .flags = AVFMT_NOTIMESTAMPS | AVFMT_NODIMENSIONS }; diff --git a/libavformat/internal.h b/libavformat/internal.h index cf8c16579cd..17a6ab07d35 100644 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@ -33,8 +33,6 @@ #define PROBE_BUF_MIN 2048 #define PROBE_BUF_MAX (1 << 20) -#define MAX_PROBE_PACKETS 2500 - #ifdef DEBUG # define hex_dump_debug(class, buf, size) av_hex_dump_log(class, AV_LOG_DEBUG, buf, size) #else @@ -154,12 +152,11 @@ struct AVStreamInternal { int reorder; /** - * bitstream filters to run on stream + * bitstream filter to run on stream * - encoding: Set by muxer using ff_stream_add_bitstream_filter * - decoding: unused */ - AVBSFContext **bsfcs; - int nb_bsfcs; + AVBSFContext *bsfc; /** * Whether or not check_bitstream should still be run on each packet @@ -191,6 +188,8 @@ struct AVStreamInternal { */ int need_context_update; + int is_intra_only; + FFFrac *priv_pts; }; @@ -232,12 +231,12 @@ char *ff_data_to_hex(char *buf, const uint8_t *src, int size, int lowercase); int ff_hex_to_data(uint8_t *data, const char *p); /** - * Add packet to AVFormatContext->packet_buffer list, determining its + * Add packet to an AVFormatContext's packet_buffer list, determining its * interleaved position using compare() function argument. - * @return 0, or < 0 on error + * @return 0 on success, < 0 on error. pkt will always be blank on return. */ int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, - int (*compare)(AVFormatContext *, AVPacket *, AVPacket *)); + int (*compare)(AVFormatContext *, const AVPacket *, const AVPacket *)); void ff_read_frame_flush(AVFormatContext *s); @@ -292,16 +291,6 @@ void ff_sdp_write_media(char *buff, int size, AVStream *st, int idx, int ff_write_chained(AVFormatContext *dst, int dst_stream, AVPacket *pkt, AVFormatContext *src, int interleave); -/** - * Get the length in bytes which is needed to store val as v. - */ -int ff_get_v_length(uint64_t val); - -/** - * Put val using a variable number of bytes. - */ -void ff_put_v(AVIOContext *bc, uint64_t val); - /** * Read a whole line of text from AVIOContext. Stop reading after reaching * either a \\n, a \\0 or EOF. The returned string is always \\0-terminated, @@ -497,19 +486,16 @@ int ff_framehash_write_header(AVFormatContext *s); int ff_read_packet(AVFormatContext *s, AVPacket *pkt); /** - * Interleave a packet per dts in an output media file. + * Interleave an AVPacket per dts so it can be muxed. * - * Packets with pkt->destruct == av_destruct_packet will be freed inside this - * function, so they cannot be used after it. Note that calling av_packet_unref() - * on them is still safe. - * - * @param s media file handle + * @param s an AVFormatContext for output. pkt resp. out will be added to + * resp. taken from its packet buffer. * @param out the interleaved packet will be output here - * @param pkt the input packet + * @param pkt the input packet; will be blank on return if not NULL * @param flush 1 if no further packets are available as input and all * remaining packets should be output - * @return 1 if a packet was output, 0 if no packet could be output, - * < 0 if an error occurred + * @return 1 if a packet was output, 0 if no packet could be output + * (in which case out may be uninitialized), < 0 if an error occurred */ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush); @@ -526,6 +512,8 @@ unsigned int ff_codec_get_tag(const AVCodecTag *tags, enum AVCodecID id); enum AVCodecID ff_codec_get_id(const AVCodecTag *tags, unsigned int tag); +int ff_is_intra_only(enum AVCodecID id); + /** * Select a PCM codec based on the given parameters. * @@ -582,25 +570,13 @@ int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *a int ff_stream_encode_params_copy(AVStream *dst, const AVStream *src); /** - * Wrap errno on rename() error. + * Wrap avpriv_io_move and log if error happens. * - * @param oldpath source path - * @param newpath destination path + * @param url_src source path + * @param url_dst destination path * @return 0 or AVERROR on failure */ -static inline int ff_rename(const char *oldpath, const char *newpath, void *logctx) -{ - int ret = 0; - if (rename(oldpath, newpath) == -1) { - ret = AVERROR(errno); - if (logctx) { - char err[AV_ERROR_MAX_STRING_SIZE] = {0}; - av_make_error_string(err, AV_ERROR_MAX_STRING_SIZE, ret); - av_log(logctx, AV_LOG_ERROR, "failed to rename file %s to %s: %s\n", oldpath, newpath, err); - } - } - return ret; -} +int ff_rename(const char *url_src, const char *url_dst, void *logctx); /** * Allocate extradata with additional AV_INPUT_BUFFER_PADDING_SIZE at end @@ -763,7 +739,8 @@ void ff_format_set_url(AVFormatContext *s, char *url); * * @param head List head element * @param tail List tail element - * @param pkt The packet being appended + * @param pkt The packet being appended. The data described in it will + * be made reference counted if it isn't already. * @param flags Any combination of FF_PACKETLIST_FLAG_* flags * @return 0 on success, negative AVERROR value on failure. On failure, the list is unchanged @@ -773,13 +750,16 @@ int ff_packet_list_put(AVPacketList **head, AVPacketList **tail, /** * Remove the oldest AVPacket in the list and return it. + * The behaviour is undefined if the packet list is empty. * * @note The pkt will be overwritten completely. The caller owns the * packet and must unref it by itself. * * @param head List head element * @param tail List tail element - * @param pkt Pointer to an initialized AVPacket struct + * @param pkt Pointer to an AVPacket struct + * @return 0 on success. Success is guaranteed + * if the packet list is not empty. */ int ff_packet_list_get(AVPacketList **head, AVPacketList **tail, AVPacket *pkt); diff --git a/libavformat/isom.c b/libavformat/isom.c index c4880878c1b..44c7b13038c 100644 --- a/libavformat/isom.c +++ b/libavformat/isom.c @@ -158,6 +158,7 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_SGIRLE, MKTAG('r', 'l', 'e', '1') }, /* SGI RLE 8-bit */ { AV_CODEC_ID_MSRLE, MKTAG('W', 'R', 'L', 'E') }, { AV_CODEC_ID_QDRAW, MKTAG('q', 'd', 'r', 'w') }, /* QuickDraw */ + { AV_CODEC_ID_CDTOONS, MKTAG('Q', 'k', 'B', 'k') }, /* CDToons */ { AV_CODEC_ID_RAWVIDEO, MKTAG('W', 'R', 'A', 'W') }, @@ -312,6 +313,8 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_PIXLET, MKTAG('p', 'x', 'l', 't') }, + { AV_CODEC_ID_NOTCHLC, MKTAG('n', 'c', 'l', 'c') }, + { AV_CODEC_ID_NONE, 0 }, }; @@ -369,7 +372,9 @@ const AVCodecTag ff_codec_movaudio_tags[] = { { AV_CODEC_ID_EVRC, MKTAG('s', 'e', 'v', 'c') }, /* 3GPP2 */ { AV_CODEC_ID_SMV, MKTAG('s', 's', 'm', 'v') }, /* 3GPP2 */ { AV_CODEC_ID_FLAC, MKTAG('f', 'L', 'a', 'C') }, /* nonstandard */ + { AV_CODEC_ID_TRUEHD, MKTAG('m', 'l', 'p', 'a') }, /* mp4ra.org */ { AV_CODEC_ID_OPUS, MKTAG('O', 'p', 'u', 's') }, /* mp4ra.org */ + { AV_CODEC_ID_MPEGH_3D_AUDIO, MKTAG('m', 'h', 'm', '1') }, /* MPEG-H 3D Audio bitstream */ { AV_CODEC_ID_NONE, 0 }, }; @@ -546,8 +551,8 @@ FF_ENABLE_DEPRECATION_WARNINGS return ret; if (st->codecpar->codec_id == AV_CODEC_ID_AAC) { MPEG4AudioConfig cfg = {0}; - ret = avpriv_mpeg4audio_get_config(&cfg, st->codecpar->extradata, - st->codecpar->extradata_size * 8, 1); + ret = avpriv_mpeg4audio_get_config2(&cfg, st->codecpar->extradata, + st->codecpar->extradata_size, 1, fc); if (ret < 0) return ret; st->codecpar->channels = cfg.channels; diff --git a/libavformat/isom.h b/libavformat/isom.h index 69452cae8e5..41a9c64c119 100644 --- a/libavformat/isom.h +++ b/libavformat/isom.h @@ -129,6 +129,7 @@ typedef struct MOVFragmentStreamInfo { int64_t sidx_pts; int64_t first_tfra_pts; int64_t tfdt_dts; + int64_t next_trun_dts; int index_entry; MOVEncryptionIndex *encryption_index; } MOVFragmentStreamInfo; @@ -163,6 +164,8 @@ typedef struct MOVStreamContext { int64_t *chunk_offsets; unsigned int stts_count; MOVStts *stts_data; + unsigned int sdtp_count; + uint8_t *sdtp_data; unsigned int ctts_count; unsigned int ctts_allocated_size; MOVStts *ctts_data; diff --git a/libavformat/iv8.c b/libavformat/iv8.c index 449a422347d..e25f24eeb90 100644 --- a/libavformat/iv8.c +++ b/libavformat/iv8.c @@ -92,7 +92,6 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) ret = av_append_packet(s->pb, pkt, size); if (ret < 0) { av_log(s, AV_LOG_ERROR, "failed to grow packet\n"); - av_packet_unref(pkt); return ret; } } diff --git a/libavformat/ivfdec.c b/libavformat/ivfdec.c index 40ae464b769..4a802573e7e 100644 --- a/libavformat/ivfdec.c +++ b/libavformat/ivfdec.c @@ -53,7 +53,8 @@ static int read_header(AVFormatContext *s) st->codecpar->height = avio_rl16(s->pb); time_base.den = avio_rl32(s->pb); time_base.num = avio_rl32(s->pb); - st->duration = avio_rl64(s->pb); + st->duration = avio_rl32(s->pb); + avio_skip(s->pb, 4); // unused st->need_parsing = AVSTREAM_PARSE_HEADERS; diff --git a/libavformat/ivfenc.c b/libavformat/ivfenc.c index adf72117e93..0951f56c92a 100644 --- a/libavformat/ivfenc.c +++ b/libavformat/ivfenc.c @@ -26,10 +26,9 @@ typedef struct IVFEncContext { uint64_t last_pts, sum_delta_pts; } IVFEncContext; -static int ivf_write_header(AVFormatContext *s) +static int ivf_init(AVFormatContext *s) { AVCodecParameters *par; - AVIOContext *pb = s->pb; if (s->nb_streams != 1) { av_log(s, AV_LOG_ERROR, "Format supports only exactly one video stream\n"); @@ -43,6 +42,25 @@ static int ivf_write_header(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "Currently only VP8, VP9 and AV1 are supported!\n"); return AVERROR(EINVAL); } + + if (par->codec_id == AV_CODEC_ID_VP9) { + int ret = ff_stream_add_bitstream_filter(s->streams[0], "vp9_superframe", NULL); + if (ret < 0) + return ret; + } else if (par->codec_id == AV_CODEC_ID_AV1) { + int ret = ff_stream_add_bitstream_filter(s->streams[0], "av1_metadata", "td=insert"); + if (ret < 0) + return ret; + } + + return 0; +} + +static int ivf_write_header(AVFormatContext *s) +{ + AVCodecParameters *par = s->streams[0]->codecpar; + AVIOContext *pb = s->pb; + avio_write(pb, "DKIF", 4); avio_wl16(pb, 0); // version avio_wl16(pb, 32); // header length @@ -53,7 +71,7 @@ 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); + avio_wl64(pb, 0xFFFFFFFFFFFFFFFFULL); // length is overwritten at the end of muxing return 0; } @@ -80,29 +98,18 @@ static int ivf_write_trailer(AVFormatContext *s) IVFEncContext *ctx = s->priv_data; if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && ctx->frame_cnt > 1) { - size_t end = avio_tell(pb); + int64_t end = avio_tell(pb); avio_seek(pb, 24, SEEK_SET); - avio_wl64(pb, ctx->frame_cnt * ctx->sum_delta_pts / (ctx->frame_cnt - 1)); + // overwrite the "length" field (duration) + avio_wl32(pb, ctx->frame_cnt * ctx->sum_delta_pts / (ctx->frame_cnt - 1)); + avio_wl32(pb, 0); // zero out unused bytes avio_seek(pb, end, SEEK_SET); } return 0; } -static int ivf_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt) -{ - int ret = 1; - AVStream *st = s->streams[pkt->stream_index]; - - if (st->codecpar->codec_id == AV_CODEC_ID_VP9) - ret = ff_stream_add_bitstream_filter(st, "vp9_superframe", NULL); - else if (st->codecpar->codec_id == AV_CODEC_ID_AV1) - ret = ff_stream_add_bitstream_filter(st, "av1_metadata", "td=insert"); - - return ret; -} - static const AVCodecTag codec_ivf_tags[] = { { AV_CODEC_ID_VP8, MKTAG('V', 'P', '8', '0') }, { AV_CODEC_ID_VP9, MKTAG('V', 'P', '9', '0') }, @@ -117,9 +124,9 @@ AVOutputFormat ff_ivf_muxer = { .extensions = "ivf", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_VP8, + .init = ivf_init, .write_header = ivf_write_header, .write_packet = ivf_write_packet, .write_trailer = ivf_write_trailer, - .check_bitstream = ivf_check_bitstream, .codec_tag = (const AVCodecTag* const []){ codec_ivf_tags, 0 }, }; diff --git a/libavformat/jacosubdec.c b/libavformat/jacosubdec.c index 121c86d6596..3414eb39386 100644 --- a/libavformat/jacosubdec.c +++ b/libavformat/jacosubdec.c @@ -188,8 +188,10 @@ static int jacosub_read_header(AVFormatContext *s) AVPacket *sub; sub = ff_subtitles_queue_insert(&jacosub->q, line, len, merge_line); - if (!sub) - return AVERROR(ENOMEM); + if (!sub) { + ret = AVERROR(ENOMEM); + goto fail; + } sub->pos = pos; merge_line = len > 1 && !strcmp(&line[len - 2], "\\\n"); continue; diff --git a/libavformat/jacosubenc.c b/libavformat/jacosubenc.c index 0954f5f0585..77575c6b3c0 100644 --- a/libavformat/jacosubenc.c +++ b/libavformat/jacosubenc.c @@ -25,7 +25,6 @@ static int jacosub_write_header(AVFormatContext *s) if (par->extradata_size) { avio_write(s->pb, par->extradata, par->extradata_size - 1); - avio_flush(s->pb); } return 0; } diff --git a/libavformat/jvdec.c b/libavformat/jvdec.c index 18c81f0de7a..551f8069e6b 100644 --- a/libavformat/jvdec.c +++ b/libavformat/jvdec.c @@ -113,9 +113,10 @@ static int read_header(AVFormatContext *s) return AVERROR(ENOMEM); jv->frames = av_malloc(ast->nb_index_entries * sizeof(JVFrame)); - if (!jv->frames) + if (!jv->frames) { + av_freep(&ast->index_entries); return AVERROR(ENOMEM); - + } offset = 0x68 + ast->nb_index_entries * 16; for (i = 0; i < ast->nb_index_entries; i++) { AVIndexEntry *e = ast->index_entries + i; @@ -137,6 +138,8 @@ static int read_header(AVFormatContext *s) - jvf->palette_size < 0) { if (s->error_recognition & AV_EF_EXPLODE) { read_close(s); + av_freep(&jv->frames); + av_freep(&ast->index_entries); return AVERROR_INVALIDDATA; } jvf->audio_size = @@ -165,6 +168,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) JVDemuxContext *jv = s->priv_data; AVIOContext *pb = s->pb; AVStream *ast = s->streams[0]; + int ret; while (!avio_feof(s->pb) && jv->pts < ast->nb_index_entries) { const AVIndexEntry *e = ast->index_entries + jv->pts; @@ -174,8 +178,8 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) case JV_AUDIO: jv->state++; if (jvf->audio_size) { - if (av_get_packet(s->pb, pkt, jvf->audio_size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_get_packet(s->pb, pkt, jvf->audio_size)) < 0) + return ret; pkt->stream_index = 0; pkt->pts = e->timestamp; pkt->flags |= AV_PKT_FLAG_KEY; @@ -184,10 +188,9 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) case JV_VIDEO: jv->state++; if (jvf->video_size || jvf->palette_size) { - int ret; int size = jvf->video_size + jvf->palette_size; - if (av_new_packet(pkt, size + JV_PREAMBLE_SIZE)) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, size + JV_PREAMBLE_SIZE)) < 0) + return ret; AV_WL32(pkt->data, jvf->video_size); pkt->data[4] = jvf->video_type; diff --git a/libavformat/kvag.c b/libavformat/kvag.c new file mode 100644 index 00000000000..0a11fc05562 --- /dev/null +++ b/libavformat/kvag.c @@ -0,0 +1,197 @@ +/* + * Simon & Schuster Interactive VAG (de)muxer + * + * Copyright (C) 2020 Zane van Iperen (zane@zanevaniperen.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 + */ +#include "avformat.h" +#include "internal.h" +#include "rawenc.h" +#include "libavutil/intreadwrite.h" + +#define KVAG_TAG MKTAG('K', 'V', 'A', 'G') +#define KVAG_HEADER_SIZE 14 +#define KVAG_MAX_READ_SIZE 4096 + +typedef struct KVAGHeader { + uint32_t magic; + uint32_t data_size; + uint32_t sample_rate; + uint16_t stereo; +} KVAGHeader; + +#if CONFIG_KVAG_DEMUXER +static int kvag_probe(const AVProbeData *p) +{ + if (AV_RL32(p->buf) != KVAG_TAG) + return 0; + + return AVPROBE_SCORE_EXTENSION + 1; +} + +static int kvag_read_header(AVFormatContext *s) +{ + int ret; + AVStream *st; + KVAGHeader hdr; + AVCodecParameters *par; + uint8_t buf[KVAG_HEADER_SIZE]; + + if (!(st = avformat_new_stream(s, NULL))) + return AVERROR(ENOMEM); + + if ((ret = avio_read(s->pb, buf, KVAG_HEADER_SIZE)) < 0) + return ret; + else if (ret != KVAG_HEADER_SIZE) + return AVERROR(EIO); + + hdr.magic = AV_RL32(buf + 0); + hdr.data_size = AV_RL32(buf + 4); + hdr.sample_rate = AV_RL32(buf + 8); + hdr.stereo = AV_RL16(buf + 12); + + par = st->codecpar; + par->codec_type = AVMEDIA_TYPE_AUDIO; + par->codec_id = AV_CODEC_ID_ADPCM_IMA_SSI; + par->format = AV_SAMPLE_FMT_S16; + + if (hdr.stereo) { + par->channel_layout = AV_CH_LAYOUT_STEREO; + par->channels = 2; + } else { + par->channel_layout = AV_CH_LAYOUT_MONO; + par->channels = 1; + } + + par->sample_rate = hdr.sample_rate; + par->bits_per_coded_sample = 4; + par->bits_per_raw_sample = 16; + par->block_align = 1; + par->bit_rate = par->channels * + par->sample_rate * + par->bits_per_coded_sample; + + avpriv_set_pts_info(st, 64, 1, par->sample_rate); + st->start_time = 0; + st->duration = hdr.data_size * + (8 / par->bits_per_coded_sample) / + par->channels; + + return 0; +} + +static int kvag_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret; + AVCodecParameters *par = s->streams[0]->codecpar; + + if ((ret = av_get_packet(s->pb, pkt, KVAG_MAX_READ_SIZE)) < 0) + return ret; + + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; + pkt->stream_index = 0; + pkt->duration = ret * (8 / par->bits_per_coded_sample) / par->channels; + + return 0; +} + +AVInputFormat ff_kvag_demuxer = { + .name = "kvag", + .long_name = NULL_IF_CONFIG_SMALL("Simon & Schuster Interactive VAG"), + .read_probe = kvag_probe, + .read_header = kvag_read_header, + .read_packet = kvag_read_packet +}; +#endif + +#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); + } + + if (par->channels > 2) { + av_log(s, AV_LOG_ERROR, "KVAG files only support up to 2 channels\n"); + return AVERROR(EINVAL); + } + + if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) { + av_log(s, AV_LOG_WARNING, "Stream not seekable, unable to write output file\n"); + return AVERROR(EINVAL); + } + + return 0; +} + +static int kvag_write_header(AVFormatContext *s) +{ + uint8_t buf[KVAG_HEADER_SIZE]; + AVCodecParameters *par = s->streams[0]->codecpar; + + AV_WL32(buf + 0, KVAG_TAG); + AV_WL32(buf + 4, 0); /* Data size, we fix this up later. */ + AV_WL32(buf + 8, par->sample_rate); + AV_WL16(buf + 12, par->channels == 2); + + avio_write(s->pb, buf, sizeof(buf)); + return 0; +} + +static int kvag_write_trailer(AVFormatContext *s) +{ + int64_t file_size, data_size; + + file_size = avio_tell(s->pb); + data_size = file_size - KVAG_HEADER_SIZE; + if (data_size < UINT32_MAX) { + avio_seek(s->pb, 4, SEEK_SET); + avio_wl32(s->pb, (uint32_t)data_size); + avio_seek(s->pb, file_size, SEEK_SET); + } else { + av_log(s, AV_LOG_WARNING, + "Filesize %"PRId64" invalid for KVAG, output file will be broken\n", + file_size); + } + + return 0; +} + +AVOutputFormat ff_kvag_muxer = { + .name = "kvag", + .long_name = NULL_IF_CONFIG_SMALL("Simon & Schuster Interactive VAG"), + .extensions = "vag", + .audio_codec = AV_CODEC_ID_ADPCM_IMA_SSI, + .video_codec = AV_CODEC_ID_NONE, + .init = kvag_write_init, + .write_header = kvag_write_header, + .write_packet = ff_raw_write_packet, + .write_trailer = kvag_write_trailer +}; +#endif diff --git a/libavformat/latmenc.c b/libavformat/latmenc.c index 8eb219f8aee..5ae677f5dad 100644 --- a/libavformat/latmenc.c +++ b/libavformat/latmenc.c @@ -53,27 +53,28 @@ static const AVClass latm_muxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -static int latm_decode_extradata(LATMContext *ctx, uint8_t *buf, int size) +static int latm_decode_extradata(AVFormatContext *s, uint8_t *buf, int size) { + LATMContext *ctx = s->priv_data; MPEG4AudioConfig m4ac; if (size > MAX_EXTRADATA_SIZE) { - av_log(ctx, AV_LOG_ERROR, "Extradata is larger than currently supported.\n"); + av_log(s, AV_LOG_ERROR, "Extradata is larger than currently supported.\n"); return AVERROR_INVALIDDATA; } - ctx->off = avpriv_mpeg4audio_get_config(&m4ac, buf, size * 8, 1); + ctx->off = avpriv_mpeg4audio_get_config2(&m4ac, buf, size, 1, s); if (ctx->off < 0) return ctx->off; if (ctx->object_type == AOT_ALS && (ctx->off & 7)) { // as long as avpriv_mpeg4audio_get_config works correctly this is impossible - av_log(ctx, AV_LOG_ERROR, "BUG: ALS offset is not byte-aligned\n"); + av_log(s, AV_LOG_ERROR, "BUG: ALS offset is not byte-aligned\n"); return AVERROR_INVALIDDATA; } /* FIXME: are any formats not allowed in LATM? */ if (m4ac.object_type > AOT_SBR && m4ac.object_type != AOT_ALS) { - av_log(ctx, AV_LOG_ERROR, "Muxing MPEG-4 AOT %d in LATM is not supported\n", m4ac.object_type); + av_log(s, AV_LOG_ERROR, "Muxing MPEG-4 AOT %d in LATM is not supported\n", m4ac.object_type); return AVERROR_INVALIDDATA; } ctx->channel_conf = m4ac.chan_config; @@ -84,18 +85,17 @@ static int latm_decode_extradata(LATMContext *ctx, uint8_t *buf, int size) static int latm_write_header(AVFormatContext *s) { - LATMContext *ctx = s->priv_data; AVCodecParameters *par = s->streams[0]->codecpar; if (par->codec_id == AV_CODEC_ID_AAC_LATM) return 0; if (par->codec_id != AV_CODEC_ID_AAC && par->codec_id != AV_CODEC_ID_MP4ALS) { - av_log(ctx, AV_LOG_ERROR, "Only AAC, LATM and ALS are supported\n"); + av_log(s, AV_LOG_ERROR, "Only AAC, LATM and ALS are supported\n"); return AVERROR(EINVAL); } if (par->extradata_size > 0 && - latm_decode_extradata(ctx, par->extradata, par->extradata_size) < 0) + latm_decode_extradata(s, par->extradata, par->extradata_size) < 0) return AVERROR_INVALIDDATA; return 0; @@ -170,13 +170,14 @@ static int latm_write_packet(AVFormatContext *s, AVPacket *pkt) side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_data_size); if (side_data_size) { - if (latm_decode_extradata(ctx, side_data, side_data_size) < 0) + if (latm_decode_extradata(s, side_data, side_data_size) < 0) return AVERROR_INVALIDDATA; ret = ff_alloc_extradata(par, side_data_size); if (ret < 0) return ret; memcpy(par->extradata, side_data, side_data_size); - } + } else + return AVERROR_INVALIDDATA; } } diff --git a/libavformat/libamqp.c b/libavformat/libamqp.c new file mode 100644 index 00000000000..aaf0e511522 --- /dev/null +++ b/libavformat/libamqp.c @@ -0,0 +1,298 @@ +/* + * Advanced Message Queuing Protocol (AMQP) 0-9-1 + * Copyright (c) 2020 Andriy Gelman + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 "avformat.h" +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "libavutil/time.h" +#include "network.h" +#include "url.h" +#include "urldecode.h" + +typedef struct AMQPContext { + const AVClass *class; + amqp_connection_state_t conn; + amqp_socket_t *socket; + const char *exchange; + const char *routing_key; + int pkt_size; + int64_t connection_timeout; + int pkt_size_overflow; +} AMQPContext; + +#define STR_LEN 1024 +#define DEFAULT_CHANNEL 1 + +#define OFFSET(x) offsetof(AMQPContext, x) +#define D AV_OPT_FLAG_DECODING_PARAM +#define E AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + { "pkt_size", "Maximum send/read packet size", OFFSET(pkt_size), AV_OPT_TYPE_INT, { .i64 = 131072 }, 4096, INT_MAX, .flags = D | E }, + { "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}, + { NULL } +}; + +static int amqp_proto_open(URLContext *h, const char *uri, int flags) +{ + int ret, server_msg; + char hostname[STR_LEN], credentials[STR_LEN]; + int port; + const char *user, *password = NULL; + const char *user_decoded, *password_decoded; + char *p; + amqp_rpc_reply_t broker_reply; + struct timeval tval = { 0 }; + + AMQPContext *s = h->priv_data; + + h->is_streamed = 1; + h->max_packet_size = s->pkt_size; + + av_url_split(NULL, 0, credentials, sizeof(credentials), + hostname, sizeof(hostname), &port, NULL, 0, uri); + + if (port < 0) + port = 5672; + + if (hostname[0] == '\0' || port <= 0 || port > 65535 ) { + av_log(h, AV_LOG_ERROR, "Invalid hostname/port\n"); + return AVERROR(EINVAL); + } + + p = strchr(credentials, ':'); + if (p) { + *p = '\0'; + password = p + 1; + } + + if (!password || *password == '\0') + password = "guest"; + + password_decoded = ff_urldecode(password, 0); + if (!password_decoded) + return AVERROR(ENOMEM); + + user = credentials; + if (*user == '\0') + user = "guest"; + + user_decoded = ff_urldecode(user, 0); + if (!user_decoded) { + av_freep(&password_decoded); + return AVERROR(ENOMEM); + } + + s->conn = amqp_new_connection(); + if (!s->conn) { + av_freep(&user_decoded); + av_freep(&password_decoded); + av_log(h, AV_LOG_ERROR, "Error creating connection\n"); + return AVERROR_EXTERNAL; + } + + s->socket = amqp_tcp_socket_new(s->conn); + if (!s->socket) { + av_log(h, AV_LOG_ERROR, "Error creating socket\n"); + goto destroy_connection; + } + + if (s->connection_timeout < 0) + s->connection_timeout = (h->rw_timeout > 0 ? h->rw_timeout : 5000000); + + tval.tv_sec = s->connection_timeout / 1000000; + tval.tv_usec = s->connection_timeout % 1000000; + ret = amqp_socket_open_noblock(s->socket, hostname, port, &tval); + + if (ret) { + av_log(h, AV_LOG_ERROR, "Error connecting to server: %s\n", + amqp_error_string2(ret)); + goto destroy_connection; + } + + broker_reply = amqp_login(s->conn, "/", 0, s->pkt_size, 0, + AMQP_SASL_METHOD_PLAIN, user_decoded, password_decoded); + + if (broker_reply.reply_type != AMQP_RESPONSE_NORMAL) { + av_log(h, AV_LOG_ERROR, "Error login\n"); + server_msg = AMQP_ACCESS_REFUSED; + goto close_connection; + } + + amqp_channel_open(s->conn, DEFAULT_CHANNEL); + broker_reply = amqp_get_rpc_reply(s->conn); + + if (broker_reply.reply_type != AMQP_RESPONSE_NORMAL) { + av_log(h, AV_LOG_ERROR, "Error set channel\n"); + server_msg = AMQP_CHANNEL_ERROR; + goto close_connection; + } + + if (h->flags & AVIO_FLAG_READ) { + amqp_bytes_t queuename; + char queuename_buff[STR_LEN]; + amqp_queue_declare_ok_t *r; + + r = amqp_queue_declare(s->conn, DEFAULT_CHANNEL, amqp_empty_bytes, + 0, 0, 0, 1, amqp_empty_table); + broker_reply = amqp_get_rpc_reply(s->conn); + if (!r || broker_reply.reply_type != AMQP_RESPONSE_NORMAL) { + av_log(h, AV_LOG_ERROR, "Error declare queue\n"); + server_msg = AMQP_RESOURCE_ERROR; + goto close_channel; + } + + /* store queuename */ + queuename.bytes = queuename_buff; + queuename.len = FFMIN(r->queue.len, STR_LEN); + memcpy(queuename.bytes, r->queue.bytes, queuename.len); + + amqp_queue_bind(s->conn, DEFAULT_CHANNEL, queuename, + amqp_cstring_bytes(s->exchange), + amqp_cstring_bytes(s->routing_key), amqp_empty_table); + + broker_reply = amqp_get_rpc_reply(s->conn); + if (broker_reply.reply_type != AMQP_RESPONSE_NORMAL) { + av_log(h, AV_LOG_ERROR, "Queue bind error\n"); + server_msg = AMQP_INTERNAL_ERROR; + goto close_channel; + } + + amqp_basic_consume(s->conn, DEFAULT_CHANNEL, queuename, amqp_empty_bytes, + 0, 1, 0, amqp_empty_table); + + broker_reply = amqp_get_rpc_reply(s->conn); + if (broker_reply.reply_type != AMQP_RESPONSE_NORMAL) { + av_log(h, AV_LOG_ERROR, "Set consume error\n"); + server_msg = AMQP_INTERNAL_ERROR; + goto close_channel; + } + } + + av_freep(&user_decoded); + av_freep(&password_decoded); + return 0; + +close_channel: + amqp_channel_close(s->conn, DEFAULT_CHANNEL, server_msg); +close_connection: + amqp_connection_close(s->conn, server_msg); +destroy_connection: + amqp_destroy_connection(s->conn); + + av_freep(&user_decoded); + av_freep(&password_decoded); + return AVERROR_EXTERNAL; +} + +static int amqp_proto_write(URLContext *h, const unsigned char *buf, int size) +{ + int ret; + AMQPContext *s = h->priv_data; + int fd = amqp_socket_get_sockfd(s->socket); + + amqp_bytes_t message = { size, (void *)buf }; + amqp_basic_properties_t props; + + ret = ff_network_wait_fd_timeout(fd, 1, h->rw_timeout, &h->interrupt_callback); + if (ret) + return ret; + + props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG; + props.content_type = amqp_cstring_bytes("octet/stream"); + props.delivery_mode = 2; /* persistent delivery mode */ + + ret = amqp_basic_publish(s->conn, DEFAULT_CHANNEL, amqp_cstring_bytes(s->exchange), + amqp_cstring_bytes(s->routing_key), 0, 0, + &props, message); + + if (ret) { + av_log(h, AV_LOG_ERROR, "Error publish: %s\n", amqp_error_string2(ret)); + return AVERROR_EXTERNAL; + } + + return size; +} + +static int amqp_proto_read(URLContext *h, unsigned char *buf, int size) +{ + AMQPContext *s = h->priv_data; + int fd = amqp_socket_get_sockfd(s->socket); + int ret; + + amqp_rpc_reply_t broker_reply; + amqp_envelope_t envelope; + + ret = ff_network_wait_fd_timeout(fd, 0, h->rw_timeout, &h->interrupt_callback); + if (ret) + return ret; + + amqp_maybe_release_buffers(s->conn); + broker_reply = amqp_consume_message(s->conn, &envelope, NULL, 0); + + if (broker_reply.reply_type != AMQP_RESPONSE_NORMAL) + return AVERROR_EXTERNAL; + + if (envelope.message.body.len > size) { + s->pkt_size_overflow = FFMAX(s->pkt_size_overflow, envelope.message.body.len); + av_log(h, AV_LOG_WARNING, "Message exceeds space in the buffer. " + "Message will be truncated. Setting -pkt_size %d " + "may resolve this issue.\n", s->pkt_size_overflow); + } + size = FFMIN(size, envelope.message.body.len); + + memcpy(buf, envelope.message.body.bytes, size); + amqp_destroy_envelope(&envelope); + + return size; +} + +static int amqp_proto_close(URLContext *h) +{ + AMQPContext *s = h->priv_data; + amqp_channel_close(s->conn, DEFAULT_CHANNEL, AMQP_REPLY_SUCCESS); + amqp_connection_close(s->conn, AMQP_REPLY_SUCCESS); + amqp_destroy_connection(s->conn); + + return 0; +} + +static const AVClass amqp_context_class = { + .class_name = "amqp", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const URLProtocol ff_libamqp_protocol = { + .name = "amqp", + .url_close = amqp_proto_close, + .url_open = amqp_proto_open, + .url_read = amqp_proto_read, + .url_write = amqp_proto_write, + .priv_data_size = sizeof(AMQPContext), + .priv_data_class = &amqp_context_class, + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; diff --git a/libavformat/libmodplug.c b/libavformat/libmodplug.c index 314bdae3e78..6e567f5f988 100644 --- a/libavformat/libmodplug.c +++ b/libavformat/libmodplug.c @@ -216,9 +216,10 @@ static int modplug_read_header(AVFormatContext *s) ModPlug_SetSettings(&settings); modplug->f = ModPlug_Load(modplug->buf, sz); - if (!modplug->f) + if (!modplug->f) { + av_freep(&modplug->buf); return AVERROR_INVALIDDATA; - + } st = avformat_new_stream(s, NULL); if (!st) return AVERROR(ENOMEM); @@ -269,6 +270,7 @@ static void write_text(uint8_t *dst, const char *s, int linesize, int x, int y) static int modplug_read_packet(AVFormatContext *s, AVPacket *pkt) { ModPlugContext *modplug = s->priv_data; + int ret; if (modplug->video_stream) { modplug->video_switch ^= 1; // one video packet for one audio packet @@ -284,8 +286,8 @@ static int modplug_read_packet(AVFormatContext *s, AVPacket *pkt) var_values[VAR_PATTERN] = ModPlug_GetCurrentPattern(modplug->f); var_values[VAR_ROW ] = ModPlug_GetCurrentRow (modplug->f); - if (av_new_packet(pkt, modplug->fsize) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, modplug->fsize)) < 0) + return ret; pkt->stream_index = 1; memset(pkt->data, 0, modplug->fsize); @@ -317,15 +319,14 @@ static int modplug_read_packet(AVFormatContext *s, AVPacket *pkt) } } - if (av_new_packet(pkt, AUDIO_PKT_SIZE) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, AUDIO_PKT_SIZE)) < 0) + return ret; if (modplug->video_stream) pkt->pts = pkt->dts = modplug->packet_count++ * modplug->ts_per_packet; pkt->size = ModPlug_Read(modplug->f, pkt->data, AUDIO_PKT_SIZE); if (pkt->size <= 0) { - av_packet_unref(pkt); return pkt->size == 0 ? AVERROR_EOF : AVERROR(EIO); } return 0; diff --git a/libavformat/libsrt.c b/libavformat/libsrt.c index b5568089fa0..4de575b37cd 100644 --- a/libavformat/libsrt.c +++ b/libavformat/libsrt.c @@ -62,6 +62,11 @@ typedef struct SRTContext { int64_t maxbw; int pbkeylen; char *passphrase; +#if SRT_VERSION_VALUE >= 0x010302 + int enforced_encryption; + int kmrefreshrate; + int kmpreannounce; +#endif int mss; int ffs; int ipttl; @@ -84,14 +89,15 @@ typedef struct SRTContext { char *smoother; int messageapi; SRT_TRANSTYPE transtype; + int linger; } SRTContext; #define D AV_OPT_FLAG_DECODING_PARAM #define E AV_OPT_FLAG_ENCODING_PARAM #define OFFSET(x) offsetof(SRTContext, x) static const AVOption libsrt_options[] = { - { "rw_timeout", "Timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, - { "listen_timeout", "Connection awaiting timeout", OFFSET(listen_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, + { "timeout", "Timeout of socket I/O operations (in microseconds)", OFFSET(rw_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, + { "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" }, @@ -101,19 +107,24 @@ static const AVOption libsrt_options[] = { { "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 }, +#if SRT_VERSION_VALUE >= 0x010302 + { "enforced_encryption", "Enforces that both connection parties have the same passphrase set", OFFSET(enforced_encryption), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, .flags = D|E }, + { "kmrefreshrate", "The number of packets to be transmitted after which the encryption key is switched to a new key", OFFSET(kmrefreshrate), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, + { "kmpreannounce", "The interval between when a new encryption key is sent and when switchover occurs", OFFSET(kmpreannounce), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, +#endif { "mss", "The Maximum Segment Size", OFFSET(mss), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1500, .flags = D|E }, { "ffs", "Flight flag size (window size) (in bytes)", OFFSET(ffs), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, { "ipttl", "IP Time To Live", OFFSET(ipttl), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 255, .flags = D|E }, { "iptos", "IP Type of Service", OFFSET(iptos), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 255, .flags = D|E }, { "inputbw", "Estimated input stream rate", OFFSET(inputbw), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, { "oheadbw", "MaxBW ceiling based on % over input stream rate", OFFSET(oheadbw), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 100, .flags = D|E }, - { "latency", "receiver delay to absorb bursts of missed packet retransmissions", OFFSET(latency), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, + { "latency", "receiver delay (in microseconds) to absorb bursts of missed packet retransmissions", OFFSET(latency), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, { "tsbpddelay", "deprecated, same effect as latency option", OFFSET(latency), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, - { "rcvlatency", "receive latency", OFFSET(rcvlatency), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, - { "peerlatency", "peer latency", OFFSET(peerlatency), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, - { "tlpktdrop", "Enable receiver pkt drop", OFFSET(tlpktdrop), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, .flags = D|E }, - { "nakreport", "Enable receiver to send periodic NAK reports", OFFSET(nakreport), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, .flags = D|E }, - { "connect_timeout", "Connect timeout. Caller default: 3000, rendezvous (x 10)", OFFSET(connect_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, + { "rcvlatency", "receive latency (in microseconds)", OFFSET(rcvlatency), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, + { "peerlatency", "peer latency (in microseconds)", OFFSET(peerlatency), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, + { "tlpktdrop", "Enable receiver 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" }, @@ -124,42 +135,47 @@ static const AVOption libsrt_options[] = { { "minversion", "The minimum SRT version that is required from the peer", OFFSET(minversion), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, { "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_INT, { .i64 = -1 }, -1, 1, .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" }, + { "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 }, { NULL } }; static int libsrt_neterrno(URLContext *h) { - int err = srt_getlasterror(NULL); - av_log(h, AV_LOG_ERROR, "%s\n", srt_getlasterror_str()); - if (err == SRT_EASYNCRCV) + int os_errno; + int err = srt_getlasterror(&os_errno); + if (err == SRT_EASYNCRCV || err == SRT_EASYNCSND) return AVERROR(EAGAIN); - return AVERROR_UNKNOWN; + av_log(h, AV_LOG_ERROR, "%s\n", srt_getlasterror_str()); + return os_errno ? AVERROR(os_errno) : AVERROR_UNKNOWN; } static int libsrt_socket_nonblock(int socket, int enable) { - int ret = srt_setsockopt(socket, 0, SRTO_SNDSYN, &enable, sizeof(enable)); + int ret, blocking = enable ? 0 : 1; + /* Setting SRTO_{SND,RCV}SYN options to 1 enable blocking mode, setting them to 0 enable non-blocking mode. */ + ret = srt_setsockopt(socket, 0, SRTO_SNDSYN, &blocking, sizeof(blocking)); if (ret < 0) return ret; - return srt_setsockopt(socket, 0, SRTO_RCVSYN, &enable, sizeof(enable)); + return srt_setsockopt(socket, 0, SRTO_RCVSYN, &blocking, sizeof(blocking)); } static int libsrt_network_wait_fd(URLContext *h, int eid, int fd, int write) { - int ret, len = 1; - int modes = write ? SRT_EPOLL_OUT : SRT_EPOLL_IN; + int ret, len = 1, errlen = 1; + int modes = SRT_EPOLL_ERR | (write ? SRT_EPOLL_OUT : SRT_EPOLL_IN); SRTSOCKET ready[1]; + SRTSOCKET error[1]; if (srt_epoll_add_usock(eid, fd, &modes) < 0) return libsrt_neterrno(h); if (write) { - ret = srt_epoll_wait(eid, 0, 0, ready, &len, POLLING_TIME, 0, 0, 0, 0); + ret = srt_epoll_wait(eid, error, &errlen, ready, &len, POLLING_TIME, 0, 0, 0, 0); } else { - ret = srt_epoll_wait(eid, ready, &len, 0, 0, POLLING_TIME, 0, 0, 0, 0); + ret = srt_epoll_wait(eid, ready, &len, error, &errlen, POLLING_TIME, 0, 0, 0, 0); } if (ret < 0) { if (srt_getlasterror(NULL) == SRT_ETIMEOUT) @@ -167,7 +183,7 @@ static int libsrt_network_wait_fd(URLContext *h, int eid, int fd, int write) else ret = libsrt_neterrno(h); } else { - ret = 0; + ret = errlen ? AVERROR(EIO) : 0; } if (srt_epoll_remove_usock(eid, fd) < 0) return libsrt_neterrno(h); @@ -196,7 +212,7 @@ static int libsrt_network_wait_fd_timeout(URLContext *h, int eid, int fd, int wr } } -static int libsrt_listen(int eid, int fd, const struct sockaddr *addr, socklen_t addrlen, URLContext *h, int timeout) +static int libsrt_listen(int eid, int fd, const struct sockaddr *addr, socklen_t addrlen, URLContext *h, int64_t timeout) { int ret; int reuse = 1; @@ -211,14 +227,9 @@ static int libsrt_listen(int eid, int fd, const struct sockaddr *addr, socklen_t if (ret) return libsrt_neterrno(h); - while ((ret = libsrt_network_wait_fd_timeout(h, eid, fd, 1, timeout, &h->interrupt_callback))) { - switch (ret) { - case AVERROR(ETIMEDOUT): - continue; - default: - return ret; - } - } + ret = libsrt_network_wait_fd_timeout(h, eid, fd, 1, timeout, &h->interrupt_callback); + if (ret < 0) + return ret; ret = srt_accept(fd, NULL, NULL); if (ret < 0) @@ -229,41 +240,23 @@ static int libsrt_listen(int eid, int fd, const struct sockaddr *addr, socklen_t return ret; } -static int libsrt_listen_connect(int eid, int fd, const struct sockaddr *addr, socklen_t addrlen, int timeout, URLContext *h, int will_try_next) +static int libsrt_listen_connect(int eid, int fd, const struct sockaddr *addr, socklen_t addrlen, int64_t timeout, URLContext *h, int will_try_next) { int ret; - if (libsrt_socket_nonblock(fd, 1) < 0) - av_log(h, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); + ret = srt_connect(fd, addr, addrlen); + if (ret < 0) + return libsrt_neterrno(h); - while ((ret = srt_connect(fd, addr, addrlen))) { - ret = libsrt_neterrno(h); - switch (ret) { - case AVERROR(EINTR): - if (ff_check_interrupt(&h->interrupt_callback)) - return AVERROR_EXIT; - continue; - case AVERROR(EINPROGRESS): - case AVERROR(EAGAIN): - ret = libsrt_network_wait_fd_timeout(h, eid, fd, 1, timeout, &h->interrupt_callback); - if (ret < 0) - return ret; - ret = srt_getlasterror(NULL); - srt_clearlasterror(); - if (ret != 0) { - char buf[128]; - ret = AVERROR(ret); - av_strerror(ret, buf, sizeof(buf)); - if (will_try_next) - av_log(h, AV_LOG_WARNING, - "Connection to %s failed (%s), trying next address\n", - h->filename, buf); - else - av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n", - h->filename, buf); - } - default: - return ret; + ret = libsrt_network_wait_fd_timeout(h, eid, fd, 1, timeout, &h->interrupt_callback); + if (ret < 0) { + if (will_try_next) { + av_log(h, AV_LOG_WARNING, + "Connection to %s failed (%s), trying next address\n", + h->filename, av_err2str(ret)); + } else { + av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n", + h->filename, av_err2str(ret)); } } return ret; @@ -319,9 +312,15 @@ static int libsrt_set_options_pre(URLContext *h, int fd) (s->maxbw >= 0 && libsrt_setsockopt(h, fd, SRTO_MAXBW, "SRTO_MAXBW", &s->maxbw, sizeof(s->maxbw)) < 0) || (s->pbkeylen >= 0 && libsrt_setsockopt(h, fd, SRTO_PBKEYLEN, "SRTO_PBKEYLEN", &s->pbkeylen, sizeof(s->pbkeylen)) < 0) || (s->passphrase && libsrt_setsockopt(h, fd, SRTO_PASSPHRASE, "SRTO_PASSPHRASE", s->passphrase, strlen(s->passphrase)) < 0) || - (s->mss >= 0 && libsrt_setsockopt(h, fd, SRTO_MSS, "SRTO_MMS", &s->mss, sizeof(s->mss)) < 0) || +#if SRT_VERSION_VALUE >= 0x010302 + /* SRTO_STRICTENC == SRTO_ENFORCEDENCRYPTION (53), but for compatibility, we used SRTO_STRICTENC */ + (s->enforced_encryption >= 0 && libsrt_setsockopt(h, fd, SRTO_STRICTENC, "SRTO_STRICTENC", &s->enforced_encryption, sizeof(s->enforced_encryption)) < 0) || + (s->kmrefreshrate >= 0 && libsrt_setsockopt(h, fd, SRTO_KMREFRESHRATE, "SRTO_KMREFRESHRATE", &s->kmrefreshrate, sizeof(s->kmrefreshrate)) < 0) || + (s->kmpreannounce >= 0 && libsrt_setsockopt(h, fd, SRTO_KMPREANNOUNCE, "SRTO_KMPREANNOUNCE", &s->kmpreannounce, sizeof(s->kmpreannounce)) < 0) || +#endif + (s->mss >= 0 && libsrt_setsockopt(h, fd, SRTO_MSS, "SRTO_MSS", &s->mss, sizeof(s->mss)) < 0) || (s->ffs >= 0 && libsrt_setsockopt(h, fd, SRTO_FC, "SRTO_FC", &s->ffs, sizeof(s->ffs)) < 0) || - (s->ipttl >= 0 && libsrt_setsockopt(h, fd, SRTO_IPTTL, "SRTO_UPTTL", &s->ipttl, sizeof(s->ipttl)) < 0) || + (s->ipttl >= 0 && libsrt_setsockopt(h, fd, SRTO_IPTTL, "SRTO_IPTTL", &s->ipttl, sizeof(s->ipttl)) < 0) || (s->iptos >= 0 && libsrt_setsockopt(h, fd, SRTO_IPTOS, "SRTO_IPTOS", &s->iptos, sizeof(s->iptos)) < 0) || (s->latency >= 0 && libsrt_setsockopt(h, fd, SRTO_LATENCY, "SRTO_LATENCY", &latency, sizeof(latency)) < 0) || (s->rcvlatency >= 0 && libsrt_setsockopt(h, fd, SRTO_RCVLATENCY, "SRTO_RCVLATENCY", &rcvlatency, sizeof(rcvlatency)) < 0) || @@ -340,6 +339,14 @@ static int libsrt_set_options_pre(URLContext *h, int fd) ((h->flags & AVIO_FLAG_WRITE) && libsrt_setsockopt(h, fd, SRTO_SENDER, "SRTO_SENDER", &yes, sizeof(yes)) < 0)) { return AVERROR(EIO); } + + if (s->linger >= 0) { + struct linger lin; + lin.l_linger = s->linger; + lin.l_onoff = lin.l_linger > 0 ? 1 : 0; + if (libsrt_setsockopt(h, fd, SRTO_LINGER, "SRTO_LINGER", &lin, sizeof(lin)) < 0) + return AVERROR(EIO); + } return 0; } @@ -354,7 +361,7 @@ static int libsrt_setup(URLContext *h, const char *uri, int flags) int ret; char hostname[1024],proto[1024],path[1024]; char portstr[10]; - int open_timeout = 5000000; + int64_t open_timeout = 0; int eid; eid = srt_epoll_create(); @@ -417,9 +424,12 @@ static int libsrt_setup(URLContext *h, const char *uri, int flags) if (s->send_buffer_size > 0) { srt_setsockopt(fd, SOL_SOCKET, SRTO_UDP_SNDBUF, &s->send_buffer_size, sizeof (s->send_buffer_size)); } + if (libsrt_socket_nonblock(fd, 1) < 0) + av_log(h, AV_LOG_DEBUG, "libsrt_socket_nonblock failed\n"); + if (s->mode == SRT_MODE_LISTENER) { // multi-client - if ((ret = libsrt_listen(s->eid, fd, cur_ai->ai_addr, cur_ai->ai_addrlen, h, open_timeout / 1000)) < 0) + if ((ret = libsrt_listen(s->eid, fd, cur_ai->ai_addr, cur_ai->ai_addrlen, h, s->listen_timeout)) < 0) goto fail1; fd = ret; } else { @@ -430,7 +440,7 @@ static int libsrt_setup(URLContext *h, const char *uri, int flags) } if ((ret = libsrt_listen_connect(s->eid, fd, cur_ai->ai_addr, cur_ai->ai_addrlen, - open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) { + open_timeout, h, !!cur_ai->ai_next)) < 0) { if (ret == AVERROR_EXIT) goto fail1; else @@ -478,6 +488,7 @@ static int libsrt_open(URLContext *h, const char *uri, int flags) SRTContext *s = h->priv_data; const char * p; char buf[256]; + int ret = 0; if (srt_startup() < 0) { return AVERROR_UNKNOWN; @@ -493,8 +504,20 @@ static int libsrt_open(URLContext *h, const char *uri, int flags) s->pbkeylen = strtol(buf, NULL, 10); } if (av_find_info_tag(buf, sizeof(buf), "passphrase", p)) { + av_freep(&s->passphrase); s->passphrase = av_strndup(buf, strlen(buf)); } +#if SRT_VERSION_VALUE >= 0x010302 + if (av_find_info_tag(buf, sizeof(buf), "enforced_encryption", p)) { + s->enforced_encryption = strtol(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "kmrefreshrate", p)) { + s->kmrefreshrate = strtol(buf, NULL, 10); + } + if (av_find_info_tag(buf, sizeof(buf), "kmpreannounce", p)) { + s->kmpreannounce = strtol(buf, NULL, 10); + } +#endif if (av_find_info_tag(buf, sizeof(buf), "mss", p)) { s->mss = strtol(buf, NULL, 10); } @@ -564,10 +587,18 @@ 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); + if (!s->streamid) { + ret = AVERROR(ENOMEM); + goto err; + } } if (av_find_info_tag(buf, sizeof(buf), "smoother", p)) { av_freep(&s->smoother); s->smoother = av_strdup(buf); + if(!s->smoother) { + ret = AVERROR(ENOMEM); + goto err; + } } if (av_find_info_tag(buf, sizeof(buf), "messageapi", p)) { s->messageapi = strtol(buf, NULL, 10); @@ -578,11 +609,19 @@ static int libsrt_open(URLContext *h, const char *uri, int flags) } else if (!strcmp(buf, "file")) { s->transtype = SRTT_FILE; } else { - return AVERROR(EINVAL); + ret = AVERROR(EINVAL); + goto err; } } + if (av_find_info_tag(buf, sizeof(buf), "linger", p)) { + s->linger = strtol(buf, NULL, 10); + } } return libsrt_setup(h, uri, flags); +err: + av_freep(&s->smoother); + av_freep(&s->streamid); + return ret; } static int libsrt_read(URLContext *h, uint8_t *buf, int size) diff --git a/libavformat/libzmq.c b/libavformat/libzmq.c new file mode 100644 index 00000000000..1b0d8638dbf --- /dev/null +++ b/libavformat/libzmq.c @@ -0,0 +1,204 @@ +/* + * ZeroMQ Protocol + * Copyright (c) 2019 Andriy Gelman + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 "url.h" +#include "network.h" +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "libavutil/time.h" + +#define ZMQ_STRERROR zmq_strerror(zmq_errno()) + +typedef struct ZMQContext { + const AVClass *class; + void *context; + void *socket; + int pkt_size; + int pkt_size_overflow; /*keep track of the largest packet during overflow*/ +} ZMQContext; + +#define OFFSET(x) offsetof(ZMQContext, x) +#define D AV_OPT_FLAG_DECODING_PARAM +#define E AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + { "pkt_size", "Maximum send/read packet size", OFFSET(pkt_size), AV_OPT_TYPE_INT, { .i64 = 131072 }, -1, INT_MAX, .flags = D | E }, + { NULL } +}; + +static int zmq_proto_wait(URLContext *h, void *socket, int write) +{ + int ret; + int ev = write ? ZMQ_POLLOUT : ZMQ_POLLIN; + zmq_pollitem_t items = { .socket = socket, .fd = 0, .events = ev, .revents = 0 }; + ret = zmq_poll(&items, 1, POLLING_TIME); + if (ret == -1) { + av_log(h, AV_LOG_ERROR, "Error occured during zmq_poll(): %s\n", ZMQ_STRERROR); + return AVERROR_EXTERNAL; + } + return items.revents & ev ? 0 : AVERROR(EAGAIN); +} + +static int zmq_proto_wait_timeout(URLContext *h, void *socket, int write, int64_t timeout, AVIOInterruptCB *int_cb) +{ + int ret; + int64_t wait_start = 0; + + while (1) { + if (ff_check_interrupt(int_cb)) + return AVERROR_EXIT; + ret = zmq_proto_wait(h, socket, write); + if (ret != AVERROR(EAGAIN)) + return ret; + if (timeout > 0) { + if (!wait_start) + wait_start = av_gettime_relative(); + else if (av_gettime_relative() - wait_start > timeout) + return AVERROR(ETIMEDOUT); + } + } +} + +static int zmq_proto_open(URLContext *h, const char *uri, int flags) +{ + int ret; + ZMQContext *s = h->priv_data; + s->pkt_size_overflow = 0; + h->is_streamed = 1; + + if (s->pkt_size > 0) + h->max_packet_size = s->pkt_size; + + s->context = zmq_ctx_new(); + if (!s->context) { + /*errno not set on failure during zmq_ctx_new()*/ + av_log(h, AV_LOG_ERROR, "Error occured during zmq_ctx_new()\n"); + return AVERROR_EXTERNAL; + } + + av_strstart(uri, "zmq:", &uri); + + /*publish during write*/ + if (h->flags & AVIO_FLAG_WRITE) { + s->socket = zmq_socket(s->context, ZMQ_PUB); + if (!s->socket) { + av_log(h, AV_LOG_ERROR, "Error occured during zmq_socket(): %s\n", ZMQ_STRERROR); + goto fail_term; + } + + ret = zmq_bind(s->socket, uri); + if (ret == -1) { + av_log(h, AV_LOG_ERROR, "Error occured during zmq_bind(): %s\n", ZMQ_STRERROR); + goto fail_close; + } + } + + /*subscribe for read*/ + if (h->flags & AVIO_FLAG_READ) { + s->socket = zmq_socket(s->context, ZMQ_SUB); + if (!s->socket) { + av_log(h, AV_LOG_ERROR, "Error occured during zmq_socket(): %s\n", ZMQ_STRERROR); + goto fail_term; + } + + ret = zmq_setsockopt(s->socket, ZMQ_SUBSCRIBE, "", 0); + if (ret == -1) { + av_log(h, AV_LOG_ERROR, "Error occured during zmq_setsockopt(): %s\n", ZMQ_STRERROR); + goto fail_close; + } + + ret = zmq_connect(s->socket, uri); + if (ret == -1) { + av_log(h, AV_LOG_ERROR, "Error occured during zmq_connect(): %s\n", ZMQ_STRERROR); + goto fail_close; + } + } + return 0; + +fail_close: + zmq_close(s->socket); +fail_term: + zmq_ctx_term(s->context); + return AVERROR_EXTERNAL; +} + +static int zmq_proto_write(URLContext *h, const unsigned char *buf, int size) +{ + int ret; + ZMQContext *s = h->priv_data; + + ret = zmq_proto_wait_timeout(h, s->socket, 1, h->rw_timeout, &h->interrupt_callback); + if (ret) + return ret; + ret = zmq_send(s->socket, buf, size, 0); + if (ret == -1) { + av_log(h, AV_LOG_ERROR, "Error occured during zmq_send(): %s\n", ZMQ_STRERROR); + return AVERROR_EXTERNAL; + } + return ret; /*number of bytes sent*/ +} + +static int zmq_proto_read(URLContext *h, unsigned char *buf, int size) +{ + int ret; + ZMQContext *s = h->priv_data; + + ret = zmq_proto_wait_timeout(h, s->socket, 0, h->rw_timeout, &h->interrupt_callback); + if (ret) + return ret; + ret = zmq_recv(s->socket, buf, size, 0); + if (ret == -1) { + av_log(h, AV_LOG_ERROR, "Error occured during zmq_recv(): %s\n", ZMQ_STRERROR); + return AVERROR_EXTERNAL; + } + if (ret > size) { + s->pkt_size_overflow = FFMAX(s->pkt_size_overflow, ret); + av_log(h, AV_LOG_WARNING, "Message exceeds available space in the buffer. Message will be truncated. Setting -pkt_size %d may resolve the issue.\n", s->pkt_size_overflow); + ret = size; + } + return ret; /*number of bytes read*/ +} + +static int zmq_proto_close(URLContext *h) +{ + ZMQContext *s = h->priv_data; + zmq_close(s->socket); + zmq_ctx_term(s->context); + return 0; +} + +static const AVClass zmq_context_class = { + .class_name = "zmq", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const URLProtocol ff_libzmq_protocol = { + .name = "zmq", + .url_close = zmq_proto_close, + .url_open = zmq_proto_open, + .url_read = zmq_proto_read, + .url_write = zmq_proto_write, + .priv_data_size = sizeof(ZMQContext), + .priv_data_class = &zmq_context_class, + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; diff --git a/libavformat/lrcdec.c b/libavformat/lrcdec.c index a9a117691ac..46d5e2bc6a2 100644 --- a/libavformat/lrcdec.c +++ b/libavformat/lrcdec.c @@ -202,6 +202,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) { + ff_subtitles_queue_clean(&lrc->q); return AVERROR(ENOMEM); } sub->pos = pos; diff --git a/libavformat/lxfdec.c b/libavformat/lxfdec.c index 434518fc592..fa84ceea78e 100644 --- a/libavformat/lxfdec.c +++ b/libavformat/lxfdec.c @@ -316,7 +316,6 @@ static int lxf_read_packet(AVFormatContext *s, AVPacket *pkt) return ret2; if ((ret2 = avio_read(pb, pkt->data, ret)) != ret) { - av_packet_unref(pkt); return ret2 < 0 ? ret2 : AVERROR_EOF; } diff --git a/libavformat/matroska.c b/libavformat/matroska.c index 4d18d147fc6..7c56aba4039 100644 --- a/libavformat/matroska.c +++ b/libavformat/matroska.c @@ -119,25 +119,6 @@ const CodecTags ff_webm_codec_tags[] = { {"" , AV_CODEC_ID_NONE} }; -const CodecMime ff_mkv_image_mime_tags[] = { - {"image/gif" , AV_CODEC_ID_GIF}, - {"image/jpeg" , AV_CODEC_ID_MJPEG}, - {"image/png" , AV_CODEC_ID_PNG}, - {"image/tiff" , AV_CODEC_ID_TIFF}, - - {"" , AV_CODEC_ID_NONE} -}; - -const CodecMime ff_mkv_mime_tags[] = { - {"text/plain" , AV_CODEC_ID_TEXT}, - {"application/x-truetype-font", AV_CODEC_ID_TTF}, - {"application/x-font" , AV_CODEC_ID_TTF}, - {"application/vnd.ms-opentype", AV_CODEC_ID_OTF}, - {"binary" , AV_CODEC_ID_BIN_DATA}, - - {"" , AV_CODEC_ID_NONE} -}; - const AVMetadataConv ff_mkv_metadata_conv[] = { { "LEAD_PERFORMER", "performer" }, { "PART_NUMBER" , "track" }, diff --git a/libavformat/matroska.h b/libavformat/matroska.h index 86968a8de1b..6f198f06e6a 100644 --- a/libavformat/matroska.h +++ b/libavformat/matroska.h @@ -271,6 +271,7 @@ typedef enum { MATROSKA_TRACK_TYPE_COMPLEX = 0x3, MATROSKA_TRACK_TYPE_LOGO = 0x10, MATROSKA_TRACK_TYPE_SUBTITLE = 0x11, + MATROSKA_TRACK_TYPE_BUTTONS = 0x12, MATROSKA_TRACK_TYPE_CONTROL = 0x20, MATROSKA_TRACK_TYPE_METADATA = 0x21, } MatroskaTrackType; @@ -285,13 +286,13 @@ typedef enum { typedef enum { MATROSKA_VIDEO_INTERLACE_FLAG_UNDETERMINED = 0, MATROSKA_VIDEO_INTERLACE_FLAG_INTERLACED = 1, - MATROSKA_VIDEO_INTERLACE_FLAG_PROGRESSIVE = 2 + MATROSKA_VIDEO_INTERLACE_FLAG_PROGRESSIVE = 2, } MatroskaVideoInterlaceFlag; typedef enum { MATROSKA_VIDEO_FIELDORDER_PROGRESSIVE = 0, - MATROSKA_VIDEO_FIELDORDER_UNDETERMINED = 2, MATROSKA_VIDEO_FIELDORDER_TT = 1, + MATROSKA_VIDEO_FIELDORDER_UNDETERMINED = 2, MATROSKA_VIDEO_FIELDORDER_BB = 6, MATROSKA_VIDEO_FIELDORDER_TB = 9, MATROSKA_VIDEO_FIELDORDER_BT = 14, @@ -361,8 +362,6 @@ typedef struct CodecTags{ extern const CodecTags ff_mkv_codec_tags[]; extern const CodecTags ff_webm_codec_tags[]; -extern const CodecMime ff_mkv_mime_tags[]; -extern const CodecMime ff_mkv_image_mime_tags[]; extern const AVMetadataConv ff_mkv_metadata_conv[]; 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]; diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index 8c4ff309357..cff7f0cb54a 100644 --- a/libavformat/matroskadec.c +++ b/libavformat/matroskadec.c @@ -249,6 +249,7 @@ typedef struct MatroskaTrack { AVStream *stream; int64_t end_timecode; int ms_compat; + int needs_decoding; uint64_t max_block_additional_id; uint32_t palette[AVPALETTE_COUNT]; @@ -258,6 +259,7 @@ typedef struct MatroskaTrack { typedef struct MatroskaAttachment { uint64_t uid; char *filename; + char *description; char *mime; EbmlBin bin; @@ -554,7 +556,7 @@ static EbmlSyntax matroska_track[] = { { MATROSKA_ID_CODECID, EBML_STR, 0, offsetof(MatroskaTrack, codec_id) }, { MATROSKA_ID_CODECPRIVATE, EBML_BIN, 0, offsetof(MatroskaTrack, codec_priv) }, { MATROSKA_ID_CODECDELAY, EBML_UINT, 0, offsetof(MatroskaTrack, codec_delay) }, - { MATROSKA_ID_TRACKLANGUAGE, EBML_UTF8, 0, offsetof(MatroskaTrack, language), { .s = "eng" } }, + { MATROSKA_ID_TRACKLANGUAGE, EBML_STR, 0, offsetof(MatroskaTrack, language), { .s = "eng" } }, { MATROSKA_ID_TRACKDEFAULTDURATION, EBML_UINT, 0, offsetof(MatroskaTrack, default_duration) }, { MATROSKA_ID_TRACKTIMECODESCALE, EBML_FLOAT, 0, offsetof(MatroskaTrack, time_scale), { .f = 1.0 } }, { MATROSKA_ID_TRACKFLAGDEFAULT, EBML_UINT, 0, offsetof(MatroskaTrack, flag_default), { .u = 1 } }, @@ -586,7 +588,7 @@ static EbmlSyntax matroska_attachment[] = { { MATROSKA_ID_FILENAME, EBML_UTF8, 0, offsetof(MatroskaAttachment, filename) }, { MATROSKA_ID_FILEMIMETYPE, EBML_STR, 0, offsetof(MatroskaAttachment, mime) }, { MATROSKA_ID_FILEDATA, EBML_BIN, 0, offsetof(MatroskaAttachment, bin) }, - { MATROSKA_ID_FILEDESC, EBML_NONE }, + { MATROSKA_ID_FILEDESC, EBML_UTF8, 0, offsetof(MatroskaAttachment, description) }, CHILD_OF(matroska_attachments) }; @@ -707,7 +709,7 @@ static EbmlSyntax matroska_segments[] = { }; static EbmlSyntax matroska_blockmore[] = { - { MATROSKA_ID_BLOCKADDID, EBML_UINT, 0, offsetof(MatroskaBlock,additional_id) }, + { MATROSKA_ID_BLOCKADDID, EBML_UINT, 0, offsetof(MatroskaBlock,additional_id), { .u = 1 } }, { MATROSKA_ID_BLOCKADDITIONAL, EBML_BIN, 0, offsetof(MatroskaBlock,additional) }, CHILD_OF(matroska_blockadditions) }; @@ -747,6 +749,25 @@ static EbmlSyntax matroska_cluster_enter[] = { }; #undef CHILD_OF +static const CodecMime mkv_image_mime_tags[] = { + {"image/gif" , AV_CODEC_ID_GIF}, + {"image/jpeg" , AV_CODEC_ID_MJPEG}, + {"image/png" , AV_CODEC_ID_PNG}, + {"image/tiff" , AV_CODEC_ID_TIFF}, + + {"" , AV_CODEC_ID_NONE} +}; + +static const CodecMime mkv_mime_tags[] = { + {"text/plain" , AV_CODEC_ID_TEXT}, + {"application/x-truetype-font", AV_CODEC_ID_TTF}, + {"application/x-font" , AV_CODEC_ID_TTF}, + {"application/vnd.ms-opentype", AV_CODEC_ID_OTF}, + {"binary" , AV_CODEC_ID_BIN_DATA}, + + {"" , AV_CODEC_ID_NONE} +}; + static const char *const matroska_doctypes[] = { "matroska", "webm" }; static int matroska_read_close(AVFormatContext *s); @@ -798,7 +819,7 @@ static int matroska_resync(MatroskaDemuxContext *matroska, int64_t last_pos) id == MATROSKA_ID_CLUSTER || id == MATROSKA_ID_CHAPTERS) { /* Prepare the context for parsing of a level 1 element. */ matroska_reset_status(matroska, id, -1); - /* Given that we are here means that an error has occured, + /* Given that we are here means that an error has occurred, * so treat the segment as unknown length in order not to * discard valid data that happens to be beyond the designated * end of the segment. */ @@ -1024,28 +1045,17 @@ static int ebml_read_master(MatroskaDemuxContext *matroska, } /* - * Read signed/unsigned "EBML" numbers. + * Read a signed "EBML number" * Return: number of bytes processed, < 0 on error */ -static int matroska_ebmlnum_uint(MatroskaDemuxContext *matroska, - uint8_t *data, uint32_t size, uint64_t *num) -{ - AVIOContext pb; - ffio_init_context(&pb, data, size, 0, NULL, NULL, NULL, NULL); - return ebml_read_num(matroska, &pb, FFMIN(size, 8), num, 1); -} - -/* - * Same as above, but signed. - */ static int matroska_ebmlnum_sint(MatroskaDemuxContext *matroska, - uint8_t *data, uint32_t size, int64_t *num) + AVIOContext *pb, int64_t *num) { uint64_t unum; int res; /* read as unsigned number first */ - if ((res = matroska_ebmlnum_uint(matroska, data, size, &unum)) < 0) + if ((res = ebml_read_num(matroska, pb, 8, &unum, 1)) < 0) return res; /* make signed (weird way) */ @@ -1126,7 +1136,7 @@ static int is_ebml_id_valid(uint32_t id) * an entry already exists, return the existing entry. */ static MatroskaLevel1Element *matroska_find_level1_elem(MatroskaDemuxContext *matroska, - uint32_t id) + uint32_t id, int64_t pos) { int i; MatroskaLevel1Element *elem; @@ -1138,19 +1148,18 @@ static MatroskaLevel1Element *matroska_find_level1_elem(MatroskaDemuxContext *ma if (id == MATROSKA_ID_CLUSTER) return NULL; - // There can be multiple seekheads. - if (id != MATROSKA_ID_SEEKHEAD) { - for (i = 0; i < matroska->num_level1_elems; i++) { - if (matroska->level1_elems[i].id == id) + // There can be multiple SeekHeads and Tags. + for (i = 0; i < matroska->num_level1_elems; i++) { + if (matroska->level1_elems[i].id == id) { + if (matroska->level1_elems[i].pos == pos || + id != MATROSKA_ID_SEEKHEAD && id != MATROSKA_ID_TAGS) return &matroska->level1_elems[i]; } } // Only a completely broken file would have more elements. - // It also provides a low-effort way to escape from circular seekheads - // (every iteration will add a level1 entry). if (matroska->num_level1_elems >= FF_ARRAY_ELEMS(matroska->level1_elems)) { - av_log(matroska->ctx, AV_LOG_ERROR, "Too many level1 elements or circular seekheads.\n"); + av_log(matroska->ctx, AV_LOG_ERROR, "Too many level1 elements.\n"); return NULL; } @@ -1331,7 +1340,7 @@ static int ebml_parse(MatroskaDemuxContext *matroska, // current element (i.e. how much would be skipped); if there were // more than a few skipped elements in a row and skipping the current // element would lead us more than SKIP_THRESHOLD away from the last - // known good position, then it is inferred that an error occured. + // known good position, then it is inferred that an error occurred. // The dependency on the number of unknown elements in a row exists // because the distance to the last known good position is // automatically big if the last parsed element was big. @@ -1399,7 +1408,7 @@ static int ebml_parse(MatroskaDemuxContext *matroska, if (id == MATROSKA_ID_CUES) matroska->cues_parsing_deferred = 0; if (syntax->type == EBML_LEVEL1 && - (level1_elem = matroska_find_level1_elem(matroska, syntax->id))) { + (level1_elem = matroska_find_level1_elem(matroska, syntax->id, pos))) { if (!level1_elem->pos) { // Zero is not a valid position for a level 1 element. level1_elem->pos = pos; @@ -1556,7 +1565,7 @@ static int matroska_probe(const AVProbeData *p) } static MatroskaTrack *matroska_find_track_by_num(MatroskaDemuxContext *matroska, - int num) + uint64_t num) { MatroskaTrack *tracks = matroska->tracks.elem; int i; @@ -1565,7 +1574,7 @@ static MatroskaTrack *matroska_find_track_by_num(MatroskaDemuxContext *matroska, if (tracks[i].num == num) return &tracks[i]; - av_log(matroska->ctx, AV_LOG_ERROR, "Invalid track number %d\n", num); + av_log(matroska->ctx, AV_LOG_ERROR, "Invalid track number %"PRIu64"\n", num); return NULL; } @@ -1610,6 +1619,7 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size, #if CONFIG_LZO case MATROSKA_TRACK_ENCODING_COMP_LZO: do { + int insize = isize; olen = pkt_size *= 3; newpktdata = av_realloc(pkt_data, pkt_size + AV_LZO_OUTPUT_PADDING + AV_INPUT_BUFFER_PADDING_SIZE); @@ -1618,7 +1628,7 @@ static int matroska_decode_buffer(uint8_t **buf, int *buf_size, goto failed; } pkt_data = newpktdata; - result = av_lzo1x_decode(pkt_data, &olen, data, &isize); + result = av_lzo1x_decode(pkt_data, &olen, data, &insize); } while (result == AV_LZO_OUTPUT_FULL && pkt_size < 10000000); if (result) { result = AVERROR_INVALIDDATA; @@ -1762,7 +1772,7 @@ static void matroska_convert_tags(AVFormatContext *s) } } if (!found) { - av_log(NULL, AV_LOG_WARNING, + av_log(s, AV_LOG_WARNING, "The tags at index %d refer to a " "non-existent attachment %"PRId64".\n", i, tags[i].target.attachuid); @@ -1779,7 +1789,7 @@ static void matroska_convert_tags(AVFormatContext *s) } } if (!found) { - av_log(NULL, AV_LOG_WARNING, + av_log(s, AV_LOG_WARNING, "The tags at index %d refer to a non-existent chapter " "%"PRId64".\n", i, tags[i].target.chapteruid); @@ -1796,7 +1806,7 @@ static void matroska_convert_tags(AVFormatContext *s) } } if (!found) { - av_log(NULL, AV_LOG_WARNING, + av_log(s, AV_LOG_WARNING, "The tags at index %d refer to a non-existent track " "%"PRId64".\n", i, tags[i].target.trackuid); @@ -1856,8 +1866,12 @@ static void matroska_execute_seekhead(MatroskaDemuxContext *matroska) MatroskaSeekhead *seekheads = seekhead_list->elem; uint32_t id = seekheads[i].id; int64_t pos = seekheads[i].pos + matroska->segment_start; + MatroskaLevel1Element *elem; + + if (id != seekheads[i].id || pos < matroska->segment_start) + continue; - MatroskaLevel1Element *elem = matroska_find_level1_elem(matroska, id); + elem = matroska_find_level1_elem(matroska, id, pos); if (!elem || elem->parsed) continue; @@ -2116,9 +2130,6 @@ static int mkv_parse_video_color(AVStream *st, const MatroskaTrack *track) { } if (has_mastering_primaries || has_mastering_luminance) { - // Use similar rationals as other standards. - const int chroma_den = 50000; - const int luma_den = 10000; AVMasteringDisplayMetadata *metadata = (AVMasteringDisplayMetadata*) av_stream_new_side_data( st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, @@ -2128,36 +2139,28 @@ static int mkv_parse_video_color(AVStream *st, const MatroskaTrack *track) { } memset(metadata, 0, sizeof(AVMasteringDisplayMetadata)); if (has_mastering_primaries) { - metadata->display_primaries[0][0] = av_make_q( - round(mastering_meta->r_x * chroma_den), chroma_den); - metadata->display_primaries[0][1] = av_make_q( - round(mastering_meta->r_y * chroma_den), chroma_den); - metadata->display_primaries[1][0] = av_make_q( - round(mastering_meta->g_x * chroma_den), chroma_den); - metadata->display_primaries[1][1] = av_make_q( - round(mastering_meta->g_y * chroma_den), chroma_den); - metadata->display_primaries[2][0] = av_make_q( - round(mastering_meta->b_x * chroma_den), chroma_den); - metadata->display_primaries[2][1] = av_make_q( - round(mastering_meta->b_y * chroma_den), chroma_den); - metadata->white_point[0] = av_make_q( - round(mastering_meta->white_x * chroma_den), chroma_den); - metadata->white_point[1] = av_make_q( - round(mastering_meta->white_y * chroma_den), chroma_den); + 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; } if (has_mastering_luminance) { - metadata->max_luminance = av_make_q( - round(mastering_meta->max_luminance * luma_den), luma_den); - metadata->min_luminance = av_make_q( - round(mastering_meta->min_luminance * luma_den), luma_den); + 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; } } return 0; } -static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track) { +static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track, + void *logctx) +{ AVSphericalMapping *spherical; enum AVSphericalProjection projection; size_t spherical_size; @@ -2170,7 +2173,7 @@ static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track) track->video.projection.private.size); if (bytestream2_get_byte(&gb) != 0) { - av_log(NULL, AV_LOG_WARNING, "Unknown spherical metadata\n"); + av_log(logctx, AV_LOG_WARNING, "Unknown spherical metadata\n"); return 0; } @@ -2185,14 +2188,14 @@ static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track) r = bytestream2_get_be32(&gb); if (b >= UINT_MAX - t || r >= UINT_MAX - l) { - av_log(NULL, AV_LOG_ERROR, + av_log(logctx, AV_LOG_ERROR, "Invalid bounding rectangle coordinates " "%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"\n", l, t, r, b); return AVERROR_INVALIDDATA; } } else if (track->video.projection.private.size != 0) { - av_log(NULL, AV_LOG_ERROR, "Unknown spherical metadata\n"); + av_log(logctx, AV_LOG_ERROR, "Unknown spherical metadata\n"); return AVERROR_INVALIDDATA; } @@ -2203,19 +2206,19 @@ static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track) break; case MATROSKA_VIDEO_PROJECTION_TYPE_CUBEMAP: if (track->video.projection.private.size < 4) { - av_log(NULL, AV_LOG_ERROR, "Missing projection private properties\n"); + av_log(logctx, AV_LOG_ERROR, "Missing projection private properties\n"); return AVERROR_INVALIDDATA; } else if (track->video.projection.private.size == 12) { uint32_t layout = bytestream2_get_be32(&gb); if (layout) { - av_log(NULL, AV_LOG_WARNING, + av_log(logctx, AV_LOG_WARNING, "Unknown spherical cubemap layout %"PRIu32"\n", layout); return 0; } projection = AV_SPHERICAL_CUBEMAP; padding = bytestream2_get_be32(&gb); } else { - av_log(NULL, AV_LOG_ERROR, "Unknown spherical metadata\n"); + av_log(logctx, AV_LOG_ERROR, "Unknown spherical metadata\n"); return AVERROR_INVALIDDATA; } break; @@ -2223,7 +2226,7 @@ static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track) /* No Spherical metadata */ return 0; default: - av_log(NULL, AV_LOG_WARNING, + av_log(logctx, AV_LOG_WARNING, "Unknown spherical metadata type %"PRIu64"\n", track->video.projection.type); return 0; @@ -2409,6 +2412,11 @@ static int matroska_parse_tracks(AVFormatContext *s) } } } + 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); for (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) { if (!strncmp(ff_mkv_codec_tags[j].str, track->codec_id, @@ -2426,8 +2434,8 @@ static int matroska_parse_tracks(AVFormatContext *s) if (key_id_base64) { /* export encryption key id as base64 metadata tag */ - av_dict_set(&st->metadata, "enc_key_id", key_id_base64, 0); - av_freep(&key_id_base64); + 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") && @@ -2558,34 +2566,33 @@ static int matroska_parse_tracks(AVFormatContext *s) memcpy(&extradata[12], track->codec_priv.data, track->codec_priv.size); } else if (codec_id == AV_CODEC_ID_TTA) { - extradata_size = 30; - extradata = av_mallocz(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); - if (!extradata) - return AVERROR(ENOMEM); - ffio_init_context(&b, extradata, extradata_size, 1, - NULL, NULL, NULL, NULL); - avio_write(&b, "TTA1", 4); - avio_wl16(&b, 1); + 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); - av_freep(&extradata); if (matroska->ctx->error_recognition & AV_EF_EXPLODE) return AVERROR_INVALIDDATA; else continue; } - avio_wl16(&b, track->audio.channels); - avio_wl16(&b, track->audio.bitdepth); if (track->audio.out_samplerate < 0 || track->audio.out_samplerate > INT_MAX) return AVERROR_INVALIDDATA; - avio_wl32(&b, track->audio.out_samplerate); - avio_wl32(&b, av_rescale((matroska->duration * matroska->time_scale), - track->audio.out_samplerate, - AV_TIME_BASE * 1000)); + 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 || @@ -2611,32 +2618,46 @@ static int matroska_parse_tracks(AVFormatContext *s) track->audio.sub_packet_h = avio_rb16(&b); track->audio.frame_size = avio_rb16(&b); track->audio.sub_packet_size = avio_rb16(&b); - if (flavor < 0 || - track->audio.coded_framesize <= 0 || + if (track->audio.coded_framesize <= 0 || track->audio.sub_packet_h <= 0 || - track->audio.frame_size <= 0 || - track->audio.sub_packet_size <= 0 && codec_id != AV_CODEC_ID_SIPR) + track->audio.frame_size <= 0) return AVERROR_INVALIDDATA; - track->audio.buf = av_malloc_array(track->audio.sub_packet_h, - track->audio.frame_size); - if (!track->audio.buf) - return AVERROR(ENOMEM); + 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 && flavor < 4) { + 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) { @@ -2761,7 +2782,7 @@ static int matroska_parse_tracks(AVFormatContext *s) ret = mkv_parse_video_color(st, track); if (ret < 0) return ret; - ret = mkv_parse_video_projection(st, track); + ret = mkv_parse_video_projection(st, track, matroska->ctx); if (ret < 0) return ret; } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) { @@ -2896,12 +2917,14 @@ static int matroska_read_header(AVFormatContext *s) break; av_dict_set(&st->metadata, "filename", attachments[j].filename, 0); av_dict_set(&st->metadata, "mimetype", attachments[j].mime, 0); + if (attachments[j].description) + av_dict_set(&st->metadata, "title", attachments[j].description, 0); st->codecpar->codec_id = AV_CODEC_ID_NONE; - for (i = 0; ff_mkv_image_mime_tags[i].id != AV_CODEC_ID_NONE; i++) { - if (!strncmp(ff_mkv_image_mime_tags[i].str, attachments[j].mime, - strlen(ff_mkv_image_mime_tags[i].str))) { - st->codecpar->codec_id = ff_mkv_image_mime_tags[i].id; + for (i = 0; mkv_image_mime_tags[i].id != AV_CODEC_ID_NONE; i++) { + if (!strncmp(mkv_image_mime_tags[i].str, attachments[j].mime, + strlen(mkv_image_mime_tags[i].str))) { + st->codecpar->codec_id = mkv_image_mime_tags[i].id; break; } } @@ -2915,9 +2938,8 @@ static int matroska_read_header(AVFormatContext *s) st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; av_init_packet(pkt); - pkt->buf = av_buffer_ref(attachments[j].bin.buf); - if (!pkt->buf) - return AVERROR(ENOMEM); + pkt->buf = attachments[j].bin.buf; + attachments[j].bin.buf = NULL; pkt->data = attachments[j].bin.data; pkt->size = attachments[j].bin.size; pkt->stream_index = st->index; @@ -2929,10 +2951,10 @@ static int matroska_read_header(AVFormatContext *s) memcpy(st->codecpar->extradata, attachments[j].bin.data, attachments[j].bin.size); - for (i = 0; ff_mkv_mime_tags[i].id != AV_CODEC_ID_NONE; i++) { - if (!strncmp(ff_mkv_mime_tags[i].str, attachments[j].mime, - strlen(ff_mkv_mime_tags[i].str))) { - st->codecpar->codec_id = ff_mkv_mime_tags[i].id; + for (i = 0; mkv_mime_tags[i].id != AV_CODEC_ID_NONE; i++) { + if (!strncmp(mkv_mime_tags[i].str, attachments[j].mime, + strlen(mkv_mime_tags[i].str))) { + st->codecpar->codec_id = mkv_mime_tags[i].id; break; } } @@ -2949,10 +2971,6 @@ static int matroska_read_header(AVFormatContext *s) (AVRational) { 1, 1000000000 }, chapters[i].start, chapters[i].end, chapters[i].title); - if (chapters[i].chapter) { - av_dict_set(&chapters[i].chapter->metadata, - "title", chapters[i].title, 0); - } max_start = chapters[i].start; } @@ -3003,67 +3021,53 @@ static void matroska_clear_queue(MatroskaDemuxContext *matroska) } static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf, - int *buf_size, int type, - uint32_t **lace_buf, int *laces) + int size, int type, AVIOContext *pb, + uint32_t lace_size[256], int *laces) { - int res = 0, n, size = *buf_size; + int n; uint8_t *data = *buf; - uint32_t *lace_size; if (!type) { *laces = 1; - *lace_buf = av_malloc(sizeof(**lace_buf)); - if (!*lace_buf) - return AVERROR(ENOMEM); - - *lace_buf[0] = size; + lace_size[0] = size; return 0; } - av_assert0(size > 0); - *laces = *data + 1; - data += 1; - size -= 1; - lace_size = av_malloc_array(*laces, sizeof(*lace_size)); - if (!lace_size) - return AVERROR(ENOMEM); + if (size <= 0) + return AVERROR_INVALIDDATA; + + *laces = *data + 1; + data += 1; + size -= 1; switch (type) { case 0x1: /* Xiph lacing */ { uint8_t temp; uint32_t total = 0; - for (n = 0; res == 0 && n < *laces - 1; n++) { + for (n = 0; n < *laces - 1; n++) { lace_size[n] = 0; - while (1) { - if (size <= total) { - res = AVERROR_INVALIDDATA; - break; - } + do { + if (size <= total) + return AVERROR_INVALIDDATA; temp = *data; total += temp; lace_size[n] += temp; data += 1; size -= 1; - if (temp != 0xff) - break; - } - } - if (size <= total) { - res = AVERROR_INVALIDDATA; - break; + } while (temp == 0xff); } + if (size < total) + return AVERROR_INVALIDDATA; lace_size[n] = size - total; break; } case 0x2: /* fixed-size lacing */ - if (size % (*laces)) { - res = AVERROR_INVALIDDATA; - break; - } + if (size % (*laces)) + return AVERROR_INVALIDDATA; for (n = 0; n < *laces; n++) lace_size[n] = size / *laces; break; @@ -3072,45 +3076,44 @@ static int matroska_parse_laces(MatroskaDemuxContext *matroska, uint8_t **buf, { uint64_t num; uint64_t total; - n = matroska_ebmlnum_uint(matroska, data, size, &num); - if (n < 0 || num > INT_MAX) { - av_log(matroska->ctx, AV_LOG_INFO, - "EBML block data error\n"); - res = n<0 ? n : AVERROR_INVALIDDATA; - break; - } - data += n; - size -= n; + int offset; + + avio_skip(pb, 4); + + n = ebml_read_num(matroska, pb, 8, &num, 1); + if (n < 0) + return n; + if (num > INT_MAX) + return AVERROR_INVALIDDATA; + total = lace_size[0] = num; - for (n = 1; res == 0 && n < *laces - 1; n++) { + offset = n; + for (n = 1; n < *laces - 1; n++) { int64_t snum; int r; - r = matroska_ebmlnum_sint(matroska, data, size, &snum); - if (r < 0 || lace_size[n - 1] + snum > (uint64_t)INT_MAX) { - av_log(matroska->ctx, AV_LOG_INFO, - "EBML block data error\n"); - res = r<0 ? r : AVERROR_INVALIDDATA; - break; - } - data += r; - size -= r; + r = matroska_ebmlnum_sint(matroska, pb, &snum); + if (r < 0) + return r; + if (lace_size[n - 1] + snum > (uint64_t)INT_MAX) + return AVERROR_INVALIDDATA; + lace_size[n] = lace_size[n - 1] + snum; total += lace_size[n]; + offset += r; } - if (size <= total) { - res = AVERROR_INVALIDDATA; - break; - } + data += offset; + size -= offset; + if (size < total) + return AVERROR_INVALIDDATA; + lace_size[*laces - 1] = size - total; break; } } - *buf = data; - *lace_buf = lace_size; - *buf_size = size; + *buf = data; - return res; + return 0; } static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, @@ -3118,12 +3121,12 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, uint8_t *data, int size, uint64_t timecode, int64_t pos) { - int a = st->codecpar->block_align; - int sps = track->audio.sub_packet_size; - int cfs = track->audio.coded_framesize; - int h = track->audio.sub_packet_h; + const int a = st->codecpar->block_align; + const int sps = track->audio.sub_packet_size; + const int cfs = track->audio.coded_framesize; + const int h = track->audio.sub_packet_h; + const int w = track->audio.frame_size; int y = track->audio.sub_packet_cnt; - int w = track->audio.frame_size; int x; if (!track->audio.pkt_cnt) { @@ -3146,7 +3149,7 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, } memcpy(track->audio.buf + y * w, data, w); } else { - if (size < sps * w / sps || h<=0 || w%sps) { + if (size < w) { av_log(matroska->ctx, AV_LOG_ERROR, "Corrupt generic RM-style audio packet size\n"); return AVERROR_INVALIDDATA; @@ -3191,19 +3194,21 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska, } /* reconstruct full wavpack blocks from mangled matroska ones */ -static int matroska_parse_wavpack(MatroskaTrack *track, uint8_t *src, - uint8_t **pdst, int *size) +static int matroska_parse_wavpack(MatroskaTrack *track, + uint8_t **data, int *size) { uint8_t *dst = NULL; + uint8_t *src = *data; int dstlen = 0; int srclen = *size; uint32_t samples; uint16_t ver; int ret, offset = 0; - if (srclen < 12 || track->stream->codecpar->extradata_size < 2) + if (srclen < 12) return AVERROR_INVALIDDATA; + av_assert1(track->stream->codecpar->extradata_size >= 2); ver = AV_RL16(track->stream->codecpar->extradata); samples = AV_RL32(src); @@ -3263,7 +3268,7 @@ static int matroska_parse_wavpack(MatroskaTrack *track, uint8_t *src, memset(dst + dstlen, 0, AV_INPUT_BUFFER_PADDING_SIZE); - *pdst = dst; + *data = dst; *size = dstlen; return 0; @@ -3273,25 +3278,22 @@ static int matroska_parse_wavpack(MatroskaTrack *track, uint8_t *src, return ret; } -static int matroska_parse_prores(MatroskaTrack *track, uint8_t *src, - uint8_t **pdst, int *size) +static int matroska_parse_prores(MatroskaTrack *track, + uint8_t **data, int *size) { - uint8_t *dst = src; - int dstlen = *size; + uint8_t *dst; + int dstlen = *size + 8; - if (AV_RB32(&src[4]) != MKBETAG('i', 'c', 'p', 'f')) { - dst = av_malloc(dstlen + 8 + AV_INPUT_BUFFER_PADDING_SIZE); - if (!dst) - return AVERROR(ENOMEM); + dst = av_malloc(dstlen + AV_INPUT_BUFFER_PADDING_SIZE); + if (!dst) + return AVERROR(ENOMEM); - AV_WB32(dst, dstlen); - AV_WB32(dst + 4, MKBETAG('i', 'c', 'p', 'f')); - memcpy(dst + 8, src, dstlen); - memset(dst + 8 + dstlen, 0, AV_INPUT_BUFFER_PADDING_SIZE); - dstlen += 8; - } + AV_WB32(dst, dstlen); + AV_WB32(dst + 4, MKBETAG('i', 'c', 'p', 'f')); + memcpy(dst + 8, *data, dstlen - 8); + memset(dst + dstlen, 0, AV_INPUT_BUFFER_PADDING_SIZE); - *pdst = dst; + *data = dst; *size = dstlen; return 0; @@ -3420,45 +3422,40 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, uint8_t *additional, uint64_t additional_id, int additional_size, int64_t discard_padding) { - MatroskaTrackEncoding *encodings = track->encodings.elem; uint8_t *pkt_data = data; - int res; + int res = 0; AVPacket pktl, *pkt = &pktl; - if (encodings && !encodings->type && encodings->scope & 1) { - res = matroska_decode_buffer(&pkt_data, &pkt_size, track); - if (res < 0) - return res; - } - if (st->codecpar->codec_id == AV_CODEC_ID_WAVPACK) { - uint8_t *wv_data; - res = matroska_parse_wavpack(track, pkt_data, &wv_data, &pkt_size); + res = matroska_parse_wavpack(track, &pkt_data, &pkt_size); if (res < 0) { av_log(matroska->ctx, AV_LOG_ERROR, "Error parsing a wavpack block.\n"); goto fail; } - if (pkt_data != data) - av_freep(&pkt_data); - pkt_data = wv_data; + if (!buf) + av_freep(&data); + buf = NULL; } - if (st->codecpar->codec_id == AV_CODEC_ID_PRORES) { - uint8_t *pr_data; - res = matroska_parse_prores(track, pkt_data, &pr_data, &pkt_size); + if (st->codecpar->codec_id == AV_CODEC_ID_PRORES && + AV_RB32(pkt_data + 4) != MKBETAG('i', 'c', 'p', 'f')) { + res = matroska_parse_prores(track, &pkt_data, &pkt_size); if (res < 0) { av_log(matroska->ctx, AV_LOG_ERROR, "Error parsing a prores block.\n"); goto fail; } - if (pkt_data != data) - av_freep(&pkt_data); - pkt_data = pr_data; + if (!buf) + av_freep(&data); + buf = NULL; } + if (!pkt_size && !additional_size) + goto no_output; + av_init_packet(pkt); - if (pkt_data != data) + if (!buf) pkt->buf = av_buffer_create(pkt_data, pkt_size + AV_INPUT_BUFFER_PADDING_SIZE, NULL, NULL, 0); else @@ -3527,9 +3524,10 @@ FF_ENABLE_DEPRECATION_WARNINGS return 0; +no_output: fail: - if (pkt_data != data) - av_freep(&pkt_data); + if (!buf) + av_free(pkt_data); return res; } @@ -3541,31 +3539,37 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, AVBufferRef *buf { uint64_t timecode = AV_NOPTS_VALUE; MatroskaTrack *track; + AVIOContext pb; int res = 0; AVStream *st; int16_t block_time; - uint32_t *lace_size = NULL; + uint32_t lace_size[256]; int n, flags, laces = 0; uint64_t num; int trust_default_duration = 1; - if ((n = matroska_ebmlnum_uint(matroska, data, size, &num)) < 0) { + ffio_init_context(&pb, data, size, 0, NULL, NULL, NULL, NULL); + + if ((n = ebml_read_num(matroska, &pb, 8, &num, 1)) < 0) return n; - } data += n; size -= n; track = matroska_find_track_by_num(matroska, num); - if (!track || !track->stream) { - av_log(matroska->ctx, AV_LOG_INFO, - "Invalid stream %"PRIu64"\n", num); + if (!track || size < 3) return AVERROR_INVALIDDATA; - } else if (size <= 3) + + if (!(st = track->stream)) { + av_log(matroska->ctx, AV_LOG_VERBOSE, + "No stream associated to TrackNumber %"PRIu64". " + "Ignoring Block with this TrackNumber.\n", num); return 0; - st = track->stream; + } + if (st->discard >= AVDISCARD_ALL) return res; - av_assert1(block_duration != AV_NOPTS_VALUE); + if (block_duration > INT64_MAX) + block_duration = INT64_MAX; block_time = sign_extend(AV_RB16(data), 16); data += 2; @@ -3602,11 +3606,12 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, AVBufferRef *buf } } - res = matroska_parse_laces(matroska, &data, &size, (flags & 0x06) >> 1, - &lace_size, &laces); - - if (res) - goto end; + res = matroska_parse_laces(matroska, &data, size, (flags & 0x06) >> 1, + &pb, lace_size, &laces); + if (res < 0) { + av_log(matroska->ctx, AV_LOG_ERROR, "Error parsing frame sizes.\n"); + return res; + } if (track->audio.samplerate == 8000) { // If this is needed for more codecs, then add them here @@ -3625,49 +3630,53 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, AVBufferRef *buf for (n = 0; n < laces; n++) { int64_t lace_duration = block_duration*(n+1) / laces - block_duration*n / laces; + uint8_t *out_data = data; + int out_size = lace_size[n]; - if (lace_size[n] > size) { - av_log(matroska->ctx, AV_LOG_ERROR, "Invalid packet size\n"); - break; + if (track->needs_decoding) { + res = matroska_decode_buffer(&out_data, &out_size, track); + if (res < 0) + return res; + /* Given that we are here means that out_data is no longer + * owned by buf, so set it to NULL. This depends upon + * zero-length header removal compression being ignored. */ + av_assert1(out_data != data); + buf = NULL; } - if ((st->codecpar->codec_id == AV_CODEC_ID_RA_288 || - st->codecpar->codec_id == AV_CODEC_ID_COOK || - st->codecpar->codec_id == AV_CODEC_ID_SIPR || - st->codecpar->codec_id == AV_CODEC_ID_ATRAC3) && - st->codecpar->block_align && track->audio.sub_packet_size) { - res = matroska_parse_rm_audio(matroska, track, st, data, - lace_size[n], + if (track->audio.buf) { + res = matroska_parse_rm_audio(matroska, track, st, + out_data, out_size, timecode, pos); + if (!buf) + av_free(out_data); if (res) - goto end; - + return res; } else if (st->codecpar->codec_id == AV_CODEC_ID_WEBVTT) { res = matroska_parse_webvtt(matroska, track, st, - data, lace_size[n], + out_data, out_size, timecode, lace_duration, pos); + if (!buf) + av_free(out_data); if (res) - goto end; + return res; } else { - res = matroska_parse_frame(matroska, track, st, buf, data, lace_size[n], - timecode, lace_duration, pos, - !n ? is_keyframe : 0, + 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, discard_padding); if (res) - goto end; + return res; } if (timecode != AV_NOPTS_VALUE) timecode = lace_duration ? timecode + lace_duration : AV_NOPTS_VALUE; data += lace_size[n]; - size -= lace_size[n]; } -end: - av_free(lace_size); - return res; + return 0; } static int matroska_parse_cluster(MatroskaDemuxContext *matroska) @@ -4154,8 +4163,8 @@ static int webm_dash_manifest_cues(AVFormatContext *s, int64_t init_range) } end += ret; } - av_dict_set(&s->streams[0]->metadata, CUE_TIMESTAMPS, buf, 0); - av_free(buf); + av_dict_set(&s->streams[0]->metadata, CUE_TIMESTAMPS, + buf, AV_DICT_DONT_STRDUP_VAL); return 0; } @@ -4171,17 +4180,20 @@ static int webm_dash_manifest_read_header(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "Failed to read file headers\n"); return -1; } - if (!s->nb_streams) { - matroska_read_close(s); - av_log(s, AV_LOG_ERROR, "No streams found\n"); - return AVERROR_INVALIDDATA; + if (!matroska->tracks.nb_elem || !s->nb_streams) { + av_log(s, AV_LOG_ERROR, "No track found\n"); + ret = AVERROR_INVALIDDATA; + goto fail; } if (!matroska->is_live) { buf = av_asprintf("%g", matroska->duration); - if (!buf) return AVERROR(ENOMEM); - av_dict_set(&s->streams[0]->metadata, DURATION, buf, 0); - av_free(buf); + if (!buf) { + ret = AVERROR(ENOMEM); + goto fail; + } + av_dict_set(&s->streams[0]->metadata, DURATION, + buf, AV_DICT_DONT_STRDUP_VAL); // initialization range // 5 is the offset of Cluster ID. @@ -4202,7 +4214,7 @@ static int webm_dash_manifest_read_header(AVFormatContext *s) ret = webm_dash_manifest_cues(s, init_range); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Error parsing Cues\n"); - return ret; + goto fail; } } @@ -4212,6 +4224,9 @@ static int webm_dash_manifest_read_header(AVFormatContext *s) matroska->bandwidth, 0); } return 0; +fail: + matroska_read_close(s); + return ret; } static int webm_dash_manifest_read_packet(AVFormatContext *s, AVPacket *pkt) diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index cef504fa057..eaed02bc92d 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -32,7 +32,6 @@ #include "isom.h" #include "matroska.h" #include "riff.h" -#include "subtitles.h" #include "vorbiscomment.h" #include "wv.h" @@ -50,18 +49,34 @@ #include "libavutil/random_seed.h" #include "libavutil/rational.h" #include "libavutil/samplefmt.h" -#include "libavutil/sha.h" #include "libavutil/stereo3d.h" #include "libavcodec/xiph.h" #include "libavcodec/mpeg4audio.h" -#include "libavcodec/internal.h" + +/* Level 1 elements we create a SeekHead entry for: + * Info, Tracks, Chapters, Attachments, Tags (potentially twice) and Cues */ +#define MAX_SEEKHEAD_ENTRIES 7 + +#define IS_SEEKABLE(pb, mkv) (((pb)->seekable & AVIO_SEEKABLE_NORMAL) && \ + !(mkv)->is_live) + +enum { + DEFAULT_MODE_INFER, + DEFAULT_MODE_INFER_NO_SUBS, + DEFAULT_MODE_PASSTHROUGH, +}; typedef struct ebml_master { int64_t pos; ///< absolute offset in the containing AVIOContext where the master's elements start int sizebytes; ///< how many bytes were reserved for the size } ebml_master; +typedef struct ebml_stored_master { + AVIOContext *bc; + int64_t pos; +} ebml_stored_master; + typedef struct mkv_seekhead_entry { uint32_t elementid; uint64_t segmentpos; @@ -69,98 +84,79 @@ typedef struct mkv_seekhead_entry { typedef struct mkv_seekhead { int64_t filepos; - int64_t segment_offset; ///< the file offset to the beginning of the segment - int reserved_size; ///< -1 if appending to file - int max_entries; - mkv_seekhead_entry *entries; + mkv_seekhead_entry entries[MAX_SEEKHEAD_ENTRIES]; int num_entries; + int reserved_size; } mkv_seekhead; typedef struct mkv_cuepoint { uint64_t pts; int stream_idx; - int tracknum; - int64_t cluster_pos; ///< file offset of the cluster containing the block + int64_t cluster_pos; ///< offset of the cluster containing the block relative to the segment int64_t relative_pos; ///< relative offset from the position of the cluster containing the block int64_t duration; ///< duration of the block according to time base } mkv_cuepoint; typedef struct mkv_cues { - int64_t segment_offset; - mkv_cuepoint *entries; + mkv_cuepoint *entries; int num_entries; } mkv_cues; typedef struct mkv_track { int write_dts; int has_cue; + uint64_t uid; + unsigned track_num; + int track_num_size; int sample_rate; int64_t sample_rate_offset; + int64_t last_timestamp; + int64_t duration; + int64_t duration_offset; int64_t codecpriv_offset; int64_t ts_offset; } mkv_track; -typedef struct mkv_attachment { - int stream_idx; - uint32_t fileuid; -} mkv_attachment; - -typedef struct mkv_attachments { - mkv_attachment *entries; - int num_entries; -} mkv_attachments; - #define MODE_MATROSKAv2 0x01 #define MODE_WEBM 0x02 -/** Maximum number of tracks allowed in a Matroska file (with track numbers in - * range 1 to 126 (inclusive) */ -#define MAX_TRACKS 126 - typedef struct MatroskaMuxContext { - const AVClass *class; - int mode; - AVIOContext *tags_bc; - int64_t tags_pos; - AVIOContext *info_bc; - int64_t info_pos; - AVIOContext *tracks_bc; - int64_t tracks_pos; - ebml_master segment; - int64_t segment_offset; - AVIOContext *cluster_bc; - int64_t cluster_pos; ///< file offset of the current cluster - int64_t cluster_pts; - int64_t duration_offset; - int64_t duration; - mkv_seekhead *seekhead; - mkv_cues *cues; - mkv_track *tracks; - mkv_attachments *attachments; - - AVPacket cur_audio_pkt; - - int have_attachments; - int have_video; - - int reserve_cues_space; - int cluster_size_limit; - int64_t cues_pos; - int64_t cluster_time_limit; - int is_dash; - int dash_track_number; - int is_live; - int write_crc; - - uint32_t chapter_id_offset; - int wrote_chapters; - - int64_t last_track_timestamp[MAX_TRACKS]; - - int64_t *stream_durations; - int64_t *stream_duration_offsets; - - int allow_raw_vfw; + const AVClass *class; + int mode; + ebml_stored_master info; + ebml_stored_master track; + ebml_stored_master tags; + int64_t segment_offset; + AVIOContext *cluster_bc; + int64_t cluster_pos; ///< file offset of the current Cluster + int64_t cluster_pts; + int64_t duration_offset; + int64_t duration; + mkv_track *tracks; + mkv_seekhead seekhead; + mkv_cues cues; + int64_t cues_pos; + + AVPacket cur_audio_pkt; + + unsigned nb_attachments; + int have_video; + + int wrote_chapters; + int wrote_tags; + + int reserve_cues_space; + int cluster_size_limit; + int64_t cluster_time_limit; + int write_crc; + int is_live; + + int is_dash; + int dash_track_number; + int allow_raw_vfw; + int default_mode; + + uint32_t segment_uid[4]; } MatroskaMuxContext; /** 2 bytes * 7 for EBML IDs, 7 1-byte EBML lengths, 6 1-byte uint, @@ -171,19 +167,15 @@ typedef struct MatroskaMuxContext { * offset, 4 bytes for target EBML ID */ #define MAX_SEEKENTRY_SIZE 21 -/** per-cuepoint-track - 5 1-byte EBML IDs, 5 1-byte EBML sizes, 3 8-byte uint max - * and one 1-byte uint for the track number (this assumes MAX_TRACKS to be <= 255) */ -#define MAX_CUETRACKPOS_SIZE 35 - -/** per-cuepoint - 1 1-byte EBML ID, 1 1-byte EBML size, 8-byte uint max */ -#define MAX_CUEPOINT_CONTENT_SIZE(num_tracks) 10 + MAX_CUETRACKPOS_SIZE * num_tracks +/** 4 * (1-byte EBML ID, 1-byte EBML size, 8-byte uint max) */ +#define MAX_CUETRACKPOS_SIZE 40 /** Seek preroll value for opus */ #define OPUS_SEEK_PREROLL 80000000 static int ebml_id_size(uint32_t id) { - return (av_log2(id + 1) - 1) / 7 + 1; + return (av_log2(id) + 7U) / 8; } static void put_ebml_id(AVIOContext *pb, uint32_t id) @@ -206,39 +198,67 @@ static void put_ebml_size_unknown(AVIOContext *pb, int bytes) } /** - * Calculate how many bytes are needed to represent a given number in EBML. + * Returns how many bytes are needed to represent a number + * as EBML variable length integer. */ static int ebml_num_size(uint64_t num) { - int bytes = 1; - while ((num + 1) >> bytes * 7) + int bytes = 0; + do { bytes++; + } while (num >>= 7); return bytes; } /** - * Write a number in EBML variable length format. + * Calculate how many bytes are needed to represent the length field + * of an EBML element whose payload has a given length. + */ +static int ebml_length_size(uint64_t length) +{ + return ebml_num_size(length + 1); +} + +/** + * Write a number as EBML variable length integer on `bytes` bytes. + * `bytes` is taken literally without checking. + */ +static void put_ebml_num(AVIOContext *pb, uint64_t num, int bytes) +{ + num |= 1ULL << bytes * 7; + for (int i = bytes - 1; i >= 0; i--) + avio_w8(pb, (uint8_t)(num >> i * 8)); +} + +/** + * Write a length as EBML variable length integer. * * @param bytes The number of bytes that need to be used to write the number. - * If zero, any number of bytes can be used. + * If zero, the minimal number of bytes will be used. */ -static void put_ebml_num(AVIOContext *pb, uint64_t num, int bytes) +static void put_ebml_length(AVIOContext *pb, uint64_t length, int bytes) { - int i, needed_bytes = ebml_num_size(num); + int needed_bytes = ebml_length_size(length); // sizes larger than this are currently undefined in EBML - av_assert0(num < (1ULL << 56) - 1); + av_assert0(length < (1ULL << 56) - 1); if (bytes == 0) - // don't care how many bytes are used, so use the min bytes = needed_bytes; - // the bytes needed to write the given size would exceed the bytes - // that we need to use, so write unknown size. This shouldn't happen. + // The bytes needed to write the given size must not exceed + // the bytes that we ought to use. av_assert0(bytes >= needed_bytes); + put_ebml_num(pb, length, bytes); +} - num |= 1ULL << bytes * 7; - for (i = bytes - 1; i >= 0; i--) - avio_w8(pb, (uint8_t)(num >> i * 8)); +/** + * Write a (random) UID with fixed size to make the output more deterministic + */ +static void put_ebml_uid(AVIOContext *pb, uint32_t elementid, uint64_t uid) +{ + put_ebml_id(pb, elementid); + put_ebml_length(pb, 8, 0); + avio_wb64(pb, uid); } static void put_ebml_uint(AVIOContext *pb, uint32_t elementid, uint64_t val) @@ -249,7 +269,7 @@ static void put_ebml_uint(AVIOContext *pb, uint32_t elementid, uint64_t val) bytes++; put_ebml_id(pb, elementid); - put_ebml_num(pb, bytes, 0); + put_ebml_length(pb, bytes, 0); for (i = bytes - 1; i >= 0; i--) avio_w8(pb, (uint8_t)(val >> i * 8)); } @@ -259,10 +279,11 @@ static void put_ebml_sint(AVIOContext *pb, uint32_t elementid, int64_t val) int i, bytes = 1; uint64_t tmp = 2*(val < 0 ? val^-1 : val); - while (tmp>>=8) bytes++; + while (tmp >>= 8) + bytes++; put_ebml_id(pb, elementid); - put_ebml_num(pb, bytes, 0); + put_ebml_length(pb, bytes, 0); for (i = bytes - 1; i >= 0; i--) avio_w8(pb, (uint8_t)(val >> i * 8)); } @@ -270,7 +291,7 @@ static void put_ebml_sint(AVIOContext *pb, uint32_t elementid, int64_t val) static void put_ebml_float(AVIOContext *pb, uint32_t elementid, double val) { put_ebml_id(pb, elementid); - put_ebml_num(pb, 8, 0); + put_ebml_length(pb, 8, 0); avio_wb64(pb, av_double2int(val)); } @@ -278,7 +299,7 @@ static void put_ebml_binary(AVIOContext *pb, uint32_t elementid, const void *buf, int size) { put_ebml_id(pb, elementid); - put_ebml_num(pb, size, 0); + put_ebml_length(pb, size, 0); avio_write(pb, buf, size); } @@ -294,27 +315,28 @@ static void put_ebml_string(AVIOContext *pb, uint32_t elementid, * * @param size The number of bytes to reserve, which must be at least 2. */ -static void put_ebml_void(AVIOContext *pb, uint64_t size) +static void put_ebml_void(AVIOContext *pb, int size) { - int64_t currentpos = avio_tell(pb); - av_assert0(size >= 2); put_ebml_id(pb, EBML_ID_VOID); // we need to subtract the length needed to store the size from the // size we need to reserve so 2 cases, we use 8 bytes to store the // size if possible, 1 byte otherwise - if (size < 10) - put_ebml_num(pb, size - 2, 0); - else - put_ebml_num(pb, size - 9, 8); - ffio_fill(pb, 0, currentpos + size - avio_tell(pb)); + if (size < 10) { + size -= 2; + put_ebml_length(pb, size, 0); + } else { + size -= 9; + put_ebml_length(pb, size, 8); + } + ffio_fill(pb, 0, size); } static ebml_master start_ebml_master(AVIOContext *pb, uint32_t elementid, uint64_t expectedsize) { - int bytes = expectedsize ? ebml_num_size(expectedsize) : 8; + int bytes = expectedsize ? ebml_length_size(expectedsize) : 8; put_ebml_id(pb, elementid); put_ebml_size_unknown(pb, bytes); @@ -327,32 +349,51 @@ static void end_ebml_master(AVIOContext *pb, ebml_master master) if (avio_seek(pb, master.pos - master.sizebytes, SEEK_SET) < 0) return; - put_ebml_num(pb, pos - master.pos, master.sizebytes); + put_ebml_length(pb, pos - master.pos, master.sizebytes); avio_seek(pb, pos, SEEK_SET); } -static int start_ebml_master_crc32(AVIOContext *pb, AVIOContext **dyn_cp, MatroskaMuxContext *mkv, - uint32_t elementid) +static void mkv_add_seekhead_entry(MatroskaMuxContext *mkv, uint32_t elementid, + uint64_t filepos) +{ + mkv_seekhead *seekhead = &mkv->seekhead; + + av_assert1(seekhead->num_entries < MAX_SEEKHEAD_ENTRIES); + + seekhead->entries[seekhead->num_entries].elementid = elementid; + seekhead->entries[seekhead->num_entries++].segmentpos = filepos - mkv->segment_offset; +} + +static int start_ebml_master_crc32(AVIOContext **dyn_cp, MatroskaMuxContext *mkv) { int ret; - if ((ret = avio_open_dyn_buf(dyn_cp)) < 0) + if (!*dyn_cp && (ret = avio_open_dyn_buf(dyn_cp)) < 0) return ret; - put_ebml_id(pb, elementid); if (mkv->write_crc) put_ebml_void(*dyn_cp, 6); /* Reserve space for CRC32 so position/size calculations using avio_tell() take it into account */ return 0; } -static void end_ebml_master_crc32(AVIOContext *pb, AVIOContext **dyn_cp, MatroskaMuxContext *mkv) +static int end_ebml_master_crc32(AVIOContext *pb, AVIOContext **dyn_cp, + MatroskaMuxContext *mkv, uint32_t id, + int length_size, int keep_buffer, + int add_seekentry) { uint8_t *buf, crc[4]; - int size, skip = 0; + int ret, size, skip = 0; + + size = avio_get_dyn_buf(*dyn_cp, &buf); + if ((ret = (*dyn_cp)->error) < 0) + goto fail; + + if (add_seekentry) + mkv_add_seekhead_entry(mkv, id, avio_tell(pb)); - size = avio_close_dyn_buf(*dyn_cp, &buf); - put_ebml_num(pb, size, 0); + put_ebml_id(pb, id); + put_ebml_length(pb, size, length_size); if (mkv->write_crc) { skip = 6; /* Skip reserved 6-byte long void element from the dynamic buffer. */ AV_WL32(crc, av_crc(av_crc_get_table(AV_CRC_32_IEEE_LE), UINT32_MAX, buf + skip, size - skip) ^ UINT32_MAX); @@ -360,23 +401,40 @@ static void end_ebml_master_crc32(AVIOContext *pb, AVIOContext **dyn_cp, Matrosk } avio_write(pb, buf + skip, size - skip); - av_free(buf); - *dyn_cp = NULL; +fail: + if (keep_buffer) { + ffio_reset_dyn_buf(*dyn_cp); + } else { + ffio_free_dyn_buf(dyn_cp); + } + return ret; } /** -* Complete ebml master without destroying the buffer, allowing for later updates -*/ -static void end_ebml_master_crc32_preliminary(AVIOContext *pb, AVIOContext **dyn_cp, MatroskaMuxContext *mkv, - int64_t *pos) + * Output EBML master. Keep the buffer if seekable, allowing for later updates. + * Furthermore always add a SeekHead Entry for this element. + */ +static int end_ebml_master_crc32_tentatively(AVIOContext *pb, + ebml_stored_master *elem, + MatroskaMuxContext *mkv, uint32_t id) { - uint8_t *buf; - int size = avio_get_dyn_buf(*dyn_cp, &buf); + if (IS_SEEKABLE(pb, mkv)) { + uint8_t *buf; + int size = avio_get_dyn_buf(elem->bc, &buf); - *pos = avio_tell(pb); + if (elem->bc->error < 0) + return elem->bc->error; - put_ebml_num(pb, size, 0); - avio_write(pb, buf, size); + elem->pos = avio_tell(pb); + mkv_add_seekhead_entry(mkv, id, elem->pos); + + put_ebml_id(pb, id); + put_ebml_length(pb, size, 0); + avio_write(pb, buf, size); + + return 0; + } else + return end_ebml_master_crc32(pb, &elem->bc, mkv, id, 0, 0, 1); } static void put_xiph_size(AVIOContext *pb, int size) @@ -388,164 +446,91 @@ static void put_xiph_size(AVIOContext *pb, int size) /** * Free the members allocated in the mux context. */ -static void mkv_free(MatroskaMuxContext *mkv) { - uint8_t* buf; - if (mkv->cluster_bc) { - avio_close_dyn_buf(mkv->cluster_bc, &buf); - av_free(buf); - } - if (mkv->info_bc) { - avio_close_dyn_buf(mkv->info_bc, &buf); - av_free(buf); - } - if (mkv->tracks_bc) { - avio_close_dyn_buf(mkv->tracks_bc, &buf); - av_free(buf); - } - if (mkv->tags_bc) { - avio_close_dyn_buf(mkv->tags_bc, &buf); - av_free(buf); - } - if (mkv->seekhead) { - av_freep(&mkv->seekhead->entries); - av_freep(&mkv->seekhead); - } - if (mkv->cues) { - av_freep(&mkv->cues->entries); - av_freep(&mkv->cues); - } - if (mkv->attachments) { - av_freep(&mkv->attachments->entries); - av_freep(&mkv->attachments); - } - av_freep(&mkv->tracks); - av_freep(&mkv->stream_durations); - av_freep(&mkv->stream_duration_offsets); -} - -/** - * Initialize a mkv_seekhead element to be ready to index level 1 Matroska - * elements. If a maximum number of elements is specified, enough space - * will be reserved at the current file location to write a seek head of - * that size. - * - * @param segment_offset The absolute offset to the position in the file - * where the segment begins. - * @param numelements The maximum number of elements that will be indexed - * by this seek head, 0 if unlimited. - */ -static mkv_seekhead *mkv_start_seekhead(AVIOContext *pb, int64_t segment_offset, - int numelements) +static void mkv_deinit(AVFormatContext *s) { - mkv_seekhead *new_seekhead = av_mallocz(sizeof(mkv_seekhead)); - if (!new_seekhead) - return NULL; + MatroskaMuxContext *mkv = s->priv_data; - new_seekhead->segment_offset = segment_offset; + av_packet_unref(&mkv->cur_audio_pkt); - if (numelements > 0) { - new_seekhead->filepos = avio_tell(pb); - // 21 bytes max for a seek entry, 10 bytes max for the SeekHead ID - // and size, 6 bytes for a CRC32 element, and 3 bytes to guarantee - // that an EBML void element will fit afterwards - new_seekhead->reserved_size = numelements * MAX_SEEKENTRY_SIZE + 19; - new_seekhead->max_entries = numelements; - put_ebml_void(pb, new_seekhead->reserved_size); - } - return new_seekhead; + ffio_free_dyn_buf(&mkv->cluster_bc); + ffio_free_dyn_buf(&mkv->info.bc); + ffio_free_dyn_buf(&mkv->track.bc); + ffio_free_dyn_buf(&mkv->tags.bc); + + av_freep(&mkv->cues.entries); + av_freep(&mkv->tracks); } -static int mkv_add_seekhead_entry(mkv_seekhead *seekhead, uint32_t elementid, uint64_t filepos) +/** + * Initialize the SeekHead element to be ready to index level 1 Matroska + * elements. Enough space to write MAX_SEEKHEAD_ENTRIES SeekHead entries + * will be reserved at the current file location. + */ +static void mkv_start_seekhead(MatroskaMuxContext *mkv, AVIOContext *pb) { - mkv_seekhead_entry *entries = seekhead->entries; - - // don't store more elements than we reserved space for - if (seekhead->max_entries > 0 && seekhead->max_entries <= seekhead->num_entries) - return -1; - - entries = av_realloc_array(entries, seekhead->num_entries + 1, sizeof(mkv_seekhead_entry)); - if (!entries) - return AVERROR(ENOMEM); - seekhead->entries = entries; - - seekhead->entries[seekhead->num_entries].elementid = elementid; - seekhead->entries[seekhead->num_entries++].segmentpos = filepos - seekhead->segment_offset; - - return 0; + mkv->seekhead.filepos = avio_tell(pb); + // 21 bytes max for a Seek entry, 6 bytes max for the SeekHead ID + // and size, 6 bytes for a CRC32 element, and 2 bytes to guarantee + // that an EBML void element will fit afterwards + mkv->seekhead.reserved_size = MAX_SEEKHEAD_ENTRIES * MAX_SEEKENTRY_SIZE + 14; + put_ebml_void(pb, mkv->seekhead.reserved_size); } /** - * Write the seek head to the file and free it. If a maximum number of - * elements was specified to mkv_start_seekhead(), the seek head will - * be written at the location reserved for it. Otherwise, it is written - * at the current location in the file. + * Write the SeekHead to the file at the location reserved for it + * and seek to destpos afterwards. When error_on_seek_failure + * is not set, failure to seek to the position designated for the + * SeekHead is not considered an error and it is presumed that + * destpos is the current position; failure to seek to destpos + * afterwards is always an error. * - * @return The file offset where the seekhead was written, - * -1 if an error occurred. + * @return 0 on success, < 0 on error. */ -static int64_t mkv_write_seekhead(AVIOContext *pb, MatroskaMuxContext *mkv) +static int mkv_write_seekhead(AVIOContext *pb, MatroskaMuxContext *mkv, + int error_on_seek_failure, int64_t destpos) { - AVIOContext *dyn_cp; - mkv_seekhead *seekhead = mkv->seekhead; - ebml_master seekentry; - int64_t currentpos; - int i; - - currentpos = avio_tell(pb); + AVIOContext *dyn_cp = NULL; + mkv_seekhead *seekhead = &mkv->seekhead; + int64_t remaining, ret64; + int i, ret; - if (seekhead->reserved_size > 0) { - if (avio_seek(pb, seekhead->filepos, SEEK_SET) < 0) { - currentpos = -1; - goto fail; - } - } + if ((ret64 = avio_seek(pb, seekhead->filepos, SEEK_SET)) < 0) + return error_on_seek_failure ? ret64 : 0; - if (start_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_SEEKHEAD) < 0) { - currentpos = -1; - goto fail; - } + ret = start_ebml_master_crc32(&dyn_cp, mkv); + if (ret < 0) + return ret; for (i = 0; i < seekhead->num_entries; i++) { mkv_seekhead_entry *entry = &seekhead->entries[i]; - - seekentry = start_ebml_master(dyn_cp, MATROSKA_ID_SEEKENTRY, MAX_SEEKENTRY_SIZE); + ebml_master seekentry = start_ebml_master(dyn_cp, MATROSKA_ID_SEEKENTRY, + MAX_SEEKENTRY_SIZE); put_ebml_id(dyn_cp, MATROSKA_ID_SEEKID); - put_ebml_num(dyn_cp, ebml_id_size(entry->elementid), 0); + put_ebml_length(dyn_cp, ebml_id_size(entry->elementid), 0); put_ebml_id(dyn_cp, entry->elementid); put_ebml_uint(dyn_cp, MATROSKA_ID_SEEKPOSITION, entry->segmentpos); end_ebml_master(dyn_cp, seekentry); } - end_ebml_master_crc32(pb, &dyn_cp, mkv); - - if (seekhead->reserved_size > 0) { - uint64_t remaining = seekhead->filepos + seekhead->reserved_size - avio_tell(pb); - put_ebml_void(pb, remaining); - avio_seek(pb, currentpos, SEEK_SET); - - currentpos = seekhead->filepos; - } -fail: - av_freep(&mkv->seekhead->entries); - av_freep(&mkv->seekhead); + ret = end_ebml_master_crc32(pb, &dyn_cp, mkv, + MATROSKA_ID_SEEKHEAD, 0, 0, 0); + if (ret < 0) + return ret; - return currentpos; -} + remaining = seekhead->filepos + seekhead->reserved_size - avio_tell(pb); + put_ebml_void(pb, remaining); -static mkv_cues *mkv_start_cues(int64_t segment_offset) -{ - mkv_cues *cues = av_mallocz(sizeof(mkv_cues)); - if (!cues) - return NULL; + if ((ret64 = avio_seek(pb, destpos, SEEK_SET)) < 0) + return ret64; - cues->segment_offset = segment_offset; - return cues; + return 0; } -static int mkv_add_cuepoint(mkv_cues *cues, int stream, int tracknum, int64_t ts, +static int mkv_add_cuepoint(MatroskaMuxContext *mkv, int stream, int64_t ts, int64_t cluster_pos, int64_t relative_pos, int64_t duration) { + mkv_cues *cues = &mkv->cues; mkv_cuepoint *entries = cues->entries; if (ts < 0) @@ -558,89 +543,80 @@ static int mkv_add_cuepoint(mkv_cues *cues, int stream, int tracknum, int64_t ts cues->entries[cues->num_entries].pts = ts; cues->entries[cues->num_entries].stream_idx = stream; - cues->entries[cues->num_entries].tracknum = tracknum; - cues->entries[cues->num_entries].cluster_pos = cluster_pos - cues->segment_offset; + cues->entries[cues->num_entries].cluster_pos = cluster_pos - mkv->segment_offset; cues->entries[cues->num_entries].relative_pos = relative_pos; cues->entries[cues->num_entries++].duration = duration; return 0; } -static int64_t mkv_write_cues(AVFormatContext *s, mkv_cues *cues, mkv_track *tracks, int num_tracks) +static int mkv_assemble_cues(AVStream **streams, AVIOContext *dyn_cp, + mkv_cues *cues, mkv_track *tracks, int num_tracks) { - MatroskaMuxContext *mkv = s->priv_data; - AVIOContext *dyn_cp, *pb = s->pb; - int64_t currentpos; - int i, j, ret; + AVIOContext *cuepoint; + int ret; - currentpos = avio_tell(pb); - ret = start_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_CUES); + ret = avio_open_dyn_buf(&cuepoint); if (ret < 0) return ret; - for (i = 0; i < cues->num_entries; i++) { - ebml_master cuepoint, track_positions; - mkv_cuepoint *entry = &cues->entries[i]; + for (mkv_cuepoint *entry = cues->entries, *end = entry + cues->num_entries; + entry < end;) { uint64_t pts = entry->pts; - int ctp_nb = 0; + uint8_t *buf; + int size; - // Calculate the number of entries, so we know the element size - for (j = 0; j < num_tracks; j++) - tracks[j].has_cue = 0; - for (j = 0; j < cues->num_entries - i && entry[j].pts == pts; j++) { - int tracknum = entry[j].stream_idx; - av_assert0(tracknum>=0 && tracknumstreams[tracknum]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) - continue; - tracks[tracknum].has_cue = 1; - ctp_nb ++; - } - - cuepoint = start_ebml_master(dyn_cp, MATROSKA_ID_POINTENTRY, MAX_CUEPOINT_CONTENT_SIZE(ctp_nb)); - put_ebml_uint(dyn_cp, MATROSKA_ID_CUETIME, pts); + put_ebml_uint(cuepoint, MATROSKA_ID_CUETIME, pts); // put all the entries from different tracks that have the exact same // timestamp into the same CuePoint - for (j = 0; j < num_tracks; j++) + for (int j = 0; j < num_tracks; j++) tracks[j].has_cue = 0; - for (j = 0; j < cues->num_entries - i && entry[j].pts == pts; j++) { - int tracknum = entry[j].stream_idx; - av_assert0(tracknum>=0 && tracknumstreams[tracknum]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) + do { + ebml_master track_positions; + int idx = entry->stream_idx; + + av_assert0(idx >= 0 && idx < num_tracks); + if (tracks[idx].has_cue && streams[idx]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) continue; - tracks[tracknum].has_cue = 1; - track_positions = start_ebml_master(dyn_cp, MATROSKA_ID_CUETRACKPOSITION, MAX_CUETRACKPOS_SIZE); - put_ebml_uint(dyn_cp, MATROSKA_ID_CUETRACK , entry[j].tracknum ); - put_ebml_uint(dyn_cp, MATROSKA_ID_CUECLUSTERPOSITION , entry[j].cluster_pos); - put_ebml_uint(dyn_cp, MATROSKA_ID_CUERELATIVEPOSITION, entry[j].relative_pos); - if (entry[j].duration != -1) - put_ebml_uint(dyn_cp, MATROSKA_ID_CUEDURATION , entry[j].duration); - end_ebml_master(dyn_cp, track_positions); - } - i += j - 1; - end_ebml_master(dyn_cp, cuepoint); + tracks[idx].has_cue = 1; + track_positions = start_ebml_master(cuepoint, MATROSKA_ID_CUETRACKPOSITION, MAX_CUETRACKPOS_SIZE); + put_ebml_uint(cuepoint, MATROSKA_ID_CUETRACK , tracks[idx].track_num); + put_ebml_uint(cuepoint, MATROSKA_ID_CUECLUSTERPOSITION , entry->cluster_pos); + put_ebml_uint(cuepoint, MATROSKA_ID_CUERELATIVEPOSITION, entry->relative_pos); + if (entry->duration != -1) + put_ebml_uint(cuepoint, MATROSKA_ID_CUEDURATION , entry->duration); + end_ebml_master(cuepoint, track_positions); + } while (++entry < end && entry->pts == pts); + size = avio_get_dyn_buf(cuepoint, &buf); + if ((ret = cuepoint->error) < 0) + break; + put_ebml_binary(dyn_cp, MATROSKA_ID_POINTENTRY, buf, size); + ffio_reset_dyn_buf(cuepoint); } - end_ebml_master_crc32(pb, &dyn_cp, mkv); + ffio_free_dyn_buf(&cuepoint); - return currentpos; + return ret; } -static int put_xiph_codecpriv(AVFormatContext *s, AVIOContext *pb, AVCodecParameters *par) +static int put_xiph_codecpriv(AVFormatContext *s, AVIOContext *pb, + const AVCodecParameters *par) { const uint8_t *header_start[3]; int header_len[3]; int first_header_size; - int j; + int err, j; if (par->codec_id == AV_CODEC_ID_VORBIS) first_header_size = 30; else first_header_size = 42; - if (avpriv_split_xiph_headers(par->extradata, par->extradata_size, - first_header_size, header_start, header_len) < 0) { + err = avpriv_split_xiph_headers(par->extradata, par->extradata_size, + first_header_size, header_start, header_len); + if (err < 0) { av_log(s, AV_LOG_ERROR, "Extradata corrupt.\n"); - return -1; + return err; } avio_w8(pb, 2); // number packets - 1 @@ -653,17 +629,17 @@ static int put_xiph_codecpriv(AVFormatContext *s, AVIOContext *pb, AVCodecParame return 0; } -static int put_wv_codecpriv(AVIOContext *pb, AVCodecParameters *par) +static int put_wv_codecpriv(AVIOContext *pb, const AVCodecParameters *par) { if (par->extradata && par->extradata_size == 2) avio_write(pb, par->extradata, 2); else - avio_wl16(pb, 0x403); // fallback to the version mentioned in matroska specs + avio_wl16(pb, 0x410); // fallback to the most recent version return 0; } -static int put_flac_codecpriv(AVFormatContext *s, - AVIOContext *pb, AVCodecParameters *par) +static int put_flac_codecpriv(AVFormatContext *s, AVIOContext *pb, + const AVCodecParameters *par) { int write_comment = (par->channel_layout && !(par->channel_layout & ~0x3ffffULL) && @@ -678,53 +654,40 @@ static int put_flac_codecpriv(AVFormatContext *s, const char *vendor = (s->flags & AVFMT_FLAG_BITEXACT) ? "Lavf" : LIBAVFORMAT_IDENT; AVDictionary *dict = NULL; - uint8_t buf[32], *data, *p; + uint8_t buf[32]; int64_t len; snprintf(buf, sizeof(buf), "0x%"PRIx64, par->channel_layout); av_dict_set(&dict, "WAVEFORMATEXTENSIBLE_CHANNEL_MASK", buf, 0); len = ff_vorbiscomment_length(dict, vendor, NULL, 0); - if (len >= ((1<<24) - 4)) { - av_dict_free(&dict); - return AVERROR(EINVAL); - } - - data = av_malloc(len + 4); - if (!data) { - av_dict_free(&dict); - return AVERROR(ENOMEM); - } + av_assert1(len < (1 << 24) - 4); - data[0] = 0x84; - AV_WB24(data + 1, len); + avio_w8(pb, 0x84); + avio_wb24(pb, len); - p = data + 4; - ff_vorbiscomment_write(&p, &dict, vendor, NULL, 0); + ff_vorbiscomment_write(pb, dict, vendor, NULL, 0); - avio_write(pb, data, len + 4); - - av_freep(&data); av_dict_free(&dict); } return 0; } -static int get_aac_sample_rates(AVFormatContext *s, uint8_t *extradata, int extradata_size, +static int get_aac_sample_rates(AVFormatContext *s, MatroskaMuxContext *mkv, + const uint8_t *extradata, int extradata_size, int *sample_rate, int *output_sample_rate) { MPEG4AudioConfig mp4ac; int ret; - ret = avpriv_mpeg4audio_get_config(&mp4ac, extradata, - extradata_size * 8, 1); + ret = avpriv_mpeg4audio_get_config2(&mp4ac, extradata, extradata_size, 1, s); /* Don't abort if the failure is because of missing extradata. Assume in that * case a bitstream filter will provide the muxer with the extradata in the * first packet. * Abort however if s->pb is not seekable, as we would not be able to seek back * to write the sample rate elements once the extradata shows up, anyway. */ - if (ret < 0 && (extradata_size || !(s->pb->seekable & AVIO_SEEKABLE_NORMAL))) { + if (ret < 0 && (extradata_size || !IS_SEEKABLE(s->pb, mkv))) { av_log(s, AV_LOG_ERROR, "Error parsing AAC extradata, unable to determine samplerate.\n"); return AVERROR(EINVAL); @@ -745,7 +708,7 @@ static int get_aac_sample_rates(AVFormatContext *s, uint8_t *extradata, int extr } static int mkv_write_native_codecprivate(AVFormatContext *s, AVIOContext *pb, - AVCodecParameters *par, + const AVCodecParameters *par, AVIOContext *dyn_cp) { switch (par->codec_id) { @@ -760,9 +723,8 @@ static int mkv_write_native_codecprivate(AVFormatContext *s, AVIOContext *pb, return ff_isom_write_avcc(dyn_cp, par->extradata, par->extradata_size); case AV_CODEC_ID_HEVC: - ff_isom_write_hvcc(dyn_cp, par->extradata, - par->extradata_size, 0); - return 0; + return ff_isom_write_hvcc(dyn_cp, par->extradata, + par->extradata_size, 0); case AV_CODEC_ID_AV1: if (par->extradata_size) return ff_isom_write_av1c(dyn_cp, par->extradata, @@ -856,24 +818,28 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb, ff_put_wav_header(s, dyn_cp, par, FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX); } - codecpriv_size = avio_close_dyn_buf(dyn_cp, &codecpriv); - if (codecpriv_size) - put_ebml_binary(pb, MATROSKA_ID_CODECPRIVATE, codecpriv, - codecpriv_size); - av_free(codecpriv); + if (ret >= 0) { + codecpriv_size = avio_get_dyn_buf(dyn_cp, &codecpriv); + if ((ret = dyn_cp->error) >= 0 && codecpriv_size) + put_ebml_binary(pb, MATROSKA_ID_CODECPRIVATE, codecpriv, + codecpriv_size); + } + ffio_free_dyn_buf(&dyn_cp); return ret; } -static int mkv_write_video_color(AVIOContext *pb, AVCodecParameters *par, AVStream *st) { - AVIOContext *dyn_cp; - uint8_t *colorinfo_ptr; - int side_data_size = 0; - int ret, colorinfo_size; - const uint8_t *side_data; +static void mkv_write_video_color(AVIOContext *pb, const AVStream *st, + const AVCodecParameters *par) +{ + /* 18 Elements with two bytes ID, one byte length field, 8 bytes payload + * a master element with two bytes ID and one byte length field + * plus another byte to stay clear of the end. */ + uint8_t colour[(2 + 1 + 8) * 18 + (2 + 1) + 1]; + AVIOContext buf, *dyn_cp = &buf; + int colorinfo_size; + const void *side_data; - ret = avio_open_dyn_buf(&dyn_cp); - if (ret < 0) - return ret; + ffio_init_context(dyn_cp, colour, sizeof(colour), 1, NULL, NULL, NULL, NULL); if (par->color_trc != AVCOL_TRC_UNSPECIFIED && par->color_trc < AVCOL_TRC_NB) { @@ -902,21 +868,19 @@ static int mkv_write_video_color(AVIOContext *pb, AVCodecParameters *par, AVStre } side_data = av_stream_get_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, - &side_data_size); - if (side_data_size) { - const AVContentLightMetadata *metadata = - (const AVContentLightMetadata*)side_data; + NULL); + if (side_data) { + const AVContentLightMetadata *metadata = side_data; put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORMAXCLL, metadata->MaxCLL); put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORMAXFALL, metadata->MaxFALL); } side_data = av_stream_get_side_data(st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, - &side_data_size); - if (side_data_size == sizeof(AVMasteringDisplayMetadata)) { + NULL); + if (side_data) { ebml_master meta_element = start_ebml_master( - dyn_cp, MATROSKA_ID_VIDEOCOLORMASTERINGMETA, 0); - const AVMasteringDisplayMetadata *metadata = - (const AVMasteringDisplayMetadata*)side_data; + dyn_cp, MATROSKA_ID_VIDEOCOLORMASTERINGMETA, 10 * (2 + 1 + 8)); + const AVMasteringDisplayMetadata *metadata = side_data; if (metadata->has_primaries) { put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_RX, av_q2d(metadata->display_primaries[0][0])); @@ -944,91 +908,76 @@ static int mkv_write_video_color(AVIOContext *pb, AVCodecParameters *par, AVStre end_ebml_master(dyn_cp, meta_element); } - colorinfo_size = avio_close_dyn_buf(dyn_cp, &colorinfo_ptr); - if (colorinfo_size) { - ebml_master colorinfo = start_ebml_master(pb, MATROSKA_ID_VIDEOCOLOR, colorinfo_size); - avio_write(pb, colorinfo_ptr, colorinfo_size); - end_ebml_master(pb, colorinfo); - } - av_free(colorinfo_ptr); - return 0; + colorinfo_size = avio_tell(dyn_cp); + if (colorinfo_size) + put_ebml_binary(pb, MATROSKA_ID_VIDEOCOLOR, colour, colorinfo_size); } -static int mkv_write_video_projection(AVFormatContext *s, AVIOContext *pb, - AVStream *st) +static void mkv_write_video_projection(AVFormatContext *s, AVIOContext *pb, + const AVStream *st) { - AVIOContext b; - AVIOContext *dyn_cp; - int side_data_size = 0; - int ret, projection_size; - uint8_t *projection_ptr; + ebml_master projection; uint8_t private[20]; const AVSphericalMapping *spherical = (const AVSphericalMapping *)av_stream_get_side_data(st, AV_PKT_DATA_SPHERICAL, - &side_data_size); + NULL); - if (!side_data_size) - return 0; + if (!spherical) + return; - ret = avio_open_dyn_buf(&dyn_cp); - if (ret < 0) - return ret; + 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; + } + + // Maximally 4 8-byte elements with id-length 2 + 1 byte length field + // and the private data of the AV_SPHERICAL_EQUIRECTANGULAR_TILE case + projection = start_ebml_master(pb, MATROSKA_ID_VIDEOPROJECTION, + 4 * (2 + 1 + 8) + (2 + 1 + 20)); switch (spherical->projection) { case AV_SPHERICAL_EQUIRECTANGULAR: - put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOPROJECTIONTYPE, + put_ebml_uint(pb, MATROSKA_ID_VIDEOPROJECTIONTYPE, MATROSKA_VIDEO_PROJECTION_TYPE_EQUIRECTANGULAR); break; case AV_SPHERICAL_EQUIRECTANGULAR_TILE: - ffio_init_context(&b, private, 20, 1, NULL, NULL, NULL, NULL); - put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOPROJECTIONTYPE, + put_ebml_uint(pb, MATROSKA_ID_VIDEOPROJECTIONTYPE, MATROSKA_VIDEO_PROJECTION_TYPE_EQUIRECTANGULAR); - avio_wb32(&b, 0); // version + flags - avio_wb32(&b, spherical->bound_top); - avio_wb32(&b, spherical->bound_bottom); - avio_wb32(&b, spherical->bound_left); - avio_wb32(&b, spherical->bound_right); - put_ebml_binary(dyn_cp, MATROSKA_ID_VIDEOPROJECTIONPRIVATE, - private, avio_tell(&b)); + AV_WB32(private, 0); // version + flags + AV_WB32(private + 4, spherical->bound_top); + AV_WB32(private + 8, spherical->bound_bottom); + AV_WB32(private + 12, spherical->bound_left); + AV_WB32(private + 16, spherical->bound_right); + put_ebml_binary(pb, MATROSKA_ID_VIDEOPROJECTIONPRIVATE, + private, 20); break; case AV_SPHERICAL_CUBEMAP: - ffio_init_context(&b, private, 12, 1, NULL, NULL, NULL, NULL); - put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOPROJECTIONTYPE, + put_ebml_uint(pb, MATROSKA_ID_VIDEOPROJECTIONTYPE, MATROSKA_VIDEO_PROJECTION_TYPE_CUBEMAP); - avio_wb32(&b, 0); // version + flags - avio_wb32(&b, 0); // layout - avio_wb32(&b, spherical->padding); - put_ebml_binary(dyn_cp, MATROSKA_ID_VIDEOPROJECTIONPRIVATE, - private, avio_tell(&b)); + AV_WB32(private, 0); // version + flags + AV_WB32(private + 4, 0); // layout + AV_WB32(private + 8, spherical->padding); + put_ebml_binary(pb, MATROSKA_ID_VIDEOPROJECTIONPRIVATE, + private, 12); break; default: - av_log(s, AV_LOG_WARNING, "Unknown projection type\n"); - goto end; + av_assert0(0); } if (spherical->yaw) - put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOPROJECTIONPOSEYAW, + put_ebml_float(pb, MATROSKA_ID_VIDEOPROJECTIONPOSEYAW, (double) spherical->yaw / (1 << 16)); if (spherical->pitch) - put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOPROJECTIONPOSEPITCH, + put_ebml_float(pb, MATROSKA_ID_VIDEOPROJECTIONPOSEPITCH, (double) spherical->pitch / (1 << 16)); if (spherical->roll) - put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOPROJECTIONPOSEROLL, + put_ebml_float(pb, MATROSKA_ID_VIDEOPROJECTIONPOSEROLL, (double) spherical->roll / (1 << 16)); -end: - projection_size = avio_close_dyn_buf(dyn_cp, &projection_ptr); - if (projection_size) { - ebml_master projection = start_ebml_master(pb, - MATROSKA_ID_VIDEOPROJECTION, - projection_size); - avio_write(pb, projection_ptr, projection_size); - end_ebml_master(pb, projection); - } - av_freep(&projection_ptr); - - return 0; + end_ebml_master(pb, projection); } static void mkv_write_field_order(AVIOContext *pb, int mode, @@ -1073,10 +1022,9 @@ static void mkv_write_field_order(AVIOContext *pb, int mode, static int mkv_write_stereo_mode(AVFormatContext *s, AVIOContext *pb, AVStream *st, int mode, int *h_width, int *h_height) { - int i; - int ret = 0; - AVDictionaryEntry *tag; + const AVDictionaryEntry *tag; MatroskaVideoStereoModeType format = MATROSKA_VIDEO_STEREOMODE_TYPE_NB; + const AVStereo3D *stereo; *h_width = 1; *h_height = 1; @@ -1085,7 +1033,7 @@ static int mkv_write_stereo_mode(AVFormatContext *s, AVIOContext *pb, (tag = av_dict_get( s->metadata, "stereo_mode", NULL, 0))) { int stereo_mode = atoi(tag->value); - for (i=0; ivalue, ff_matroska_video_stereo_mode[i])){ stereo_mode = i; break; @@ -1099,57 +1047,52 @@ static int mkv_write_stereo_mode(AVFormatContext *s, AVIOContext *pb, } } - // iterate to find the stereo3d side data - for (i = 0; i < st->nb_side_data; i++) { - AVPacketSideData sd = st->side_data[i]; - if (sd.type == AV_PKT_DATA_STEREO3D) { - AVStereo3D *stereo = (AVStereo3D *)sd.data; - - 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; - } + 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) - return ret; + return 0; // if webm, do not write unsupported modes if ((mode == MODE_WEBM && @@ -1158,75 +1101,56 @@ static int mkv_write_stereo_mode(AVFormatContext *s, AVIOContext *pb, || format >= MATROSKA_VIDEO_STEREOMODE_TYPE_NB) { av_log(s, AV_LOG_ERROR, "The specified stereo mode is not valid.\n"); - format = MATROSKA_VIDEO_STEREOMODE_TYPE_NB; return AVERROR(EINVAL); } // write StereoMode if format is valid put_ebml_uint(pb, MATROSKA_ID_VIDEOSTEREOMODE, format); - return ret; + return 0; } static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, - int i, AVIOContext *pb, int default_stream_exists) + AVStream *st, mkv_track *track, AVIOContext *pb, + int is_default) { - AVStream *st = s->streams[i]; AVCodecParameters *par = st->codecpar; - ebml_master subinfo, track; + ebml_master subinfo, track_master; int native_id = 0; int qt_id = 0; - int bit_depth = av_get_bits_per_sample(par->codec_id); + int bit_depth; int sample_rate = par->sample_rate; int output_sample_rate = 0; int display_width_div = 1; int display_height_div = 1; int j, ret; - AVDictionaryEntry *tag; + const AVDictionaryEntry *tag; - if (par->codec_type == AVMEDIA_TYPE_ATTACHMENT) { - mkv->have_attachments = 1; + if (par->codec_type == AVMEDIA_TYPE_ATTACHMENT) return 0; - } - - if (par->codec_type == AVMEDIA_TYPE_AUDIO) { - if (!bit_depth && par->codec_id != AV_CODEC_ID_ADPCM_G726) { - if (par->bits_per_raw_sample) - bit_depth = par->bits_per_raw_sample; - else - bit_depth = av_get_bytes_per_sample(par->format) << 3; - } - if (!bit_depth) - bit_depth = par->bits_per_coded_sample; - } if (par->codec_id == AV_CODEC_ID_AAC) { - ret = get_aac_sample_rates(s, par->extradata, par->extradata_size, &sample_rate, - &output_sample_rate); + ret = get_aac_sample_rates(s, mkv, par->extradata, par->extradata_size, + &sample_rate, &output_sample_rate); if (ret < 0) return ret; } - track = start_ebml_master(pb, MATROSKA_ID_TRACKENTRY, 0); - put_ebml_uint (pb, MATROSKA_ID_TRACKNUMBER, - mkv->is_dash ? mkv->dash_track_number : i + 1); - put_ebml_uint (pb, MATROSKA_ID_TRACKUID, - mkv->is_dash ? mkv->dash_track_number : i + 1); - put_ebml_uint (pb, MATROSKA_ID_TRACKFLAGLACING , 0); // no lacing (yet) + track_master = start_ebml_master(pb, MATROSKA_ID_TRACKENTRY, 0); + put_ebml_uint(pb, MATROSKA_ID_TRACKNUMBER, track->track_num); + put_ebml_uid (pb, MATROSKA_ID_TRACKUID, track->uid); + put_ebml_uint(pb, MATROSKA_ID_TRACKFLAGLACING, 0); // no lacing (yet) if ((tag = av_dict_get(st->metadata, "title", NULL, 0))) put_ebml_string(pb, MATROSKA_ID_TRACKNAME, tag->value); tag = av_dict_get(st->metadata, "language", NULL, 0); - if (mkv->mode != MODE_WEBM || par->codec_id != AV_CODEC_ID_WEBVTT) { - put_ebml_string(pb, MATROSKA_ID_TRACKLANGUAGE, tag && tag->value ? tag->value:"und"); - } else if (tag && tag->value) { - put_ebml_string(pb, MATROSKA_ID_TRACKLANGUAGE, tag->value); - } + put_ebml_string(pb, MATROSKA_ID_TRACKLANGUAGE, + tag && tag->value ? tag->value : "und"); // The default value for TRACKFLAGDEFAULT is 1, so add element // if we need to clear it. - if (default_stream_exists && !(st->disposition & AV_DISPOSITION_DEFAULT)) - put_ebml_uint(pb, MATROSKA_ID_TRACKFLAGDEFAULT, !!(st->disposition & AV_DISPOSITION_DEFAULT)); + if (!is_default) + put_ebml_uint(pb, MATROSKA_ID_TRACKFLAGDEFAULT, 0); if (st->disposition & AV_DISPOSITION_FORCED) put_ebml_uint(pb, MATROSKA_ID_TRACKFLAGFORCED, 1); @@ -1286,24 +1210,6 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, } } - if (par->codec_type == AVMEDIA_TYPE_AUDIO && par->initial_padding && par->codec_id == AV_CODEC_ID_OPUS) { - int64_t codecdelay = av_rescale_q(par->initial_padding, - (AVRational){ 1, 48000 }, - (AVRational){ 1, 1000000000 }); - if (codecdelay < 0) { - av_log(s, AV_LOG_ERROR, "Initial padding is invalid\n"); - return AVERROR(EINVAL); - } -// mkv->tracks[i].ts_offset = av_rescale_q(par->initial_padding, -// (AVRational){ 1, par->sample_rate }, -// st->time_base); - - put_ebml_uint(pb, MATROSKA_ID_CODECDELAY, codecdelay); - } - if (par->codec_id == AV_CODEC_ID_OPUS) { - put_ebml_uint(pb, MATROSKA_ID_SEEKPREROLL, OPUS_SEEK_PREROLL); - } - switch (par->codec_type) { case AVMEDIA_TYPE_VIDEO: mkv->have_video = 1; @@ -1312,6 +1218,9 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, 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); if (!native_id && ff_codec_get_tag(ff_codec_movvideo_tags, par->codec_id) && @@ -1326,7 +1235,7 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, else if (!native_id) { // if there is no mkv-specific codec ID, use VFW mode put_ebml_string(pb, MATROSKA_ID_CODECID, "V_MS/VFW/FOURCC"); - mkv->tracks[i].write_dts = 1; + track->write_dts = 1; s->internal->avoid_negative_ts_use_pts = 0; } @@ -1384,29 +1293,53 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, uint32_t color_space = av_le2ne32(par->codec_tag); put_ebml_binary(pb, MATROSKA_ID_VIDEOCOLORSPACE, &color_space, sizeof(color_space)); } - ret = mkv_write_video_color(pb, par, st); - if (ret < 0) - return ret; - ret = mkv_write_video_projection(s, pb, st); - if (ret < 0) - return ret; + mkv_write_video_color(pb, st, par); + mkv_write_video_projection(s, pb, st); + end_ebml_master(pb, subinfo); break; case AVMEDIA_TYPE_AUDIO: + if (par->initial_padding && par->codec_id == AV_CODEC_ID_OPUS) { + int64_t codecdelay = av_rescale_q(par->initial_padding, + (AVRational){ 1, 48000 }, + (AVRational){ 1, 1000000000 }); + if (codecdelay < 0) { + av_log(s, AV_LOG_ERROR, "Initial padding is invalid\n"); + return AVERROR(EINVAL); + } +// track->ts_offset = av_rescale_q(par->initial_padding, +// (AVRational){ 1, par->sample_rate }, +// st->time_base); + + put_ebml_uint(pb, MATROSKA_ID_CODECDELAY, codecdelay); + } + if (par->codec_id == AV_CODEC_ID_OPUS) + put_ebml_uint(pb, MATROSKA_ID_SEEKPREROLL, OPUS_SEEK_PREROLL); + put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_AUDIO); if (!native_id) // no mkv-specific ID, use ACM mode put_ebml_string(pb, MATROSKA_ID_CODECID, "A_MS/ACM"); - subinfo = start_ebml_master(pb, MATROSKA_ID_TRACKAUDIO, 0); + subinfo = start_ebml_master(pb, MATROSKA_ID_TRACKAUDIO, 6 + 4 * 9); put_ebml_uint (pb, MATROSKA_ID_AUDIOCHANNELS , par->channels); - mkv->tracks[i].sample_rate_offset = avio_tell(pb); + track->sample_rate_offset = avio_tell(pb); put_ebml_float (pb, MATROSKA_ID_AUDIOSAMPLINGFREQ, sample_rate); if (output_sample_rate) put_ebml_float(pb, MATROSKA_ID_AUDIOOUTSAMPLINGFREQ, output_sample_rate); + + bit_depth = av_get_bits_per_sample(par->codec_id); + if (!bit_depth && par->codec_id != AV_CODEC_ID_ADPCM_G726) { + if (par->bits_per_raw_sample) + bit_depth = par->bits_per_raw_sample; + else + bit_depth = av_get_bytes_per_sample(par->format) << 3; + } + if (!bit_depth) + bit_depth = par->bits_per_coded_sample; if (bit_depth) put_ebml_uint(pb, MATROSKA_ID_AUDIOBITDEPTH, bit_depth); end_ebml_master(pb, subinfo); @@ -1429,13 +1362,13 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, } if (mkv->mode != MODE_WEBM || par->codec_id != AV_CODEC_ID_WEBVTT) { - mkv->tracks[i].codecpriv_offset = avio_tell(pb); + track->codecpriv_offset = avio_tell(pb); ret = mkv_write_codecprivate(s, pb, par, native_id, qt_id); if (ret < 0) return ret; } - end_ebml_master(pb, track); + end_ebml_master(pb, track_master); return 0; } @@ -1444,105 +1377,73 @@ static int mkv_write_tracks(AVFormatContext *s) { MatroskaMuxContext *mkv = s->priv_data; AVIOContext *pb = s->pb; - int i, ret, default_stream_exists = 0; + int i, ret, video_default_idx, audio_default_idx, subtitle_default_idx; - ret = mkv_add_seekhead_entry(mkv->seekhead, MATROSKA_ID_TRACKS, avio_tell(pb)); - if (ret < 0) - return ret; + if (mkv->nb_attachments == s->nb_streams) + return 0; - ret = start_ebml_master_crc32(pb, &mkv->tracks_bc, mkv, MATROSKA_ID_TRACKS); + ret = start_ebml_master_crc32(&mkv->track.bc, mkv); if (ret < 0) return ret; - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - default_stream_exists |= st->disposition & AV_DISPOSITION_DEFAULT; + if (mkv->default_mode != DEFAULT_MODE_PASSTHROUGH) { + int video_idx, audio_idx, subtitle_idx; + + video_idx = video_default_idx = + audio_idx = audio_default_idx = + subtitle_idx = subtitle_default_idx = -1; + + for (i = s->nb_streams - 1; i >= 0; i--) { + AVStream *st = s->streams[i]; + + switch (st->codecpar->codec_type) { +#define CASE(type, variable) \ + case AVMEDIA_TYPE_ ## type: \ + variable ## _idx = i; \ + if (st->disposition & AV_DISPOSITION_DEFAULT) \ + variable ## _default_idx = i; \ + break; + CASE(VIDEO, video) + CASE(AUDIO, audio) + CASE(SUBTITLE, subtitle) +#undef CASE + } + } + + video_default_idx = FFMAX(video_default_idx, video_idx); + audio_default_idx = FFMAX(audio_default_idx, audio_idx); + if (mkv->default_mode != DEFAULT_MODE_INFER_NO_SUBS) + subtitle_default_idx = FFMAX(subtitle_default_idx, subtitle_idx); } for (i = 0; i < s->nb_streams; i++) { - ret = mkv_write_track(s, mkv, i, mkv->tracks_bc, default_stream_exists); + AVStream *st = s->streams[i]; + int is_default = mkv->default_mode == DEFAULT_MODE_PASSTHROUGH ? + st->disposition & AV_DISPOSITION_DEFAULT : + i == video_default_idx || i == audio_default_idx || + i == subtitle_default_idx; + ret = mkv_write_track(s, mkv, st, &mkv->tracks[i], + mkv->track.bc, is_default); if (ret < 0) return ret; } - if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) - end_ebml_master_crc32_preliminary(pb, &mkv->tracks_bc, mkv, &mkv->tracks_pos); - else - end_ebml_master_crc32(pb, &mkv->tracks_bc, mkv); - - return 0; + return end_ebml_master_crc32_tentatively(pb, &mkv->track, mkv, + MATROSKA_ID_TRACKS); } -static int mkv_write_chapters(AVFormatContext *s) +static int mkv_write_simpletag(AVIOContext *pb, const AVDictionaryEntry *t) { - MatroskaMuxContext *mkv = s->priv_data; - AVIOContext *dyn_cp, *pb = s->pb; - ebml_master editionentry; - AVRational scale = {1, 1E9}; - int i, ret; - - if (!s->nb_chapters || mkv->wrote_chapters) - return 0; + uint8_t *key = av_strdup(t->key); + uint8_t *p = key; + const uint8_t *lang = NULL; + ebml_master tag; - ret = mkv_add_seekhead_entry(mkv->seekhead, MATROSKA_ID_CHAPTERS, avio_tell(pb)); - if (ret < 0) return ret; + if (!key) + return AVERROR(ENOMEM); - ret = start_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_CHAPTERS); - if (ret < 0) return ret; - - editionentry = start_ebml_master(dyn_cp, MATROSKA_ID_EDITIONENTRY, 0); - if (mkv->mode != MODE_WEBM) { - put_ebml_uint(dyn_cp, MATROSKA_ID_EDITIONFLAGDEFAULT, 1); - put_ebml_uint(dyn_cp, MATROSKA_ID_EDITIONFLAGHIDDEN , 0); - } - for (i = 0; i < s->nb_chapters; i++) { - ebml_master chapteratom, chapterdisplay; - AVChapter *c = s->chapters[i]; - int64_t chapterstart = av_rescale_q(c->start, c->time_base, scale); - int64_t chapterend = av_rescale_q(c->end, c->time_base, scale); - AVDictionaryEntry *t = NULL; - if (chapterstart < 0 || chapterstart > chapterend || chapterend < 0) { - av_log(s, AV_LOG_ERROR, - "Invalid chapter start (%"PRId64") or end (%"PRId64").\n", - chapterstart, chapterend); - return AVERROR_INVALIDDATA; - } - - chapteratom = start_ebml_master(dyn_cp, MATROSKA_ID_CHAPTERATOM, 0); - put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERUID, c->id + mkv->chapter_id_offset); - put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERTIMESTART, chapterstart); - put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERTIMEEND, chapterend); - if (mkv->mode != MODE_WEBM) { - put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERFLAGHIDDEN , 0); - put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERFLAGENABLED, 1); - } - if ((t = av_dict_get(c->metadata, "title", NULL, 0))) { - chapterdisplay = start_ebml_master(dyn_cp, MATROSKA_ID_CHAPTERDISPLAY, 0); - put_ebml_string(dyn_cp, MATROSKA_ID_CHAPSTRING, t->value); - put_ebml_string(dyn_cp, MATROSKA_ID_CHAPLANG , "und"); - end_ebml_master(dyn_cp, chapterdisplay); - } - end_ebml_master(dyn_cp, chapteratom); - } - end_ebml_master(dyn_cp, editionentry); - end_ebml_master_crc32(pb, &dyn_cp, mkv); - - mkv->wrote_chapters = 1; - return 0; -} - -static int mkv_write_simpletag(AVIOContext *pb, AVDictionaryEntry *t) -{ - uint8_t *key = av_strdup(t->key); - uint8_t *p = key; - const uint8_t *lang = NULL; - ebml_master tag; - - if (!key) - return AVERROR(ENOMEM); - - if ((p = strrchr(p, '-')) && - (lang = ff_convert_lang_to(p + 1, AV_LANG_ISO639_2_BIBL))) - *p = 0; + if ((p = strrchr(p, '-')) && + (lang = ff_convert_lang_to(p + 1, AV_LANG_ISO639_2_BIBL))) + *p = 0; p = key; while (*p) { @@ -1564,27 +1465,23 @@ static int mkv_write_simpletag(AVIOContext *pb, AVDictionaryEntry *t) return 0; } -static int mkv_write_tag_targets(AVFormatContext *s, uint32_t elementid, - unsigned int uid, ebml_master *tag) +static int mkv_write_tag_targets(MatroskaMuxContext *mkv, AVIOContext **pb, + ebml_master *tag, uint32_t elementid, uint64_t uid) { - AVIOContext *pb; - MatroskaMuxContext *mkv = s->priv_data; ebml_master targets; int ret; - if (!mkv->tags_bc) { - ret = mkv_add_seekhead_entry(mkv->seekhead, MATROSKA_ID_TAGS, avio_tell(s->pb)); - if (ret < 0) return ret; - - start_ebml_master_crc32(s->pb, &mkv->tags_bc, mkv, MATROSKA_ID_TAGS); + if (!*pb) { + ret = start_ebml_master_crc32(pb, mkv); + if (ret < 0) + return ret; } - pb = mkv->tags_bc; - *tag = start_ebml_master(pb, MATROSKA_ID_TAG, 0); - targets = start_ebml_master(pb, MATROSKA_ID_TAGTARGETS, 0); + *tag = start_ebml_master(*pb, MATROSKA_ID_TAG, 0); + targets = start_ebml_master(*pb, MATROSKA_ID_TAGTARGETS, 4 + 1 + 8); if (elementid) - put_ebml_uint(pb, elementid, uid); - end_ebml_master(pb, targets); + put_ebml_uid(*pb, elementid, uid); + end_ebml_master(*pb, targets); return 0; } @@ -1602,33 +1499,35 @@ static int mkv_check_tag_name(const char *name, uint32_t elementid) av_strcasecmp(name, "mimetype"))); } -static int mkv_write_tag(AVFormatContext *s, AVDictionary *m, uint32_t elementid, - unsigned int uid) +static int mkv_write_tag(MatroskaMuxContext *mkv, const AVDictionary *m, + AVIOContext **pb, ebml_master *tag, + uint32_t elementid, uint64_t uid) { - MatroskaMuxContext *mkv = s->priv_data; - ebml_master tag; + const AVDictionaryEntry *t = NULL; + ebml_master tag2; int ret; - AVDictionaryEntry *t = NULL; - ret = mkv_write_tag_targets(s, elementid, uid, &tag); + ret = mkv_write_tag_targets(mkv, pb, tag ? tag : &tag2, elementid, uid); if (ret < 0) return ret; while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX))) { if (mkv_check_tag_name(t->key, elementid)) { - ret = mkv_write_simpletag(mkv->tags_bc, t); + ret = mkv_write_simpletag(*pb, t); if (ret < 0) return ret; } } - end_ebml_master(mkv->tags_bc, tag); + if (!tag) + end_ebml_master(*pb, tag2); + return 0; } -static int mkv_check_tag(AVDictionary *m, uint32_t elementid) +static int mkv_check_tag(const AVDictionary *m, uint32_t elementid) { - AVDictionaryEntry *t = NULL; + const AVDictionaryEntry *t = NULL; while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX))) if (mkv_check_tag_name(t->key, elementid)) @@ -1640,127 +1539,203 @@ static int mkv_check_tag(AVDictionary *m, uint32_t elementid) static int mkv_write_tags(AVFormatContext *s) { MatroskaMuxContext *mkv = s->priv_data; + ebml_master tag, *tagp = IS_SEEKABLE(s->pb, mkv) ? &tag : NULL; int i, ret; + mkv->wrote_tags = 1; + ff_metadata_conv_ctx(s, ff_mkv_metadata_conv, NULL); if (mkv_check_tag(s->metadata, 0)) { - ret = mkv_write_tag(s, s->metadata, 0, 0); - if (ret < 0) return ret; + ret = mkv_write_tag(mkv, s->metadata, &mkv->tags.bc, NULL, 0, 0); + if (ret < 0) + return ret; } for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; + const AVStream *st = s->streams[i]; + mkv_track *track = &mkv->tracks[i]; if (st->codecpar->codec_type == AVMEDIA_TYPE_ATTACHMENT) continue; - if (!mkv_check_tag(st->metadata, MATROSKA_ID_TAGTARGETS_TRACKUID)) + if (!tagp && !mkv_check_tag(st->metadata, MATROSKA_ID_TAGTARGETS_TRACKUID)) continue; - ret = mkv_write_tag(s, st->metadata, MATROSKA_ID_TAGTARGETS_TRACKUID, i + 1); - if (ret < 0) return ret; - } - - if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) { - for (i = 0; i < s->nb_streams; i++) { - AVIOContext *pb; - AVStream *st = s->streams[i]; - ebml_master tag_target; - ebml_master tag; - - if (st->codecpar->codec_type == AVMEDIA_TYPE_ATTACHMENT) - continue; + ret = mkv_write_tag(mkv, st->metadata, &mkv->tags.bc, tagp, + MATROSKA_ID_TAGTARGETS_TRACKUID, track->uid); + if (ret < 0) + return ret; - mkv_write_tag_targets(s, MATROSKA_ID_TAGTARGETS_TRACKUID, i + 1, &tag_target); - pb = mkv->tags_bc; + if (tagp) { + AVIOContext *pb = mkv->tags.bc; + ebml_master simpletag; - tag = start_ebml_master(pb, MATROSKA_ID_SIMPLETAG, 0); + simpletag = start_ebml_master(pb, MATROSKA_ID_SIMPLETAG, + 2 + 1 + 8 + 23); put_ebml_string(pb, MATROSKA_ID_TAGNAME, "DURATION"); - mkv->stream_duration_offsets[i] = avio_tell(pb); + track->duration_offset = avio_tell(pb); // Reserve space to write duration as a 20-byte string. // 2 (ebml id) + 1 (data size) + 20 (data) put_ebml_void(pb, 23); + end_ebml_master(pb, simpletag); end_ebml_master(pb, tag); - end_ebml_master(pb, tag_target); } } - if (mkv->mode != MODE_WEBM) { - for (i = 0; i < s->nb_chapters; i++) { - AVChapter *ch = s->chapters[i]; + if (mkv->nb_attachments && mkv->mode != MODE_WEBM) { + for (i = 0; i < s->nb_streams; i++) { + const mkv_track *track = &mkv->tracks[i]; + const AVStream *st = s->streams[i]; - if (!mkv_check_tag(ch->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID)) + if (st->codecpar->codec_type != AVMEDIA_TYPE_ATTACHMENT) continue; - ret = mkv_write_tag(s, ch->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID, ch->id + mkv->chapter_id_offset); - if (ret < 0) - return ret; - } - } - - if (mkv->have_attachments && mkv->mode != MODE_WEBM) { - for (i = 0; i < mkv->attachments->num_entries; i++) { - mkv_attachment *attachment = &mkv->attachments->entries[i]; - AVStream *st = s->streams[attachment->stream_idx]; - if (!mkv_check_tag(st->metadata, MATROSKA_ID_TAGTARGETS_ATTACHUID)) continue; - ret = mkv_write_tag(s, st->metadata, MATROSKA_ID_TAGTARGETS_ATTACHUID, attachment->fileuid); + ret = mkv_write_tag(mkv, st->metadata, &mkv->tags.bc, NULL, + MATROSKA_ID_TAGTARGETS_ATTACHUID, track->uid); if (ret < 0) return ret; } } - if (mkv->tags_bc) { - if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) - end_ebml_master_crc32_preliminary(s->pb, &mkv->tags_bc, mkv, &mkv->tags_pos); - else - end_ebml_master_crc32(s->pb, &mkv->tags_bc, mkv); + if (mkv->tags.bc) { + return end_ebml_master_crc32_tentatively(s->pb, &mkv->tags, mkv, + MATROSKA_ID_TAGS); } return 0; } -static int mkv_write_attachments(AVFormatContext *s) +static int mkv_write_chapters(AVFormatContext *s) { MatroskaMuxContext *mkv = s->priv_data; - AVIOContext *dyn_cp, *pb = s->pb; - AVLFG c; + AVIOContext *dyn_cp = NULL, *dyn_tags = NULL, **tags, *pb = s->pb; + ebml_master editionentry; + uint64_t chapter_id_offset = 0; + AVRational scale = {1, 1E9}; int i, ret; - if (!mkv->have_attachments) + if (!s->nb_chapters || mkv->wrote_chapters) return 0; - mkv->attachments = av_mallocz(sizeof(*mkv->attachments)); - if (!mkv->attachments) - return AVERROR(ENOMEM); + for (i = 0; i < s->nb_chapters; i++) + if (!s->chapters[i]->id) { + chapter_id_offset = 1; + break; + } - av_lfg_init(&c, av_get_random_seed()); + ret = start_ebml_master_crc32(&dyn_cp, mkv); + if (ret < 0) + return ret; - ret = mkv_add_seekhead_entry(mkv->seekhead, MATROSKA_ID_ATTACHMENTS, avio_tell(pb)); - if (ret < 0) return ret; + editionentry = start_ebml_master(dyn_cp, MATROSKA_ID_EDITIONENTRY, 0); + if (mkv->mode != MODE_WEBM) { + put_ebml_uint(dyn_cp, MATROSKA_ID_EDITIONFLAGDEFAULT, 1); + /* If mkv_write_tags() has already been called, then any tags + * corresponding to chapters will be put into a new Tags element. */ + tags = mkv->wrote_tags ? &dyn_tags : &mkv->tags.bc; + } else + tags = NULL; - ret = start_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_ATTACHMENTS); - if (ret < 0) return ret; + for (i = 0; i < s->nb_chapters; i++) { + ebml_master chapteratom, chapterdisplay; + const AVChapter *c = s->chapters[i]; + int64_t chapterstart = av_rescale_q(c->start, c->time_base, scale); + int64_t chapterend = av_rescale_q(c->end, c->time_base, scale); + const AVDictionaryEntry *t; + if (chapterstart < 0 || chapterstart > chapterend || chapterend < 0) { + av_log(s, AV_LOG_ERROR, + "Invalid chapter start (%"PRId64") or end (%"PRId64").\n", + chapterstart, chapterend); + ret = AVERROR_INVALIDDATA; + goto fail; + } + + chapteratom = start_ebml_master(dyn_cp, MATROSKA_ID_CHAPTERATOM, 0); + put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERUID, + (uint32_t)c->id + chapter_id_offset); + put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERTIMESTART, chapterstart); + put_ebml_uint(dyn_cp, MATROSKA_ID_CHAPTERTIMEEND, chapterend); + if ((t = av_dict_get(c->metadata, "title", NULL, 0))) { + chapterdisplay = start_ebml_master(dyn_cp, MATROSKA_ID_CHAPTERDISPLAY, 0); + put_ebml_string(dyn_cp, MATROSKA_ID_CHAPSTRING, t->value); + put_ebml_string(dyn_cp, MATROSKA_ID_CHAPLANG , "und"); + end_ebml_master(dyn_cp, chapterdisplay); + } + end_ebml_master(dyn_cp, chapteratom); + + if (tags && mkv_check_tag(c->metadata, MATROSKA_ID_TAGTARGETS_CHAPTERUID)) { + ret = mkv_write_tag(mkv, c->metadata, tags, NULL, + MATROSKA_ID_TAGTARGETS_CHAPTERUID, + (uint32_t)c->id + chapter_id_offset); + if (ret < 0) + goto fail; + } + } + end_ebml_master(dyn_cp, editionentry); + mkv->wrote_chapters = 1; + + ret = end_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_CHAPTERS, 0, 0, 1); + if (ret < 0) + goto fail; + if (dyn_tags) + return end_ebml_master_crc32(pb, &dyn_tags, mkv, + MATROSKA_ID_TAGS, 0, 0, 1); + return 0; + +fail: + if (tags) { + /* tags == &mkv->tags.bc can only happen if mkv->tags.bc was + * initially NULL, so we never free older tags. */ + ffio_free_dyn_buf(tags); + } + ffio_free_dyn_buf(&dyn_cp); + return ret; +} + +static const char *get_mimetype(const AVStream *st) +{ + const AVDictionaryEntry *t; + + if (t = av_dict_get(st->metadata, "mimetype", NULL, 0)) + return t->value; + if (st->codecpar->codec_id != AV_CODEC_ID_NONE) { + const AVCodecDescriptor *desc = avcodec_descriptor_get(st->codecpar->codec_id); + if (desc && desc->mime_types) { + return desc->mime_types[0]; + } else if (st->codecpar->codec_id == AV_CODEC_ID_TEXT) + return "text/plain"; + } + + return NULL; +} + +static int mkv_write_attachments(AVFormatContext *s) +{ + MatroskaMuxContext *mkv = s->priv_data; + AVIOContext *dyn_cp = NULL, *pb = s->pb; + int i, ret; + + if (!mkv->nb_attachments) + return 0; + + ret = start_ebml_master_crc32(&dyn_cp, mkv); + if (ret < 0) + return ret; for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; + const AVStream *st = s->streams[i]; + mkv_track *track = &mkv->tracks[i]; ebml_master attached_file; - mkv_attachment *attachment = mkv->attachments->entries; - AVDictionaryEntry *t; - const char *mimetype = NULL; - uint32_t fileuid; + const AVDictionaryEntry *t; + const char *mimetype; if (st->codecpar->codec_type != AVMEDIA_TYPE_ATTACHMENT) continue; - attachment = av_realloc_array(attachment, mkv->attachments->num_entries + 1, sizeof(mkv_attachment)); - if (!attachment) - return AVERROR(ENOMEM); - mkv->attachments->entries = attachment; - attached_file = start_ebml_master(dyn_cp, MATROSKA_ID_ATTACHEDFILE, 0); if (t = av_dict_get(st->metadata, "title", NULL, 0)) @@ -1770,71 +1745,33 @@ static int mkv_write_attachments(AVFormatContext *s) return AVERROR(EINVAL); } put_ebml_string(dyn_cp, MATROSKA_ID_FILENAME, t->value); - if (t = av_dict_get(st->metadata, "mimetype", NULL, 0)) - mimetype = t->value; - else if (st->codecpar->codec_id != AV_CODEC_ID_NONE ) { - int i; - for (i = 0; ff_mkv_mime_tags[i].id != AV_CODEC_ID_NONE; i++) - if (ff_mkv_mime_tags[i].id == st->codecpar->codec_id) { - mimetype = ff_mkv_mime_tags[i].str; - break; - } - for (i = 0; ff_mkv_image_mime_tags[i].id != AV_CODEC_ID_NONE; i++) - if (ff_mkv_image_mime_tags[i].id == st->codecpar->codec_id) { - mimetype = ff_mkv_image_mime_tags[i].str; - break; - } - } - if (!mimetype) { - av_log(s, AV_LOG_ERROR, "Attachment stream %d has no mimetype tag and " - "it cannot be deduced from the codec id.\n", i); - return AVERROR(EINVAL); - } - - if (s->flags & AVFMT_FLAG_BITEXACT) { - struct AVSHA *sha = av_sha_alloc(); - uint8_t digest[20]; - if (!sha) - return AVERROR(ENOMEM); - av_sha_init(sha, 160); - av_sha_update(sha, st->codecpar->extradata, st->codecpar->extradata_size); - av_sha_final(sha, digest); - av_free(sha); - fileuid = AV_RL32(digest); - } else { - fileuid = av_lfg_get(&c); - } - av_log(s, AV_LOG_VERBOSE, "Using %.8"PRIx32" for attachment %d\n", - fileuid, mkv->attachments->num_entries); + mimetype = get_mimetype(st); + av_assert0(mimetype); put_ebml_string(dyn_cp, MATROSKA_ID_FILEMIMETYPE, mimetype); put_ebml_binary(dyn_cp, MATROSKA_ID_FILEDATA, st->codecpar->extradata, st->codecpar->extradata_size); - put_ebml_uint(dyn_cp, MATROSKA_ID_FILEUID, fileuid); + put_ebml_uid(dyn_cp, MATROSKA_ID_FILEUID, track->uid); end_ebml_master(dyn_cp, attached_file); - - mkv->attachments->entries[mkv->attachments->num_entries].stream_idx = i; - mkv->attachments->entries[mkv->attachments->num_entries++].fileuid = fileuid; } - end_ebml_master_crc32(pb, &dyn_cp, mkv); - - return 0; + return end_ebml_master_crc32(pb, &dyn_cp, mkv, + MATROSKA_ID_ATTACHMENTS, 0, 0, 1); } static int64_t get_metadata_duration(AVFormatContext *s) { - int i = 0; + const AVDictionaryEntry *duration = av_dict_get(s->metadata, "DURATION", + NULL, 0); int64_t max = 0; int64_t us; - AVDictionaryEntry *explicitDuration = av_dict_get(s->metadata, "DURATION", NULL, 0); - if (explicitDuration && (av_parse_time(&us, explicitDuration->value, 1) == 0) && us > 0) { + if (duration && (av_parse_time(&us, duration->value, 1) == 0) && us > 0) { av_log(s, AV_LOG_DEBUG, "get_metadata_duration found duration in context metadata: %" PRId64 "\n", us); return us; } - for (i = 0; i < s->nb_streams; i++) { + for (unsigned i = 0; i < s->nb_streams; i++) { int64_t us; - AVDictionaryEntry *duration = av_dict_get(s->streams[i]->metadata, "DURATION", NULL, 0); + duration = av_dict_get(s->streams[i]->metadata, "DURATION", NULL, 0); if (duration && (av_parse_time(&us, duration->value, 1) == 0)) max = FFMAX(max, us); @@ -1849,16 +1786,10 @@ static int mkv_write_header(AVFormatContext *s) MatroskaMuxContext *mkv = s->priv_data; AVIOContext *pb = s->pb; ebml_master ebml_header; - AVDictionaryEntry *tag; + const AVDictionaryEntry *tag; int ret, i, version = 2; int64_t creation_time; - if (!strcmp(s->oformat->name, "webm")) { - mkv->mode = MODE_WEBM; - mkv->write_crc = 0; - } else - mkv->mode = MODE_MATROSKAv2; - if (mkv->mode != MODE_WEBM || av_dict_get(s->metadata, "stereo_mode", NULL, 0) || av_dict_get(s->metadata, "alpha_mode", NULL, 0)) @@ -1871,11 +1802,6 @@ static int mkv_write_header(AVFormatContext *s) version = 4; } - mkv->tracks = av_mallocz_array(s->nb_streams, sizeof(*mkv->tracks)); - if (!mkv->tracks) { - ret = AVERROR(ENOMEM); - goto fail; - } ebml_header = start_ebml_master(pb, EBML_ID_HEADER, MAX_EBML_HEADER_SIZE); put_ebml_uint (pb, EBML_ID_EBMLVERSION , 1); put_ebml_uint (pb, EBML_ID_EBMLREADVERSION , 1); @@ -1886,25 +1812,18 @@ static int mkv_write_header(AVFormatContext *s) put_ebml_uint (pb, EBML_ID_DOCTYPEREADVERSION, 2); end_ebml_master(pb, ebml_header); - mkv->segment = start_ebml_master(pb, MATROSKA_ID_SEGMENT, 0); + put_ebml_id(pb, MATROSKA_ID_SEGMENT); + put_ebml_size_unknown(pb, 8); mkv->segment_offset = avio_tell(pb); - // we write a seek head at the beginning to point to all other level - // one elements, which aren't more than 10 elements as we write only one - // of every other currently defined level 1 element - mkv->seekhead = mkv_start_seekhead(pb, mkv->segment_offset, 10); - if (!mkv->seekhead) { - ret = AVERROR(ENOMEM); - goto fail; - } + // We write a SeekHead at the beginning to point to all other level + // one elements (except Clusters). + mkv_start_seekhead(mkv, pb); - ret = mkv_add_seekhead_entry(mkv->seekhead, MATROSKA_ID_INFO, avio_tell(pb)); - if (ret < 0) goto fail; - - ret = start_ebml_master_crc32(pb, &mkv->info_bc, mkv, MATROSKA_ID_INFO); + ret = start_ebml_master_crc32(&mkv->info.bc, mkv); if (ret < 0) return ret; - pb = mkv->info_bc; + pb = mkv->info.bc; put_ebml_uint(pb, MATROSKA_ID_TIMECODESCALE, 1000000); if ((tag = av_dict_get(s->metadata, "title", NULL, 0))) @@ -1916,17 +1835,8 @@ static int mkv_write_header(AVFormatContext *s) else put_ebml_string(pb, MATROSKA_ID_WRITINGAPP, LIBAVFORMAT_IDENT); - if (mkv->mode != MODE_WEBM) { - uint32_t segment_uid[4]; - AVLFG lfg; - - av_lfg_init(&lfg, av_get_random_seed()); - - for (i = 0; i < 4; i++) - segment_uid[i] = av_lfg_get(&lfg); - - put_ebml_binary(pb, MATROSKA_ID_SEGMENTUID, segment_uid, 16); - } + if (mkv->mode != MODE_WEBM) + put_ebml_binary(pb, MATROSKA_ID_SEGMENTUID, mkv->segment_uid, 16); } else { const char *ident = "Lavf"; put_ebml_string(pb, MATROSKA_ID_MUXINGAPP , ident); @@ -1955,52 +1865,40 @@ static int mkv_write_header(AVFormatContext *s) int64_t scaledDuration = av_rescale(metadata_duration, 1000, AV_TIME_BASE); put_ebml_float(pb, MATROSKA_ID_DURATION, scaledDuration); av_log(s, AV_LOG_DEBUG, "Write early duration from metadata = %" PRIu64 "\n", scaledDuration); - } else { + } else if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) { put_ebml_void(pb, 11); // assumes double-precision float to be written } } - if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) - end_ebml_master_crc32_preliminary(s->pb, &mkv->info_bc, mkv, &mkv->info_pos); - else - end_ebml_master_crc32(s->pb, &mkv->info_bc, mkv); + ret = end_ebml_master_crc32_tentatively(s->pb, &mkv->info, + mkv, MATROSKA_ID_INFO); + if (ret < 0) + return ret; pb = s->pb; - // initialize stream_duration fields - mkv->stream_durations = av_mallocz(s->nb_streams * sizeof(int64_t)); - mkv->stream_duration_offsets = av_mallocz(s->nb_streams * sizeof(int64_t)); - if (!mkv->stream_durations || !mkv->stream_duration_offsets) { - ret = AVERROR(ENOMEM); - goto fail; - } - ret = mkv_write_tracks(s); if (ret < 0) - goto fail; - - for (i = 0; i < s->nb_chapters; i++) - mkv->chapter_id_offset = FFMAX(mkv->chapter_id_offset, 1LL - s->chapters[i]->id); + return ret; ret = mkv_write_chapters(s); if (ret < 0) - goto fail; + return ret; if (mkv->mode != MODE_WEBM) { ret = mkv_write_attachments(s); if (ret < 0) - goto fail; + return ret; } + /* Must come after mkv_write_chapters() to write chapter tags + * into the same Tags element as the other tags. */ ret = mkv_write_tags(s); if (ret < 0) - goto fail; - - if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) - mkv_write_seekhead(pb, mkv); + return ret; - mkv->cues = mkv_start_cues(mkv->segment_offset); - if (!mkv->cues) { - ret = AVERROR(ENOMEM); - goto fail; + if (!IS_SEEKABLE(pb, mkv)) { + ret = mkv_write_seekhead(pb, mkv, 0, avio_tell(pb)); + if (ret < 0) + return ret; } if (s->metadata_header_padding > 0) { @@ -2009,22 +1907,23 @@ static int mkv_write_header(AVFormatContext *s) put_ebml_void(pb, s->metadata_header_padding); } - if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && mkv->reserve_cues_space) { - mkv->cues_pos = avio_tell(pb); - if (mkv->reserve_cues_space == 1) - mkv->reserve_cues_space++; - put_ebml_void(pb, mkv->reserve_cues_space); + if (mkv->reserve_cues_space) { + if (IS_SEEKABLE(pb, mkv)) { + mkv->cues_pos = avio_tell(pb); + if (mkv->reserve_cues_space == 1) + mkv->reserve_cues_space++; + put_ebml_void(pb, mkv->reserve_cues_space); + } else + mkv->reserve_cues_space = -1; } av_init_packet(&mkv->cur_audio_pkt); mkv->cur_audio_pkt.size = 0; mkv->cluster_pos = -1; - avio_flush(pb); - // start a new cluster every 5 MB or 5 sec, or 32k / 1 sec for streaming or // after 4k and on a keyframe - if (pb->seekable & AVIO_SEEKABLE_NORMAL) { + if (IS_SEEKABLE(pb, mkv)) { if (mkv->cluster_time_limit < 0) mkv->cluster_time_limit = 5000; if (mkv->cluster_size_limit < 0) @@ -2037,15 +1936,12 @@ static int mkv_write_header(AVFormatContext *s) } return 0; -fail: - mkv_free(mkv); - return ret; } -static int mkv_blockgroup_size(int pkt_size) +static int mkv_blockgroup_size(int pkt_size, int track_num_size) { - int size = pkt_size + 4; - size += ebml_num_size(size); + int size = pkt_size + track_num_size + 3; + size += ebml_length_size(size); size += 2; // EBML ID for block and block duration size += 9; // max size of block duration incl. length field return size; @@ -2104,47 +2000,51 @@ static int mkv_strip_wavpack(const uint8_t *src, uint8_t **pdst, int *size) return ret; } -static void mkv_write_block(AVFormatContext *s, AVIOContext *pb, - uint32_t blockid, AVPacket *pkt, int keyframe) +static int mkv_write_block(AVFormatContext *s, AVIOContext *pb, + uint32_t blockid, const AVPacket *pkt, int keyframe) { MatroskaMuxContext *mkv = s->priv_data; AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar; + mkv_track *track = &mkv->tracks[pkt->stream_index]; uint8_t *data = NULL, *side_data = NULL; - int offset = 0, size = pkt->size, side_data_size = 0; - int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts; - uint64_t additional_id = 0; + int err = 0, offset = 0, size = pkt->size, side_data_size = 0; + int64_t ts = track->write_dts ? pkt->dts : pkt->pts; + uint64_t additional_id; int64_t discard_padding = 0; - uint8_t track_number = (mkv->is_dash ? mkv->dash_track_number : (pkt->stream_index + 1)); + unsigned track_number = track->track_num; ebml_master block_group, block_additions, block_more; - ts += mkv->tracks[pkt->stream_index].ts_offset; + ts += track->ts_offset; /* The following string is identical to the one in mkv_write_vtt_blocks * so that only one copy needs to exist in binaries. */ av_log(s, AV_LOG_DEBUG, "Writing block of size %d with pts %" PRId64 ", dts %" PRId64 ", " "duration %" PRId64 " at relative offset %" PRId64 " in cluster " - "at offset %" PRId64 ". TrackNumber %d, keyframe %d\n", + "at offset %" PRId64 ". TrackNumber %u, keyframe %d\n", pkt->size, pkt->pts, pkt->dts, pkt->duration, avio_tell(pb), mkv->cluster_pos, track_number, keyframe != 0); + if (par->codec_id == AV_CODEC_ID_H264 && par->extradata_size > 0 && - (AV_RB24(par->extradata) == 1 || AV_RB32(par->extradata) == 1)) - ff_avc_parse_nal_units_buf(pkt->data, &data, &size); - else if (par->codec_id == AV_CODEC_ID_HEVC && par->extradata_size > 6 && - (AV_RB24(par->extradata) == 1 || AV_RB32(par->extradata) == 1)) + (AV_RB24(par->extradata) == 1 || AV_RB32(par->extradata) == 1)) { + err = ff_avc_parse_nal_units_buf(pkt->data, &data, &size); + } else if (par->codec_id == AV_CODEC_ID_HEVC && par->extradata_size > 6 && + (AV_RB24(par->extradata) == 1 || AV_RB32(par->extradata) == 1)) { /* extradata is Annex B, assume the bitstream is too and convert it */ - ff_hevc_annexb2mp4_buf(pkt->data, &data, &size, 0, NULL); - else if (par->codec_id == AV_CODEC_ID_AV1) - ff_av1_filter_obus_buf(pkt->data, &data, &size); - else if (par->codec_id == AV_CODEC_ID_WAVPACK) { - int ret = mkv_strip_wavpack(pkt->data, &data, &size); - if (ret < 0) { - av_log(s, AV_LOG_ERROR, "Error stripping a WavPack packet.\n"); - return; - } + err = ff_hevc_annexb2mp4_buf(pkt->data, &data, &size, 0, NULL); + } else if (par->codec_id == AV_CODEC_ID_AV1) { + err = ff_av1_filter_obus_buf(pkt->data, &data, &size, &offset); + } else if (par->codec_id == AV_CODEC_ID_WAVPACK) { + err = mkv_strip_wavpack(pkt->data, &data, &size); } else data = pkt->data; + if (err < 0) { + av_log(s, AV_LOG_ERROR, "Error when reformatting data of " + "a packet from stream %d.\n", pkt->stream_index); + return err; + } + if (par->codec_id == AV_CODEC_ID_PRORES && size >= 8) { /* Matroska specification requires to remove the first QuickTime atom */ @@ -2155,7 +2055,6 @@ static void mkv_write_block(AVFormatContext *s, AVIOContext *pb, side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, &side_data_size); - if (side_data && side_data_size >= 10) { discard_padding = av_rescale_q(AV_RL32(side_data + 4), (AVRational){1, par->sample_rate}, @@ -2166,67 +2065,72 @@ static void mkv_write_block(AVFormatContext *s, AVIOContext *pb, AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, &side_data_size); if (side_data) { - additional_id = AV_RB64(side_data); - side_data += 8; - side_data_size -= 8; + // Only the Codec-specific BlockMore (id == 1) is currently supported. + if (side_data_size < 8 || (additional_id = AV_RB64(side_data)) != 1) { + side_data_size = 0; + } else { + side_data += 8; + side_data_size -= 8; + } } - if ((side_data_size && additional_id == 1) || discard_padding) { + if (side_data_size || discard_padding) { block_group = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, 0); blockid = MATROSKA_ID_BLOCK; } put_ebml_id(pb, blockid); - put_ebml_num(pb, size + 4, 0); - // this assumes stream_index is less than 126 - avio_w8(pb, 0x80 | track_number); + put_ebml_length(pb, size + track->track_num_size + 3, 0); + put_ebml_num(pb, track_number, track->track_num_size); avio_wb16(pb, ts - mkv->cluster_pts); avio_w8(pb, (blockid == MATROSKA_ID_SIMPLEBLOCK && keyframe) ? (1 << 7) : 0); avio_write(pb, data + offset, size); if (data != pkt->data) av_free(data); - if (blockid == MATROSKA_ID_BLOCK && !keyframe) { - put_ebml_sint(pb, MATROSKA_ID_BLOCKREFERENCE, - mkv->last_track_timestamp[track_number - 1]); - } - mkv->last_track_timestamp[track_number - 1] = ts - mkv->cluster_pts; + if (blockid == MATROSKA_ID_BLOCK && !keyframe) + put_ebml_sint(pb, MATROSKA_ID_BLOCKREFERENCE, track->last_timestamp - ts); + track->last_timestamp = ts; - if (discard_padding) { + if (discard_padding) put_ebml_sint(pb, MATROSKA_ID_DISCARDPADDING, discard_padding); - } - if (side_data_size && additional_id == 1) { + if (side_data_size) { block_additions = start_ebml_master(pb, MATROSKA_ID_BLOCKADDITIONS, 0); block_more = start_ebml_master(pb, MATROSKA_ID_BLOCKMORE, 0); - put_ebml_uint(pb, MATROSKA_ID_BLOCKADDID, 1); - put_ebml_id(pb, MATROSKA_ID_BLOCKADDITIONAL); - put_ebml_num(pb, side_data_size, 0); - avio_write(pb, side_data, side_data_size); + /* Until dbc50f8a our demuxer used a wrong default value + * of BlockAddID, so we write it unconditionally. */ + put_ebml_uint (pb, MATROSKA_ID_BLOCKADDID, additional_id); + put_ebml_binary(pb, MATROSKA_ID_BLOCKADDITIONAL, + side_data, side_data_size); end_ebml_master(pb, block_more); end_ebml_master(pb, block_additions); } - if ((side_data_size && additional_id == 1) || discard_padding) { + if (side_data_size || discard_padding) end_ebml_master(pb, block_group); - } + + return 0; } -static int mkv_write_vtt_blocks(AVFormatContext *s, AVIOContext *pb, AVPacket *pkt) +static int mkv_write_vtt_blocks(AVFormatContext *s, AVIOContext *pb, const AVPacket *pkt) { MatroskaMuxContext *mkv = s->priv_data; + mkv_track *track = &mkv->tracks[pkt->stream_index]; ebml_master blockgroup; int id_size, settings_size, size; - uint8_t *id, *settings; - int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts; + const char *id, *settings; + int64_t ts = track->write_dts ? pkt->dts : pkt->pts; const int flags = 0; id_size = 0; id = av_packet_get_side_data(pkt, AV_PKT_DATA_WEBVTT_IDENTIFIER, &id_size); + id = id ? id : ""; settings_size = 0; settings = av_packet_get_side_data(pkt, AV_PKT_DATA_WEBVTT_SETTINGS, &settings_size); + settings = settings ? settings : ""; size = id_size + 1 + settings_size + 1 + pkt->size; @@ -2235,15 +2139,16 @@ static int mkv_write_vtt_blocks(AVFormatContext *s, AVIOContext *pb, AVPacket *p av_log(s, AV_LOG_DEBUG, "Writing block of size %d with pts %" PRId64 ", dts %" PRId64 ", " "duration %" PRId64 " at relative offset %" PRId64 " in cluster " - "at offset %" PRId64 ". TrackNumber %d, keyframe %d\n", + "at offset %" PRId64 ". TrackNumber %u, keyframe %d\n", size, pkt->pts, pkt->dts, pkt->duration, avio_tell(pb), - mkv->cluster_pos, pkt->stream_index + 1, 1); + mkv->cluster_pos, track->track_num, 1); - blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, mkv_blockgroup_size(size)); + blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, + mkv_blockgroup_size(size, track->track_num_size)); put_ebml_id(pb, MATROSKA_ID_BLOCK); - put_ebml_num(pb, size + 4, 0); - avio_w8(pb, 0x80 | (pkt->stream_index + 1)); // this assumes stream_index is less than 126 + put_ebml_length(pb, size + track->track_num_size + 3, 0); + put_ebml_num(pb, track->track_num, track->track_num_size); avio_wb16(pb, ts - mkv->cluster_pts); avio_w8(pb, flags); avio_printf(pb, "%.*s\n%.*s\n%.*s", id_size, id, settings_size, settings, pkt->size, pkt->data); @@ -2254,20 +2159,26 @@ static int mkv_write_vtt_blocks(AVFormatContext *s, AVIOContext *pb, AVPacket *p return pkt->duration; } -static void mkv_start_new_cluster(AVFormatContext *s, AVPacket *pkt) +static int mkv_end_cluster(AVFormatContext *s) { MatroskaMuxContext *mkv = s->priv_data; + int ret; - end_ebml_master_crc32(s->pb, &mkv->cluster_bc, mkv); + if (!mkv->have_video) { + for (unsigned i = 0; i < s->nb_streams; i++) + mkv->tracks[i].has_cue = 0; + } mkv->cluster_pos = -1; - av_log(s, AV_LOG_DEBUG, - "Starting new cluster at offset %" PRIu64 " bytes, " - "pts %" PRIu64 ", dts %" PRIu64 "\n", - avio_tell(s->pb), pkt->pts, pkt->dts); - avio_flush(s->pb); + ret = end_ebml_master_crc32(s->pb, &mkv->cluster_bc, mkv, + MATROSKA_ID_CLUSTER, 0, 1, 0); + if (ret < 0) + return ret; + + avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); + return 0; } -static int mkv_check_new_extra_data(AVFormatContext *s, AVPacket *pkt) +static int mkv_check_new_extra_data(AVFormatContext *s, const AVPacket *pkt) { MatroskaMuxContext *mkv = s->priv_data; mkv_track *track = &mkv->tracks[pkt->stream_index]; @@ -2280,30 +2191,26 @@ static int mkv_check_new_extra_data(AVFormatContext *s, AVPacket *pkt) switch (par->codec_id) { case AV_CODEC_ID_AAC: - if (side_data_size && (s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) { + if (side_data_size && mkv->track.bc) { int filler, output_sample_rate = 0; - int64_t curpos; - ret = get_aac_sample_rates(s, side_data, side_data_size, &track->sample_rate, - &output_sample_rate); + ret = get_aac_sample_rates(s, mkv, side_data, side_data_size, + &track->sample_rate, &output_sample_rate); if (ret < 0) return ret; if (!output_sample_rate) output_sample_rate = track->sample_rate; // Space is already reserved, so it's this or a void element. - av_freep(&par->extradata); ret = ff_alloc_extradata(par, side_data_size); if (ret < 0) return ret; memcpy(par->extradata, side_data, side_data_size); - curpos = avio_tell(mkv->tracks_bc); - avio_seek(mkv->tracks_bc, track->codecpriv_offset, SEEK_SET); - mkv_write_codecprivate(s, mkv->tracks_bc, par, 1, 0); - filler = MAX_PCE_SIZE + 2 + 4 - (avio_tell(mkv->tracks_bc) - track->codecpriv_offset); + avio_seek(mkv->track.bc, track->codecpriv_offset, SEEK_SET); + mkv_write_codecprivate(s, mkv->track.bc, par, 1, 0); + filler = MAX_PCE_SIZE + 2 + 4 - (avio_tell(mkv->track.bc) - track->codecpriv_offset); if (filler) - put_ebml_void(mkv->tracks_bc, filler); - avio_seek(mkv->tracks_bc, track->sample_rate_offset, SEEK_SET); - put_ebml_float(mkv->tracks_bc, MATROSKA_ID_AUDIOSAMPLINGFREQ, track->sample_rate); - put_ebml_float(mkv->tracks_bc, MATROSKA_ID_AUDIOOUTSAMPLINGFREQ, output_sample_rate); - avio_seek(mkv->tracks_bc, curpos, SEEK_SET); + put_ebml_void(mkv->track.bc, filler); + avio_seek(mkv->track.bc, track->sample_rate_offset, SEEK_SET); + put_ebml_float(mkv->track.bc, MATROSKA_ID_AUDIOSAMPLINGFREQ, track->sample_rate); + put_ebml_float(mkv->track.bc, MATROSKA_ID_AUDIOOUTSAMPLINGFREQ, output_sample_rate); } else if (!par->extradata_size && !track->sample_rate) { // No extradata (codecpar or packet side data). av_log(s, AV_LOG_ERROR, "Error parsing AAC extradata, unable to determine samplerate.\n"); @@ -2311,54 +2218,40 @@ static int mkv_check_new_extra_data(AVFormatContext *s, AVPacket *pkt) } break; case AV_CODEC_ID_FLAC: - if (side_data_size && (s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) { - AVCodecParameters *codecpriv_par; - int64_t curpos; + if (side_data_size && mkv->track.bc) { + uint8_t *old_extradata = par->extradata; if (side_data_size != par->extradata_size) { av_log(s, AV_LOG_ERROR, "Invalid FLAC STREAMINFO metadata for output stream %d\n", pkt->stream_index); return AVERROR(EINVAL); } - codecpriv_par = avcodec_parameters_alloc(); - if (!codecpriv_par) - return AVERROR(ENOMEM); - ret = avcodec_parameters_copy(codecpriv_par, par); - if (ret < 0) { - avcodec_parameters_free(&codecpriv_par); - return ret; - } - memcpy(codecpriv_par->extradata, side_data, side_data_size); - curpos = avio_tell(mkv->tracks_bc); - avio_seek(mkv->tracks_bc, track->codecpriv_offset, SEEK_SET); - mkv_write_codecprivate(s, mkv->tracks_bc, codecpriv_par, 1, 0); - avio_seek(mkv->tracks_bc, curpos, SEEK_SET); - avcodec_parameters_free(&codecpriv_par); + par->extradata = side_data; + avio_seek(mkv->track.bc, track->codecpriv_offset, SEEK_SET); + mkv_write_codecprivate(s, mkv->track.bc, par, 1, 0); + par->extradata = old_extradata; } break; // FIXME: Remove the following once libaom starts propagating extradata during init() // See https://bugs.chromium.org/p/aomedia/issues/detail?id=2012 case AV_CODEC_ID_AV1: - if (side_data_size && (s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live && - !par->extradata_size) { + if (side_data_size && mkv->track.bc && !par->extradata_size) { AVIOContext *dyn_cp; uint8_t *codecpriv; int codecpriv_size; - int64_t curpos; ret = avio_open_dyn_buf(&dyn_cp); if (ret < 0) return ret; ff_isom_write_av1c(dyn_cp, side_data, side_data_size); - codecpriv_size = avio_close_dyn_buf(dyn_cp, &codecpriv); - if (!codecpriv_size) { - av_free(codecpriv); - return AVERROR_INVALIDDATA; + codecpriv_size = avio_get_dyn_buf(dyn_cp, &codecpriv); + if ((ret = dyn_cp->error) < 0 || + !codecpriv_size && (ret = AVERROR_INVALIDDATA)) { + ffio_free_dyn_buf(&dyn_cp); + return ret; } - curpos = avio_tell(mkv->tracks_bc); - avio_seek(mkv->tracks_bc, track->codecpriv_offset, SEEK_SET); + avio_seek(mkv->track.bc, track->codecpriv_offset, SEEK_SET); // Do not write the OBUs as we don't have space saved for them - put_ebml_binary(mkv->tracks_bc, MATROSKA_ID_CODECPRIVATE, codecpriv, 4); - av_free(codecpriv); - avio_seek(mkv->tracks_bc, curpos, SEEK_SET); + put_ebml_binary(mkv->track.bc, MATROSKA_ID_CODECPRIVATE, codecpriv, 4); + ffio_free_dyn_buf(&dyn_cp); ret = ff_alloc_extradata(par, side_data_size); if (ret < 0) return ret; @@ -2375,56 +2268,69 @@ static int mkv_check_new_extra_data(AVFormatContext *s, AVPacket *pkt) return 0; } -static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt, int add_cue) +static int mkv_write_packet_internal(AVFormatContext *s, const AVPacket *pkt) { MatroskaMuxContext *mkv = s->priv_data; - AVIOContext *pb = s->pb; + AVIOContext *pb; AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar; + mkv_track *track = &mkv->tracks[pkt->stream_index]; int keyframe = !!(pkt->flags & AV_PKT_FLAG_KEY); int duration = pkt->duration; int ret; - int64_t ts = mkv->tracks[pkt->stream_index].write_dts ? pkt->dts : pkt->pts; + int64_t ts = track->write_dts ? pkt->dts : pkt->pts; int64_t relative_packet_pos; - int dash_tracknum = mkv->is_dash ? mkv->dash_track_number : pkt->stream_index + 1; if (ts == AV_NOPTS_VALUE) { av_log(s, AV_LOG_ERROR, "Can't write packet with unknown timestamp\n"); return AVERROR(EINVAL); } - ts += mkv->tracks[pkt->stream_index].ts_offset; + ts += track->ts_offset; if (mkv->cluster_pos != -1) { int64_t cluster_time = ts - mkv->cluster_pts; if ((int16_t)cluster_time != cluster_time) { + ret = mkv_end_cluster(s); + if (ret < 0) + return ret; av_log(s, AV_LOG_WARNING, "Starting new cluster due to timestamp\n"); - mkv_start_new_cluster(s, pkt); } } if (mkv->cluster_pos == -1) { - mkv->cluster_pos = avio_tell(s->pb); - ret = start_ebml_master_crc32(s->pb, &mkv->cluster_bc, mkv, MATROSKA_ID_CLUSTER); + ret = start_ebml_master_crc32(&mkv->cluster_bc, mkv); if (ret < 0) return ret; + mkv->cluster_pos = avio_tell(s->pb); put_ebml_uint(mkv->cluster_bc, MATROSKA_ID_CLUSTERTIMECODE, FFMAX(0, ts)); mkv->cluster_pts = FFMAX(0, ts); + av_log(s, AV_LOG_DEBUG, + "Starting new cluster with timestamp " + "%" PRId64 " at offset %" PRId64 " bytes\n", + mkv->cluster_pts, mkv->cluster_pos); } pb = mkv->cluster_bc; relative_packet_pos = avio_tell(pb); if (par->codec_type != AVMEDIA_TYPE_SUBTITLE) { - mkv_write_block(s, pb, MATROSKA_ID_SIMPLEBLOCK, pkt, keyframe); - if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && (par->codec_type == AVMEDIA_TYPE_VIDEO && keyframe || add_cue)) { - ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, dash_tracknum, ts, mkv->cluster_pos, relative_packet_pos, -1); - if (ret < 0) return ret; + ret = mkv_write_block(s, pb, MATROSKA_ID_SIMPLEBLOCK, pkt, keyframe); + if (ret < 0) + return ret; + if (keyframe && IS_SEEKABLE(s->pb, mkv) && + (par->codec_type == AVMEDIA_TYPE_VIDEO || !mkv->have_video && !track->has_cue)) { + ret = mkv_add_cuepoint(mkv, pkt->stream_index, ts, + mkv->cluster_pos, relative_packet_pos, -1); + if (ret < 0) + return ret; + track->has_cue = 1; } } else { if (par->codec_id == AV_CODEC_ID_WEBVTT) { duration = mkv_write_vtt_blocks(s, pb, pkt); } else { ebml_master blockgroup = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, - mkv_blockgroup_size(pkt->size)); + mkv_blockgroup_size(pkt->size, + track->track_num_size)); #if FF_API_CONVERGENCE_DURATION FF_DISABLE_DEPRECATION_WARNINGS @@ -2440,24 +2346,21 @@ FF_ENABLE_DEPRECATION_WARNINGS end_ebml_master(pb, blockgroup); } - if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) { - ret = mkv_add_cuepoint(mkv->cues, pkt->stream_index, dash_tracknum, ts, + if (IS_SEEKABLE(s->pb, mkv)) { + ret = mkv_add_cuepoint(mkv, pkt->stream_index, ts, mkv->cluster_pos, relative_packet_pos, duration); if (ret < 0) return ret; } } - mkv->duration = FFMAX(mkv->duration, ts + duration); - - if (mkv->stream_durations) - mkv->stream_durations[pkt->stream_index] = - FFMAX(mkv->stream_durations[pkt->stream_index], ts + duration); + mkv->duration = FFMAX(mkv->duration, ts + duration); + track->duration = FFMAX(track->duration, ts + duration); return 0; } -static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) +static int mkv_write_packet(AVFormatContext *s, const AVPacket *pkt) { MatroskaMuxContext *mkv = s->priv_data; int codec_type = s->streams[pkt->stream_index]->codecpar->codec_type; @@ -2471,38 +2374,38 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) if (ret < 0) return ret; - if (mkv->tracks[pkt->stream_index].write_dts) - cluster_time = pkt->dts - mkv->cluster_pts; - else - cluster_time = pkt->pts - mkv->cluster_pts; - cluster_time += mkv->tracks[pkt->stream_index].ts_offset; - - // start a new cluster every 5 MB or 5 sec, or 32k / 1 sec for streaming or - // after 4k and on a keyframe - cluster_size = avio_tell(mkv->cluster_bc); - - if (mkv->is_dash && codec_type == AVMEDIA_TYPE_VIDEO) { - // WebM DASH specification states that the first block of every cluster - // has to be a key frame. So for DASH video, we only create a cluster - // on seeing key frames. - start_new_cluster = keyframe; - } else if (mkv->is_dash && codec_type == AVMEDIA_TYPE_AUDIO && - (mkv->cluster_pos == -1 || - cluster_time > mkv->cluster_time_limit)) { - // For DASH audio, we create a Cluster based on cluster_time_limit - start_new_cluster = 1; - } else if (!mkv->is_dash && - (cluster_size > mkv->cluster_size_limit || - cluster_time > mkv->cluster_time_limit || - (codec_type == AVMEDIA_TYPE_VIDEO && keyframe && - cluster_size > 4 * 1024))) { - start_new_cluster = 1; - } else { - start_new_cluster = 0; - } + if (mkv->cluster_pos != -1) { + if (mkv->tracks[pkt->stream_index].write_dts) + cluster_time = pkt->dts - mkv->cluster_pts; + else + cluster_time = pkt->pts - mkv->cluster_pts; + cluster_time += mkv->tracks[pkt->stream_index].ts_offset; + + cluster_size = avio_tell(mkv->cluster_bc); + + if (mkv->is_dash && codec_type == AVMEDIA_TYPE_VIDEO) { + // WebM DASH specification states that the first block of + // every Cluster has to be a key frame. So for DASH video, + // we only create a Cluster on seeing key frames. + start_new_cluster = keyframe; + } else if (mkv->is_dash && codec_type == AVMEDIA_TYPE_AUDIO && + cluster_time > mkv->cluster_time_limit) { + // For DASH audio, we create a Cluster based on cluster_time_limit. + start_new_cluster = 1; + } else if (!mkv->is_dash && + (cluster_size > mkv->cluster_size_limit || + cluster_time > mkv->cluster_time_limit || + (codec_type == AVMEDIA_TYPE_VIDEO && keyframe && + cluster_size > 4 * 1024))) { + start_new_cluster = 1; + } else + start_new_cluster = 0; - if (mkv->cluster_pos != -1 && start_new_cluster) { - mkv_start_new_cluster(s, pkt); + if (start_new_cluster) { + ret = mkv_end_cluster(s); + if (ret < 0) + return ret; + } } if (!mkv->cluster_pos) @@ -2512,9 +2415,7 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) // check if we have an audio packet cached if (mkv->cur_audio_pkt.size > 0) { - // for DASH audio, a CuePoint has to be added when there is a new cluster. - ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt, - mkv->is_dash ? start_new_cluster : 0); + ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt); av_packet_unref(&mkv->cur_audio_pkt); if (ret < 0) { av_log(s, AV_LOG_ERROR, @@ -2529,7 +2430,7 @@ static int mkv_write_packet(AVFormatContext *s, AVPacket *pkt) if (pkt->size > 0) ret = av_packet_ref(&mkv->cur_audio_pkt, pkt); } else - ret = mkv_write_packet_internal(s, pkt, 0); + ret = mkv_write_packet_internal(s, pkt); return ret; } @@ -2539,12 +2440,12 @@ static int mkv_write_flush_packet(AVFormatContext *s, AVPacket *pkt) if (!pkt) { if (mkv->cluster_pos != -1) { - end_ebml_master_crc32(s->pb, &mkv->cluster_bc, mkv); - mkv->cluster_pos = -1; + int ret = mkv_end_cluster(s); + if (ret < 0) + return ret; av_log(s, AV_LOG_DEBUG, "Flushing cluster at offset %" PRIu64 " bytes\n", avio_tell(s->pb)); - avio_flush(s->pb); } return 1; } @@ -2555,13 +2456,12 @@ static int mkv_write_trailer(AVFormatContext *s) { MatroskaMuxContext *mkv = s->priv_data; AVIOContext *pb = s->pb; - int64_t currentpos, cuespos; - int ret; + int64_t endpos, ret64; + int ret, ret2 = 0; // check if we have an audio packet cached if (mkv->cur_audio_pkt.size > 0) { - ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt, 0); - av_packet_unref(&mkv->cur_audio_pkt); + ret = mkv_write_packet_internal(s, &mkv->cur_audio_pkt); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Could not write cached audio packet ret:%d\n", ret); @@ -2569,101 +2469,143 @@ static int mkv_write_trailer(AVFormatContext *s) } } - if (mkv->cluster_bc) { - end_ebml_master_crc32(pb, &mkv->cluster_bc, mkv); + if (mkv->cluster_pos != -1) { + ret = end_ebml_master_crc32(pb, &mkv->cluster_bc, mkv, + MATROSKA_ID_CLUSTER, 0, 0, 0); + if (ret < 0) + return ret; } ret = mkv_write_chapters(s); if (ret < 0) return ret; + if (!IS_SEEKABLE(pb, mkv)) + return 0; - if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) { - if (mkv->cues->num_entries) { - if (mkv->reserve_cues_space) { - int64_t cues_end; + endpos = avio_tell(pb); - currentpos = avio_tell(pb); - avio_seek(pb, mkv->cues_pos, SEEK_SET); + if (mkv->cues.num_entries && mkv->reserve_cues_space >= 0) { + AVIOContext *cues = NULL; + uint64_t size; + int length_size = 0; - cuespos = mkv_write_cues(s, mkv->cues, mkv->tracks, s->nb_streams); - cues_end = avio_tell(pb); - if (cues_end > cuespos + mkv->reserve_cues_space) { - av_log(s, AV_LOG_ERROR, - "Insufficient space reserved for cues: %d " - "(needed: %" PRId64 ").\n", - mkv->reserve_cues_space, cues_end - cuespos); - return AVERROR(EINVAL); - } + ret = start_ebml_master_crc32(&cues, mkv); + if (ret < 0) + return ret; - if (cues_end < cuespos + mkv->reserve_cues_space) - put_ebml_void(pb, mkv->reserve_cues_space - - (cues_end - cuespos)); + ret = mkv_assemble_cues(s->streams, cues, &mkv->cues, + mkv->tracks, s->nb_streams); + if (ret < 0) { + ffio_free_dyn_buf(&cues); + return ret; + } - avio_seek(pb, currentpos, SEEK_SET); + if (mkv->reserve_cues_space) { + size = avio_tell(cues); + length_size = ebml_length_size(size); + size += 4 + length_size; + if (mkv->reserve_cues_space < size) { + av_log(s, AV_LOG_WARNING, + "Insufficient space reserved for Cues: " + "%d < %"PRIu64". No Cues will be output.\n", + mkv->reserve_cues_space, size); + ret2 = AVERROR(EINVAL); + goto after_cues; } else { - cuespos = mkv_write_cues(s, mkv->cues, mkv->tracks, s->nb_streams); + if ((ret64 = avio_seek(pb, mkv->cues_pos, SEEK_SET)) < 0) { + ffio_free_dyn_buf(&cues); + return ret64; + } + if (mkv->reserve_cues_space == size + 1) { + /* There is no way to reserve a single byte because + * the minimal size of an EBML Void element is 2 + * (1 byte ID, 1 byte length field). This problem + * is solved by writing the Cues' length field on + * one byte more than necessary. */ + length_size++; + size++; + } } - - ret = mkv_add_seekhead_entry(mkv->seekhead, MATROSKA_ID_CUES, - cuespos); - if (ret < 0) - return ret; } + ret = end_ebml_master_crc32(pb, &cues, mkv, MATROSKA_ID_CUES, + length_size, 0, 1); + if (ret < 0) + return ret; + if (mkv->reserve_cues_space) { + if (size < mkv->reserve_cues_space) + put_ebml_void(pb, mkv->reserve_cues_space - size); + } else + endpos = avio_tell(pb); + } + +after_cues: + /* Lengths greater than (1ULL << 56) - 1 can't be represented + * via an EBML number, so leave the unknown length field. */ + if (endpos - mkv->segment_offset < (1ULL << 56) - 1) { + if ((ret64 = avio_seek(pb, mkv->segment_offset - 8, SEEK_SET)) < 0) + return ret64; + put_ebml_length(pb, endpos - mkv->segment_offset, 8); + } - mkv_write_seekhead(pb, mkv); + ret = mkv_write_seekhead(pb, mkv, 1, mkv->info.pos); + if (ret < 0) + return ret; + if (mkv->info.bc) { // update the duration av_log(s, AV_LOG_DEBUG, "end duration = %" PRIu64 "\n", mkv->duration); - currentpos = avio_tell(pb); - avio_seek(mkv->info_bc, mkv->duration_offset, SEEK_SET); - put_ebml_float(mkv->info_bc, MATROSKA_ID_DURATION, mkv->duration); - avio_seek(pb, mkv->info_pos, SEEK_SET); - end_ebml_master_crc32(pb, &mkv->info_bc, mkv); + avio_seek(mkv->info.bc, mkv->duration_offset, SEEK_SET); + put_ebml_float(mkv->info.bc, MATROSKA_ID_DURATION, mkv->duration); + ret = end_ebml_master_crc32(pb, &mkv->info.bc, mkv, + MATROSKA_ID_INFO, 0, 0, 0); + if (ret < 0) + return ret; + } - // write tracks master - avio_seek(pb, mkv->tracks_pos, SEEK_SET); - end_ebml_master_crc32(pb, &mkv->tracks_bc, mkv); + if (mkv->track.bc) { + // write Tracks 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); + if (ret < 0) + return ret; + } - // update stream durations - if (!mkv->is_live && mkv->stream_durations) { - int i; - int64_t curr = avio_tell(mkv->tags_bc); - for (i = 0; i < s->nb_streams; ++i) { - AVStream *st = s->streams[i]; + // update stream durations + if (mkv->tags.bc) { + int i; + for (i = 0; i < s->nb_streams; ++i) { + const AVStream *st = s->streams[i]; + const mkv_track *track = &mkv->tracks[i]; - if (mkv->stream_duration_offsets[i] > 0) { - double duration_sec = mkv->stream_durations[i] * av_q2d(st->time_base); - char duration_string[20] = ""; + if (track->duration_offset > 0) { + double duration_sec = track->duration * av_q2d(st->time_base); + char duration_string[20] = ""; - av_log(s, AV_LOG_DEBUG, "stream %d end duration = %" PRIu64 "\n", i, - mkv->stream_durations[i]); + av_log(s, AV_LOG_DEBUG, "stream %d end duration = %" PRIu64 "\n", i, + track->duration); - avio_seek(mkv->tags_bc, mkv->stream_duration_offsets[i], SEEK_SET); + avio_seek(mkv->tags.bc, track->duration_offset, SEEK_SET); - snprintf(duration_string, 20, "%02d:%02d:%012.9f", - (int) duration_sec / 3600, ((int) duration_sec / 60) % 60, - fmod(duration_sec, 60)); + snprintf(duration_string, 20, "%02d:%02d:%012.9f", + (int) duration_sec / 3600, ((int) duration_sec / 60) % 60, + fmod(duration_sec, 60)); - put_ebml_binary(mkv->tags_bc, MATROSKA_ID_TAGSTRING, duration_string, 20); - } + put_ebml_binary(mkv->tags.bc, MATROSKA_ID_TAGSTRING, duration_string, 20); } - avio_seek(mkv->tags_bc, curr, SEEK_SET); - } - if (mkv->tags_bc && !mkv->is_live) { - avio_seek(pb, mkv->tags_pos, SEEK_SET); - end_ebml_master_crc32(pb, &mkv->tags_bc, mkv); } - avio_seek(pb, currentpos, SEEK_SET); + avio_seek(pb, mkv->tags.pos, SEEK_SET); + ret = end_ebml_master_crc32(pb, &mkv->tags.bc, mkv, + MATROSKA_ID_TAGS, 0, 0, 0); + if (ret < 0) + return ret; } - if (!mkv->is_live) { - end_ebml_master(pb, mkv->segment); - } + avio_seek(pb, endpos, SEEK_SET); - mkv_free(mkv); - return 0; + return ret2; } static int mkv_query_codec(enum AVCodecID codec_id, int std_compliance) @@ -2693,17 +2635,31 @@ static int webm_query_codec(enum AVCodecID codec_id, int std_compliance) return 0; } +static uint64_t mkv_get_uid(const mkv_track *tracks, int i, AVLFG *c) +{ + while (1) { + uint64_t uid; + int k; + uid = (uint64_t)av_lfg_get(c) << 32; + uid |= av_lfg_get(c); + if (!uid) + continue; + for (k = 0; k < i; k++) { + if (tracks[k].uid == uid) + break; + } + if (k == i) + return uid; + } +} + static int mkv_init(struct AVFormatContext *s) { + MatroskaMuxContext *mkv = s->priv_data; + AVLFG c; + unsigned nb_tracks = 0; int i; - if (s->nb_streams > MAX_TRACKS) { - av_log(s, AV_LOG_ERROR, - "At most %d streams are supported for muxing in Matroska\n", - MAX_TRACKS); - return AVERROR(EINVAL); - } - for (i = 0; i < s->nb_streams; i++) { if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_ATRAC3 || s->streams[i]->codecpar->codec_id == AV_CODEC_ID_COOK || @@ -2723,11 +2679,58 @@ static int mkv_init(struct AVFormatContext *s) s->internal->avoid_negative_ts_use_pts = 1; } + if (!strcmp(s->oformat->name, "webm")) { + mkv->mode = MODE_WEBM; + mkv->write_crc = 0; + } else + mkv->mode = MODE_MATROSKAv2; + + mkv->tracks = av_mallocz_array(s->nb_streams, sizeof(*mkv->tracks)); + if (!mkv->tracks) + return AVERROR(ENOMEM); + + if (!(s->flags & AVFMT_FLAG_BITEXACT)) { + av_lfg_init(&c, av_get_random_seed()); + + // Calculate the SegmentUID now in order not to waste our random seed. + for (i = 0; i < 4; i++) + mkv->segment_uid[i] = av_lfg_get(&c); + } + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + mkv_track *track = &mkv->tracks[i]; + + if (s->flags & AVFMT_FLAG_BITEXACT) { + track->uid = i + 1; + } else { + track->uid = mkv_get_uid(mkv->tracks, i, &c); + } + // ms precision is the de-facto standard timescale for mkv files - avpriv_set_pts_info(s->streams[i], 64, 1, 1000); + avpriv_set_pts_info(st, 64, 1, 1000); + + if (st->codecpar->codec_type == AVMEDIA_TYPE_ATTACHMENT) { + if (mkv->mode == MODE_WEBM) { + av_log(s, AV_LOG_WARNING, "Stream %d will be ignored " + "as WebM doesn't support attachments.\n", i); + } else if (!get_mimetype(st)) { + av_log(s, AV_LOG_ERROR, "Attachment stream %d has no mimetype " + "tag and it cannot be deduced from the codec id.\n", i); + return AVERROR(EINVAL); + } + mkv->nb_attachments++; + continue; + } + + nb_tracks++; + track->track_num = mkv->is_dash ? mkv->dash_track_number : nb_tracks; + track->track_num_size = ebml_num_size(track->track_num); } + if (mkv->is_dash && nb_tracks != 1) + return AVERROR(EINVAL); + return 0; } @@ -2783,10 +2786,14 @@ static const AVOption options[] = { { "cluster_size_limit", "Store at most the provided amount of bytes in a cluster. ", OFFSET(cluster_size_limit), AV_OPT_TYPE_INT , { .i64 = -1 }, -1, INT_MAX, FLAGS }, { "cluster_time_limit", "Store at most the provided number of milliseconds in a cluster.", OFFSET(cluster_time_limit), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, { "dash", "Create a WebM file conforming to WebM DASH specification", OFFSET(is_dash), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, - { "dash_track_number", "Track number for the DASH stream", OFFSET(dash_track_number), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 127, FLAGS }, + { "dash_track_number", "Track number for the DASH stream", OFFSET(dash_track_number), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, FLAGS }, { "live", "Write files assuming it is a live stream.", OFFSET(is_live), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { "allow_raw_vfw", "allow RAW VFW mode", OFFSET(allow_raw_vfw), 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_INFER }, DEFAULT_MODE_INFER, DEFAULT_MODE_PASSTHROUGH, FLAGS, "default_mode" }, + { "infer", "For each track type, mark the first 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 the first 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" }, { NULL }, }; @@ -2809,6 +2816,7 @@ AVOutputFormat ff_matroska_muxer = { .video_codec = CONFIG_LIBX264_ENCODER ? AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4, .init = mkv_init, + .deinit = mkv_deinit, .write_header = mkv_write_header, .write_packet = mkv_write_flush_packet, .write_trailer = mkv_write_trailer, @@ -2843,6 +2851,7 @@ AVOutputFormat ff_webm_muxer = { .video_codec = CONFIG_LIBVPX_VP9_ENCODER? AV_CODEC_ID_VP9 : AV_CODEC_ID_VP8, .subtitle_codec = AV_CODEC_ID_WEBVTT, .init = mkv_init, + .deinit = mkv_deinit, .write_header = mkv_write_header, .write_packet = mkv_write_flush_packet, .write_trailer = mkv_write_trailer, @@ -2871,6 +2880,7 @@ AVOutputFormat ff_matroska_audio_muxer = { AV_CODEC_ID_VORBIS : AV_CODEC_ID_AC3, .video_codec = AV_CODEC_ID_NONE, .init = mkv_init, + .deinit = mkv_deinit, .write_header = mkv_write_header, .write_packet = mkv_write_flush_packet, .write_trailer = mkv_write_trailer, diff --git a/libavformat/microdvddec.c b/libavformat/microdvddec.c index ca9086afe9b..1f871b25182 100644 --- a/libavformat/microdvddec.c +++ b/libavformat/microdvddec.c @@ -81,7 +81,7 @@ static int microdvd_read_header(AVFormatContext *s) AVRational pts_info = (AVRational){ 2997, 125 }; /* default: 23.976 fps */ MicroDVDContext *microdvd = s->priv_data; AVStream *st = avformat_new_stream(s, NULL); - int i = 0; + int i = 0, ret; char line_buf[MAX_LINESIZE]; int has_real_fps = 0; @@ -94,6 +94,7 @@ static int microdvd_read_header(AVFormatContext *s) int64_t pos = avio_tell(s->pb); int len = ff_get_line(s->pb, line_buf, sizeof(line_buf)); char *line = line_buf; + int64_t pts; if (!strncmp(line, bom, 3)) line += 3; @@ -117,10 +118,11 @@ static int microdvd_read_header(AVFormatContext *s) continue; } if (!st->codecpar->extradata && sscanf(line, "{DEFAULT}{}%c", &c) == 1) { - st->codecpar->extradata = av_strdup(line + 11); - if (!st->codecpar->extradata) - return AVERROR(ENOMEM); - st->codecpar->extradata_size = strlen(st->codecpar->extradata) + 1; + int size = strlen(line + 11); + ret = ff_alloc_extradata(st->codecpar, size); + if (ret < 0) + goto fail; + memcpy(st->codecpar->extradata, line + 11, size); continue; } } @@ -136,11 +138,16 @@ static int microdvd_read_header(AVFormatContext *s) SKIP_FRAME_ID; if (!*p) continue; + pts = get_pts(line); + if (pts == AV_NOPTS_VALUE) + continue; sub = ff_subtitles_queue_insert(µdvd->q, p, strlen(p), 0); - if (!sub) - return AVERROR(ENOMEM); + if (!sub) { + ret = AVERROR(ENOMEM); + goto fail; + } sub->pos = pos; - sub->pts = get_pts(line); + sub->pts = pts; sub->duration = get_duration(line); } ff_subtitles_queue_finalize(s, µdvd->q); @@ -155,6 +162,9 @@ static int microdvd_read_header(AVFormatContext *s) st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; st->codecpar->codec_id = AV_CODEC_ID_MICRODVD; return 0; +fail: + ff_subtitles_queue_clean(µdvd->q); + return ret; } static int microdvd_read_packet(AVFormatContext *s, AVPacket *pkt) diff --git a/libavformat/microdvdenc.c b/libavformat/microdvdenc.c index 04f475b6453..1cd215d8def 100644 --- a/libavformat/microdvdenc.c +++ b/libavformat/microdvdenc.c @@ -36,7 +36,7 @@ static int microdvd_write_header(struct AVFormatContext *s) if (par->extradata && par->extradata_size > 0) { avio_write(s->pb, "{DEFAULT}{}", 11); avio_write(s->pb, par->extradata, par->extradata_size); - avio_flush(s->pb); + avio_w8(s->pb, '\n'); } avpriv_set_pts_info(s->streams[0], 64, framerate.num, framerate.den); @@ -51,7 +51,7 @@ static int microdvd_write_packet(AVFormatContext *avf, AVPacket *pkt) else avio_printf(avf->pb, "{%"PRId64"}", pkt->pts + pkt->duration); avio_write(avf->pb, pkt->data, pkt->size); - avio_write(avf->pb, "\n", 1); + avio_w8(avf->pb, '\n'); return 0; } diff --git a/libavformat/mlvdec.c b/libavformat/mlvdec.c index 68ca2c5e1c2..03aed710244 100644 --- a/libavformat/mlvdec.c +++ b/libavformat/mlvdec.c @@ -393,10 +393,14 @@ static int read_packet(AVFormatContext *avctx, AVPacket *pkt) { MlvContext *mlv = avctx->priv_data; AVIOContext *pb; - AVStream *st = avctx->streams[mlv->stream_index]; + AVStream *st; int index, ret; unsigned int size, space; + if (!avctx->nb_streams) + return AVERROR_EOF; + + st = avctx->streams[mlv->stream_index]; if (mlv->pts >= st->duration) return AVERROR_EOF; @@ -462,8 +466,7 @@ static int read_close(AVFormatContext *s) MlvContext *mlv = s->priv_data; int i; for (i = 0; i < 100; i++) - if (mlv->pb[i]) - ff_format_io_close(s, &mlv->pb[i]); + ff_format_io_close(s, &mlv->pb[i]); return 0; } diff --git a/libavformat/mm.c b/libavformat/mm.c index 83b3c200c68..02ffbcd8242 100644 --- a/libavformat/mm.c +++ b/libavformat/mm.c @@ -142,6 +142,7 @@ static int read_packet(AVFormatContext *s, AVIOContext *pb = s->pb; unsigned char preamble[MM_PREAMBLE_SIZE]; unsigned int type, length; + int ret; while(1) { @@ -161,8 +162,8 @@ static int read_packet(AVFormatContext *s, case MM_TYPE_INTRA_HHV : case MM_TYPE_INTER_HHV : /* output preamble + data */ - if (av_new_packet(pkt, length + MM_PREAMBLE_SIZE)) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, length + MM_PREAMBLE_SIZE)) < 0) + return ret; memcpy(pkt->data, preamble, MM_PREAMBLE_SIZE); if (avio_read(pb, pkt->data + MM_PREAMBLE_SIZE, length) != length) return AVERROR(EIO); @@ -174,8 +175,10 @@ static int read_packet(AVFormatContext *s, return 0; case MM_TYPE_AUDIO : - if (av_get_packet(s->pb, pkt, length)<0) - return AVERROR(ENOMEM); + if (s->nb_streams < 2) + return AVERROR_INVALIDDATA; + if ((ret = av_get_packet(s->pb, pkt, length)) < 0) + return ret; pkt->stream_index = 1; pkt->pts = mm->audio_pts++; return 0; diff --git a/libavformat/mmf.c b/libavformat/mmf.c index 917113066aa..e4768db0646 100644 --- a/libavformat/mmf.c +++ b/libavformat/mmf.c @@ -123,8 +123,6 @@ static int mmf_write_header(AVFormatContext *s) avpriv_set_pts_info(s->streams[0], 64, 1, s->streams[0]->codecpar->sample_rate); - avio_flush(pb); - return 0; } @@ -173,8 +171,6 @@ static int mmf_write_trailer(AVFormatContext *s) avio_write(pb, "\x00\x00\x00\x00", 4); avio_seek(pb, pos, SEEK_SET); - - avio_flush(pb); } return 0; } diff --git a/libavformat/mms.c b/libavformat/mms.c index 768fda65254..16babc09542 100644 --- a/libavformat/mms.c +++ b/libavformat/mms.c @@ -60,7 +60,7 @@ int ff_mms_asf_header_parser(MMSContext *mms) if (mms->asf_header_size < sizeof(ff_asf_guid) * 2 + 22 || memcmp(p, ff_asf_header, sizeof(ff_asf_guid))) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Corrupt stream (invalid ASF header, size=%d)\n", mms->asf_header_size); return AVERROR_INVALIDDATA; @@ -77,7 +77,7 @@ int ff_mms_asf_header_parser(MMSContext *mms) chunksize = AV_RL64(p + sizeof(ff_asf_guid)); } if (!chunksize || chunksize > end - p) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Corrupt stream (header chunksize %"PRId64" is invalid)\n", chunksize); return AVERROR_INVALIDDATA; @@ -87,7 +87,7 @@ int ff_mms_asf_header_parser(MMSContext *mms) if (end - p > sizeof(ff_asf_guid) * 2 + 68) { mms->asf_packet_len = AV_RL32(p + sizeof(ff_asf_guid) * 2 + 64); if (mms->asf_packet_len <= 0 || mms->asf_packet_len > sizeof(mms->in_buffer)) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Corrupt stream (too large pkt_len %d)\n", mms->asf_packet_len); return AVERROR_INVALIDDATA; @@ -110,7 +110,7 @@ int ff_mms_asf_header_parser(MMSContext *mms) mms->streams[mms->stream_num].id = stream_id; mms->stream_num++; } else { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Corrupt stream (too many A/V streams)\n"); return AVERROR_INVALIDDATA; } @@ -121,7 +121,7 @@ int ff_mms_asf_header_parser(MMSContext *mms) uint64_t skip_bytes = 88; while (stream_count--) { if (end - p < skip_bytes + 4) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Corrupt stream (next stream name length is not in the buffer)\n"); return AVERROR_INVALIDDATA; } @@ -129,14 +129,14 @@ int ff_mms_asf_header_parser(MMSContext *mms) } while (ext_len_count--) { if (end - p < skip_bytes + 22) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Corrupt stream (next extension system info length is not in the buffer)\n"); return AVERROR_INVALIDDATA; } skip_bytes += 22 + AV_RL32(p + skip_bytes + 18); } if (end - p < skip_bytes) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Corrupt stream (the last extension system info length is invalid)\n"); return AVERROR_INVALIDDATA; } @@ -146,7 +146,7 @@ int ff_mms_asf_header_parser(MMSContext *mms) } else if (!memcmp(p, ff_asf_head1_guid, sizeof(ff_asf_guid))) { chunksize = 46; // see references [2] section 3.4. This should be set 46. if (chunksize > end - p) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Corrupt stream (header chunksize %"PRId64" is invalid)\n", chunksize); return AVERROR_INVALIDDATA; diff --git a/libavformat/mmsh.c b/libavformat/mmsh.c index 13c0ffe4387..495d7fb73ba 100644 --- a/libavformat/mmsh.c +++ b/libavformat/mmsh.c @@ -65,8 +65,7 @@ static int mmsh_close(URLContext *h) { MMSHContext *mmsh = (MMSHContext *)h->priv_data; MMSContext *mms = &mmsh->mms; - if (mms->mms_hd) - ffurl_closep(&mms->mms_hd); + ffurl_closep(&mms->mms_hd); av_freep(&mms->streams); av_freep(&mms->asf_header); return 0; @@ -265,7 +264,7 @@ static int mmsh_open_internal(URLContext *h, const char *uri, int flags, int tim } // close the socket and then reopen it for sending the second play request. - ffurl_close(mms->mms_hd); + ffurl_closep(&mms->mms_hd); memset(headers, 0, sizeof(headers)); if ((err = ffurl_alloc(&mms->mms_hd, httpname, AVIO_FLAG_READ, &h->interrupt_callback)) < 0) { diff --git a/libavformat/mmst.c b/libavformat/mmst.c index a97c2e04a2c..377323fe274 100644 --- a/libavformat/mmst.c +++ b/libavformat/mmst.c @@ -141,7 +141,7 @@ static int send_command_packet(MMSTContext *mmst) // write it out. write_result= ffurl_write(mms->mms_hd, mms->out_buffer, exact_length); if(write_result != exact_length) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Failed to write data of length %d: %d (%s)\n", exact_length, write_result, write_result < 0 ? strerror(AVUNERROR(write_result)) : @@ -215,11 +215,11 @@ static int send_media_file_request(MMSTContext *mmst) static void handle_packet_stream_changing_type(MMSTContext *mmst) { MMSContext *mms = &mmst->mms; - av_log(NULL, AV_LOG_TRACE, "Stream changing!\n"); + av_log(mms->mms_hd, AV_LOG_TRACE, "Stream changing!\n"); // 40 is the packet header size, 7 is the prefix size. mmst->header_packet_id= AV_RL32(mms->in_buffer + 40 + 7); - av_log(NULL, AV_LOG_TRACE, "Changed header prefix to 0x%x", mmst->header_packet_id); + av_log(mms->mms_hd, AV_LOG_TRACE, "Changed header prefix to 0x%x", mmst->header_packet_id); } static int send_keepalive_packet(MMSTContext *mmst) @@ -251,12 +251,12 @@ static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst) read_result = ffurl_read_complete(mms->mms_hd, mms->in_buffer, 8); if (read_result != 8) { if(read_result < 0) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Error reading packet header: %d (%s)\n", read_result, strerror(AVUNERROR(read_result))); packet_type = SC_PKT_CANCEL; } else { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "The server closed the connection\n"); packet_type = SC_PKT_NO_DATA; } @@ -270,7 +270,7 @@ static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst) mmst->incoming_flags= mms->in_buffer[3]; read_result= ffurl_read_complete(mms->mms_hd, mms->in_buffer+8, 4); if(read_result != 4) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Reading command packet length failed: %d (%s)\n", read_result, read_result < 0 ? strerror(AVUNERROR(read_result)) : @@ -279,11 +279,11 @@ static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst) } length_remaining= AV_RL32(mms->in_buffer+8) + 4; - av_log(NULL, AV_LOG_TRACE, "Length remaining is %d\n", length_remaining); + av_log(mms->mms_hd, AV_LOG_TRACE, "Length remaining is %d\n", length_remaining); // read the rest of the packet. if (length_remaining < 0 || length_remaining > sizeof(mms->in_buffer) - 12) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Incoming packet length %d exceeds bufsize %"SIZE_SPECIFIER"\n", length_remaining, sizeof(mms->in_buffer) - 12); return AVERROR_INVALIDDATA; @@ -291,7 +291,7 @@ static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst) read_result = ffurl_read_complete(mms->mms_hd, mms->in_buffer + 12, length_remaining) ; if (read_result != length_remaining) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Reading pkt data (length=%d) failed: %d (%s)\n", length_remaining, read_result, read_result < 0 ? strerror(AVUNERROR(read_result)) : @@ -300,7 +300,7 @@ static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst) } packet_type= AV_RL16(mms->in_buffer+36); if (read_result >= 44 && (hr = AV_RL32(mms->in_buffer + 40))) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Server sent a message with packet type 0x%x and error status code 0x%08x\n", packet_type, hr); return AVERROR(EINVAL); } @@ -319,7 +319,7 @@ static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst) if (length_remaining < 0 || length_remaining > sizeof(mms->in_buffer) - 8) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Data length %d is invalid or too large (max=%"SIZE_SPECIFIER")\n", length_remaining, sizeof(mms->in_buffer)); return AVERROR_INVALIDDATA; @@ -328,7 +328,7 @@ static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst) mms->read_in_ptr = mms->in_buffer; read_result= ffurl_read_complete(mms->mms_hd, mms->in_buffer, length_remaining); if(read_result != length_remaining) { - av_log(NULL, AV_LOG_ERROR, + av_log(mms->mms_hd, AV_LOG_ERROR, "Failed to read packet data of size %d: %d (%s)\n", length_remaining, read_result, read_result < 0 ? strerror(AVUNERROR(read_result)) : @@ -358,7 +358,7 @@ static MMSSCPacketType get_tcp_server_response(MMSTContext *mmst) } else if(packet_id_type == mmst->packet_id) { packet_type = SC_PKT_ASF_MEDIA; } else { - av_log(NULL, AV_LOG_TRACE, "packet id type %d is old.", packet_id_type); + av_log(mms->mms_hd, AV_LOG_TRACE, "packet id type %d is old.", packet_id_type); continue; } } @@ -473,7 +473,7 @@ static int mms_close(URLContext *h) MMSContext *mms = &mmst->mms; if(mms->mms_hd) { send_close_packet(mmst); - ffurl_close(mms->mms_hd); + ffurl_closep(&mms->mms_hd); } /* free all separately allocated pointers in mms */ @@ -555,14 +555,14 @@ static int mms_open(URLContext *h, const char *uri, int flags) if (err) goto fail; if((mmst->incoming_flags != 0X08) && (mmst->incoming_flags != 0X0C)) { - av_log(NULL, AV_LOG_ERROR, + av_log(h, AV_LOG_ERROR, "The server does not support MMST (try MMSH or RTSP)\n"); err = AVERROR(EINVAL); goto fail; } err = ff_mms_asf_header_parser(mms); if (err) { - av_log(NULL, AV_LOG_TRACE, "asf header parsed failed!\n"); + av_log(h, AV_LOG_TRACE, "asf header parsed failed!\n"); goto fail; } mms->header_parsed = 1; @@ -579,11 +579,11 @@ static int mms_open(URLContext *h, const char *uri, int flags) if (err) { goto fail; } - av_log(NULL, AV_LOG_TRACE, "Leaving open (success)\n"); + av_log(h, AV_LOG_TRACE, "Leaving open (success)\n"); return 0; fail: mms_close(h); - av_log(NULL, AV_LOG_TRACE, "Leaving open (failure: %d)\n", err); + av_log(mms->mms_hd, AV_LOG_TRACE, "Leaving open (failure: %d)\n", err); return err; } @@ -608,7 +608,7 @@ static int mms_read(URLContext *h, uint8_t *buf, int size) int err = mms_safe_send_recv(mmst, NULL, SC_PKT_ASF_MEDIA); if (err == 0) { if(mms->remaining_in_len>mms->asf_packet_len) { - av_log(NULL, AV_LOG_ERROR, + av_log(h, AV_LOG_ERROR, "Incoming pktlen %d is larger than ASF pktsize %d\n", mms->remaining_in_len, mms->asf_packet_len); result= AVERROR(EIO); @@ -616,12 +616,12 @@ static int mms_read(URLContext *h, uint8_t *buf, int size) // copy the data to the packet buffer. result = ff_mms_read_data(mms, buf, size); if (result == 0) { - av_log(NULL, AV_LOG_TRACE, "Read ASF media packet size is zero!\n"); + av_log(h, AV_LOG_TRACE, "Read ASF media packet size is zero!\n"); break; } } } else { - av_log(NULL, AV_LOG_TRACE, "read packet error!\n"); + av_log(h, AV_LOG_TRACE, "read packet error!\n"); break; } } diff --git a/libavformat/mov.c b/libavformat/mov.c index 327a25bbdf1..dfb41b93bf7 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -46,9 +46,11 @@ #include "libavutil/spherical.h" #include "libavutil/stereo3d.h" #include "libavutil/timecode.h" +#include "libavutil/dovi_meta.h" #include "libavcodec/ac3tab.h" #include "libavcodec/flac.h" #include "libavcodec/mpegaudiodecheader.h" +#include "libavcodec/mlp_parse.h" #include "avformat.h" #include "internal.h" #include "avio_internal.h" @@ -1004,6 +1006,7 @@ static int mov_read_adrm(MOVContext *c, AVIOContext *pb, MOVAtom atom) sha = av_sha_alloc(); if (!sha) return AVERROR(ENOMEM); + av_free(c->aes_decrypt); c->aes_decrypt = av_aes_alloc(); if (!c->aes_decrypt) { ret = AVERROR(ENOMEM); @@ -1128,8 +1131,8 @@ static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom) return ret; } comp_brands_str[comp_brand_size] = 0; - av_dict_set(&c->fc->metadata, "compatible_brands", comp_brands_str, 0); - av_freep(&comp_brands_str); + av_dict_set(&c->fc->metadata, "compatible_brands", + comp_brands_str, AV_DICT_DONT_STRDUP_VAL); return 0; } @@ -1326,12 +1329,15 @@ 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) + if (c->fc->streams[i]->id < 0) { + av_free(frag_stream_info); return AVERROR_INVALIDDATA; + } frag_stream_info[i].id = c->fc->streams[i]->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; frag_stream_info[i].first_tfra_pts = AV_NOPTS_VALUE; frag_stream_info[i].index_entry = -1; frag_stream_info[i].encryption_index = NULL; @@ -1393,14 +1399,14 @@ 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) +static void mov_metadata_creation_time(AVDictionary **metadata, int64_t time, void *logctx) { if (time) { if(time >= 2082844800) time -= 2082844800; /* seconds between 1904-01-01 and Epoch */ if ((int64_t)(time * 1000000ULL) / 1000000 != time) { - av_log(NULL, AV_LOG_DEBUG, "creation_time is not representable\n"); + av_log(logctx, AV_LOG_DEBUG, "creation_time is not representable\n"); return; } @@ -1440,7 +1446,7 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) creation_time = avio_rb32(pb); avio_rb32(pb); /* modification time */ } - mov_metadata_creation_time(&st->metadata, creation_time); + mov_metadata_creation_time(&st->metadata, creation_time, c->fc); sc->time_scale = avio_rb32(pb); if (sc->time_scale <= 0) { @@ -1471,7 +1477,7 @@ static int mov_read_mvhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) creation_time = avio_rb32(pb); avio_rb32(pb); /* modification time */ } - mov_metadata_creation_time(&c->fc->metadata, creation_time); + mov_metadata_creation_time(&c->fc->metadata, creation_time, c->fc); 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); @@ -1543,6 +1549,7 @@ static int mov_read_enda(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; @@ -1555,41 +1562,51 @@ static int mov_read_colr(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (ret < 0) return ret; if (strncmp(color_parameter_type, "nclx", 4) && - strncmp(color_parameter_type, "nclc", 4)) { + strncmp(color_parameter_type, "nclc", 4) && + strncmp(color_parameter_type, "prof", 4)) { av_log(c->fc, AV_LOG_WARNING, "unsupported color_parameter_type %s\n", color_parameter_type); return 0; } - color_primaries = avio_rb16(pb); - color_trc = avio_rb16(pb); - color_matrix = avio_rb16(pb); - - av_log(c->fc, AV_LOG_TRACE, - "%s: pri %d trc %d matrix %d", - color_parameter_type, color_primaries, color_trc, color_matrix); - - if (!strncmp(color_parameter_type, "nclx", 4)) { - uint8_t color_range = avio_r8(pb) >> 7; - av_log(c->fc, AV_LOG_TRACE, " full %"PRIu8"", color_range); - if (color_range) - st->codecpar->color_range = AVCOL_RANGE_JPEG; - else - st->codecpar->color_range = AVCOL_RANGE_MPEG; + 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) + return AVERROR(ENOMEM); + ret = ffio_read_size(pb, icc_profile, atom.size - 4); + if (ret < 0) + return ret; } + else { + color_primaries = avio_rb16(pb); + color_trc = avio_rb16(pb); + color_matrix = avio_rb16(pb); - if (!av_color_primaries_name(color_primaries)) - color_primaries = AVCOL_PRI_UNSPECIFIED; - if (!av_color_transfer_name(color_trc)) - color_trc = AVCOL_TRC_UNSPECIFIED; - if (!av_color_space_name(color_matrix)) - color_matrix = AVCOL_SPC_UNSPECIFIED; + av_log(c->fc, AV_LOG_TRACE, + "%s: pri %d trc %d matrix %d", + color_parameter_type, color_primaries, color_trc, color_matrix); + + if (!strncmp(color_parameter_type, "nclx", 4)) { + uint8_t color_range = avio_r8(pb) >> 7; + av_log(c->fc, AV_LOG_TRACE, " full %"PRIu8"", color_range); + if (color_range) + st->codecpar->color_range = AVCOL_RANGE_JPEG; + else + st->codecpar->color_range = AVCOL_RANGE_MPEG; + } - st->codecpar->color_primaries = color_primaries; - st->codecpar->color_trc = color_trc; - st->codecpar->color_space = color_matrix; - av_log(c->fc, AV_LOG_TRACE, "\n"); + if (!av_color_primaries_name(color_primaries)) + color_primaries = AVCOL_PRI_UNSPECIFIED; + if (!av_color_transfer_name(color_trc)) + color_trc = AVCOL_TRC_UNSPECIFIED; + if (!av_color_space_name(color_matrix)) + color_matrix = AVCOL_SPC_UNSPECIFIED; + st->codecpar->color_primaries = color_primaries; + st->codecpar->color_trc = color_trc; + st->codecpar->color_space = color_matrix; + av_log(c->fc, AV_LOG_TRACE, "\n"); + } return 0; } @@ -1620,7 +1637,7 @@ static int mov_read_fiel(MOVContext *c, AVIOContext *pb, MOVAtom atom) } } if (decoded_field_order == AV_FIELD_UNKNOWN && mov_field_order) { - av_log(NULL, AV_LOG_ERROR, "Unknown MOV field order 0x%04x\n", mov_field_order); + av_log(c->fc, AV_LOG_ERROR, "Unknown MOV field order 0x%04x\n", mov_field_order); } st->codecpar->field_order = decoded_field_order; @@ -1797,19 +1814,19 @@ static int mov_read_aclr(MOVContext *c, AVIOContext *pb, MOVAtom atom) par->color_range = AVCOL_RANGE_JPEG; break; default: - av_log(c, AV_LOG_WARNING, "ignored unknown aclr value (%d)\n", range_value); + av_log(c->fc, AV_LOG_WARNING, "ignored unknown aclr value (%d)\n", range_value); break; } - ff_dlog(c, "color_range: %d\n", par->color_range); + ff_dlog(c->fc, "color_range: %d\n", par->color_range); } else { /* For some reason the whole atom was not added to the extradata */ - av_log(c, AV_LOG_ERROR, "aclr not decoded - incomplete atom\n"); + av_log(c->fc, AV_LOG_ERROR, "aclr not decoded - incomplete atom\n"); } } else { - av_log(c, AV_LOG_ERROR, "aclr not decoded - unable to add atom to extradata\n"); + av_log(c->fc, AV_LOG_ERROR, "aclr not decoded - unable to add atom to extradata\n"); } } else { - av_log(c, AV_LOG_WARNING, "aclr not decoded - unexpected size %"PRId64"\n", atom.size); + av_log(c->fc, AV_LOG_WARNING, "aclr not decoded - unexpected size %"PRId64"\n", atom.size); } } @@ -1837,7 +1854,6 @@ static int mov_read_wave(MOVContext *c, AVIOContext *pb, MOVAtom atom) st->codecpar->codec_id == AV_CODEC_ID_QDMC || st->codecpar->codec_id == AV_CODEC_ID_SPEEX) { // pass all frma atom to codec, needed at least for QDMC and QDM2 - av_freep(&st->codecpar->extradata); ret = ff_get_extradata(c->fc, st->codecpar, pb, atom.size); if (ret < 0) return ret; @@ -1901,10 +1917,9 @@ static int mov_read_glbl(MOVContext *c, AVIOContext *pb, MOVAtom atom) return mov_read_default(c, pb, atom); } if (st->codecpar->extradata_size > 1 && st->codecpar->extradata) { - av_log(c, AV_LOG_WARNING, "ignoring multiple glbl\n"); + av_log(c->fc, AV_LOG_WARNING, "ignoring multiple glbl\n"); return 0; } - av_freep(&st->codecpar->extradata); ret = ff_get_extradata(c->fc, st->codecpar, pb, atom.size); if (ret < 0) return ret; @@ -1937,7 +1952,6 @@ static int mov_read_dvc1(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; avio_seek(pb, 6, SEEK_CUR); - av_freep(&st->codecpar->extradata); ret = ff_get_extradata(c->fc, st->codecpar, pb, atom.size - 7); if (ret < 0) return ret; @@ -1965,7 +1979,6 @@ static int mov_read_strf(MOVContext *c, AVIOContext *pb, MOVAtom atom) return AVERROR_INVALIDDATA; avio_skip(pb, 40); - av_freep(&st->codecpar->extradata); ret = ff_get_extradata(c->fc, st->codecpar, pb, atom.size - 40); if (ret < 0) return ret; @@ -1979,6 +1992,10 @@ static int mov_read_stco(MOVContext *c, AVIOContext *pb, MOVAtom atom) MOVStreamContext *sc; unsigned int i, entries; + if (c->trak_index < 0) { + av_log(c->fc, AV_LOG_WARNING, "STCO outside TRAK\n"); + return 0; + } if (c->fc->nb_streams < 1) return 0; st = c->fc->streams[c->fc->nb_streams-1]; @@ -2254,7 +2271,7 @@ static int mov_rewrite_dvd_sub_extradata(AVStream *st) { char buf[256] = {0}; uint8_t *src = st->codecpar->extradata; - int i; + int i, ret; if (st->codecpar->extradata_size != 64) return 0; @@ -2274,12 +2291,9 @@ static int mov_rewrite_dvd_sub_extradata(AVStream *st) if (av_strlcat(buf, "\n", sizeof(buf)) >= sizeof(buf)) return 0; - av_freep(&st->codecpar->extradata); - st->codecpar->extradata_size = 0; - st->codecpar->extradata = av_mallocz(strlen(buf) + AV_INPUT_BUFFER_PADDING_SIZE); - if (!st->codecpar->extradata) - return AVERROR(ENOMEM); - st->codecpar->extradata_size = strlen(buf); + ret = ff_alloc_extradata(st->codecpar, strlen(buf)); + if (ret < 0) + return ret; memcpy(st->codecpar->extradata, buf, st->codecpar->extradata_size); return 0; @@ -2303,8 +2317,8 @@ static int mov_parse_stsd_data(MOVContext *c, AVIOContext *pb, int val; val = AV_RB32(st->codecpar->extradata + 4); tmcd_ctx->tmcd_flags = val; - st->avg_frame_rate.num = st->codecpar->extradata[16]; /* number of frame */ - st->avg_frame_rate.den = 1; + st->avg_frame_rate.num = AV_RB32(st->codecpar->extradata + 8); /* timescale */ + st->avg_frame_rate.den = AV_RB32(st->codecpar->extradata + 12); /* frameDuration */ #if FF_API_LAVF_AVCTX FF_DISABLE_DEPRECATION_WARNINGS st->codec->time_base = av_inv_q(st->avg_frame_rate); @@ -2328,7 +2342,7 @@ FF_ENABLE_DEPRECATION_WARNINGS uint32_t format = AV_RB32(st->codecpar->extradata + 22); if (format == AV_RB32("name") && (int64_t)size >= (int64_t)len + 18) { uint16_t str_size = AV_RB16(st->codecpar->extradata + 26); /* string length */ - if (str_size > 0 && size >= (int)str_size + 26) { + if (str_size > 0 && size >= (int)str_size + 30) { char *reel_name = av_malloc(str_size + 1); if (!reel_name) return AVERROR(ENOMEM); @@ -2665,6 +2679,10 @@ static int mov_read_stsc(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc->stsc_data[i].id < 1) { av_log(c->fc, AV_LOG_WARNING, "STSC entry %d is invalid (first=%d count=%d id=%d)\n", i, sc->stsc_data[i].first, sc->stsc_data[i].count, sc->stsc_data[i].id); if (i+1 >= sc->stsc_count) { + if (sc->stsc_data[i].count == 0 && i > 0) { + sc->stsc_count --; + continue; + } sc->stsc_data[i].first = FFMAX(sc->stsc_data[i].first, first_min); if (i > 0 && sc->stsc_data[i].first <= sc->stsc_data[i-1].first) sc->stsc_data[i].first = FFMIN(sc->stsc_data[i-1].first + 1LL, INT_MAX); @@ -2867,6 +2885,11 @@ static int mov_read_stsz(MOVContext *c, AVIOContext *pb, MOVAtom atom) for (i = 0; i < entries && !pb->eof_reached; i++) { sc->sample_sizes[i] = get_bits_long(&gb, field_size); + if (sc->sample_sizes[i] < 0) { + av_free(buf); + av_log(c->fc, AV_LOG_ERROR, "Invalid sample size %d\n", sc->sample_sizes[i]); + return AVERROR_INVALIDDATA; + } sc->data_size += sc->sample_sizes[i]; } @@ -2958,11 +2981,45 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } -static void mov_update_dts_shift(MOVStreamContext *sc, int duration) +static int mov_read_sdtp(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + int64_t i, entries; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams - 1]; + sc = st->priv_data; + + avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + entries = atom.size - 4; + + av_log(c->fc, AV_LOG_TRACE, "track[%u].sdtp.entries = %" PRId64 "\n", + c->fc->nb_streams - 1, entries); + + if (sc->sdtp_data) + av_log(c->fc, AV_LOG_WARNING, "Duplicated SDTP atom\n"); + av_freep(&sc->sdtp_data); + sc->sdtp_count = 0; + + sc->sdtp_data = av_mallocz(entries); + if (!sc->sdtp_data) + return AVERROR(ENOMEM); + + for (i = 0; i < entries && !pb->eof_reached; i++) + sc->sdtp_data[i] = avio_r8(pb); + sc->sdtp_count = i; + + return 0; +} + +static void mov_update_dts_shift(MOVStreamContext *sc, int duration, void *logctx) { if (duration < 0) { if (duration == INT_MIN) { - av_log(NULL, AV_LOG_WARNING, "mov_update_dts_shift(): dts_shift set to %d\n", INT_MAX); + av_log(logctx, AV_LOG_WARNING, "mov_update_dts_shift(): dts_shift set to %d\n", INT_MAX); duration++; } sc->dts_shift = FFMAX(sc->dts_shift, -duration); @@ -3020,7 +3077,7 @@ static int mov_read_ctts(MOVContext *c, AVIOContext *pb, MOVAtom atom) } if (i+2fc); } sc->ctts_count = ctts_count; @@ -4377,6 +4434,9 @@ static int mov_read_custom(MOVContext *c, AVIOContext *pb, MOVAtom atom) } else break; + if (*p) + break; + *p = av_malloc(len + 1); if (!*p) { ret = AVERROR(ENOMEM); @@ -4418,7 +4478,10 @@ static int mov_read_custom(MOVContext *c, AVIOContext *pb, MOVAtom atom) static int mov_read_meta(MOVContext *c, AVIOContext *pb, MOVAtom atom) { while (atom.size > 8) { - uint32_t tag = avio_rl32(pb); + uint32_t tag; + if (avio_feof(pb)) + return AVERROR_EOF; + tag = avio_rl32(pb); atom.size -= 4; if (tag == MKTAG('h','d','l','r')) { avio_seek(pb, -8, SEEK_CUR); @@ -4561,6 +4624,7 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) MOVFragment *frag = &c->fragment; MOVTrackExt *trex = NULL; int flags, track_id, i; + MOVFragmentStreamInfo * frag_stream_info; avio_r8(pb); /* version */ flags = avio_rb24(pb); @@ -4594,6 +4658,10 @@ static int mov_read_tfhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_rb32(pb) : trex->flags; av_log(c->fc, AV_LOG_TRACE, "frag flags 0x%x\n", frag->flags); + frag_stream_info = get_current_frag_stream_info(&c->frag_index); + if (frag_stream_info) + frag_stream_info->next_trun_dts = AV_NOPTS_VALUE; + return 0; } @@ -4747,11 +4815,18 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) frag_stream_info = get_current_frag_stream_info(&c->frag_index); if (frag_stream_info) { - if (frag_stream_info->first_tfra_pts != AV_NOPTS_VALUE && + if (frag_stream_info->next_trun_dts != AV_NOPTS_VALUE) { + dts = frag_stream_info->next_trun_dts - sc->time_offset; + } else if (frag_stream_info->first_tfra_pts != AV_NOPTS_VALUE && c->use_mfra_for == FF_MOV_FLAG_MFRA_PTS) { pts = frag_stream_info->first_tfra_pts; av_log(c->fc, AV_LOG_DEBUG, "found mfra time %"PRId64 ", using it for pts\n", pts); + } else if (frag_stream_info->first_tfra_pts != AV_NOPTS_VALUE && + c->use_mfra_for == FF_MOV_FLAG_MFRA_DTS) { + dts = frag_stream_info->first_tfra_pts; + av_log(c->fc, AV_LOG_DEBUG, "found mfra time %"PRId64 + ", using it for dts\n", pts); } else if (frag_stream_info->sidx_pts != AV_NOPTS_VALUE) { // FIXME: sidx earliest_presentation_time is *PTS*, s.b. // pts = frag_stream_info->sidx_pts; @@ -4781,8 +4856,8 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) entries = UINT_MAX / sizeof(AVIndexEntry) - st->nb_index_entries; av_log(c->fc, AV_LOG_ERROR, "Failed to add index entry\n"); } - if (entries <= 0) - return -1; + if (entries == 0) + return 0; requested_size = (st->nb_index_entries + entries) * sizeof(AVIndexEntry); new_entries = av_fast_realloc(st->index_entries, @@ -4843,7 +4918,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (flags & MOV_TRUN_SAMPLE_FLAGS) sample_flags = avio_rb32(pb); if (flags & MOV_TRUN_SAMPLE_CTS) ctts_duration = avio_rb32(pb); - mov_update_dts_shift(sc, ctts_duration); + mov_update_dts_shift(sc, ctts_duration, c->fc); if (pts != AV_NOPTS_VALUE) { dts = pts - sc->dts_shift; if (flags & MOV_TRUN_SAMPLE_CTS) { @@ -4902,6 +4977,8 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc->nb_frames_for_fps ++; } } + if (frag_stream_info) + frag_stream_info->next_trun_dts = dts + sc->time_offset; if (i < entries) { // EOF found before reading all entries. Fix the hole this would // leave in index_entries and ctts_data @@ -5016,7 +5093,7 @@ static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom) return AVERROR_PATCHWELCOME; } avio_rb32(pb); // sap_flags - timestamp = av_rescale_q(pts, st->time_base, timescale); + timestamp = av_rescale_q(pts, timescale, st->time_base); index = update_frag_index(c, offset); frag_stream_info = get_frag_stream_info(&c->frag_index, index, track_id); @@ -5763,8 +5840,8 @@ static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom) return AVERROR_INVALIDDATA; } buffer[len] = '\0'; - av_dict_set(&c->fc->metadata, "xmp", buffer, 0); - av_free(buffer); + av_dict_set(&c->fc->metadata, "xmp", + buffer, AV_DICT_DONT_STRDUP_VAL); } else { // skip all uuid atom, which makes it fast for long uuid-xmp file ret = avio_skip(pb, len); @@ -6298,8 +6375,10 @@ static int mov_read_pssh(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (version > 0) { kid_count = avio_rb32(pb); - if (kid_count >= INT_MAX / sizeof(*key_ids)) - return AVERROR(ENOMEM); + if (kid_count >= INT_MAX / sizeof(*key_ids)) { + ret = AVERROR(ENOMEM); + goto finish; + } for (unsigned int i = 0; i < kid_count && !pb->eof_reached; i++) { unsigned int min_kid_count = FFMIN(FFMAX(i + 1, 1024), kid_count); @@ -6638,6 +6717,7 @@ static int cenc_filter(MOVContext *mov, AVStream* st, MOVStreamContext *sc, AVPa static int mov_read_dops(MOVContext *c, AVIOContext *pb, MOVAtom atom) { const int OPUS_SEEK_PREROLL_MS = 80; + int ret; AVStream *st; size_t size; uint16_t pre_skip; @@ -6658,8 +6738,8 @@ static int mov_read_dops(MOVContext *c, AVIOContext *pb, MOVAtom atom) /* OpusSpecificBox size plus magic for Ogg OpusHead header. */ size = atom.size + 8; - if (ff_alloc_extradata(st->codecpar, size)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, size)) < 0) + return ret; AV_WL32(st->codecpar->extradata, MKTAG('O','p','u','s')); AV_WL32(st->codecpar->extradata + 4, MKTAG('H','e','a','d')); @@ -6683,6 +6763,95 @@ static int mov_read_dops(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +static int mov_read_dmlp(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + unsigned format_info; + int channel_assignment, channel_assignment1, channel_assignment2; + int ratebits; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + if (atom.size < 10) + return AVERROR_INVALIDDATA; + + format_info = avio_rb32(pb); + + ratebits = (format_info >> 28) & 0xF; + channel_assignment1 = (format_info >> 15) & 0x1F; + channel_assignment2 = format_info & 0x1FFF; + if (channel_assignment2) + channel_assignment = channel_assignment2; + else + channel_assignment = channel_assignment1; + + st->codecpar->frame_size = 40 << (ratebits & 0x7); + st->codecpar->sample_rate = mlp_samplerate(ratebits); + st->codecpar->channels = truehd_channels(channel_assignment); + st->codecpar->channel_layout = truehd_layout(channel_assignment); + + return 0; +} + +static int mov_read_dvcc_dvvc(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + uint32_t buf; + AVDOVIDecoderConfigurationRecord *dovi; + size_t dovi_size; + int ret; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + if ((uint64_t)atom.size > (1<<30) || atom.size < 4) + return AVERROR_INVALIDDATA; + + dovi = av_dovi_alloc(&dovi_size); + if (!dovi) + return AVERROR(ENOMEM); + + dovi->dv_version_major = avio_r8(pb); + dovi->dv_version_minor = avio_r8(pb); + + buf = avio_rb16(pb); + dovi->dv_profile = (buf >> 9) & 0x7f; // 7 bits + dovi->dv_level = (buf >> 3) & 0x3f; // 6 bits + dovi->rpu_present_flag = (buf >> 2) & 0x01; // 1 bit + dovi->el_present_flag = (buf >> 1) & 0x01; // 1 bit + dovi->bl_present_flag = buf & 0x01; // 1 bit + if (atom.size >= 24) { // 4 + 4 + 4 * 4 + buf = avio_r8(pb); + dovi->dv_bl_signal_compatibility_id = (buf >> 4) & 0x0f; // 4 bits + } else { + // 0 stands for None + // Dolby Vision V1.2.93 profiles and levels + 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) { + av_free(dovi); + return ret; + } + + av_log(c, AV_LOG_TRACE, "DOVI in dvcC/dvvC 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, + dovi->rpu_present_flag, + dovi->el_present_flag, + dovi->bl_present_flag, + dovi->dv_bl_signal_compatibility_id + ); + + return 0; +} + static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('A','C','L','R'), mov_read_aclr }, { MKTAG('A','P','R','G'), mov_read_avid }, @@ -6731,6 +6900,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('s','t','s','z'), mov_read_stsz }, /* sample size */ { MKTAG('s','t','t','s'), mov_read_stts }, { MKTAG('s','t','z','2'), mov_read_stsz }, /* compact sample size */ +{ MKTAG('s','d','t','p'), mov_read_sdtp }, /* independent and disposable samples */ { MKTAG('t','k','h','d'), mov_read_tkhd }, /* track header */ { MKTAG('t','f','d','t'), mov_read_tfdt }, { MKTAG('t','f','h','d'), mov_read_tfhd }, /* track fragment header */ @@ -6771,11 +6941,14 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('s','t','3','d'), mov_read_st3d }, /* stereoscopic 3D video box */ { MKTAG('s','v','3','d'), mov_read_sv3d }, /* spherical video box */ { MKTAG('d','O','p','s'), mov_read_dops }, +{ MKTAG('d','m','l','p'), mov_read_dmlp }, { MKTAG('S','m','D','m'), mov_read_smdm }, { MKTAG('C','o','L','L'), mov_read_coll }, { MKTAG('v','p','c','C'), mov_read_vpcc }, { MKTAG('m','d','c','v'), mov_read_mdcv }, { MKTAG('c','l','l','i'), mov_read_clli }, +{ MKTAG('d','v','c','C'), mov_read_dvcc_dvvc }, +{ MKTAG('d','v','v','C'), mov_read_dvcc_dvvc }, { 0, NULL } }; @@ -6800,18 +6973,17 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom) if (atom.size >= 8) { a.size = avio_rb32(pb); a.type = avio_rl32(pb); - if (a.type == MKTAG('f','r','e','e') && + if (((a.type == MKTAG('f','r','e','e') && c->moov_retry) || + a.type == MKTAG('h','o','o','v')) && a.size >= 8 && - c->fc->strict_std_compliance < FF_COMPLIANCE_STRICT && - c->moov_retry) { - uint8_t buf[8]; - uint32_t *type = (uint32_t *)buf + 1; - if (avio_read(pb, buf, 8) != 8) - return AVERROR_INVALIDDATA; + c->fc->strict_std_compliance < FF_COMPLIANCE_STRICT) { + uint32_t type; + avio_skip(pb, 4); + type = avio_rl32(pb); avio_seek(pb, -8, SEEK_CUR); - if (*type == MKTAG('m','v','h','d') || - *type == MKTAG('c','m','o','v')) { - av_log(c->fc, AV_LOG_ERROR, "Detected moov in a free atom.\n"); + if (type == MKTAG('m','v','h','d') || + type == MKTAG('c','m','o','v')) { + av_log(c->fc, AV_LOG_ERROR, "Detected moov in a free or hoov atom.\n"); a.type = MKTAG('m','o','o','v'); } } @@ -6857,7 +7029,8 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom) // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html if (!parse && c->found_hdlr_mdta && atom.type == MKTAG('m','e','t','a') && - a.type == MKTAG('k','e','y','s')) { + a.type == MKTAG('k','e','y','s') && + c->meta_keys_count == 0) { parse = mov_read_keys; } @@ -7194,6 +7367,7 @@ static int mov_read_close(AVFormatContext *s) 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); @@ -7216,10 +7390,9 @@ static int mov_read_close(AVFormatContext *s) av_freep(&sc->coll); } - if (mov->dv_demux) { - avformat_free_context(mov->dv_fctx); - mov->dv_fctx = NULL; - } + av_freep(&mov->dv_demux); + avformat_free_context(mov->dv_fctx); + mov->dv_fctx = NULL; if (mov->meta_keys) { for (i = 1; i < mov->meta_keys_count; i++) { @@ -7408,14 +7581,13 @@ static int mov_read_header(AVFormatContext *s) avio_seek(pb, 0, SEEK_SET); if ((err = mov_read_default(mov, pb, atom)) < 0) { av_log(s, AV_LOG_ERROR, "error reading header\n"); - mov_read_close(s); - return err; + goto fail; } } while ((pb->seekable & AVIO_SEEKABLE_NORMAL) && !mov->found_moov && !mov->moov_retry++); if (!mov->found_moov) { av_log(s, AV_LOG_ERROR, "moov atom not found\n"); - mov_read_close(s); - return AVERROR_INVALIDDATA; + err = AVERROR_INVALIDDATA; + goto fail; } av_log(mov->fc, AV_LOG_TRACE, "on_parse_exit_offset=%"PRId64"\n", avio_tell(pb)); @@ -7468,7 +7640,7 @@ static int mov_read_header(AVFormatContext *s) } if (st->codecpar->codec_id == AV_CODEC_ID_DVD_SUBTITLE) { if ((err = mov_rewrite_dvd_sub_extradata(st)) < 0) - return err; + goto fail; } } if (mov->handbrake_version && @@ -7488,8 +7660,8 @@ static int mov_read_header(AVFormatContext *s) if (sc->data_size > INT64_MAX / sc->time_scale / 8) { av_log(s, AV_LOG_ERROR, "Overflow during bit rate calculation %"PRId64" * 8 * %d\n", sc->data_size, sc->time_scale); - mov_read_close(s); - return AVERROR_INVALIDDATA; + err = AVERROR_INVALIDDATA; + goto fail; } st->codecpar->bit_rate = sc->data_size * 8 * sc->time_scale / st->duration; } @@ -7504,8 +7676,8 @@ static int mov_read_header(AVFormatContext *s) if (sc->data_size > INT64_MAX / sc->time_scale / 8) { av_log(s, AV_LOG_ERROR, "Overflow during bit rate calculation %"PRId64" * 8 * %d\n", sc->data_size, sc->time_scale); - mov_read_close(s); - return AVERROR_INVALIDDATA; + err = AVERROR_INVALIDDATA; + goto fail; } st->codecpar->bit_rate = sc->data_size * 8 * sc->time_scale / sc->duration_for_fps; @@ -7529,8 +7701,7 @@ static int mov_read_header(AVFormatContext *s) case AVMEDIA_TYPE_AUDIO: err = ff_replaygain_export(st, s->metadata); if (err < 0) { - mov_read_close(s); - return err; + goto fail; } break; case AVMEDIA_TYPE_VIDEO: @@ -7538,7 +7709,7 @@ static int mov_read_header(AVFormatContext *s) 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; + goto fail; sc->display_matrix = NULL; } @@ -7547,7 +7718,7 @@ static int mov_read_header(AVFormatContext *s) (uint8_t *)sc->stereo3d, sizeof(*sc->stereo3d)); if (err < 0) - return err; + goto fail; sc->stereo3d = NULL; } @@ -7556,7 +7727,7 @@ static int mov_read_header(AVFormatContext *s) (uint8_t *)sc->spherical, sc->spherical_size); if (err < 0) - return err; + goto fail; sc->spherical = NULL; } @@ -7565,7 +7736,7 @@ static int mov_read_header(AVFormatContext *s) (uint8_t *)sc->mastering, sizeof(*sc->mastering)); if (err < 0) - return err; + goto fail; sc->mastering = NULL; } @@ -7574,7 +7745,7 @@ static int mov_read_header(AVFormatContext *s) (uint8_t *)sc->coll, sc->coll_size); if (err < 0) - return err; + goto fail; sc->coll = NULL; } @@ -7588,6 +7759,9 @@ static int mov_read_header(AVFormatContext *s) mov->frag_index.item[i].headers_read = 1; return 0; +fail: + mov_read_close(s); + return err; } static AVIndexEntry *mov_find_next_sample(AVFormatContext *s, AVStream **st) @@ -7604,7 +7778,7 @@ static AVIndexEntry *mov_find_next_sample(AVFormatContext *s, AVStream **st) 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) || ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && - ((msc->pb != s->pb && dts < best_dts) || (msc->pb == s->pb && + ((msc->pb != s->pb && dts < best_dts) || (msc->pb == s->pb && dts != AV_NOPTS_VALUE && ((FFABS(best_dts - dts) <= AV_TIME_BASE && current_sample->pos < sample->pos) || (FFABS(best_dts - dts) > AV_TIME_BASE && dts < best_dts)))))) { sample = current_sample; @@ -7638,7 +7812,8 @@ static int mov_switch_root(AVFormatContext *s, int64_t target, int index) mov->next_root_atom = 0; if (index < 0 || index >= mov->frag_index.nb_items) index = search_frag_moof_offset(&mov->frag_index, target); - if (index < mov->frag_index.nb_items) { + if (index < mov->frag_index.nb_items && + mov->frag_index.item[index].moof_offset == target) { if (index + 1 < mov->frag_index.nb_items) mov->next_root_atom = mov->frag_index.item[index + 1].moof_offset; if (mov->frag_index.item[index].headers_read) @@ -7732,6 +7907,19 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) } return ret; } +#if CONFIG_DV_DEMUXER + if (mov->dv_demux && sc->dv_audio_container) { + AVBufferRef *buf = pkt->buf; + ret = avpriv_dv_produce_packet(mov->dv_demux, pkt, pkt->data, pkt->size, pkt->pos); + pkt->buf = buf; + av_packet_unref(pkt); + if (ret < 0) + return ret; + ret = avpriv_dv_get_packet(mov->dv_demux, pkt); + if (ret < 0) + return ret; + } +#endif if (sc->has_palette) { uint8_t *pal; @@ -7743,16 +7931,6 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) sc->has_palette = 0; } } -#if CONFIG_DV_DEMUXER - if (mov->dv_demux && sc->dv_audio_container) { - avpriv_dv_produce_packet(mov->dv_demux, pkt, pkt->data, pkt->size, pkt->pos); - av_freep(&pkt->data); - pkt->size = 0; - ret = avpriv_dv_get_packet(mov->dv_demux, pkt); - if (ret < 0) - return ret; - } -#endif if (st->codecpar->codec_id == AV_CODEC_ID_MP3 && !st->need_parsing && pkt->size > 4) { if (ff_mpa_check_header(AV_RB32(pkt->data)) < 0) st->need_parsing = AVSTREAM_PARSE_FULL; @@ -7783,6 +7961,11 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) } 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; @@ -7809,8 +7992,9 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) aax_filter(pkt->data, pkt->size, mov); ret = cenc_filter(mov, st, sc, pkt, current_index); - if (ret < 0) + if (ret < 0) { return ret; + } return 0; } @@ -7948,7 +8132,7 @@ static const AVOption mov_options[] = { OFFSET(use_absolute_path), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS}, {"seek_streams_individually", - "Seek each stream individually to the to the closest point", + "Seek each stream individually to the closest point", OFFSET(seek_individually), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS}, {"ignore_editlist", "Ignore the edit list atom.", OFFSET(ignore_editlist), AV_OPT_TYPE_BOOL, {.i64 = 0}, @@ -7999,7 +8183,7 @@ AVInputFormat ff_mov_demuxer = { .long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"), .priv_class = &mov_class, .priv_data_size = sizeof(MOVContext), - .extensions = "mov,mp4,m4a,3gp,3g2,mj2", + .extensions = "mov,mp4,m4a,3gp,3g2,mj2,psp,m4b,ism,ismv,isma,f4v", .read_probe = mov_probe, .read_header = mov_read_header, .read_packet = mov_read_packet, diff --git a/libavformat/movenc.c b/libavformat/movenc.c index a96139077b2..5d8dc4fd5d7 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -51,6 +51,7 @@ #include "libavutil/pixdesc.h" #include "libavutil/stereo3d.h" #include "libavutil/timecode.h" +#include "libavutil/dovi_meta.h" #include "libavutil/color_utils.h" #include "hevc.h" #include "rtpenc.h" @@ -72,11 +73,13 @@ static const AVOption options[] = { { "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 (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" }, @@ -518,8 +521,6 @@ static int handle_eac3(MOVMuxContext *mov, AVPacket *pkt, MOVTrack *track) memcpy(info->pkt.data + info->pkt.size - pkt->size, pkt->data, pkt->size); info->num_blocks += num_blocks; info->pkt.duration += pkt->duration; - if ((ret = av_copy_packet_side_data(&info->pkt, pkt)) < 0) - goto end; if (info->num_blocks != 6) goto end; av_packet_unref(pkt); @@ -771,6 +772,35 @@ static int mov_write_dops_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra return update_size(pb, pos); } +static int mov_write_dmlp_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + int length; + avio_wb32(pb, 0); + ffio_wfourcc(pb, "dmlp"); + + if (track->vos_len < 20) { + av_log(s, AV_LOG_ERROR, + "Cannot write moov atom before TrueHD packets." + " Set the delay_moov flag to fix this.\n"); + return AVERROR(EINVAL); + } + + length = (AV_RB16(track->vos_data) & 0xFFF) * 2; + if (length < 20 || length > track->vos_len) + return AVERROR_INVALIDDATA; + + // Only TrueHD is supported + if (AV_RB32(track->vos_data + 4) != 0xF8726FBA) + return AVERROR_INVALIDDATA; + + avio_wb32(pb, AV_RB32(track->vos_data + 8)); /* format_info */ + avio_wb16(pb, AV_RB16(track->vos_data + 18) << 1); /* peak_data_rate */ + avio_wb32(pb, 0); /* reserved */ + + return update_size(pb, pos); +} + static int mov_write_chan_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track) { uint32_t layout_tag, bitmap; @@ -1100,10 +1130,14 @@ 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) avio_wb16(pb, 48000); + else if (track->par->codec_id == AV_CODEC_ID_TRUEHD) + avio_wb32(pb, track->par->sample_rate); else avio_wb16(pb, track->par->sample_rate <= UINT16_MAX ? track->par->sample_rate : 0); - avio_wb16(pb, 0); /* Reserved */ + + if (track->par->codec_id != AV_CODEC_ID_TRUEHD) + avio_wb16(pb, 0); /* Reserved */ } if (version == 1) { /* SoundDescription V1 extended info */ @@ -1145,6 +1179,8 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex ret = mov_write_dfla_tag(pb, track); else if (track->par->codec_id == AV_CODEC_ID_OPUS) 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) ret = mov_write_glbl_tag(pb, track); @@ -1779,6 +1815,36 @@ static int mov_write_sv3d_tag(AVFormatContext *s, AVIOContext *pb, AVSphericalMa return update_size(pb, sv3d_pos); } +static int mov_write_dvcc_dvvc_tag(AVFormatContext *s, AVIOContext *pb, AVDOVIDecoderConfigurationRecord *dovi) +{ + avio_wb32(pb, 32); /* size = 8 + 24 */ + if (dovi->dv_profile > 7) + ffio_wfourcc(pb, "dvvC"); + else + ffio_wfourcc(pb, "dvcC"); + avio_w8(pb, dovi->dv_version_major); + avio_w8(pb, dovi->dv_version_minor); + avio_wb16(pb, (dovi->dv_profile << 9) | (dovi->dv_level << 3) | + (dovi->rpu_present_flag << 2) | (dovi->el_present_flag << 1) | + dovi->bl_present_flag); + avio_wb32(pb, (dovi->dv_bl_signal_compatibility_id << 28) | 0); + + avio_wb32(pb, 0); /* reserved */ + avio_wb32(pb, 0); /* reserved */ + avio_wb32(pb, 0); /* reserved */ + avio_wb32(pb, 0); /* reserved */ + av_log(s, 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 > 7 ? "dvvC" : "dvcC", + dovi->dv_version_major, dovi->dv_version_minor, + dovi->dv_profile, dovi->dv_level, + dovi->rpu_present_flag, + dovi->el_present_flag, + dovi->bl_present_flag, + dovi->dv_bl_signal_compatibility_id); + return 32; /* 8 + 24 */ +} + static int mov_write_clap_tag(AVIOContext *pb, MOVTrack *track) { avio_wb32(pb, 40); @@ -1830,85 +1896,105 @@ static int mov_write_gama_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra return 0; } -static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track) +static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track, int prefer_icc) { + int64_t pos = avio_tell(pb); + // 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 - if (track->par->color_primaries == AVCOL_PRI_UNSPECIFIED && - track->par->color_trc == AVCOL_TRC_UNSPECIFIED && - track->par->color_space == AVCOL_SPC_UNSPECIFIED) { - if ((track->par->width >= 1920 && track->par->height >= 1080) - || (track->par->width == 1280 && track->par->height == 720)) { - av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming bt709\n"); - track->par->color_primaries = AVCOL_PRI_BT709; - } else if (track->par->width == 720 && track->height == 576) { - av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming bt470bg\n"); - track->par->color_primaries = AVCOL_PRI_BT470BG; - } else if (track->par->width == 720 && - (track->height == 486 || track->height == 480)) { - av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming smpte170\n"); - track->par->color_primaries = AVCOL_PRI_SMPTE170M; - } else { - av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, unable to assume anything\n"); + const uint8_t *icc_profile; + int icc_profile_size; + + if (prefer_icc) { + icc_profile = av_stream_get_side_data(track->st, AV_PKT_DATA_ICC_PROFILE, &icc_profile_size); + + if (icc_profile) { + avio_wb32(pb, 12 + icc_profile_size); + ffio_wfourcc(pb, "colr"); + ffio_wfourcc(pb, "prof"); + avio_write(pb, icc_profile, icc_profile_size); + return 12 + icc_profile_size; } - switch (track->par->color_primaries) { - case AVCOL_PRI_BT709: - track->par->color_trc = AVCOL_TRC_BT709; - track->par->color_space = AVCOL_SPC_BT709; - break; - case AVCOL_PRI_SMPTE170M: - case AVCOL_PRI_BT470BG: - track->par->color_trc = AVCOL_TRC_BT709; - track->par->color_space = AVCOL_SPC_SMPTE170M; - break; + else { + av_log(NULL, AV_LOG_INFO, "no ICC profile found, will write nclx/nclc colour info instead\n"); } } /* We should only ever be called by MOV or MP4. */ av_assert0(track->mode == MODE_MOV || track->mode == MODE_MP4); - avio_wb32(pb, 18 + (track->mode == MODE_MP4)); + avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "colr"); if (track->mode == MODE_MP4) ffio_wfourcc(pb, "nclx"); else ffio_wfourcc(pb, "nclc"); - switch (track->par->color_primaries) { - case AVCOL_PRI_BT709: avio_wb16(pb, 1); break; - case AVCOL_PRI_BT470BG: avio_wb16(pb, 5); break; - case AVCOL_PRI_SMPTE170M: - case AVCOL_PRI_SMPTE240M: avio_wb16(pb, 6); break; - case AVCOL_PRI_BT2020: avio_wb16(pb, 9); break; - case AVCOL_PRI_SMPTE431: avio_wb16(pb, 11); break; - case AVCOL_PRI_SMPTE432: avio_wb16(pb, 12); break; - default: avio_wb16(pb, 2); - } - switch (track->par->color_trc) { - case AVCOL_TRC_BT709: avio_wb16(pb, 1); break; - case AVCOL_TRC_SMPTE170M: avio_wb16(pb, 1); break; // remapped - case AVCOL_TRC_SMPTE240M: avio_wb16(pb, 7); break; - case AVCOL_TRC_SMPTEST2084: avio_wb16(pb, 16); break; - case AVCOL_TRC_SMPTE428: avio_wb16(pb, 17); break; - case AVCOL_TRC_ARIB_STD_B67: avio_wb16(pb, 18); break; - default: avio_wb16(pb, 2); - } - switch (track->par->color_space) { - case AVCOL_SPC_BT709: avio_wb16(pb, 1); break; - case AVCOL_SPC_BT470BG: - case AVCOL_SPC_SMPTE170M: avio_wb16(pb, 6); break; - case AVCOL_SPC_SMPTE240M: avio_wb16(pb, 7); break; - case AVCOL_SPC_BT2020_NCL: avio_wb16(pb, 9); break; - default: avio_wb16(pb, 2); - } - + // Do not try to guess the color info if it is AVCOL_PRI_UNSPECIFIED. + // e.g., Dolby Vision for Apple devices should be set to AVCOL_PRI_UNSPECIFIED. See + // https://developer.apple.com/av-foundation/High-Dynamic-Range-Metadata-for-Apple-Devices.pdf + avio_wb16(pb, track->par->color_primaries); + avio_wb16(pb, track->par->color_trc); + avio_wb16(pb, track->par->color_space); if (track->mode == MODE_MP4) { int full_range = track->par->color_range == AVCOL_RANGE_JPEG; avio_w8(pb, full_range << 7); - return 19; - } else { - return 18; } + + return update_size(pb, pos); +} + +static int mov_write_clli_tag(AVIOContext *pb, MOVTrack *track) +{ + const uint8_t *side_data; + const AVContentLightMetadata *content_light_metadata; + + side_data = av_stream_get_side_data(track->st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, NULL); + if (!side_data) { + av_log(NULL, AV_LOG_VERBOSE, "Not writing 'clli' atom. No content light level info.\n"); + return 0; + } + content_light_metadata = (const AVContentLightMetadata*)side_data; + + avio_wb32(pb, 12); // size + ffio_wfourcc(pb, "clli"); + avio_wb16(pb, content_light_metadata->MaxCLL); + avio_wb16(pb, content_light_metadata->MaxFALL); + return 12; +} + +static inline int64_t rescale_mdcv(AVRational q, int b) +{ + return av_rescale(q.num, b, q.den); +} + +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; + if (!metadata || !metadata->has_primaries || !metadata->has_luminance) { + av_log(NULL, AV_LOG_VERBOSE, "Not writing 'mdcv' atom. Missing mastering metadata.\n"); + 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)); + return 32; } static void find_compressor(char * compressor_name, int len, MOVTrack *track) @@ -2080,19 +2166,27 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex } if (mov->flags & FF_MOV_FLAG_WRITE_COLR) { if (track->mode == MODE_MOV || track->mode == MODE_MP4) - mov_write_colr_tag(pb, track); + mov_write_colr_tag(pb, track, mov->flags & FF_MOV_FLAG_PREFER_ICC); else av_log(mov->fc, AV_LOG_WARNING, "Not writing 'colr' atom. Format is not MOV or MP4.\n"); } + if (track->mode == MODE_MOV || track->mode == MODE_MP4) { + mov_write_clli_tag(pb, track); + mov_write_mdcv_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);; if (stereo_3d) mov_write_st3d_tag(s, pb, stereo_3d); if (spherical_mapping) mov_write_sv3d_tag(mov->fc, pb, spherical_mapping); + if (dovi) + mov_write_dvcc_dvvc_tag(s, pb, dovi); } if (track->par->sample_aspect_ratio.den && track->par->sample_aspect_ratio.num) { @@ -2456,6 +2550,8 @@ static int mov_write_stbl_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext return ret; mov_write_stts_tag(pb, track); if ((track->par->codec_type == AVMEDIA_TYPE_VIDEO || + track->par->codec_id == AV_CODEC_ID_TRUEHD || + track->par->codec_id == AV_CODEC_ID_MPEGH_3D_AUDIO || track->par->codec_tag == MKTAG('r','t','p',' ')) && track->has_keyframes && track->has_keyframes < track->entry) mov_write_stss_tag(pb, track, MOV_SYNC_SAMPLE); @@ -2627,7 +2723,7 @@ static int mov_write_hdlr_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra } else { hdlr_type = "text"; } - descr = "SubtitleHandler"; + descr = "SubtitleHandler"; } } else if (track->par->codec_tag == MKTAG('r','t','p',' ')) { hdlr_type = "hint"; @@ -2723,10 +2819,28 @@ static int mov_write_minf_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext return update_size(pb, pos); } +static int64_t calc_pts_duration(MOVMuxContext *mov, MOVTrack *track) +{ + if (track->tag == MKTAG('t','m','c','d')) { + // tmcd tracks gets track_duration set in mov_write_moov_tag from + // another track's duration, while the end_pts may be left at zero. + // Calculate the pts duration for that track instead. + return av_rescale(calc_pts_duration(mov, &mov->tracks[track->src_track]), + track->timescale, mov->tracks[track->src_track].timescale); + } + if (track->end_pts != AV_NOPTS_VALUE && + track->start_dts != AV_NOPTS_VALUE && + track->start_cts != AV_NOPTS_VALUE) { + return track->end_pts - (track->start_dts + track->start_cts); + } + return track->track_duration; +} + static int mov_write_mdhd_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { - int version = track->track_duration < INT32_MAX ? 0 : 1; + int64_t duration = calc_pts_duration(mov, track); + int version = duration < INT32_MAX ? 0 : 1; if (track->mode == MODE_ISM) version = 1; @@ -2748,7 +2862,7 @@ static int mov_write_mdhd_tag(AVIOContext *pb, MOVMuxContext *mov, else if (!track->entry) (version == 1) ? avio_wb64(pb, 0) : avio_wb32(pb, 0); else - (version == 1) ? avio_wb64(pb, track->track_duration) : avio_wb32(pb, track->track_duration); /* duration */ + (version == 1) ? avio_wb64(pb, duration) : avio_wb32(pb, duration); /* duration */ avio_wb16(pb, track->language); /* language */ avio_wb16(pb, 0); /* reserved (quality) */ @@ -2798,8 +2912,9 @@ static void write_matrix(AVIOContext *pb, int16_t a, int16_t b, int16_t c, static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track, AVStream *st) { - int64_t duration = av_rescale_rnd(track->track_duration, MOV_TIMESCALE, - track->timescale, AV_ROUND_UP); + int64_t duration = av_rescale_rnd(calc_pts_duration(mov, track), + MOV_TIMESCALE, track->timescale, + AV_ROUND_UP); int version = duration < INT32_MAX ? 0 : 1; int flags = MOV_TKHD_FLAG_IN_MOVIE; int rotation = 0; @@ -2945,8 +3060,9 @@ static int mov_write_tapt_tag(AVIOContext *pb, MOVTrack *track) static int mov_write_edts_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { - int64_t duration = av_rescale_rnd(track->track_duration, MOV_TIMESCALE, - track->timescale, AV_ROUND_UP); + int64_t duration = av_rescale_rnd(calc_pts_duration(mov, track), + MOV_TIMESCALE, track->timescale, + AV_ROUND_UP); int version = duration < INT32_MAX ? 0 : 1; int entry_size, entry_count, size; int64_t delay, start_ct = track->start_cts; @@ -3108,12 +3224,12 @@ static int mov_write_track_udta_tag(AVIOContext *pb, MOVMuxContext *mov, if (mov->mode & (MODE_MP4|MODE_MOV)) mov_write_track_metadata(pb_buf, st, "name", "title"); - if ((size = avio_close_dyn_buf(pb_buf, &buf)) > 0) { + if ((size = avio_get_dyn_buf(pb_buf, &buf)) > 0) { avio_wb32(pb, size + 8); ffio_wfourcc(pb, "udta"); avio_write(pb, buf, size); } - av_free(buf); + ffio_free_dyn_buf(&pb_buf); return 0; } @@ -3232,7 +3348,8 @@ static int mov_write_mvhd_tag(AVIOContext *pb, MOVMuxContext *mov) for (i = 0; i < mov->nb_streams; i++) { if (mov->tracks[i].entry > 0 && mov->tracks[i].timescale) { - int64_t max_track_len_temp = av_rescale_rnd(mov->tracks[i].track_duration, + int64_t max_track_len_temp = av_rescale_rnd( + calc_pts_duration(mov, &mov->tracks[i]), MOV_TIMESCALE, mov->tracks[i].timescale, AV_ROUND_UP); @@ -3783,12 +3900,12 @@ static int mov_write_udta_tag(AVIOContext *pb, MOVMuxContext *mov, if (s->nb_chapters && !(mov->flags & FF_MOV_FLAG_DISABLE_CHPL)) mov_write_chpl_tag(pb_buf, s); - if ((size = avio_close_dyn_buf(pb_buf, &buf)) > 0) { + if ((size = avio_get_dyn_buf(pb_buf, &buf)) > 0) { avio_wb32(pb, size + 8); ffio_wfourcc(pb, "udta"); avio_write(pb, buf, size); } - av_free(buf); + ffio_free_dyn_buf(&pb_buf); return 0; } @@ -4172,6 +4289,9 @@ static int mov_write_tfhd_tag(AVIOContext *pb, MOVMuxContext *mov, flags &= ~MOV_TFHD_BASE_DATA_OFFSET; flags |= MOV_TFHD_DEFAULT_BASE_IS_MOOF; } + /* CMAF requires all values to be explicit in tfhd atoms */ + if (mov->flags & FF_MOV_FLAG_CMAF) + flags |= MOV_TFHD_STSD_ID; /* Don't set a default sample size, the silverlight player refuses * to play files with that set. Don't set a default sample duration, @@ -4179,7 +4299,7 @@ static int mov_write_tfhd_tag(AVIOContext *pb, MOVMuxContext *mov, * file format says it MUST NOT be set. */ if (track->mode == MODE_ISM) flags &= ~(MOV_TFHD_DEFAULT_SIZE | MOV_TFHD_DEFAULT_DURATION | - MOV_TFHD_BASE_DATA_OFFSET); + MOV_TFHD_BASE_DATA_OFFSET | MOV_TFHD_STSD_ID); avio_wb32(pb, 0); /* size placeholder */ ffio_wfourcc(pb, "tfhd"); @@ -4189,6 +4309,9 @@ static int mov_write_tfhd_tag(AVIOContext *pb, MOVMuxContext *mov, avio_wb32(pb, track->track_id); /* track-id */ if (flags & MOV_TFHD_BASE_DATA_OFFSET) avio_wb64(pb, moof_offset); + if (flags & MOV_TFHD_STSD_ID) { + avio_wb32(pb, 1); + } if (flags & MOV_TFHD_DEFAULT_DURATION) { track->default_duration = get_cluster_duration(track, 0); avio_wb32(pb, track->default_duration); @@ -4475,7 +4598,8 @@ static int mov_write_sidx_tag(AVIOContext *pb, { int64_t pos = avio_tell(pb), offset_pos, end_pos; int64_t presentation_time, duration, offset; - int starts_with_SAP, i, entries; + unsigned starts_with_SAP; + int i, entries; if (track->entry) { entries = 1; @@ -4570,6 +4694,7 @@ static int mov_write_prft_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks) { int64_t pos = avio_tell(pb), pts_us, ntp_ts; MOVTrack *first_track; + int flags = 24; /* PRFT should be associated with at most one track. So, choosing only the * first track. */ @@ -4588,7 +4713,13 @@ static int mov_write_prft_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks) } if (mov->write_prft == MOV_PRFT_SRC_WALLCLOCK) { - ntp_ts = ff_get_formatted_ntp_time(ff_ntp_time()); + if (first_track->cluster[0].prft.wallclock) { + /* Round the NTP time to whole milliseconds. */ + ntp_ts = ff_get_formatted_ntp_time((first_track->cluster[0].prft.wallclock / 1000) * 1000 + + NTP_OFFSET_US); + flags = first_track->cluster[0].prft.flags; + } else + ntp_ts = ff_get_formatted_ntp_time(ff_ntp_time()); } else if (mov->write_prft == MOV_PRFT_SRC_PTS) { pts_us = av_rescale_q(first_track->cluster[0].pts, first_track->st->time_base, AV_TIME_BASE_Q); @@ -4602,7 +4733,7 @@ static int mov_write_prft_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks) avio_wb32(pb, 0); // Size place holder ffio_wfourcc(pb, "prft"); // Type avio_w8(pb, 1); // Version - avio_wb24(pb, 0); // Flags + avio_wb24(pb, flags); // Flags avio_wb32(pb, first_track->track_id); // reference track ID avio_wb64(pb, ntp_ts); // NTP time stamp avio_wb64(pb, first_track->cluster[0].pts); //media time @@ -4702,27 +4833,11 @@ static int mov_write_mdat_tag(AVIOContext *pb, MOVMuxContext *mov) return 0; } -/* TODO: This needs to be more general */ -static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) +static void mov_write_ftyp_tag_internal(AVIOContext *pb, AVFormatContext *s, + int has_h264, int has_video, int write_minor) { MOVMuxContext *mov = s->priv_data; - int64_t pos = avio_tell(pb); - int has_h264 = 0, has_video = 0; int minor = 0x200; - int i; - - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - if (is_cover_image(st)) - continue; - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) - has_video = 1; - if (st->codecpar->codec_id == AV_CODEC_ID_H264) - has_h264 = 1; - } - - avio_wb32(pb, 0); /* size */ - ffio_wfourcc(pb, "ftyp"); if (mov->major_brand && strlen(mov->major_brand) >= 4) ffio_wfourcc(pb, mov->major_brand); @@ -4734,6 +4849,9 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) minor = has_h264 ? 0x20000 : 0x10000; } else if (mov->mode == MODE_PSP) ffio_wfourcc(pb, "MSNV"); + else if (mov->mode == MODE_MP4 && mov->flags & FF_MOV_FLAG_FRAGMENT && + mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS) + ffio_wfourcc(pb, "iso6"); // Required when using signed CTS offsets in trun boxes else if (mov->mode == MODE_MP4 && mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF) ffio_wfourcc(pb, "iso5"); // Required when using default-base-is-moof else if (mov->mode == MODE_MP4 && mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS) @@ -4749,31 +4867,68 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) else ffio_wfourcc(pb, "qt "); - avio_wb32(pb, minor); + if (write_minor) + avio_wb32(pb, minor); +} - if (mov->mode == MODE_MOV) - ffio_wfourcc(pb, "qt "); - else if (mov->mode == MODE_ISM) { - ffio_wfourcc(pb, "piff"); - } else if (!(mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF)) { - ffio_wfourcc(pb, "isom"); - ffio_wfourcc(pb, "iso2"); - if (has_h264) - ffio_wfourcc(pb, "avc1"); +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_video = 0; + int i; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (is_cover_image(st)) + continue; + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) + has_video = 1; + if (st->codecpar->codec_id == AV_CODEC_ID_H264) + has_h264 = 1; } - // We add tfdt atoms when fragmenting, signal this with the iso6 compatible - // brand. This is compatible with users that don't understand tfdt. - if (mov->flags & FF_MOV_FLAG_FRAGMENT && mov->mode != MODE_ISM) - ffio_wfourcc(pb, "iso6"); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "ftyp"); - if (mov->mode == MODE_3GP) - ffio_wfourcc(pb, has_h264 ? "3gp6":"3gp4"); - else if (mov->mode & MODE_3G2) - ffio_wfourcc(pb, has_h264 ? "3g2b":"3g2a"); - else if (mov->mode == MODE_PSP) - ffio_wfourcc(pb, "MSNV"); - else if (mov->mode == MODE_MP4) + // Write major brand + mov_write_ftyp_tag_internal(pb, s, has_h264, has_video, 1); + // Write the major brand as the first compatible brand as well + mov_write_ftyp_tag_internal(pb, s, has_h264, has_video, 0); + + // Write compatible brands, ensuring that we don't write the major brand as a + // compatible brand a second time. + if (mov->mode == MODE_ISM) { + ffio_wfourcc(pb, "piff"); + } else if (mov->mode != MODE_MOV) { + // We add tfdt atoms when fragmenting, signal this with the iso6 compatible + // brand, if not already the major brand. This is compatible with users that + // don't understand tfdt. + if (mov->mode == MODE_MP4) { + if (mov->flags & FF_MOV_FLAG_CMAF) + ffio_wfourcc(pb, "cmfc"); + if (mov->flags & FF_MOV_FLAG_FRAGMENT && !(mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS)) + ffio_wfourcc(pb, "iso6"); + } else { + if (mov->flags & FF_MOV_FLAG_FRAGMENT) + ffio_wfourcc(pb, "iso6"); + if (mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF) + ffio_wfourcc(pb, "iso5"); + else if (mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS) + ffio_wfourcc(pb, "iso4"); + } + // Brands prior to iso5 can't be signaled when using default-base-is-moof + if (!(mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF)) { + // write isom for mp4 only if it it's not the major brand already. + if (mov->mode != MODE_MP4 || mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS) + ffio_wfourcc(pb, "isom"); + ffio_wfourcc(pb, "iso2"); + if (has_h264) + ffio_wfourcc(pb, "avc1"); + } + } + + if (mov->mode == MODE_MP4) ffio_wfourcc(pb, "mp41"); if (mov->flags & FF_MOV_FLAG_DASH && mov->flags & FF_MOV_FLAG_GLOBAL_SIDX) @@ -4963,6 +5118,25 @@ static void mov_parse_vc1_frame(AVPacket *pkt, MOVTrack *trk) } } +static void mov_parse_truehd_frame(AVPacket *pkt, MOVTrack *trk) +{ + int length; + + if (pkt->size < 8) + return; + + length = (AV_RB16(pkt->data) & 0xFFF) * 2; + if (length < 8 || length > pkt->size) + return; + + if (AV_RB32(pkt->data + 4) == 0xF8726FBA) { + trk->cluster[trk->entry].flags |= MOV_SYNC_SAMPLE; + trk->has_keyframes++; + } + + return; +} + static int mov_flush_fragment_interleaving(AVFormatContext *s, MOVTrack *track) { MOVMuxContext *mov = s->priv_data; @@ -4976,12 +5150,11 @@ static int mov_flush_fragment_interleaving(AVFormatContext *s, MOVTrack *track) if ((ret = avio_open_dyn_buf(&mov->mdat_buf)) < 0) return ret; } - buf_size = avio_close_dyn_buf(track->mdat_buf, &buf); - track->mdat_buf = NULL; + buf_size = avio_get_dyn_buf(track->mdat_buf, &buf); offset = avio_tell(mov->mdat_buf); avio_write(mov->mdat_buf, buf, buf_size); - av_free(buf); + ffio_free_dyn_buf(&track->mdat_buf); for (i = track->entries_flushed; i < track->entry; i++) track->cluster[i].pos += offset; @@ -5073,17 +5246,16 @@ static int mov_flush_fragment(AVFormatContext *s, int force) if (mov->flags & FF_MOV_FLAG_DELAY_MOOV) { if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX) mov->reserved_header_pos = avio_tell(s->pb); - avio_flush(s->pb); + avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); mov->moov_written = 1; return 0; } - buf_size = avio_close_dyn_buf(mov->mdat_buf, &buf); - mov->mdat_buf = NULL; + buf_size = avio_get_dyn_buf(mov->mdat_buf, &buf); avio_wb32(s->pb, buf_size + 8); ffio_wfourcc(s->pb, "mdat"); avio_write(s->pb, buf, buf_size); - av_free(buf); + ffio_free_dyn_buf(&mov->mdat_buf); if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX) mov->reserved_header_pos = avio_tell(s->pb); @@ -5098,7 +5270,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) mov->tracks[i].entry = 0; mov->tracks[i].end_reliable = 0; } - avio_flush(s->pb); + avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); return 0; } @@ -5163,7 +5335,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) } if (write_moof) { - avio_flush(s->pb); + avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); mov_write_moof_tag(s->pb, mov, moof_tracks, mdat_size); mov->fragments++; @@ -5195,7 +5367,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) mov->mdat_size = 0; - avio_flush(s->pb); + avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); return 0; } @@ -5257,8 +5429,10 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) AVIOContext *pb = s->pb; MOVTrack *trk = &mov->tracks[pkt->stream_index]; AVCodecParameters *par = trk->par; + AVProducerReferenceTime *prft; unsigned int samples_in_chunk = 0; - int size = pkt->size, ret = 0; + int size = pkt->size, ret = 0, offset = 0; + int prft_size; uint8_t *reformatted_data = NULL; ret = check_pkt(s, pkt); @@ -5321,12 +5495,30 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) !TAG_IS_AVCI(trk->tag) && (par->codec_id != AV_CODEC_ID_DNXHD)) { trk->vos_len = par->extradata_size; - trk->vos_data = av_malloc(trk->vos_len); + trk->vos_data = av_malloc(trk->vos_len + AV_INPUT_BUFFER_PADDING_SIZE); if (!trk->vos_data) { ret = AVERROR(ENOMEM); goto err; } memcpy(trk->vos_data, par->extradata, trk->vos_len); + memset(trk->vos_data + trk->vos_len, 0, AV_INPUT_BUFFER_PADDING_SIZE); + } + + 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_TRUEHD || + par->codec_id == AV_CODEC_ID_AC3) && !trk->vos_len && + !TAG_IS_AVCI(trk->tag)) { + /* copy frame to create needed atoms */ + trk->vos_len = size; + trk->vos_data = av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!trk->vos_data) { + ret = AVERROR(ENOMEM); + goto err; + } + memcpy(trk->vos_data, pkt->data, size); + memset(trk->vos_data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE); } if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 && @@ -5343,8 +5535,10 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) /* from x264 or from bytestream H.264 */ /* NAL reformatting needed */ if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) { - ff_avc_parse_nal_units_buf(pkt->data, &reformatted_data, - &size); + ret = ff_avc_parse_nal_units_buf(pkt->data, &reformatted_data, + &size); + if (ret < 0) + return ret; avio_write(pb, reformatted_data, size); } else { if (trk->cenc.aes_ctr) { @@ -5361,14 +5555,20 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) (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) { - ff_hevc_annexb2mp4_buf(pkt->data, &reformatted_data, &size, 0, NULL); + ret = ff_hevc_annexb2mp4_buf(pkt->data, &reformatted_data, + &size, 0, NULL); + if (ret < 0) + return ret; avio_write(pb, reformatted_data, size); } else { size = ff_hevc_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) { - ff_av1_filter_obus_buf(pkt->data, &reformatted_data, &size); + ret = ff_av1_filter_obus_buf(pkt->data, &reformatted_data, + &size, &offset); + if (ret < 0) + return ret; avio_write(pb, reformatted_data, size); } else { size = ff_av1_filter_obus(pb, pkt->data, pkt->size); @@ -5399,20 +5599,8 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) } } - if ((par->codec_id == AV_CODEC_ID_DNXHD || - par->codec_id == AV_CODEC_ID_AC3) && !trk->vos_len) { - /* copy frame to create needed atoms */ - trk->vos_len = size; - trk->vos_data = av_malloc(size); - if (!trk->vos_data) { - ret = AVERROR(ENOMEM); - goto err; - } - memcpy(trk->vos_data, pkt->data, size); - } - if (trk->entry >= trk->cluster_capacity) { - unsigned new_capacity = 2 * (trk->entry + MOV_INDEX_CLUSTER_SIZE); + unsigned new_capacity = trk->entry + MOV_INDEX_CLUSTER_SIZE; if (av_reallocp_array(&trk->cluster, new_capacity, sizeof(*trk->cluster))) { ret = AVERROR(ENOMEM); @@ -5509,6 +5697,8 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) if (par->codec_id == AV_CODEC_ID_VC1) { mov_parse_vc1_frame(pkt, trk); + } else if (par->codec_id == AV_CODEC_ID_TRUEHD) { + mov_parse_truehd_frame(pkt, trk); } else if (pkt->flags & AV_PKT_FLAG_KEY) { if (mov->mode == MODE_MOV && par->codec_id == AV_CODEC_ID_MPEG2VIDEO && trk->entry > 0) { // force sync sample for the first key frame @@ -5525,104 +5715,113 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) trk->cluster[trk->entry].flags |= MOV_DISPOSABLE_SAMPLE; trk->has_disposable++; } + + prft = (AVProducerReferenceTime *)av_packet_get_side_data(pkt, AV_PKT_DATA_PRFT, &prft_size); + if (prft && prft_size == sizeof(AVProducerReferenceTime)) + memcpy(&trk->cluster[trk->entry].prft, prft, prft_size); + else + memset(&trk->cluster[trk->entry].prft, 0, sizeof(AVProducerReferenceTime)); + trk->entry++; trk->sample_count += samples_in_chunk; mov->mdat_size += size; if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) ff_mov_add_hinted_packet(s, pkt, trk->hint_track, trk->entry, - reformatted_data, size); + reformatted_data ? reformatted_data + offset + : NULL, size); end: err: - av_free(reformatted_data); + if (pkt->data != reformatted_data) + av_free(reformatted_data); return ret; } static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt) { - MOVMuxContext *mov = s->priv_data; - MOVTrack *trk = &mov->tracks[pkt->stream_index]; - AVCodecParameters *par = trk->par; - int64_t frag_duration = 0; - int size = pkt->size; + MOVMuxContext *mov = s->priv_data; + MOVTrack *trk = &mov->tracks[pkt->stream_index]; + AVCodecParameters *par = trk->par; + int64_t frag_duration = 0; + int size = pkt->size; - int ret = check_pkt(s, pkt); - if (ret < 0) - return ret; + int ret = check_pkt(s, pkt); + if (ret < 0) + return ret; - if (mov->flags & FF_MOV_FLAG_FRAG_DISCONT) { - int i; - for (i = 0; i < s->nb_streams; i++) - mov->tracks[i].frag_discont = 1; - mov->flags &= ~FF_MOV_FLAG_FRAG_DISCONT; - } + if (mov->flags & FF_MOV_FLAG_FRAG_DISCONT) { + int i; + for (i = 0; i < s->nb_streams; i++) + mov->tracks[i].frag_discont = 1; + mov->flags &= ~FF_MOV_FLAG_FRAG_DISCONT; + } - if (mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS) { - if (trk->dts_shift == AV_NOPTS_VALUE) - trk->dts_shift = pkt->pts - pkt->dts; - pkt->dts += trk->dts_shift; - } + if (mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS) { + if (trk->dts_shift == AV_NOPTS_VALUE) + trk->dts_shift = pkt->pts - pkt->dts; + pkt->dts += trk->dts_shift; + } - if (trk->par->codec_id == AV_CODEC_ID_MP4ALS || + if (trk->par->codec_id == AV_CODEC_ID_MP4ALS || trk->par->codec_id == AV_CODEC_ID_AAC || trk->par->codec_id == AV_CODEC_ID_AV1 || trk->par->codec_id == AV_CODEC_ID_FLAC) { - int side_size = 0; - 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))) { - void *newextra = av_mallocz(side_size + AV_INPUT_BUFFER_PADDING_SIZE); - if (!newextra) - return AVERROR(ENOMEM); - av_free(par->extradata); - par->extradata = newextra; - memcpy(par->extradata, side, side_size); - par->extradata_size = side_size; - if (!pkt->size) // Flush packet - mov->need_rewrite_extradata = 1; - } + int side_size = 0; + 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))) { + void *newextra = av_mallocz(side_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!newextra) + return AVERROR(ENOMEM); + av_free(par->extradata); + par->extradata = newextra; + memcpy(par->extradata, side, side_size); + par->extradata_size = side_size; + if (!pkt->size) // Flush packet + mov->need_rewrite_extradata = 1; } + } - if (!pkt->size) { - if (trk->start_dts == AV_NOPTS_VALUE && trk->frag_discont) { - trk->start_dts = pkt->dts; - if (pkt->pts != AV_NOPTS_VALUE) - trk->start_cts = pkt->pts - pkt->dts; - else - trk->start_cts = 0; - } - - return 0; /* Discard 0 sized packets */ + if (!pkt->size) { + if (trk->start_dts == AV_NOPTS_VALUE && trk->frag_discont) { + trk->start_dts = pkt->dts; + if (pkt->pts != AV_NOPTS_VALUE) + trk->start_cts = pkt->pts - pkt->dts; + else + trk->start_cts = 0; } - if (trk->entry && pkt->stream_index < s->nb_streams) - frag_duration = av_rescale_q(pkt->dts - trk->cluster[0].dts, - s->streams[pkt->stream_index]->time_base, - AV_TIME_BASE_Q); - if ((mov->max_fragment_duration && - frag_duration >= mov->max_fragment_duration) || - (mov->max_fragment_size && mov->mdat_size + size >= mov->max_fragment_size) || - (mov->flags & FF_MOV_FLAG_FRAG_KEYFRAME && - par->codec_type == AVMEDIA_TYPE_VIDEO && - trk->entry && pkt->flags & AV_PKT_FLAG_KEY) || - (mov->flags & FF_MOV_FLAG_FRAG_EVERY_FRAME)) { - if (frag_duration >= mov->min_fragment_duration) { - // Set the duration of this track to line up with the next - // sample in this track. This avoids relying on AVPacket - // duration, but only helps for this particular track, not - // for the other ones that are flushed at the same time. - trk->track_duration = pkt->dts - trk->start_dts; - if (pkt->pts != AV_NOPTS_VALUE) - trk->end_pts = pkt->pts; - else - trk->end_pts = pkt->dts; - trk->end_reliable = 1; - mov_auto_flush_fragment(s, 0); - } + return 0; /* Discard 0 sized packets */ + } + + if (trk->entry && pkt->stream_index < s->nb_streams) + frag_duration = av_rescale_q(pkt->dts - trk->cluster[0].dts, + s->streams[pkt->stream_index]->time_base, + AV_TIME_BASE_Q); + if ((mov->max_fragment_duration && + frag_duration >= mov->max_fragment_duration) || + (mov->max_fragment_size && mov->mdat_size + size >= mov->max_fragment_size) || + (mov->flags & FF_MOV_FLAG_FRAG_KEYFRAME && + par->codec_type == AVMEDIA_TYPE_VIDEO && + trk->entry && pkt->flags & AV_PKT_FLAG_KEY) || + (mov->flags & FF_MOV_FLAG_FRAG_EVERY_FRAME)) { + if (frag_duration >= mov->min_fragment_duration) { + // Set the duration of this track to line up with the next + // sample in this track. This avoids relying on AVPacket + // duration, but only helps for this particular track, not + // for the other ones that are flushed at the same time. + trk->track_duration = pkt->dts - trk->start_dts; + if (pkt->pts != AV_NOPTS_VALUE) + trk->end_pts = pkt->pts; + else + trk->end_pts = pkt->dts; + trk->end_reliable = 1; + mov_auto_flush_fragment(s, 0); } + } - return ff_mov_write_packet(s, pkt); + return ff_mov_write_packet(s, pkt); } static int mov_write_subtitle_end_packet(AVFormatContext *s, @@ -6031,12 +6230,13 @@ static int mov_create_dvd_sub_decoder_specific_info(MOVTrack *track, cur += strspn(cur, "\n\r"); } if (have_palette) { - track->vos_data = av_malloc(16*4); + track->vos_data = av_malloc(16*4 + AV_INPUT_BUFFER_PADDING_SIZE); if (!track->vos_data) return AVERROR(ENOMEM); for (i = 0; i < 16; i++) { AV_WB32(track->vos_data + i * 4, palette[i]); } + memset(track->vos_data + 16*4, 0, AV_INPUT_BUFFER_PADDING_SIZE); track->vos_len = 16 * 4; } st->codecpar->width = width; @@ -6085,6 +6285,9 @@ static int mov_init(AVFormatContext *s) if (mov->flags & FF_MOV_FLAG_DASH) mov->flags |= FF_MOV_FLAG_FRAGMENT | FF_MOV_FLAG_EMPTY_MOOV | FF_MOV_FLAG_DEFAULT_BASE_MOOF; + if (mov->flags & FF_MOV_FLAG_CMAF) + mov->flags |= FF_MOV_FLAG_FRAGMENT | FF_MOV_FLAG_EMPTY_MOOV | + FF_MOV_FLAG_DEFAULT_BASE_MOOF | FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS; if (mov->flags & FF_MOV_FLAG_EMPTY_MOOV && s->flags & AVFMT_FLAG_AUTO_BSF) { av_log(s, AV_LOG_VERBOSE, "Empty MOOV enabled; disabling automatic bitstream filtering\n"); @@ -6111,12 +6314,21 @@ static int mov_init(AVFormatContext *s) s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_MAKE_ZERO) mov->use_editlist = 0; } + if (mov->flags & FF_MOV_FLAG_CMAF) { + // CMAF Track requires negative cts offsets without edit lists + mov->use_editlist = 0; + } } if (mov->flags & FF_MOV_FLAG_EMPTY_MOOV && !(mov->flags & FF_MOV_FLAG_DELAY_MOOV) && mov->use_editlist) av_log(s, AV_LOG_WARNING, "No meaningful edit list will be written when using empty_moov without delay_moov\n"); - if (!mov->use_editlist && s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO) + if (mov->flags & FF_MOV_FLAG_CMAF && mov->use_editlist) { + av_log(s, AV_LOG_WARNING, "Edit list enabled; Assuming writing CMAF Track File\n"); + mov->flags &= ~FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS; + } + if (!mov->use_editlist && s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO && + !(mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS)) s->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_ZERO; /* Clear the omit_tfhd_offset flag if default_base_moof is set; @@ -6247,6 +6459,8 @@ static int mov_init(AVFormatContext *s) } if (mov->video_track_timescale) { track->timescale = mov->video_track_timescale; + if (mov->mode == MODE_ISM && mov->video_track_timescale != 10000000) + av_log(s, AV_LOG_WARNING, "Warning: some tools, like mp4split, assume a timescale of 10000000 for ISMV.\n"); } else { track->timescale = st->time_base.den; while(track->timescale < 10000) @@ -6321,12 +6535,14 @@ static int mov_init(AVFormatContext *s) } } if (track->par->codec_id == AV_CODEC_ID_FLAC || + track->par->codec_id == AV_CODEC_ID_TRUEHD || track->par->codec_id == AV_CODEC_ID_OPUS) { if (track->mode != MODE_MP4) { av_log(s, AV_LOG_ERROR, "%s only supported in MP4.\n", avcodec_get_name(track->par->codec_id)); return AVERROR(EINVAL); } - if (s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { + if (track->par->codec_id != AV_CODEC_ID_OPUS && + s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { av_log(s, AV_LOG_ERROR, "%s in MP4 support is experimental, add " "'-strict %d' if you want to use it.\n", @@ -6343,10 +6559,14 @@ static int mov_init(AVFormatContext *s) } if (!track->height) track->height = st->codecpar->height; - /* The ism specific timescale isn't mandatory, but is assumed by - * some tools, such as mp4split. */ - if (mov->mode == MODE_ISM) - track->timescale = 10000000; + /* The Protected Interoperable File Format (PIFF) standard, used by ISMV recommends but + doesn't mandate a track timescale of 10,000,000. The muxer allows a custom timescale + for video tracks, so if user-set, it isn't overwritten */ + if (mov->mode == MODE_ISM && + (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO || + (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && !mov->video_track_timescale))) { + track->timescale = 10000000; + } avpriv_set_pts_info(st, 64, 1, track->timescale); @@ -6393,11 +6613,12 @@ static int mov_write_header(AVFormatContext *s) mov_create_dvd_sub_decoder_specific_info(track, st); else if (!TAG_IS_AVCI(track->tag) && st->codecpar->codec_id != AV_CODEC_ID_DNXHD) { track->vos_len = st->codecpar->extradata_size; - track->vos_data = av_malloc(track->vos_len); + track->vos_data = av_malloc(track->vos_len + AV_INPUT_BUFFER_PADDING_SIZE); if (!track->vos_data) { return AVERROR(ENOMEM); } memcpy(track->vos_data, st->codecpar->extradata, track->vos_len); + memset(track->vos_data + track->vos_len, 0, AV_INPUT_BUFFER_PADDING_SIZE); } } @@ -6493,7 +6714,6 @@ static int mov_write_header(AVFormatContext *s) !(mov->flags & FF_MOV_FLAG_DELAY_MOOV)) { if ((ret = mov_write_moov_tag(pb, mov, s)) < 0) return ret; - avio_flush(pb); mov->moov_written = 1; if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX) mov->reserved_header_pos = avio_tell(pb); @@ -6577,7 +6797,7 @@ static int shift_data(AVFormatContext *s) { int ret = 0, moov_size; MOVMuxContext *mov = s->priv_data; - int64_t pos, pos_end = avio_tell(s->pb); + int64_t pos, pos_end; uint8_t *buf, *read_buf[2]; int read_buf_id = 0; int read_size[2]; @@ -6653,10 +6873,11 @@ static int mov_write_trailer(AVFormatContext *s) AVCodecParameters *par = track->par; track->vos_len = par->extradata_size; - track->vos_data = av_malloc(track->vos_len); + track->vos_data = av_malloc(track->vos_len + AV_INPUT_BUFFER_PADDING_SIZE); if (!track->vos_data) return AVERROR(ENOMEM); memcpy(track->vos_data, par->extradata, track->vos_len); + memset(track->vos_data + track->vos_len, 0, AV_INPUT_BUFFER_PADDING_SIZE); } mov->need_rewrite_extradata = 0; } @@ -6780,37 +7001,39 @@ static const AVCodecTag codec_3gp_tags[] = { }; const AVCodecTag codec_mp4_tags[] = { - { AV_CODEC_ID_MPEG4 , MKTAG('m', 'p', '4', 'v') }, - { AV_CODEC_ID_H264 , MKTAG('a', 'v', 'c', '1') }, - { 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_MPEG2VIDEO , MKTAG('m', 'p', '4', 'v') }, - { AV_CODEC_ID_MPEG1VIDEO , MKTAG('m', 'p', '4', 'v') }, - { AV_CODEC_ID_MJPEG , MKTAG('m', 'p', '4', 'v') }, - { AV_CODEC_ID_PNG , MKTAG('m', 'p', '4', 'v') }, - { AV_CODEC_ID_JPEG2000 , MKTAG('m', 'p', '4', 'v') }, - { AV_CODEC_ID_VC1 , MKTAG('v', 'c', '-', '1') }, - { AV_CODEC_ID_DIRAC , MKTAG('d', 'r', 'a', 'c') }, - { AV_CODEC_ID_TSCC2 , MKTAG('m', 'p', '4', 'v') }, - { AV_CODEC_ID_VP9 , MKTAG('v', 'p', '0', '9') }, - { AV_CODEC_ID_AV1 , MKTAG('a', 'v', '0', '1') }, - { AV_CODEC_ID_AAC , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_MP4ALS , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_MP3 , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_MP2 , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_AC3 , MKTAG('a', 'c', '-', '3') }, - { AV_CODEC_ID_EAC3 , MKTAG('e', 'c', '-', '3') }, - { AV_CODEC_ID_DTS , 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_VORBIS , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_QCELP , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_EVRC , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_DVD_SUBTITLE, MKTAG('m', 'p', '4', 's') }, - { AV_CODEC_ID_MOV_TEXT , MKTAG('t', 'x', '3', 'g') }, - { AV_CODEC_ID_BIN_DATA , MKTAG('g', 'p', 'm', 'd') }, - { AV_CODEC_ID_NONE , 0 }, + { AV_CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') }, + { 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_MPEG2VIDEO, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_MJPEG, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_PNG, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_JPEG2000, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_VC1, MKTAG('v', 'c', '-', '1') }, + { AV_CODEC_ID_DIRAC, MKTAG('d', 'r', 'a', 'c') }, + { AV_CODEC_ID_TSCC2, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_VP9, MKTAG('v', 'p', '0', '9') }, + { AV_CODEC_ID_AV1, MKTAG('a', 'v', '0', '1') }, + { AV_CODEC_ID_AAC, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_MP4ALS, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_MP3, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_MP2, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_AC3, MKTAG('a', 'c', '-', '3') }, + { AV_CODEC_ID_EAC3, MKTAG('e', 'c', '-', '3') }, + { AV_CODEC_ID_DTS, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_TRUEHD, MKTAG('m', 'l', 'p', 'a') }, + { AV_CODEC_ID_FLAC, MKTAG('f', 'L', 'a', 'C') }, + { AV_CODEC_ID_OPUS, MKTAG('O', 'p', 'u', 's') }, + { AV_CODEC_ID_VORBIS, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_QCELP, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_EVRC, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_DVD_SUBTITLE, MKTAG('m', 'p', '4', 's') }, + { AV_CODEC_ID_MOV_TEXT, MKTAG('t', 'x', '3', 'g') }, + { AV_CODEC_ID_BIN_DATA, MKTAG('g', 'p', 'm', 'd') }, + { AV_CODEC_ID_MPEGH_3D_AUDIO, MKTAG('m', 'h', 'm', '1') }, + { AV_CODEC_ID_NONE, 0 }, }; const AVCodecTag codec_ism_tags[] = { diff --git a/libavformat/movenc.h b/libavformat/movenc.h index 68d6f23a5a6..997b2d61c02 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -56,6 +56,7 @@ typedef struct MOVIentry { #define MOV_PARTIAL_SYNC_SAMPLE 0x0002 #define MOV_DISPOSABLE_SAMPLE 0x0004 uint32_t flags; + AVProducerReferenceTime prft; } MOVIentry; typedef struct HintSample { @@ -258,6 +259,8 @@ typedef struct MOVMuxContext { #define FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS (1 << 19) #define FF_MOV_FLAG_FRAG_EVERY_FRAME (1 << 20) #define FF_MOV_FLAG_SKIP_SIDX (1 << 21) +#define FF_MOV_FLAG_CMAF (1 << 22) +#define FF_MOV_FLAG_PREFER_ICC (1 << 23) int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt); diff --git a/libavformat/mp3dec.c b/libavformat/mp3dec.c index 258f19174b9..b044679c029 100644 --- a/libavformat/mp3dec.c +++ b/libavformat/mp3dec.c @@ -42,8 +42,6 @@ #define XING_TOC_COUNT 100 -#define SAME_HEADER_MASK \ - (0xffe00000 | (3 << 17) | (3 << 10) | (3 << 19)) typedef struct { AVClass *class; @@ -73,32 +71,46 @@ static int mp3_read_probe(const AVProbeData *p) int frames, ret; int framesizes, max_framesizes; uint32_t header; - const uint8_t *buf, *buf0, *buf2, *end; + const uint8_t *buf, *buf0, *buf2, *buf3, *end; buf0 = p->buf; end = p->buf + p->buf_size - sizeof(uint32_t); - while(buf0 < end && !*buf0) + while (buf0 < end && !*buf0) buf0++; max_frames = 0; max_framesizes = 0; buf = buf0; - for(; buf < end; buf= buf2+1) { + for (; buf < end; buf = buf2+1) { buf2 = buf; - for(framesizes = frames = 0; buf2 < end; frames++) { + for (framesizes = frames = 0; buf2 < end; frames++) { MPADecodeHeader h; + int header_emu = 0; + int available; header = AV_RB32(buf2); ret = avpriv_mpegaudio_decode_header(&h, header); if (ret != 0) break; - buf2 += h.frame_size; + + available = FFMIN(h.frame_size, end - buf2); + for (buf3 = buf2 + 4; buf3 < buf2 + available; buf3++) { + uint32_t next_sync = AV_RB32(buf3); + header_emu += (next_sync & MP3_MASK) == (header & MP3_MASK); + } + if (header_emu > 2) + break; framesizes += h.frame_size; + if (available < h.frame_size) { + frames++; + break; + } + buf2 += h.frame_size; } max_frames = FFMAX(max_frames, frames); max_framesizes = FFMAX(max_framesizes, framesizes); - if(buf == buf0) { + if (buf == buf0) { first_frames= frames; if (buf2 == end + sizeof(uint32_t)) whole_used = 1; @@ -107,14 +119,14 @@ static int mp3_read_probe(const AVProbeData *p) // keep this in sync with ac3 probe, both need to avoid // issues with MPEG-files! if (first_frames>=7) return AVPROBE_SCORE_EXTENSION + 1; - else if(max_frames>200 && p->buf_size < 2*max_framesizes)return AVPROBE_SCORE_EXTENSION; - else if(max_frames>=4 && p->buf_size < 2*max_framesizes) return AVPROBE_SCORE_EXTENSION / 2; - else if(ff_id3v2_match(buf0, ID3v2_DEFAULT_MAGIC) && 2*ff_id3v2_tag_len(buf0) >= p->buf_size) + else if (max_frames>200 && p->buf_size < 2*max_framesizes)return AVPROBE_SCORE_EXTENSION; + else if (max_frames>=4 && p->buf_size < 2*max_framesizes) return AVPROBE_SCORE_EXTENSION / 2; + else if (ff_id3v2_match(buf0, ID3v2_DEFAULT_MAGIC) && 2*ff_id3v2_tag_len(buf0) >= p->buf_size) return p->buf_size < PROBE_BUF_MAX ? AVPROBE_SCORE_EXTENSION / 4 : AVPROBE_SCORE_EXTENSION - 2; - else if(first_frames > 1 && whole_used) return 5; - else if(max_frames>=1 && p->buf_size < 10*max_framesizes) return 1; + else if (first_frames > 1 && whole_used) return 5; + else if (max_frames>=1 && p->buf_size < 10*max_framesizes) return 1; else return 0; -//mpegps_mp3_unrecognized_format.mpg has max_frames=3 + //mpegps_mp3_unrecognized_format.mpg has max_frames=3 } static void read_xing_toc(AVFormatContext *s, int64_t filesize, int64_t duration) @@ -235,8 +247,8 @@ static void mp3_parse_info_tag(AVFormatContext *s, AVStream *st, avio_r8(s->pb); /* Encoder delays */ - v= avio_rb24(s->pb); - if(AV_RB32(version) == MKBETAG('L', 'A', 'M', 'E') + v = avio_rb24(s->pb); + if (AV_RB32(version) == MKBETAG('L', 'A', 'M', 'E') || AV_RB32(version) == MKBETAG('L', 'a', 'v', 'f') || AV_RB32(version) == MKBETAG('L', 'a', 'v', 'c') ) { @@ -319,7 +331,7 @@ static int mp3_parse_vbr_tags(AVFormatContext *s, AVStream *st, int64_t base) return ret; else if (ret == 0) vbrtag_size = c.frame_size; - if(c.layer != 3) + if (c.layer != 3) return -1; spf = c.lsf ? 576 : 1152; /* Samples per frame, layer 3 */ @@ -374,7 +386,7 @@ static int mp3_read_header(AVFormatContext *s) if (!av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) ff_id3v1_read(s); - if(s->pb->seekable & AVIO_SEEKABLE_NORMAL) + if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) mp3->filesize = avio_size(s->pb); if (mp3_parse_vbr_tags(s, st, off) < 0) @@ -398,7 +410,7 @@ static int mp3_read_header(AVFormatContext *s) ffio_ensure_seekback(s->pb, i + 1024 + frame_size + 4); ret = check(s->pb, off + i + frame_size, &header2); if (ret >= 0 && - (header & SAME_HEADER_MASK) == (header2 & SAME_HEADER_MASK)) + (header & MP3_MASK) == (header2 & MP3_MASK)) { av_log(s, i > 0 ? AV_LOG_INFO : AV_LOG_VERBOSE, "Skipping %d bytes of junk at %"PRId64".\n", i, off); ret = avio_seek(s->pb, off + i, SEEK_SET); @@ -434,12 +446,12 @@ static int mp3_read_packet(AVFormatContext *s, AVPacket *pkt) int ret, size; int64_t pos; - size= MP3_PACKET_SIZE; + size = MP3_PACKET_SIZE; pos = avio_tell(s->pb); - if(mp3->filesize > ID3v1_TAG_SIZE && pos < mp3->filesize) + if (mp3->filesize > ID3v1_TAG_SIZE && pos < mp3->filesize) size= FFMIN(size, mp3->filesize - pos); - ret= av_get_packet(s->pb, pkt, size); + ret = av_get_packet(s->pb, pkt, size); if (ret <= 0) { if(ret<0) return ret; @@ -494,7 +506,7 @@ static int64_t mp3_sync(AVFormatContext *s, int64_t target_pos, int flags) #define MIN_VALID 3 best_pos = target_pos; best_score = 999; - for(i=0; i 0 ? i - SEEK_WINDOW/4 : -i); int64_t candidate = -1; int score = 999; @@ -502,9 +514,9 @@ static int64_t mp3_sync(AVFormatContext *s, int64_t target_pos, int flags) if (pos < 0) continue; - for(j=0; jpb, pos, NULL); - if(ret < 0) { + if (ret < 0) { if (ret == CHECK_WRONG_HEADER) { break; } else if (ret == CHECK_SEEK_FAILED) { diff --git a/libavformat/mp3enc.c b/libavformat/mp3enc.c index f4814be80e1..14d4b6e0af4 100644 --- a/libavformat/mp3enc.c +++ b/libavformat/mp3enc.c @@ -45,6 +45,7 @@ static int id3v1_set_string(AVFormatContext *s, const char *key, return !!tag; } +// refer to: http://id3.org/ID3v1 static int id3v1_create_tag(AVFormatContext *s, uint8_t *buf) { AVDictionaryEntry *tag; @@ -58,7 +59,17 @@ static int id3v1_create_tag(AVFormatContext *s, uint8_t *buf) count += id3v1_set_string(s, "TIT2", buf + 3, 30 + 1); //title count += id3v1_set_string(s, "TPE1", buf + 33, 30 + 1); //author|artist count += id3v1_set_string(s, "TALB", buf + 63, 30 + 1); //album - count += id3v1_set_string(s, "TDRC", buf + 93, 4 + 1); //date + if ((tag = av_dict_get(s->metadata, "TYER", NULL, 0))) { //year + av_strlcpy(buf + 93, tag->value, 4 + 1); + count++; + } else if ((tag = av_dict_get(s->metadata, "TDRC", NULL, 0))) { + av_strlcpy(buf + 93, tag->value, 4 + 1); + count++; + } else if ((tag = av_dict_get(s->metadata, "TDAT", NULL, 0))) { + av_strlcpy(buf + 93, tag->value, 4 + 1); + count++; + } + count += id3v1_set_string(s, "comment", buf + 97, 30 + 1); if ((tag = av_dict_get(s->metadata, "TRCK", NULL, 0))) { //track buf[125] = 0; @@ -473,8 +484,6 @@ static int mp3_write_trailer(struct AVFormatContext *s) if (mp3->xing_offset) mp3_update_xing(s); - av_freep(&mp3->xing_frame); - return 0; } @@ -551,10 +560,10 @@ static int mp3_write_packet(AVFormatContext *s, AVPacket *pkt) * Write an ID3v2 header at beginning of stream */ -static int mp3_write_header(struct AVFormatContext *s) +static int mp3_init(struct AVFormatContext *s) { MP3Context *mp3 = s->priv_data; - int ret, i; + int i; if (mp3->id3v2_version && mp3->id3v2_version != 3 && @@ -593,6 +602,14 @@ static int mp3_write_header(struct AVFormatContext *s) return AVERROR(EINVAL); } + return 0; +} + +static int mp3_write_header(struct AVFormatContext *s) +{ + MP3Context *mp3 = s->priv_data; + int ret; + if (mp3->id3v2_version) { ff_id3v2_start(&mp3->id3, s->pb, mp3->id3v2_version, ID3v2_DEFAULT_MAGIC); ret = ff_id3v2_write_metadata(s, &mp3->id3); @@ -609,6 +626,14 @@ static int mp3_write_header(struct AVFormatContext *s) return 0; } +static void mp3_deinit(struct AVFormatContext *s) +{ + MP3Context *mp3 = s->priv_data; + + ff_packet_list_free(&mp3->queue, &mp3->queue_end); + av_freep(&mp3->xing_frame); +} + AVOutputFormat ff_mp3_muxer = { .name = "mp3", .long_name = NULL_IF_CONFIG_SMALL("MP3 (MPEG audio layer 3)"), @@ -617,9 +642,11 @@ AVOutputFormat ff_mp3_muxer = { .priv_data_size = sizeof(MP3Context), .audio_codec = AV_CODEC_ID_MP3, .video_codec = AV_CODEC_ID_PNG, + .init = mp3_init, .write_header = mp3_write_header, .write_packet = mp3_write_packet, .write_trailer = mp3_write_trailer, + .deinit = mp3_deinit, .query_codec = query_codec, .flags = AVFMT_NOTIMESTAMPS, .priv_class = &mp3_muxer_class, diff --git a/libavformat/mpc.c b/libavformat/mpc.c index a7b2e116edb..6a94b5d1d03 100644 --- a/libavformat/mpc.c +++ b/libavformat/mpc.c @@ -57,6 +57,7 @@ static int mpc_read_header(AVFormatContext *s) { MPCContext *c = s->priv_data; AVStream *st; + int ret; if(avio_rl24(s->pb) != MKTAG('M', 'P', '+', 0)){ av_log(s, AV_LOG_ERROR, "Not a Musepack file\n"); @@ -72,15 +73,6 @@ static int mpc_read_header(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "Too many frames, seeking is not possible\n"); return AVERROR_INVALIDDATA; } - if(c->fcount){ - c->frames = av_malloc(c->fcount * sizeof(MPCFrame)); - if(!c->frames){ - av_log(s, AV_LOG_ERROR, "Cannot allocate seektable\n"); - return AVERROR(ENOMEM); - } - }else{ - av_log(s, AV_LOG_WARNING, "Container reports no frames\n"); - } c->curframe = 0; c->lastframe = -1; c->curbits = 8; @@ -88,15 +80,27 @@ static int mpc_read_header(AVFormatContext *s) st = avformat_new_stream(s, NULL); if (!st) - goto mem_error; + return AVERROR(ENOMEM); + + if (c->fcount) { + c->frames = av_malloc(c->fcount * sizeof(MPCFrame)); + if (!c->frames) { + av_log(s, AV_LOG_ERROR, "Cannot allocate seektable\n"); + return AVERROR(ENOMEM); + } + st->priv_data = c->frames; + } else { + av_log(s, AV_LOG_WARNING, "Container reports no frames\n"); + } + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; st->codecpar->codec_id = AV_CODEC_ID_MUSEPACK7; st->codecpar->channels = 2; st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO; st->codecpar->bits_per_coded_sample = 16; - if (ff_get_extradata(s, st->codecpar, s->pb, 16) < 0) - goto mem_error; + if ((ret = ff_get_extradata(s, st->codecpar, s->pb, 16)) < 0) + return ret; st->codecpar->sample_rate = mpc_rate[st->codecpar->extradata[2] & 3]; avpriv_set_pts_info(st, 32, MPC_FRAMESIZE, st->codecpar->sample_rate); /* scan for seekpoints */ @@ -113,9 +117,6 @@ static int mpc_read_header(AVFormatContext *s) } return 0; -mem_error: - av_freep(&c->frames); - return AVERROR(ENOMEM); } static int mpc_read_packet(AVFormatContext *s, AVPacket *pkt) @@ -169,7 +170,6 @@ static int mpc_read_packet(AVFormatContext *s, AVPacket *pkt) if(c->curbits) avio_seek(s->pb, -4, SEEK_CUR); if(ret < size){ - av_packet_unref(pkt); return ret < 0 ? ret : AVERROR(EIO); } pkt->size = ret + 4; @@ -177,14 +177,6 @@ static int mpc_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -static int mpc_read_close(AVFormatContext *s) -{ - MPCContext *c = s->priv_data; - - av_freep(&c->frames); - return 0; -} - /** * Seek to the given position * If position is unknown but is within the limits of file @@ -233,7 +225,6 @@ AVInputFormat ff_mpc_demuxer = { .read_probe = mpc_probe, .read_header = mpc_read_header, .read_packet = mpc_read_packet, - .read_close = mpc_read_close, .read_seek = mpc_read_seek, .extensions = "mpc", }; diff --git a/libavformat/mpc8.c b/libavformat/mpc8.c index 0eb879ffc0d..dd13bbd0a49 100644 --- a/libavformat/mpc8.c +++ b/libavformat/mpc8.c @@ -168,6 +168,7 @@ static void mpc8_parse_seektable(AVFormatContext *s, int64_t off) size = gb_get_v(&gb); if(size > UINT_MAX/4 || size > c->samples/1152){ av_log(s, AV_LOG_ERROR, "Seek table is too big\n"); + av_free(buf); return; } seekd = get_bits(&gb, 4); @@ -211,7 +212,7 @@ static int mpc8_read_header(AVFormatContext *s) MPCContext *c = s->priv_data; AVIOContext *pb = s->pb; AVStream *st; - int tag = 0; + int tag = 0, ret; int64_t size, pos; c->header_pos = avio_tell(pb); @@ -252,8 +253,8 @@ static int mpc8_read_header(AVFormatContext *s) st->codecpar->codec_id = AV_CODEC_ID_MUSEPACK8; st->codecpar->bits_per_coded_sample = 16; - if (ff_get_extradata(s, st->codecpar, pb, 2) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_get_extradata(s, st->codecpar, pb, 2)) < 0) + return ret; st->codecpar->channels = (st->codecpar->extradata[1] >> 4) + 1; st->codecpar->sample_rate = mpc8_rate[st->codecpar->extradata[0] >> 5]; @@ -276,7 +277,7 @@ static int mpc8_read_header(AVFormatContext *s) static int mpc8_read_packet(AVFormatContext *s, AVPacket *pkt) { MPCContext *c = s->priv_data; - int tag; + int tag, ret; int64_t pos, size; while(!avio_feof(s->pb)){ @@ -290,8 +291,8 @@ static int mpc8_read_packet(AVFormatContext *s, AVPacket *pkt) if (size < 0) return -1; if(tag == TAG_AUDIOPACKET){ - if(av_get_packet(s->pb, pkt, size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_get_packet(s->pb, pkt, size)) < 0) + return ret; pkt->stream_index = 0; pkt->duration = 1; return 0; diff --git a/libavformat/mpeg.c b/libavformat/mpeg.c index 3205f209e68..265b2bd1adb 100644 --- a/libavformat/mpeg.c +++ b/libavformat/mpeg.c @@ -24,14 +24,6 @@ #include "internal.h" #include "mpeg.h" -#if CONFIG_VOBSUB_DEMUXER -# include "subtitles.h" -# include "libavutil/bprint.h" -# include "libavutil/opt.h" -#endif - -#include "libavutil/avassert.h" - /*********************************************/ /* demux code */ @@ -107,7 +99,7 @@ static int mpegps_probe(const AVProbeData *p) if (sys > invalid && sys * 9 <= pspack * 10) return (audio > 12 || vid > 3 || pspack > 2) ? AVPROBE_SCORE_EXTENSION + 2 - : AVPROBE_SCORE_EXTENSION / 2 + 1; // 1 more than mp3 + : AVPROBE_SCORE_EXTENSION / 2 + (audio + vid + pspack > 1); // 1 more than mp3 if (pspack > invalid && (priv1 + vid + audio) * 10 >= pspack * 9) return pspack > 2 ? AVPROBE_SCORE_EXTENSION + 2 : AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpg @@ -123,18 +115,12 @@ static int mpegps_probe(const AVProbeData *p) } typedef struct MpegDemuxContext { - AVClass *class; int32_t header_state; unsigned char psm_es_type[256]; int sofdec; int dvd; int imkh_cctv; int raw_ac3; -#if CONFIG_VOBSUB_DEMUXER - AVFormatContext *sub_ctx; - FFDemuxSubtitlesQueue q[32]; - char *sub_name; -#endif } MpegDemuxContext; static int mpegps_read_header(AVFormatContext *s) @@ -489,7 +475,7 @@ static int mpegps_read_packet(AVFormatContext *s, MpegDemuxContext *m = s->priv_data; AVStream *st; int len, startcode, i, es_type, ret; - int lpcm_header_len = -1; //Init to suppress warning + int pcm_dvd = 0; int request_probe= 0; enum AVCodecID codec_id = AV_CODEC_ID_NONE; enum AVMediaType type; @@ -506,13 +492,18 @@ static int mpegps_read_packet(AVFormatContext *s, if (!m->raw_ac3) { /* audio: skip header */ - avio_r8(s->pb); - lpcm_header_len = avio_rb16(s->pb); + avio_skip(s->pb, 3); len -= 3; if (startcode >= 0xb0 && startcode <= 0xbf) { /* MLP/TrueHD audio has a 4-byte header */ avio_r8(s->pb); len--; + } else if (startcode >= 0xa0 && startcode <= 0xaf) { + ret = ffio_ensure_seekback(s->pb, 3); + if (ret < 0) + return ret; + pcm_dvd = (avio_rb24(s->pb) & 0xFF) == 0x80; + avio_skip(s->pb, -3); } } } @@ -591,7 +582,7 @@ static int mpegps_read_packet(AVFormatContext *s, codec_id = AV_CODEC_ID_DTS; } else if (startcode >= 0xa0 && startcode <= 0xaf) { type = AVMEDIA_TYPE_AUDIO; - if (lpcm_header_len >= 6 && startcode == 0xa1) { + if (!pcm_dvd) { codec_id = AV_CODEC_ID_MLP; } else { codec_id = AV_CODEC_ID_PCM_DVD; @@ -700,9 +691,21 @@ AVInputFormat ff_mpegps_demuxer = { #if CONFIG_VOBSUB_DEMUXER +#include "subtitles.h" +#include "libavutil/avassert.h" +#include "libavutil/bprint.h" +#include "libavutil/opt.h" + #define REF_STRING "# VobSub index file," #define MAX_LINE_SIZE 2048 +typedef struct VobSubDemuxContext { + const AVClass *class; + AVFormatContext *sub_ctx; + FFDemuxSubtitlesQueue q[32]; + char *sub_name; +} VobSubDemuxContext; + static int vobsub_probe(const AVProbeData *p) { if (!strncmp(p->buf, REF_STRING, sizeof(REF_STRING) - 1)) @@ -710,12 +713,23 @@ static int vobsub_probe(const AVProbeData *p) return 0; } +static int vobsub_read_close(AVFormatContext *s) +{ + VobSubDemuxContext *vobsub = s->priv_data; + int i; + + for (i = 0; i < s->nb_streams; i++) + ff_subtitles_queue_clean(&vobsub->q[i]); + if (vobsub->sub_ctx) + avformat_close_input(&vobsub->sub_ctx); + return 0; +} + static int vobsub_read_header(AVFormatContext *s) { int i, ret = 0, header_parsed = 0, langidx = 0; - MpegDemuxContext *vobsub = s->priv_data; + VobSubDemuxContext *vobsub = s->priv_data; size_t fname_len; - char *header_str; AVBPrint header; int64_t delay = 0; AVStream *st = NULL; @@ -728,8 +742,7 @@ static int vobsub_read_header(AVFormatContext *s) char *ext; vobsub->sub_name = av_strdup(s->url); if (!vobsub->sub_name) { - ret = AVERROR(ENOMEM); - goto end; + return AVERROR(ENOMEM); } fname_len = strlen(vobsub->sub_name); @@ -737,24 +750,23 @@ static int vobsub_read_header(AVFormatContext *s) if (fname_len < 4 || *(ext - 1) != '.') { av_log(s, AV_LOG_ERROR, "The input index filename is too short " "to guess the associated .SUB file\n"); - ret = AVERROR_INVALIDDATA; - goto end; + return AVERROR_INVALIDDATA; } memcpy(ext, !strncmp(ext, "IDX", 3) ? "SUB" : "sub", 3); av_log(s, AV_LOG_VERBOSE, "IDX/SUB: %s -> %s\n", s->url, vobsub->sub_name); } if (!(iformat = av_find_input_format("mpeg"))) { - ret = AVERROR_DEMUXER_NOT_FOUND; - goto end; + return AVERROR_DEMUXER_NOT_FOUND; } vobsub->sub_ctx = avformat_alloc_context(); if (!vobsub->sub_ctx) { - ret = AVERROR(ENOMEM); - goto end; + return AVERROR(ENOMEM); } + av_bprint_init(&header, 0, INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE); + if ((ret = ff_copy_whiteblacklists(vobsub->sub_ctx, s)) < 0) goto end; @@ -764,7 +776,6 @@ static int vobsub_read_header(AVFormatContext *s) goto end; } - av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED); while (!avio_feof(s->pb)) { char line[MAX_LINE_SIZE]; int len = ff_get_line(s->pb, line, sizeof(line)); @@ -885,29 +896,30 @@ static int vobsub_read_header(AVFormatContext *s) } if (!av_bprint_is_complete(&header)) { - av_bprint_finalize(&header, NULL); ret = AVERROR(ENOMEM); goto end; } - av_bprint_finalize(&header, &header_str); for (i = 0; i < s->nb_streams; i++) { - AVStream *sub_st = s->streams[i]; - sub_st->codecpar->extradata = av_strdup(header_str); - sub_st->codecpar->extradata_size = header.len; + AVCodecParameters *par = s->streams[i]->codecpar; + ret = ff_alloc_extradata(par, header.len); + if (ret < 0) { + goto end; + } + memcpy(par->extradata, header.str, header.len); } - av_free(header_str); - end: + if (ret < 0) + vobsub_read_close(s); + av_bprint_finalize(&header, NULL); return ret; } static int vobsub_read_packet(AVFormatContext *s, AVPacket *pkt) { - MpegDemuxContext *vobsub = s->priv_data; + VobSubDemuxContext *vobsub = s->priv_data; FFDemuxSubtitlesQueue *q; AVIOContext *pb = vobsub->sub_ctx->pb; int ret, psize, total_read = 0, i; - AVPacket idx_pkt = { 0 }; int64_t min_ts = INT64_MAX; int sid = 0; @@ -915,6 +927,10 @@ static int vobsub_read_packet(AVFormatContext *s, AVPacket *pkt) FFDemuxSubtitlesQueue *tmpq = &vobsub->q[i]; int64_t ts; av_assert0(tmpq->nb_subs); + + if (tmpq->current_sub_idx >= tmpq->nb_subs) + continue; + ts = tmpq->subs[tmpq->current_sub_idx].pts; if (ts < min_ts) { min_ts = ts; @@ -922,24 +938,22 @@ static int vobsub_read_packet(AVFormatContext *s, AVPacket *pkt) } } q = &vobsub->q[sid]; - ret = ff_subtitles_queue_read_packet(q, &idx_pkt); + /* The returned packet will have size zero, + * so that it can be directly used with av_grow_packet. */ + ret = ff_subtitles_queue_read_packet(q, pkt); if (ret < 0) return ret; /* compute maximum packet size using the next packet position. This is * useful when the len in the header is non-sense */ if (q->current_sub_idx < q->nb_subs) { - psize = q->subs[q->current_sub_idx].pos - idx_pkt.pos; + psize = q->subs[q->current_sub_idx].pos - pkt->pos; } else { int64_t fsize = avio_size(pb); - psize = fsize < 0 ? 0xffff : fsize - idx_pkt.pos; + psize = fsize < 0 ? 0xffff : fsize - pkt->pos; } - avio_seek(pb, idx_pkt.pos, SEEK_SET); - - av_init_packet(pkt); - pkt->size = 0; - pkt->data = NULL; + avio_seek(pb, pkt->pos, SEEK_SET); do { int n, to_read, startcode; @@ -951,7 +965,7 @@ static int vobsub_read_packet(AVFormatContext *s, AVPacket *pkt) if (ret < 0) { if (pkt->size) // raise packet even if incomplete break; - goto fail; + return ret; } to_read = ret & 0xffff; new_pos = avio_tell(pb); @@ -963,35 +977,25 @@ static int vobsub_read_packet(AVFormatContext *s, AVPacket *pkt) total_read += pkt_size; /* the current chunk doesn't match the stream index (unlikely) */ - if ((startcode & 0x1f) != s->streams[idx_pkt.stream_index]->id) + if ((startcode & 0x1f) != s->streams[pkt->stream_index]->id) break; ret = av_grow_packet(pkt, to_read); if (ret < 0) - goto fail; + return ret; n = avio_read(pb, pkt->data + (pkt->size - to_read), to_read); if (n < to_read) pkt->size -= to_read - n; } while (total_read < psize); - pkt->pts = pkt->dts = idx_pkt.pts; - pkt->pos = idx_pkt.pos; - pkt->stream_index = idx_pkt.stream_index; - - av_packet_unref(&idx_pkt); return 0; - -fail: - av_packet_unref(pkt); - av_packet_unref(&idx_pkt); - return ret; } static int vobsub_read_seek(AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags) { - MpegDemuxContext *vobsub = s->priv_data; + VobSubDemuxContext *vobsub = s->priv_data; /* Rescale requested timestamps based on the first stream (timebase is the * same for all subtitles stream within a .idx/.sub). Rescaling is done just @@ -1021,20 +1025,8 @@ static int vobsub_read_seek(AVFormatContext *s, int stream_index, min_ts, ts, max_ts, flags); } -static int vobsub_read_close(AVFormatContext *s) -{ - int i; - MpegDemuxContext *vobsub = s->priv_data; - - for (i = 0; i < s->nb_streams; i++) - ff_subtitles_queue_clean(&vobsub->q[i]); - if (vobsub->sub_ctx) - avformat_close_input(&vobsub->sub_ctx); - return 0; -} - static const AVOption options[] = { - { "sub_name", "URI for .sub file", offsetof(MpegDemuxContext, sub_name), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, + { "sub_name", "URI for .sub file", offsetof(VobSubDemuxContext, sub_name), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, { NULL } }; @@ -1048,7 +1040,7 @@ static const AVClass vobsub_demuxer_class = { AVInputFormat ff_vobsub_demuxer = { .name = "vobsub", .long_name = NULL_IF_CONFIG_SMALL("VobSub subtitle format"), - .priv_data_size = sizeof(MpegDemuxContext), + .priv_data_size = sizeof(VobSubDemuxContext), .read_probe = vobsub_probe, .read_header = vobsub_read_header, .read_packet = vobsub_read_packet, diff --git a/libavformat/mpegenc.c b/libavformat/mpegenc.c index 43ebc46e0eb..9bd0a555d46 100644 --- a/libavformat/mpegenc.c +++ b/libavformat/mpegenc.c @@ -315,7 +315,7 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) if (ctx->packet_size < 20 || ctx->packet_size > (1 << 23) + 10) { av_log(ctx, AV_LOG_ERROR, "Invalid packet size %d\n", ctx->packet_size); - goto fail; + return AVERROR(EINVAL); } s->packet_size = ctx->packet_size; } else @@ -343,7 +343,7 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) st = ctx->streams[i]; stream = av_mallocz(sizeof(StreamInfo)); if (!stream) - goto fail; + return AVERROR(ENOMEM); st->priv_data = stream; avpriv_set_pts_info(st, 64, 1, 90000); @@ -377,11 +377,11 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) for (sr = 0; sr < 4; sr++) av_log(ctx, AV_LOG_INFO, " %d", lpcm_freq_tab[sr]); av_log(ctx, AV_LOG_INFO, "\n"); - goto fail; + return AVERROR(EINVAL); } if (st->codecpar->channels > 8) { av_log(ctx, AV_LOG_ERROR, "At most 8 channels allowed for LPCM streams.\n"); - goto fail; + return AVERROR(EINVAL); } stream->lpcm_header[0] = 0x0c; stream->lpcm_header[1] = (st->codecpar->channels - 1) | (j << 4); @@ -416,7 +416,7 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) st->codecpar->codec_id != AV_CODEC_ID_MP2 && st->codecpar->codec_id != AV_CODEC_ID_MP3) { av_log(ctx, AV_LOG_ERROR, "Unsupported audio codec. Must be one of mp1, mp2, mp3, 16-bit pcm_dvd, pcm_s16be, ac3 or dts.\n"); - goto fail; + return AVERROR(EINVAL); } else { stream->id = mpa_id++; } @@ -460,7 +460,7 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) } stream->fifo = av_fifo_alloc(16); if (!stream->fifo) - goto fail; + return AVERROR(ENOMEM); } bitrate = 0; audio_bitrate = 0; @@ -560,11 +560,6 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) s->system_header_size = get_system_header_size(ctx); s->last_scr = AV_NOPTS_VALUE; return 0; - -fail: - for (i = 0; i < ctx->nb_streams; i++) - av_freep(&ctx->streams[i]->priv_data); - return AVERROR(ENOMEM); } static inline void put_timestamp(AVIOContext *pb, int id, int64_t timestamp) @@ -933,7 +928,7 @@ static int flush_packet(AVFormatContext *ctx, int stream_index, for (i = 0; i < zero_trail_bytes; i++) avio_w8(ctx->pb, 0x00); - avio_flush(ctx->pb); + avio_write_marker(ctx->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); s->packet_number++; @@ -962,7 +957,7 @@ static void put_vcd_padding_sector(AVFormatContext *ctx) s->vcd_padding_bytes_written += s->packet_size; - avio_flush(ctx->pb); + avio_write_marker(ctx->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); /* increasing the packet number is correct. The SCR of the following packs * is calculated from the packet_number and it has to include the padding @@ -1215,7 +1210,7 @@ static int mpeg_mux_write_packet(AVFormatContext *ctx, AVPacket *pkt) if (s->is_dvd) { // min VOBU length 0.4 seconds (mpucoder) if (is_iframe && - (s->packet_number == 0 || + (s->packet_number == 0 || pts != AV_NOPTS_VALUE && (pts - stream->vobu_start_pts >= 36000))) { stream->bytes_to_iframe = av_fifo_size(stream->fifo); stream->align_iframe = 1; @@ -1249,17 +1244,25 @@ static int mpeg_mux_end(AVFormatContext *ctx) * it as it is usually not needed by decoders and because it * complicates MPEG stream concatenation. */ // avio_wb32(ctx->pb, ISO_11172_END_CODE); - // avio_flush(ctx->pb); for (i = 0; i < ctx->nb_streams; i++) { stream = ctx->streams[i]->priv_data; av_assert0(av_fifo_size(stream->fifo) == 0); - av_fifo_freep(&stream->fifo); } return 0; } +static void mpeg_mux_deinit(AVFormatContext *ctx) +{ + for (int i = 0; i < ctx->nb_streams; i++) { + StreamInfo *stream = ctx->streams[i]->priv_data; + if (!stream) + continue; + av_fifo_freep(&stream->fifo); + } +} + #define OFFSET(x) offsetof(MpegMuxContext, x) #define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { @@ -1289,6 +1292,7 @@ AVOutputFormat ff_mpeg1system_muxer = { .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, + .deinit = mpeg_mux_deinit, .priv_class = &mpeg_class, }; #endif @@ -1305,6 +1309,7 @@ AVOutputFormat ff_mpeg1vcd_muxer = { .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, + .deinit = mpeg_mux_deinit, .priv_class = &vcd_class, }; #endif @@ -1322,6 +1327,7 @@ AVOutputFormat ff_mpeg2vob_muxer = { .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, + .deinit = mpeg_mux_deinit, .priv_class = &vob_class, }; #endif @@ -1340,6 +1346,7 @@ AVOutputFormat ff_mpeg2svcd_muxer = { .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, + .deinit = mpeg_mux_deinit, .priv_class = &svcd_class, }; #endif @@ -1358,6 +1365,7 @@ AVOutputFormat ff_mpeg2dvd_muxer = { .write_header = mpeg_mux_init, .write_packet = mpeg_mux_write_packet, .write_trailer = mpeg_mux_end, + .deinit = mpeg_mux_deinit, .priv_class = &dvd_class, }; #endif diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c index 0415ceea029..c6fd3e1cef6 100644 --- a/libavformat/mpegts.c +++ b/libavformat/mpegts.c @@ -28,6 +28,7 @@ #include "libavutil/mathematics.h" #include "libavutil/opt.h" #include "libavutil/avassert.h" +#include "libavutil/dovi_meta.h" #include "libavcodec/bytestream.h" #include "libavcodec/get_bits.h" #include "libavcodec/opus.h" @@ -122,10 +123,6 @@ struct MpegTSContext { /** raw packet size, including FEC if present */ int raw_packet_size; - int size_stat[3]; - int size_stat_count; -#define SIZE_STAT_THRESHOLD 10 - int64_t pos47_full; /** if true, all pids are analyzed to find streams */ @@ -168,6 +165,9 @@ struct MpegTSContext { /** filters for various streams specified by PMT + for the PAT and PMT */ MpegTSFilter *pids[NB_PID_MAX]; int current_pid; + + AVStream *epg_stream; + AVBufferPool* pools[32]; }; #define MPEGTS_OPTIONS \ @@ -803,6 +803,7 @@ static const StreamType ISO_types[] = { { 0x24, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC }, { 0x42, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_CAVS }, { 0xd1, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, + { 0xd2, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_AVS2 }, { 0xea, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, { 0 }, }; @@ -1100,6 +1101,18 @@ static int read_sl_header(PESContext *pes, SLConfigDescr *sl, return (get_bits_count(&gb) + 7) >> 3; } +static AVBufferRef *buffer_pool_get(MpegTSContext *ts, int size) +{ + int index = av_log2(size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!ts->pools[index]) { + int pool_size = FFMIN(MAX_PES_PAYLOAD + AV_INPUT_BUFFER_PADDING_SIZE, 2 << index); + ts->pools[index] = av_buffer_pool_init(pool_size, NULL); + if (!ts->pools[index]) + return NULL; + } + return av_buffer_pool_get(ts->pools[index]); +} + /* return non zero if a packet could be constructed */ static int mpegts_push_data(MpegTSFilter *filter, const uint8_t *buf, int buf_size, int is_start, @@ -1174,8 +1187,7 @@ static int mpegts_push_data(MpegTSFilter *filter, pes->total_size = MAX_PES_PAYLOAD; /* allocate pes buffer */ - pes->buffer = av_buffer_alloc(pes->total_size + - AV_INPUT_BUFFER_PADDING_SIZE); + pes->buffer = buffer_pool_get(ts, pes->total_size); if (!pes->buffer) return AVERROR(ENOMEM); @@ -1348,8 +1360,7 @@ static int mpegts_push_data(MpegTSFilter *filter, if (ret < 0) return ret; pes->total_size = MAX_PES_PAYLOAD; - pes->buffer = av_buffer_alloc(pes->total_size + - AV_INPUT_BUFFER_PADDING_SIZE); + pes->buffer = buffer_pool_get(ts, pes->total_size); if (!pes->buffer) return AVERROR(ENOMEM); ts->stop_parse = 1; @@ -1842,7 +1853,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type case 0x56: /* DVB teletext descriptor */ { uint8_t *extradata = NULL; - int language_count = desc_len / 5; + int language_count = desc_len / 5, ret; if (desc_len > 0 && desc_len % 5 != 0) return AVERROR_INVALIDDATA; @@ -1852,9 +1863,9 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type av_assert0(language_count <= sizeof(language) / 4); if (st->codecpar->extradata == NULL) { - if (ff_alloc_extradata(st->codecpar, language_count * 2)) { - return AVERROR(ENOMEM); - } + ret = ff_alloc_extradata(st->codecpar, language_count * 2); + if (ret < 0) + return ret; } if (st->codecpar->extradata_size < language_count * 2) @@ -1887,7 +1898,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type * subtitling_type (1 byte), * composition_page_id (2 bytes), * ancillary_page_id (2 bytes) */ - int language_count = desc_len / 8; + int language_count = desc_len / 8, ret; if (desc_len > 0 && desc_len % 8 != 0) return AVERROR_INVALIDDATA; @@ -1903,9 +1914,9 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type av_assert0(language_count <= sizeof(language) / 4); if (st->codecpar->extradata == NULL) { - if (ff_alloc_extradata(st->codecpar, language_count * 5)) { - return AVERROR(ENOMEM); - } + ret = ff_alloc_extradata(st->codecpar, language_count * 5); + if (ret < 0) + return ret; } if (st->codecpar->extradata_size < language_count * 5) @@ -2132,6 +2143,53 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type st->request_probe = 0; } break; + case 0xb0: /* DOVI video stream descriptor */ + { + uint32_t buf; + AVDOVIDecoderConfigurationRecord *dovi; + size_t dovi_size; + int ret; + if (desc_end - *pp < 4) // (8 + 8 + 7 + 6 + 1 + 1 + 1) / 8 + return AVERROR_INVALIDDATA; + + dovi = av_dovi_alloc(&dovi_size); + if (!dovi) + return AVERROR(ENOMEM); + + dovi->dv_version_major = get8(pp, desc_end); + dovi->dv_version_minor = get8(pp, desc_end); + buf = get16(pp, desc_end); + dovi->dv_profile = (buf >> 9) & 0x7f; // 7 bits + dovi->dv_level = (buf >> 3) & 0x3f; // 6 bits + dovi->rpu_present_flag = (buf >> 2) & 0x01; // 1 bit + dovi->el_present_flag = (buf >> 1) & 0x01; // 1 bit + dovi->bl_present_flag = buf & 0x01; // 1 bit + if (desc_end - *pp >= 20) { // 4 + 4 * 4 + buf = get8(pp, desc_end); + dovi->dv_bl_signal_compatibility_id = (buf >> 4) & 0x0f; // 4 bits + } else { + // 0 stands for None + // Dolby Vision V1.2.93 profiles and levels + 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) { + av_free(dovi); + return ret; + } + + av_log(fc, AV_LOG_TRACE, "DOVI, 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, + dovi->rpu_present_flag, + dovi->el_present_flag, + dovi->bl_present_flag, + dovi->dv_bl_signal_compatibility_id); + } + break; default: break; } @@ -2498,6 +2556,60 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len } } +static void eit_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) +{ + MpegTSContext *ts = filter->u.section_filter.opaque; + const uint8_t *p, *p_end; + SectionHeader h1, *h = &h1; + + /* + * Sometimes we receive EPG packets but SDT table do not have + * eit_pres_following or eit_sched turned on, so we open EPG + * stream directly here. + */ + if (!ts->epg_stream) { + ts->epg_stream = avformat_new_stream(ts->stream, NULL); + if (!ts->epg_stream) + return; + ts->epg_stream->id = EIT_PID; + ts->epg_stream->codecpar->codec_type = AVMEDIA_TYPE_DATA; + ts->epg_stream->codecpar->codec_id = AV_CODEC_ID_EPG; + } + + if (ts->epg_stream->discard == AVDISCARD_ALL) + return; + + p_end = section + section_len - 4; + p = section; + + if (parse_section_header(h, &p, p_end) < 0) + return; + if (h->tid < EIT_TID || h->tid > OEITS_END_TID) + return; + + av_log(ts->stream, AV_LOG_TRACE, "EIT: tid received = %.02x\n", h->tid); + + /** + * Service_id 0xFFFF is reserved, it indicates that the current EIT table + * is scrambled. + */ + if (h->id == 0xFFFF) { + av_log(ts->stream, AV_LOG_TRACE, "Scrambled EIT table received.\n"); + return; + } + + /** + * In case we receive an EPG packet before mpegts context is fully + * initialized. + */ + if (!ts->pkt) + return; + + new_data_packet(section, section_len, ts->pkt); + ts->pkt->stream_index = ts->epg_stream->index; + ts->stop_parse = 1; +} + static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) { MpegTSContext *ts = filter->u.section_filter.opaque; @@ -2586,13 +2698,12 @@ static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, const uint8_t *packet); /* handle one TS packet */ -static int handle_packet(MpegTSContext *ts, const uint8_t *packet) +static int handle_packet(MpegTSContext *ts, const uint8_t *packet, int64_t pos) { MpegTSFilter *tss; int len, pid, cc, expected_cc, cc_ok, afc, is_start, is_discontinuity, has_adaptation, has_payload; const uint8_t *p, *p_end; - int64_t pos; pid = AV_RB16(packet + 1) & 0x1fff; is_start = packet[1] & 0x40; @@ -2659,7 +2770,6 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) if (p >= p_end || !has_payload) return 0; - pos = avio_tell(ts->stream->pb); if (pos >= 0) { av_assert0(pos >= TS_PACKET_SIZE); ts->pos47_full = pos - TS_PACKET_SIZE; @@ -2726,63 +2836,39 @@ static int handle_packet(MpegTSContext *ts, const uint8_t *packet) return 0; } -static void reanalyze(MpegTSContext *ts) { - AVIOContext *pb = ts->stream->pb; - int64_t pos = avio_tell(pb); - if (pos < 0) - return; - pos -= ts->pos47_full; - if (pos == TS_PACKET_SIZE) { - ts->size_stat[0] ++; - } else if (pos == TS_DVHS_PACKET_SIZE) { - ts->size_stat[1] ++; - } else if (pos == TS_FEC_PACKET_SIZE) { - ts->size_stat[2] ++; - } - - ts->size_stat_count ++; - if (ts->size_stat_count > SIZE_STAT_THRESHOLD) { - int newsize = 0; - if (ts->size_stat[0] > SIZE_STAT_THRESHOLD) { - newsize = TS_PACKET_SIZE; - } else if (ts->size_stat[1] > SIZE_STAT_THRESHOLD) { - newsize = TS_DVHS_PACKET_SIZE; - } else if (ts->size_stat[2] > SIZE_STAT_THRESHOLD) { - newsize = TS_FEC_PACKET_SIZE; - } - if (newsize && newsize != ts->raw_packet_size) { - av_log(ts->stream, AV_LOG_WARNING, "changing packet size to %d\n", newsize); - ts->raw_packet_size = newsize; - } - ts->size_stat_count = 0; - memset(ts->size_stat, 0, sizeof(ts->size_stat)); - } -} - -/* XXX: try to find a better synchro over several packets (use - * get_packet_size() ?) */ static int mpegts_resync(AVFormatContext *s, int seekback, const uint8_t *current_packet) { MpegTSContext *ts = s->priv_data; AVIOContext *pb = s->pb; int c, i; uint64_t pos = avio_tell(pb); - - avio_seek(pb, -FFMIN(seekback, pos), SEEK_CUR); + int64_t back = FFMIN(seekback, pos); //Special case for files like 01c56b0dc1.ts if (current_packet[0] == 0x80 && current_packet[12] == 0x47) { - avio_seek(pb, 12, SEEK_CUR); + avio_seek(pb, 12 - back, SEEK_CUR); return 0; } + avio_seek(pb, -back, SEEK_CUR); + for (i = 0; i < ts->resync_size; i++) { c = avio_r8(pb); if (avio_feof(pb)) return AVERROR_EOF; if (c == 0x47) { + int new_packet_size, ret; avio_seek(pb, -1, SEEK_CUR); - reanalyze(s->priv_data); + pos = avio_tell(pb); + ret = ffio_ensure_seekback(pb, PROBE_PACKET_MAX_BUF); + if (ret < 0) + return ret; + new_packet_size = get_packet_size(s); + if (new_packet_size > 0 && new_packet_size != ts->raw_packet_size) { + av_log(ts->stream, AV_LOG_WARNING, "changing packet size to %d\n", new_packet_size); + ts->raw_packet_size = new_packet_size; + } + avio_seek(pb, pos, SEEK_SET); return 0; } } @@ -2870,7 +2956,7 @@ static int handle_packets(MpegTSContext *ts, int64_t nb_packets) ret = read_packet(s, packet, ts->raw_packet_size, &data); if (ret != 0) break; - ret = handle_packet(ts, data); + ret = handle_packet(ts, data, avio_tell(s->pb)); finished_reading_packet(s, ts->raw_packet_size); if (ret != 0) break; @@ -2984,8 +3070,8 @@ static int mpegts_read_header(AVFormatContext *s) seek_back(s, pb, pos); mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); - mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1); + mpegts_open_section_filter(ts, EIT_PID, eit_cb, ts, 1); handle_packets(ts, probesize / ts->raw_packet_size); /* if could not find service, enable auto_guess */ @@ -3071,12 +3157,11 @@ static int mpegts_raw_read_packet(AVFormatContext *s, AVPacket *pkt) uint8_t pcr_buf[12]; const uint8_t *data; - if (av_new_packet(pkt, TS_PACKET_SIZE) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, TS_PACKET_SIZE)) < 0) + return ret; ret = read_packet(s, pkt->data, ts->raw_packet_size, &data); pkt->pos = avio_tell(s->pb); if (ret < 0) { - av_packet_unref(pkt); return ret; } if (data != pkt->data) @@ -3146,6 +3231,9 @@ static void mpegts_free(MpegTSContext *ts) clear_programs(ts); + for (i = 0; i < FF_ARRAY_ELEMS(ts->pools); i++) + av_buffer_pool_uninit(&ts->pools[i]); + for (i = 0; i < NB_PID_MAX; i++) if (ts->pids[i]) mpegts_close_filter(ts, ts->pids[i]); @@ -3240,8 +3328,10 @@ MpegTSContext *avpriv_mpegts_parse_open(AVFormatContext *s) ts->raw_packet_size = TS_PACKET_SIZE; ts->stream = s; ts->auto_guess = 1; + mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1); + mpegts_open_section_filter(ts, EIT_PID, eit_cb, ts, 1); return ts; } @@ -3263,7 +3353,7 @@ int avpriv_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, buf++; len--; } else { - handle_packet(ts, buf); + handle_packet(ts, buf, len1 - len + TS_PACKET_SIZE); buf += TS_PACKET_SIZE; len -= TS_PACKET_SIZE; if (ts->stop_parse == 1) diff --git a/libavformat/mpegts.h b/libavformat/mpegts.h index 272e2be4f7b..059b693f078 100644 --- a/libavformat/mpegts.h +++ b/libavformat/mpegts.h @@ -30,17 +30,91 @@ #define TS_MAX_PACKET_SIZE 204 #define NB_PID_MAX 8192 +#define USUAL_SECTION_SIZE 1024 /* except EIT which is limited to 4096 */ #define MAX_SECTION_SIZE 4096 /* pids */ -#define PAT_PID 0x0000 -#define SDT_PID 0x0011 +#define PAT_PID 0x0000 /* Program Association Table */ +#define CAT_PID 0x0001 /* Conditional Access Table */ +#define TSDT_PID 0x0002 /* Transport Stream Description Table */ +#define IPMP_PID 0x0003 +/* PID from 0x0004 to 0x000F are reserved */ +#define NIT_PID 0x0010 /* Network Information Table */ +#define SDT_PID 0x0011 /* Service Description Table */ +#define BAT_PID 0x0011 /* Bouquet Association Table */ +#define EIT_PID 0x0012 /* Event Information Table */ +#define RST_PID 0x0013 /* Running Status Table */ +#define TDT_PID 0x0014 /* Time and Date Table */ +#define TOT_PID 0x0014 +#define NET_SYNC_PID 0x0015 +#define RNT_PID 0x0016 /* RAR Notification Table */ +/* PID from 0x0017 to 0x001B are reserved for future use */ +/* PID value 0x001C allocated to link-local inband signalling shall not be + * used on any broadcast signals. It shall only be used between devices in a + * controlled environment. */ +#define LINK_LOCAL_PID 0x001C +#define MEASUREMENT_PID 0x001D +#define DIT_PID 0x001E /* Discontinuity Information Table */ +#define SIT_PID 0x001F /* Selection Information Table */ +/* PID from 0x0020 to 0x1FFA may be assigned as needed to PMT, elementary + * streams and other data tables */ +#define FIRST_OTHER_PID 0x0020 +#define LAST_OTHER_PID 0x1FFA +/* PID 0x1FFB is used by DigiCipher 2/ATSC MGT metadata */ +/* PID from 0x1FFC to 0x1FFE may be assigned as needed to PMT, elementary + * streams and other data tables */ +#define NULL_PID 0x1FFF /* Null packet (used for fixed bandwidth padding) */ + +/* m2ts pids */ +#define M2TS_PMT_PID 0x0100 +#define M2TS_PCR_PID 0x1001 +#define M2TS_VIDEO_PID 0x1011 +#define M2TS_AUDIO_START_PID 0x1100 +#define M2TS_PGSSUB_START_PID 0x1200 +#define M2TS_TEXTSUB_PID 0x1800 +#define M2TS_SECONDARY_AUDIO_START_PID 0x1A00 +#define M2TS_SECONDARY_VIDEO_START_PID 0x1B00 /* table ids */ -#define PAT_TID 0x00 -#define PMT_TID 0x02 -#define M4OD_TID 0x05 -#define SDT_TID 0x42 +#define PAT_TID 0x00 /* Program Association section */ +#define CAT_TID 0x01 /* Conditional Access section */ +#define PMT_TID 0x02 /* Program Map section */ +#define TSDT_TID 0x03 /* Transport Stream Description section */ +/* TID from 0x04 to 0x3F are reserved */ +#define M4OD_TID 0x05 +#define NIT_TID 0x40 /* Network Information section - actual network */ +#define ONIT_TID 0x41 /* Network Information section - other network */ +#define SDT_TID 0x42 /* Service Description section - actual TS */ +/* TID from 0x43 to 0x45 are reserved for future use */ +#define OSDT_TID 0x46 /* Service Descrition section - other TS */ +/* TID from 0x47 to 0x49 are reserved for future use */ +#define BAT_TID 0x4A /* Bouquet Association section */ +#define UNT_TID 0x4B /* Update Notification Table section */ +#define DFI_TID 0x4C /* Downloadable Font Info section */ +/* TID 0x4D is reserved for future use */ +#define EIT_TID 0x4E /* Event Information section - actual TS */ +#define OEIT_TID 0x4F /* Event Information section - other TS */ +#define EITS_START_TID 0x50 /* Event Information section schedule - actual TS */ +#define EITS_END_TID 0x5F /* Event Information section schedule - actual TS */ +#define OEITS_START_TID 0x60 /* Event Information section schedule - other TS */ +#define OEITS_END_TID 0x6F /* Event Information section schedule - other TS */ +#define TDT_TID 0x70 /* Time Date section */ +#define RST_TID 0x71 /* Running Status section */ +#define ST_TID 0x72 /* Stuffing section */ +#define TOT_TID 0x73 /* Time Offset section */ +#define AIT_TID 0x74 /* Application Inforamtion section */ +#define CT_TID 0x75 /* Container section */ +#define RCT_TID 0x76 /* Related Content section */ +#define CIT_TID 0x77 /* Content Identifier section */ +#define MPE_FEC_TID 0x78 /* MPE-FEC section */ +#define RPNT_TID 0x79 /* Resolution Provider Notification section */ +#define MPE_IFEC_TID 0x7A /* MPE-IFEC section */ +#define PROTMT_TID 0x7B /* Protection Message section */ +/* TID from 0x7C to 0x7D are reserved for future use */ +#define DIT_TID 0x7E /* Discontinuity Information section */ +#define SIT_TID 0x7F /* Selection Information section */ +/* TID from 0x80 to 0xFE are user defined */ +/* TID 0xFF is reserved */ #define STREAM_TYPE_VIDEO_MPEG1 0x01 #define STREAM_TYPE_VIDEO_MPEG2 0x02 diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c index fc0ea225c6c..b5ee48d0154 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -57,8 +57,6 @@ typedef struct MpegTSService { uint8_t name[256]; uint8_t provider_name[256]; int pcr_pid; - int pcr_packet_count; - int pcr_packet_period; AVProgram *program; } MpegTSService; @@ -78,14 +76,11 @@ typedef struct MpegTSWrite { MpegTSSection pat; /* MPEG-2 PAT table */ MpegTSSection sdt; /* MPEG-2 SDT table context */ MpegTSService **services; - int sdt_packet_count; - int sdt_packet_period; - int pat_packet_count; - int pat_packet_period; + int64_t sdt_period; /* SDT period in PCR time base */ + int64_t pat_period; /* PAT/PMT period in PCR time base */ int nb_services; - int onid; - int tsid; int64_t first_pcr; + int64_t next_pcr; int mux_rate; ///< set to 1 when VBR int pes_payload_size; @@ -97,10 +92,12 @@ typedef struct MpegTSWrite { int pmt_start_pid; int start_pid; int m2ts_mode; + int m2ts_video_pid; + int m2ts_audio_pid; + int m2ts_pgssub_pid; + int m2ts_textsub_pid; - int reemit_pat_pmt; // backward compatibility - - int pcr_period; + int pcr_period_ms; #define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01 #define MPEGTS_FLAG_AAC_LATM 0x02 #define MPEGTS_FLAG_PAT_PMT_AT_FRAMES 0x04 @@ -109,8 +106,8 @@ typedef struct MpegTSWrite { int flags; int copyts; int tables_version; - double pat_period; - double sdt_period; + int64_t pat_period_us; + int64_t sdt_period_us; int64_t last_pat_ts; int64_t last_sdt_ts; @@ -220,7 +217,7 @@ static int mpegts_write_section1(MpegTSSection *s, int tid, int id, /* mpegts writer */ #define DEFAULT_PROVIDER_NAME "FFmpeg" -#define DEFAULT_SERVICE_NAME "Service01" +#define DEFAULT_SERVICE_NAME "Service" /* we retransmit the SI info at this rate */ #define SDT_RETRANS_TIME 500 @@ -228,7 +225,6 @@ static int mpegts_write_section1(MpegTSSection *s, int tid, int id, #define PCR_RETRANS_TIME 20 typedef struct MpegTSWriteStream { - struct MpegTSService *service; int pid; /* stream associated pid */ int cc; int discontinuity; @@ -240,7 +236,10 @@ typedef struct MpegTSWriteStream { int payload_flags; uint8_t *payload; AVFormatContext *amux; - AVRational user_tb; + int data_st_warning; + + int64_t pcr_period; /* PCR period in PCR time base */ + int64_t last_pcr; /* For Opus */ int opus_queued_samples; @@ -260,7 +259,7 @@ static void mpegts_write_pat(AVFormatContext *s) put16(&q, service->sid); put16(&q, 0xe000 | service->pmt.pid); } - mpegts_write_section1(&ts->pat, PAT_TID, ts->tsid, ts->tables_version, 0, 0, + mpegts_write_section1(&ts->pat, PAT_TID, ts->transport_stream_id, ts->tables_version, 0, 0, data, q - data); } @@ -282,6 +281,148 @@ static void put_registration_descriptor(uint8_t **q_ptr, uint32_t tag) *q_ptr = q; } +static int get_dvb_stream_type(AVFormatContext *s, AVStream *st) +{ + MpegTSWrite *ts = s->priv_data; + MpegTSWriteStream *ts_st = st->priv_data; + int stream_type; + + switch (st->codecpar->codec_id) { + case AV_CODEC_ID_MPEG1VIDEO: + case AV_CODEC_ID_MPEG2VIDEO: + stream_type = STREAM_TYPE_VIDEO_MPEG2; + break; + case AV_CODEC_ID_MPEG4: + stream_type = STREAM_TYPE_VIDEO_MPEG4; + break; + case AV_CODEC_ID_H264: + stream_type = STREAM_TYPE_VIDEO_H264; + break; + case AV_CODEC_ID_HEVC: + stream_type = STREAM_TYPE_VIDEO_HEVC; + break; + case AV_CODEC_ID_CAVS: + stream_type = STREAM_TYPE_VIDEO_CAVS; + break; + case AV_CODEC_ID_DIRAC: + stream_type = STREAM_TYPE_VIDEO_DIRAC; + break; + case AV_CODEC_ID_VC1: + stream_type = STREAM_TYPE_VIDEO_VC1; + break; + case AV_CODEC_ID_MP2: + case AV_CODEC_ID_MP3: + if ( st->codecpar->sample_rate > 0 + && st->codecpar->sample_rate < 32000) { + stream_type = STREAM_TYPE_AUDIO_MPEG2; + } else { + stream_type = STREAM_TYPE_AUDIO_MPEG1; + } + break; + case AV_CODEC_ID_AAC: + stream_type = (ts->flags & MPEGTS_FLAG_AAC_LATM) + ? STREAM_TYPE_AUDIO_AAC_LATM + : STREAM_TYPE_AUDIO_AAC; + break; + case AV_CODEC_ID_AAC_LATM: + stream_type = STREAM_TYPE_AUDIO_AAC_LATM; + break; + case AV_CODEC_ID_AC3: + stream_type = (ts->flags & MPEGTS_FLAG_SYSTEM_B) + ? STREAM_TYPE_PRIVATE_DATA + : STREAM_TYPE_AUDIO_AC3; + break; + case AV_CODEC_ID_EAC3: + stream_type = (ts->flags & MPEGTS_FLAG_SYSTEM_B) + ? STREAM_TYPE_PRIVATE_DATA + : STREAM_TYPE_AUDIO_EAC3; + break; + case AV_CODEC_ID_DTS: + stream_type = STREAM_TYPE_AUDIO_DTS; + break; + case AV_CODEC_ID_TRUEHD: + stream_type = STREAM_TYPE_AUDIO_TRUEHD; + break; + case AV_CODEC_ID_OPUS: + stream_type = STREAM_TYPE_PRIVATE_DATA; + break; + case AV_CODEC_ID_TIMED_ID3: + stream_type = STREAM_TYPE_METADATA; + break; + case AV_CODEC_ID_DVB_SUBTITLE: + case AV_CODEC_ID_DVB_TELETEXT: + stream_type = STREAM_TYPE_PRIVATE_DATA; + break; + case AV_CODEC_ID_SMPTE_KLV: + if (st->codecpar->profile == FF_PROFILE_KLVA_SYNC) { + stream_type = STREAM_TYPE_METADATA; + } else { + stream_type = STREAM_TYPE_PRIVATE_DATA; + } + break; + default: + av_log_once(s, AV_LOG_WARNING, AV_LOG_DEBUG, &ts_st->data_st_warning, + "Stream %d, codec %s, is muxed as a private data stream " + "and may not be recognized upon reading.\n", st->index, + avcodec_get_name(st->codecpar->codec_id)); + stream_type = STREAM_TYPE_PRIVATE_DATA; + break; + } + + return stream_type; +} + +static int get_m2ts_stream_type(AVFormatContext *s, AVStream *st) +{ + int stream_type; + MpegTSWriteStream *ts_st = st->priv_data; + + switch (st->codecpar->codec_id) { + case AV_CODEC_ID_MPEG2VIDEO: + stream_type = STREAM_TYPE_VIDEO_MPEG2; + break; + case AV_CODEC_ID_H264: + stream_type = STREAM_TYPE_VIDEO_H264; + break; + case AV_CODEC_ID_VC1: + stream_type = STREAM_TYPE_VIDEO_VC1; + break; + case AV_CODEC_ID_HEVC: + stream_type = STREAM_TYPE_VIDEO_HEVC; + break; + case AV_CODEC_ID_PCM_BLURAY: + stream_type = 0x80; + break; + case AV_CODEC_ID_AC3: + stream_type = 0x81; + break; + case AV_CODEC_ID_DTS: + stream_type = (st->codecpar->channels > 6) ? 0x85 : 0x82; + break; + case AV_CODEC_ID_TRUEHD: + stream_type = 0x83; + break; + case AV_CODEC_ID_EAC3: + stream_type = 0x84; + break; + case AV_CODEC_ID_HDMV_PGS_SUBTITLE: + stream_type = 0x90; + break; + case AV_CODEC_ID_HDMV_TEXT_SUBTITLE: + stream_type = 0x92; + break; + default: + av_log_once(s, AV_LOG_WARNING, AV_LOG_DEBUG, &ts_st->data_st_warning, + "Stream %d, codec %s, is muxed as a private data stream " + "and may not be recognized upon reading.\n", st->index, + avcodec_get_name(st->codecpar->codec_id)); + stream_type = STREAM_TYPE_PRIVATE_DATA; + break; + } + + return stream_type; +} + static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) { MpegTSWrite *ts = s->priv_data; @@ -295,6 +436,14 @@ static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) q += 2; /* patched after */ /* put program info here */ + if (ts->m2ts_mode) { + put_registration_descriptor(&q, MKTAG('H', 'D', 'M', 'V')); + *q++ = 0x88; // descriptor_tag - hdmv_copy_control_descriptor + *q++ = 0x04; // descriptor_length + put16(&q, 0x0fff); // CA_System_ID + *q++ = 0xfc; // private_data_byte + *q++ = 0xfc; // private_data_byte + } val = 0xf000 | (q - program_info_length_ptr - 2); program_info_length_ptr[0] = val >> 8; @@ -323,72 +472,8 @@ static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) err = 1; break; } - switch (st->codecpar->codec_id) { - case AV_CODEC_ID_MPEG1VIDEO: - case AV_CODEC_ID_MPEG2VIDEO: - stream_type = STREAM_TYPE_VIDEO_MPEG2; - break; - case AV_CODEC_ID_MPEG4: - stream_type = STREAM_TYPE_VIDEO_MPEG4; - break; - case AV_CODEC_ID_H264: - stream_type = STREAM_TYPE_VIDEO_H264; - break; - case AV_CODEC_ID_HEVC: - stream_type = STREAM_TYPE_VIDEO_HEVC; - break; - case AV_CODEC_ID_CAVS: - stream_type = STREAM_TYPE_VIDEO_CAVS; - break; - case AV_CODEC_ID_DIRAC: - stream_type = STREAM_TYPE_VIDEO_DIRAC; - break; - case AV_CODEC_ID_VC1: - stream_type = STREAM_TYPE_VIDEO_VC1; - break; - case AV_CODEC_ID_MP2: - case AV_CODEC_ID_MP3: - if ( st->codecpar->sample_rate > 0 - && st->codecpar->sample_rate < 32000) { - stream_type = STREAM_TYPE_AUDIO_MPEG2; - } else { - stream_type = STREAM_TYPE_AUDIO_MPEG1; - } - break; - case AV_CODEC_ID_AAC: - stream_type = (ts->flags & MPEGTS_FLAG_AAC_LATM) - ? STREAM_TYPE_AUDIO_AAC_LATM - : STREAM_TYPE_AUDIO_AAC; - break; - case AV_CODEC_ID_AAC_LATM: - stream_type = STREAM_TYPE_AUDIO_AAC_LATM; - break; - case AV_CODEC_ID_AC3: - stream_type = (ts->flags & MPEGTS_FLAG_SYSTEM_B) - ? STREAM_TYPE_PRIVATE_DATA - : STREAM_TYPE_AUDIO_AC3; - break; - case AV_CODEC_ID_EAC3: - stream_type = (ts->flags & MPEGTS_FLAG_SYSTEM_B) - ? STREAM_TYPE_PRIVATE_DATA - : STREAM_TYPE_AUDIO_EAC3; - break; - case AV_CODEC_ID_DTS: - stream_type = STREAM_TYPE_AUDIO_DTS; - break; - case AV_CODEC_ID_TRUEHD: - stream_type = STREAM_TYPE_AUDIO_TRUEHD; - break; - case AV_CODEC_ID_OPUS: - stream_type = STREAM_TYPE_PRIVATE_DATA; - break; - case AV_CODEC_ID_TIMED_ID3: - stream_type = STREAM_TYPE_METADATA; - break; - default: - stream_type = STREAM_TYPE_PRIVATE_DATA; - break; - } + + stream_type = ts->m2ts_mode ? get_m2ts_stream_type(s, st) : get_dvb_stream_type(s, st); *q++ = stream_type; put16(&q, 0xe000 | ts_st->pid); @@ -651,7 +736,7 @@ static void mpegts_write_sdt(AVFormatContext *s) int i, running_status, free_ca_mode, val; q = data; - put16(&q, ts->onid); + put16(&q, ts->original_network_id); *q++ = 0xff; for (i = 0; i < ts->nb_services; i++) { service = ts->services[i]; @@ -677,7 +762,7 @@ static void mpegts_write_sdt(AVFormatContext *s) desc_list_len_ptr[0] = val >> 8; desc_list_len_ptr[1] = val; } - mpegts_write_section1(&ts->sdt, SDT_TID, ts->tsid, ts->tables_version, 0, 0, + mpegts_write_section1(&ts->sdt, SDT_TID, ts->transport_stream_id, ts->tables_version, 0, 0, data, q - data); } @@ -717,12 +802,49 @@ static int encode_str8(uint8_t *buf, const char *str) return 0; } +static int64_t get_pcr(const MpegTSWrite *ts, AVIOContext *pb) +{ + return av_rescale(avio_tell(pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate) + + ts->first_pcr; +} + +static void write_packet(AVFormatContext *s, const uint8_t *packet) +{ + MpegTSWrite *ts = s->priv_data; + if (ts->m2ts_mode) { + int64_t pcr = get_pcr(s->priv_data, s->pb); + uint32_t tp_extra_header = pcr % 0x3fffffff; + tp_extra_header = AV_RB32(&tp_extra_header); + avio_write(s->pb, (unsigned char *) &tp_extra_header, + sizeof(tp_extra_header)); + } + avio_write(s->pb, packet, TS_PACKET_SIZE); +} + +static void section_write_packet(MpegTSSection *s, const uint8_t *packet) +{ + AVFormatContext *ctx = s->opaque; + write_packet(ctx, packet); +} + static MpegTSService *mpegts_add_service(AVFormatContext *s, int sid, - const char *provider_name, - const char *name) + const AVDictionary *metadata, + AVProgram *program) { MpegTSWrite *ts = s->priv_data; MpegTSService *service; + AVDictionaryEntry *title, *provider; + char default_service_name[32]; + const char *service_name; + const char *provider_name; + + title = av_dict_get(metadata, "service_name", NULL, 0); + if (!title) + title = av_dict_get(metadata, "title", NULL, 0); + snprintf(default_service_name, sizeof(default_service_name), "%s%02d", DEFAULT_SERVICE_NAME, ts->nb_services + 1); + service_name = title ? title->value : default_service_name; + provider = av_dict_get(metadata, "service_provider", NULL, 0); + provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME; service = av_mallocz(sizeof(MpegTSService)); if (!service) @@ -731,103 +853,127 @@ static MpegTSService *mpegts_add_service(AVFormatContext *s, int sid, service->sid = sid; service->pcr_pid = 0x1fff; if (encode_str8(service->provider_name, provider_name) < 0 || - encode_str8(service->name, name) < 0) { + encode_str8(service->name, service_name) < 0) { av_log(s, AV_LOG_ERROR, "Too long service or provider name\n"); goto fail; } if (av_dynarray_add_nofree(&ts->services, &ts->nb_services, service) < 0) goto fail; + service->pmt.write_packet = section_write_packet; + service->pmt.opaque = s; + service->pmt.cc = 15; + service->pmt.discontinuity= ts->flags & MPEGTS_FLAG_DISCONT; + service->program = program; + return service; fail: av_free(service); return NULL; } -static int64_t get_pcr(const MpegTSWrite *ts, AVIOContext *pb) -{ - return av_rescale(avio_tell(pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate) + - ts->first_pcr; -} - -static void mpegts_prefix_m2ts_header(AVFormatContext *s) +static void enable_pcr_generation_for_stream(AVFormatContext *s, AVStream *pcr_st) { MpegTSWrite *ts = s->priv_data; - if (ts->m2ts_mode) { - int64_t pcr = get_pcr(s->priv_data, s->pb); - uint32_t tp_extra_header = pcr % 0x3fffffff; - tp_extra_header = AV_RB32(&tp_extra_header); - avio_write(s->pb, (unsigned char *) &tp_extra_header, - sizeof(tp_extra_header)); + MpegTSWriteStream *ts_st = pcr_st->priv_data; + + if (ts->mux_rate > 1 || ts->pcr_period_ms >= 0) { + int pcr_period_ms = ts->pcr_period_ms == -1 ? PCR_RETRANS_TIME : ts->pcr_period_ms; + ts_st->pcr_period = av_rescale(pcr_period_ms, PCR_TIME_BASE, 1000); + } else { + /* By default, for VBR we select the highest multiple of frame duration which is less than 100 ms. */ + int64_t frame_period = 0; + if (pcr_st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + int frame_size = av_get_audio_frame_duration2(pcr_st->codecpar, 0); + if (!frame_size) { + av_log(s, AV_LOG_WARNING, "frame size not set\n"); + frame_size = 512; + } + frame_period = av_rescale_rnd(frame_size, PCR_TIME_BASE, pcr_st->codecpar->sample_rate, AV_ROUND_UP); + } else if (pcr_st->avg_frame_rate.num) { + frame_period = av_rescale_rnd(pcr_st->avg_frame_rate.den, PCR_TIME_BASE, pcr_st->avg_frame_rate.num, AV_ROUND_UP); + } + if (frame_period > 0 && frame_period <= PCR_TIME_BASE / 10) + ts_st->pcr_period = frame_period * (PCR_TIME_BASE / 10 / frame_period); + else + ts_st->pcr_period = 1; } + + // output a PCR as soon as possible + ts_st->last_pcr = ts->first_pcr - ts_st->pcr_period; } -static void section_write_packet(MpegTSSection *s, const uint8_t *packet) +static void select_pcr_streams(AVFormatContext *s) { - AVFormatContext *ctx = s->opaque; - mpegts_prefix_m2ts_header(ctx); - avio_write(ctx->pb, packet, TS_PACKET_SIZE); + MpegTSWrite *ts = s->priv_data; + + for (int i = 0; i < ts->nb_services; i++) { + MpegTSService *service = ts->services[i]; + AVStream *pcr_st = NULL; + AVProgram *program = service->program; + int nb_streams = program ? program->nb_stream_indexes : s->nb_streams; + + for (int j = 0; j < nb_streams; j++) { + AVStream *st = s->streams[program ? program->stream_index[j] : j]; + if (!pcr_st || + pcr_st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) + { + pcr_st = st; + } + } + + if (pcr_st) { + MpegTSWriteStream *ts_st = pcr_st->priv_data; + service->pcr_pid = ts_st->pid; + enable_pcr_generation_for_stream(s, pcr_st); + av_log(s, AV_LOG_VERBOSE, "service %i using PCR in pid=%i, pcr_period=%"PRId64"ms\n", + service->sid, service->pcr_pid, av_rescale(ts_st->pcr_period, 1000, PCR_TIME_BASE)); + } + } } static int mpegts_init(AVFormatContext *s) { MpegTSWrite *ts = s->priv_data; - MpegTSWriteStream *ts_st; - MpegTSService *service; - AVStream *st, *pcr_st = NULL; - AVDictionaryEntry *title, *provider; int i, j; - const char *service_name; - const char *provider_name; - int *pids; int ret; + if (ts->m2ts_mode == -1) { + if (av_match_ext(s->url, "m2ts")) { + ts->m2ts_mode = 1; + } else { + ts->m2ts_mode = 0; + } + } + + ts->m2ts_video_pid = M2TS_VIDEO_PID; + ts->m2ts_audio_pid = M2TS_AUDIO_START_PID; + ts->m2ts_pgssub_pid = M2TS_PGSSUB_START_PID; + ts->m2ts_textsub_pid = M2TS_TEXTSUB_PID; + + if (ts->m2ts_mode) { + ts->pmt_start_pid = M2TS_PMT_PID; + if (s->nb_programs > 1) { + av_log(s, AV_LOG_ERROR, "Only one program is allowed in m2ts mode!\n"); + return AVERROR(EINVAL); + } + } + if (s->max_delay < 0) /* Not set by the caller */ s->max_delay = 0; // round up to a whole number of TS packets ts->pes_payload_size = (ts->pes_payload_size + 14 + 183) / 184 * 184 - 14; - ts->tsid = ts->transport_stream_id; - ts->onid = ts->original_network_id; if (!s->nb_programs) { /* allocate a single DVB service */ - title = av_dict_get(s->metadata, "service_name", NULL, 0); - if (!title) - title = av_dict_get(s->metadata, "title", NULL, 0); - service_name = title ? title->value : DEFAULT_SERVICE_NAME; - provider = av_dict_get(s->metadata, "service_provider", NULL, 0); - provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME; - service = mpegts_add_service(s, ts->service_id, - provider_name, service_name); - - if (!service) + if (!mpegts_add_service(s, ts->service_id, s->metadata, NULL)) return AVERROR(ENOMEM); - - service->pmt.write_packet = section_write_packet; - service->pmt.opaque = s; - service->pmt.cc = 15; - service->pmt.discontinuity= ts->flags & MPEGTS_FLAG_DISCONT; } else { for (i = 0; i < s->nb_programs; i++) { AVProgram *program = s->programs[i]; - title = av_dict_get(program->metadata, "service_name", NULL, 0); - if (!title) - title = av_dict_get(program->metadata, "title", NULL, 0); - service_name = title ? title->value : DEFAULT_SERVICE_NAME; - provider = av_dict_get(program->metadata, "service_provider", NULL, 0); - provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME; - service = mpegts_add_service(s, program->id, - provider_name, service_name); - - if (!service) + if (!mpegts_add_service(s, program->id, program->metadata, program)) return AVERROR(ENOMEM); - - service->pmt.write_packet = section_write_packet; - service->pmt.opaque = s; - service->pmt.cc = 15; - service->pmt.discontinuity= ts->flags & MPEGTS_FLAG_DISCONT; - service->program = program; } } @@ -845,217 +991,158 @@ static int mpegts_init(AVFormatContext *s) ts->sdt.write_packet = section_write_packet; ts->sdt.opaque = s; - pids = av_malloc_array(s->nb_streams, sizeof(*pids)); - if (!pids) { - ret = AVERROR(ENOMEM); - goto fail; - } - /* assign pids to each stream */ for (i = 0; i < s->nb_streams; i++) { - AVProgram *program; - st = s->streams[i]; + AVStream *st = s->streams[i]; + MpegTSWriteStream *ts_st; ts_st = av_mallocz(sizeof(MpegTSWriteStream)); if (!ts_st) { - ret = AVERROR(ENOMEM); - goto fail; + return AVERROR(ENOMEM); } st->priv_data = ts_st; - ts_st->user_tb = st->time_base; avpriv_set_pts_info(st, 33, 1, 90000); ts_st->payload = av_mallocz(ts->pes_payload_size); if (!ts_st->payload) { - ret = AVERROR(ENOMEM); - goto fail; - } - - program = av_find_program_from_stream(s, NULL, i); - if (program) { - for (j = 0; j < ts->nb_services; j++) { - if (ts->services[j]->program == program) { - service = ts->services[j]; - break; - } - } + return AVERROR(ENOMEM); } - ts_st->service = service; /* MPEG pid values < 16 are reserved. Applications which set st->id in * this range are assigned a calculated pid. */ if (st->id < 16) { - ts_st->pid = ts->start_pid + i; - } else if (st->id < 0x1FFF) { - ts_st->pid = st->id; + if (ts->m2ts_mode) { + switch (st->codecpar->codec_type) { + case AVMEDIA_TYPE_VIDEO: + ts_st->pid = ts->m2ts_video_pid++; + break; + case AVMEDIA_TYPE_AUDIO: + ts_st->pid = ts->m2ts_audio_pid++; + break; + case AVMEDIA_TYPE_SUBTITLE: + switch (st->codecpar->codec_id) { + case AV_CODEC_ID_HDMV_PGS_SUBTITLE: + ts_st->pid = ts->m2ts_pgssub_pid++; + break; + case AV_CODEC_ID_HDMV_TEXT_SUBTITLE: + ts_st->pid = ts->m2ts_textsub_pid++; + break; + } + break; + } + if (ts->m2ts_video_pid > M2TS_VIDEO_PID + 1 || + ts->m2ts_audio_pid > M2TS_AUDIO_START_PID + 32 || + ts->m2ts_pgssub_pid > M2TS_PGSSUB_START_PID + 32 || + ts->m2ts_textsub_pid > M2TS_TEXTSUB_PID + 1 || + ts_st->pid < 16) { + av_log(s, AV_LOG_ERROR, "Cannot automatically assign PID for stream %d\n", st->index); + return AVERROR(EINVAL); + } + } else { + ts_st->pid = ts->start_pid + i; + } } else { + ts_st->pid = st->id; + } + if (ts_st->pid >= 0x1FFF) { av_log(s, AV_LOG_ERROR, "Invalid stream id %d, must be less than 8191\n", st->id); - ret = AVERROR(EINVAL); - goto fail; + return AVERROR(EINVAL); } - if (ts_st->pid == service->pmt.pid) { - av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", ts_st->pid); - ret = AVERROR(EINVAL); - goto fail; + for (j = 0; j < ts->nb_services; j++) { + if (ts->services[j]->pmt.pid > LAST_OTHER_PID) { + av_log(s, AV_LOG_ERROR, + "Invalid PMT PID %d, must be less than %d\n", ts->services[j]->pmt.pid, LAST_OTHER_PID + 1); + return AVERROR(EINVAL); + } + if (ts_st->pid == ts->services[j]->pmt.pid) { + av_log(s, AV_LOG_ERROR, "PID %d cannot be both elementary and PMT PID\n", ts_st->pid); + return AVERROR(EINVAL); + } } for (j = 0; j < i; j++) { - if (pids[j] == ts_st->pid) { + MpegTSWriteStream *ts_st_prev = s->streams[j]->priv_data; + if (ts_st_prev->pid == ts_st->pid) { av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", ts_st->pid); - ret = AVERROR(EINVAL); - goto fail; + return AVERROR(EINVAL); } } - pids[i] = ts_st->pid; ts_st->payload_pts = AV_NOPTS_VALUE; ts_st->payload_dts = AV_NOPTS_VALUE; ts_st->first_pts_check = 1; ts_st->cc = 15; ts_st->discontinuity = ts->flags & MPEGTS_FLAG_DISCONT; - /* update PCR pid by using the first video stream */ - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && - service->pcr_pid == 0x1fff) { - service->pcr_pid = ts_st->pid; - pcr_st = st; - } if (st->codecpar->codec_id == AV_CODEC_ID_AAC && st->codecpar->extradata_size > 0) { AVStream *ast; ts_st->amux = avformat_alloc_context(); if (!ts_st->amux) { - ret = AVERROR(ENOMEM); - goto fail; + return AVERROR(ENOMEM); } ts_st->amux->oformat = av_guess_format((ts->flags & MPEGTS_FLAG_AAC_LATM) ? "latm" : "adts", NULL, NULL); if (!ts_st->amux->oformat) { - ret = AVERROR(EINVAL); - goto fail; + return AVERROR(EINVAL); } if (!(ast = avformat_new_stream(ts_st->amux, NULL))) { - ret = AVERROR(ENOMEM); - goto fail; + return AVERROR(ENOMEM); } ret = avcodec_parameters_copy(ast->codecpar, st->codecpar); if (ret != 0) - goto fail; + return ret; ast->time_base = st->time_base; ret = avformat_write_header(ts_st->amux, NULL); if (ret < 0) - goto fail; + return ret; } if (st->codecpar->codec_id == AV_CODEC_ID_OPUS) { ts_st->opus_pending_trim_start = st->codecpar->initial_padding * 48000 / st->codecpar->sample_rate; } } - av_freep(&pids); - - /* if no video stream, use the first stream as PCR */ - if (service->pcr_pid == 0x1fff && s->nb_streams > 0) { - pcr_st = s->streams[0]; - ts_st = pcr_st->priv_data; - service->pcr_pid = ts_st->pid; - } else - ts_st = pcr_st->priv_data; - - if (ts->mux_rate > 1) { - service->pcr_packet_period = (int64_t)ts->mux_rate * ts->pcr_period / - (TS_PACKET_SIZE * 8 * 1000); - ts->sdt_packet_period = (int64_t)ts->mux_rate * SDT_RETRANS_TIME / - (TS_PACKET_SIZE * 8 * 1000); - ts->pat_packet_period = (int64_t)ts->mux_rate * PAT_RETRANS_TIME / - (TS_PACKET_SIZE * 8 * 1000); - - if (ts->copyts < 1) - ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE); - } else { - /* Arbitrary values, PAT/PMT will also be written on video key frames */ - ts->sdt_packet_period = 200; - ts->pat_packet_period = 40; - if (pcr_st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - int frame_size = av_get_audio_frame_duration2(pcr_st->codecpar, 0); - if (!frame_size) { - av_log(s, AV_LOG_WARNING, "frame size not set\n"); - service->pcr_packet_period = - pcr_st->codecpar->sample_rate / (10 * 512); - } else { - service->pcr_packet_period = - pcr_st->codecpar->sample_rate / (10 * frame_size); - } - } else { - // max delta PCR 0.1s - // TODO: should be avg_frame_rate - service->pcr_packet_period = - ts_st->user_tb.den / (10 * ts_st->user_tb.num); - } - if (!service->pcr_packet_period) - service->pcr_packet_period = 1; - } + if (ts->copyts < 1) + ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE); + + select_pcr_streams(s); ts->last_pat_ts = AV_NOPTS_VALUE; ts->last_sdt_ts = AV_NOPTS_VALUE; - // The user specified a period, use only it - if (ts->pat_period < INT_MAX/2) { - ts->pat_packet_period = INT_MAX; - } - if (ts->sdt_period < INT_MAX/2) { - ts->sdt_packet_period = INT_MAX; - } - - // output a PCR as soon as possible - service->pcr_packet_count = service->pcr_packet_period; - ts->pat_packet_count = ts->pat_packet_period - 1; - ts->sdt_packet_count = ts->sdt_packet_period - 1; + ts->pat_period = av_rescale(ts->pat_period_us, PCR_TIME_BASE, AV_TIME_BASE); + ts->sdt_period = av_rescale(ts->sdt_period_us, PCR_TIME_BASE, AV_TIME_BASE); if (ts->mux_rate == 1) av_log(s, AV_LOG_VERBOSE, "muxrate VBR, "); else av_log(s, AV_LOG_VERBOSE, "muxrate %d, ", ts->mux_rate); av_log(s, AV_LOG_VERBOSE, - "pcr every %d pkts, sdt every %d, pat/pmt every %d pkts\n", - service->pcr_packet_period, - ts->sdt_packet_period, ts->pat_packet_period); - - if (ts->m2ts_mode == -1) { - if (av_match_ext(s->url, "m2ts")) { - ts->m2ts_mode = 1; - } else { - ts->m2ts_mode = 0; - } - } + "sdt every %"PRId64" ms, pat/pmt every %"PRId64" ms\n", + av_rescale(ts->sdt_period, 1000, PCR_TIME_BASE), + av_rescale(ts->pat_period, 1000, PCR_TIME_BASE)); return 0; - -fail: - av_freep(&pids); - return ret; } /* send SDT, PAT and PMT tables regularly */ -static void retransmit_si_info(AVFormatContext *s, int force_pat, int64_t dts) +static void retransmit_si_info(AVFormatContext *s, int force_pat, int force_sdt, int64_t pcr) { MpegTSWrite *ts = s->priv_data; int i; - if (++ts->sdt_packet_count == ts->sdt_packet_period || - (dts != AV_NOPTS_VALUE && ts->last_sdt_ts == AV_NOPTS_VALUE) || - (dts != AV_NOPTS_VALUE && dts - ts->last_sdt_ts >= ts->sdt_period*90000.0) + if ((pcr != AV_NOPTS_VALUE && ts->last_sdt_ts == AV_NOPTS_VALUE) || + (pcr != AV_NOPTS_VALUE && pcr - ts->last_sdt_ts >= ts->sdt_period) || + force_sdt ) { - ts->sdt_packet_count = 0; - if (dts != AV_NOPTS_VALUE) - ts->last_sdt_ts = FFMAX(dts, ts->last_sdt_ts); + if (pcr != AV_NOPTS_VALUE) + ts->last_sdt_ts = FFMAX(pcr, ts->last_sdt_ts); mpegts_write_sdt(s); } - if (++ts->pat_packet_count == ts->pat_packet_period || - (dts != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE) || - (dts != AV_NOPTS_VALUE && dts - ts->last_pat_ts >= ts->pat_period*90000.0) || + if ((pcr != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE) || + (pcr != AV_NOPTS_VALUE && pcr - ts->last_pat_ts >= ts->pat_period) || force_pat) { - ts->pat_packet_count = 0; - if (dts != AV_NOPTS_VALUE) - ts->last_pat_ts = FFMAX(dts, ts->last_pat_ts); + if (pcr != AV_NOPTS_VALUE) + ts->last_pat_ts = FFMAX(pcr, ts->last_pat_ts); mpegts_write_pat(s); for (i = 0; i < ts->nb_services; i++) mpegts_write_pmt(s, ts->services[i]); @@ -1088,8 +1175,7 @@ static void mpegts_insert_null_packet(AVFormatContext *s) *q++ = 0xff; *q++ = 0x10; memset(q, 0x0FF, TS_PACKET_SIZE - (q - buf)); - mpegts_prefix_m2ts_header(s); - avio_write(s->pb, buf, TS_PACKET_SIZE); + write_packet(s, buf); } /* Write a single transport stream packet with a PCR and no payload */ @@ -1118,8 +1204,7 @@ static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st) /* stuffing bytes */ memset(q, 0xFF, TS_PACKET_SIZE - (q - buf)); - mpegts_prefix_m2ts_header(s); - avio_write(s->pb, buf, TS_PACKET_SIZE); + write_packet(s, buf); } static void write_pts(uint8_t *q, int fourbits, int64_t pts) @@ -1183,46 +1268,82 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, uint8_t *q; int val, is_start, len, header_len, write_pcr, is_dvb_subtitle, is_dvb_teletext, flags; int afc_len, stuffing_len; - int64_t pcr = -1; /* avoid warning */ int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE); int force_pat = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key; + int force_sdt = 0; av_assert0(ts_st->payload != buf || st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO); if (ts->flags & MPEGTS_FLAG_PAT_PMT_AT_FRAMES && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { force_pat = 1; } + if (ts->flags & MPEGTS_FLAG_REEMIT_PAT_PMT) { + force_pat = 1; + force_sdt = 1; + ts->flags &= ~MPEGTS_FLAG_REEMIT_PAT_PMT; + } + is_start = 1; while (payload_size > 0) { - retransmit_si_info(s, force_pat, dts); + int64_t pcr = AV_NOPTS_VALUE; + if (ts->mux_rate > 1) + pcr = get_pcr(ts, s->pb); + else if (dts != AV_NOPTS_VALUE) + pcr = (dts - delay) * 300; + + retransmit_si_info(s, force_pat, force_sdt, pcr); force_pat = 0; + force_sdt = 0; write_pcr = 0; - if (ts_st->pid == ts_st->service->pcr_pid) { - if (ts->mux_rate > 1 || is_start) // VBR pcr period is based on frames - ts_st->service->pcr_packet_count++; - if (ts_st->service->pcr_packet_count >= - ts_st->service->pcr_packet_period) { - ts_st->service->pcr_packet_count = 0; + if (ts->mux_rate > 1) { + /* Send PCR packets for all PCR streams if needed */ + pcr = get_pcr(ts, s->pb); + if (pcr >= ts->next_pcr) { + int64_t next_pcr = INT64_MAX; + for (int i = 0; i < s->nb_streams; i++) { + /* Make the current stream the last, because for that we + * can insert the pcr into the payload later */ + int st2_index = i < st->index ? i : (i + 1 == s->nb_streams ? st->index : i + 1); + AVStream *st2 = s->streams[st2_index]; + MpegTSWriteStream *ts_st2 = st2->priv_data; + if (ts_st2->pcr_period) { + if (pcr - ts_st2->last_pcr >= ts_st2->pcr_period) { + ts_st2->last_pcr = FFMAX(pcr - ts_st2->pcr_period, ts_st2->last_pcr + ts_st2->pcr_period); + if (st2 != st) { + mpegts_insert_pcr_only(s, st2); + pcr = get_pcr(ts, s->pb); + } else { + write_pcr = 1; + } + } + next_pcr = FFMIN(next_pcr, ts_st2->last_pcr + ts_st2->pcr_period); + } + } + ts->next_pcr = next_pcr; + } + if (dts != AV_NOPTS_VALUE && (dts - pcr / 300) > delay) { + /* pcr insert gets priority over null packet insert */ + if (write_pcr) + mpegts_insert_pcr_only(s, st); + else + mpegts_insert_null_packet(s); + /* recalculate write_pcr and possibly retransmit si_info */ + continue; + } + } else if (ts_st->pcr_period && pcr != AV_NOPTS_VALUE) { + if (pcr - ts_st->last_pcr >= ts_st->pcr_period && is_start) { + ts_st->last_pcr = FFMAX(pcr - ts_st->pcr_period, ts_st->last_pcr + ts_st->pcr_period); write_pcr = 1; } } - if (ts->mux_rate > 1 && dts != AV_NOPTS_VALUE && - (dts - get_pcr(ts, s->pb) / 300) > delay) { - /* pcr insert gets priority over null packet insert */ - if (write_pcr) - mpegts_insert_pcr_only(s, st); - else - mpegts_insert_null_packet(s); - /* recalculate write_pcr and possibly retransmit si_info */ - continue; - } - /* prepare packet header */ q = buf; *q++ = 0x47; val = ts_st->pid >> 8; + if (ts->m2ts_mode && st->codecpar->codec_id == AV_CODEC_ID_AC3) + val |= 0x20; if (is_start) val |= 0x40; *q++ = val; @@ -1236,7 +1357,7 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, } if (key && is_start && pts != AV_NOPTS_VALUE) { // set Random Access for key frames - if (ts_st->pid == ts_st->service->pcr_pid) + if (ts_st->pcr_period) write_pcr = 1; set_af_flag(buf, 0x40); q = get_ts_payload_start(buf); @@ -1245,10 +1366,6 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, set_af_flag(buf, 0x10); q = get_ts_payload_start(buf); // add 11, pcr references the last byte of program clock reference base - if (ts->mux_rate > 1) - pcr = get_pcr(ts, s->pb); - else - pcr = (dts - delay) * 300; if (dts != AV_NOPTS_VALUE && dts < pcr / 300) av_log(s, AV_LOG_WARNING, "dts < pcr, TS is invalid\n"); extend_af(buf, write_pcr_bits(q, pcr)); @@ -1322,10 +1439,10 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, if (ts->m2ts_mode && st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->codec_id == AV_CODEC_ID_AC3) { - /* set PES_extension_flag */ - pes_extension = 1; - flags |= 0x01; - header_len += 3; + /* set PES_extension_flag */ + pes_extension = 1; + flags |= 0x01; + header_len += 3; } if (is_dvb_teletext) { pes_header_stuffing_bytes = 0x24 - header_len; @@ -1368,14 +1485,14 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, *q++ = 0x00 | 0x60; } /* For Blu-ray AC3 Audio Setting extended flags */ - if (ts->m2ts_mode && - pes_extension && - st->codecpar->codec_id == AV_CODEC_ID_AC3) { - flags = 0x01; /* set PES_extension_flag_2 */ - *q++ = flags; - *q++ = 0x80 | 0x01; /* marker bit + extension length */ - *q++ = 0x00 | 0x71; /* for AC3 Audio (specifically on blue-rays) */ - } + if (ts->m2ts_mode && + pes_extension && + st->codecpar->codec_id == AV_CODEC_ID_AC3) { + flags = 0x01; /* set PES_extension_flag_2 */ + *q++ = flags; + *q++ = 0x80 | 0x01; /* marker bit + extension length */ + *q++ = 0x00 | 0x71; /* for AC3 Audio (specifically on blue-rays) */ + } if (is_dvb_subtitle) { @@ -1429,8 +1546,7 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, payload += len; payload_size -= len; - mpegts_prefix_m2ts_header(s); - avio_write(s->pb, buf, TS_PACKET_SIZE); + write_packet(s, buf); } ts_st->prev_payload_key = key; } @@ -1528,6 +1644,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) MpegTSWrite *ts = s->priv_data; MpegTSWriteStream *ts_st = st->priv_data; const int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE) * 2; + const int64_t max_audio_delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE) / 2; int64_t dts = pkt->dts, pts = pkt->pts; int opus_samples = 0; int side_data_size; @@ -1540,19 +1657,6 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) if (side_data) stream_id = side_data[0]; - if (ts->reemit_pat_pmt) { - av_log(s, AV_LOG_WARNING, - "resend_headers option is deprecated, use -mpegts_flags resend_headers\n"); - ts->reemit_pat_pmt = 0; - ts->flags |= MPEGTS_FLAG_REEMIT_PAT_PMT; - } - - if (ts->flags & MPEGTS_FLAG_REEMIT_PAT_PMT) { - ts->pat_packet_count = ts->pat_packet_period - 1; - ts->sdt_packet_count = ts->sdt_packet_period - 1; - ts->flags &= ~MPEGTS_FLAG_REEMIT_PAT_PMT; - } - if (ts->copyts < 1) { if (pts != AV_NOPTS_VALUE) pts += delay; @@ -1620,7 +1724,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) ret = avio_open_dyn_buf(&ts_st->amux->pb); if (ret < 0) - return AVERROR(ENOMEM); + return ret; ret = av_write_frame(ts_st->amux, &pkt2); if (ret < 0) { @@ -1651,7 +1755,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) } while (p < buf_end && (state & 0x7e) != 2*35 && (state & 0x7e) >= 2*32); - if ((state & 0x7e) < 2*16 && (state & 0x7e) >= 2*24) + if ((state & 0x7e) < 2*16 || (state & 0x7e) >= 2*24) extradd = 0; if ((state & 0x7e) != 2*35) { // AUD NAL data = av_malloc(pkt->size + 7 + extradd); @@ -1739,25 +1843,9 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) } } - if (pkt->dts != AV_NOPTS_VALUE) { - int i; - for(i=0; inb_streams; i++) { - AVStream *st2 = s->streams[i]; - MpegTSWriteStream *ts_st2 = st2->priv_data; - if ( ts_st2->payload_size - && (ts_st2->payload_dts == AV_NOPTS_VALUE || dts - ts_st2->payload_dts > delay/2)) { - mpegts_write_pes(s, st2, ts_st2->payload, ts_st2->payload_size, - ts_st2->payload_pts, ts_st2->payload_dts, - ts_st2->payload_flags & AV_PKT_FLAG_KEY, stream_id); - ts_st2->payload_size = 0; - } - } - } - if (ts_st->payload_size && (ts_st->payload_size + size > ts->pes_payload_size || (dts != AV_NOPTS_VALUE && ts_st->payload_dts != AV_NOPTS_VALUE && - av_compare_ts(dts - ts_st->payload_dts, st->time_base, - s->max_delay, AV_TIME_BASE_Q) >= 0) || + dts - ts_st->payload_dts >= max_audio_delay) || ts_st->opus_queued_samples + opus_samples >= 5760 /* 120ms */)) { mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_size, ts_st->payload_pts, ts_st->payload_dts, @@ -1793,6 +1881,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) static void mpegts_write_flush(AVFormatContext *s) { + MpegTSWrite *ts = s->priv_data; int i; /* flush current packets */ @@ -1807,6 +1896,12 @@ static void mpegts_write_flush(AVFormatContext *s) ts_st->opus_queued_samples = 0; } } + + if (ts->m2ts_mode) { + int packets = (avio_tell(s->pb) / (TS_PACKET_SIZE + 4)) % 32; + while (packets++ < 32) + mpegts_insert_null_packet(s); + } } static int mpegts_write_packet(AVFormatContext *s, AVPacket *pkt) @@ -1874,98 +1969,62 @@ static int mpegts_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt return ret; } +#define OFFSET(x) offsetof(MpegTSWrite, x) +#define ENC AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { { "mpegts_transport_stream_id", "Set transport_stream_id field.", - offsetof(MpegTSWrite, transport_stream_id), AV_OPT_TYPE_INT, - { .i64 = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM }, + OFFSET(transport_stream_id), AV_OPT_TYPE_INT, { .i64 = 0x0001 }, 0x0001, 0xffff, ENC }, { "mpegts_original_network_id", "Set original_network_id field.", - offsetof(MpegTSWrite, original_network_id), AV_OPT_TYPE_INT, - { .i64 = DVB_PRIVATE_NETWORK_START }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM }, + OFFSET(original_network_id), AV_OPT_TYPE_INT, { .i64 = DVB_PRIVATE_NETWORK_START }, 0x0001, 0xffff, ENC }, { "mpegts_service_id", "Set service_id field.", - offsetof(MpegTSWrite, service_id), AV_OPT_TYPE_INT, - { .i64 = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM }, + OFFSET(service_id), AV_OPT_TYPE_INT, { .i64 = 0x0001 }, 0x0001, 0xffff, ENC }, { "mpegts_service_type", "Set service_type field.", - offsetof(MpegTSWrite, service_type), AV_OPT_TYPE_INT, - { .i64 = 0x01 }, 0x01, 0xff, AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, + OFFSET(service_type), AV_OPT_TYPE_INT, { .i64 = 0x01 }, 0x01, 0xff, ENC, "mpegts_service_type" }, { "digital_tv", "Digital Television.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_TV }, 0x01, 0xff, - AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_TV }, 0x01, 0xff, ENC, "mpegts_service_type" }, { "digital_radio", "Digital Radio.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_RADIO }, 0x01, 0xff, - AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_RADIO }, 0x01, 0xff, ENC, "mpegts_service_type" }, { "teletext", "Teletext.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_TELETEXT }, 0x01, 0xff, - AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_TELETEXT }, 0x01, 0xff, ENC, "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, - AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_RADIO }, 0x01, 0xff, ENC, "mpegts_service_type" }, { "mpeg2_digital_hdtv", "MPEG2 Digital HDTV.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_MPEG2_DIGITAL_HDTV }, 0x01, 0xff, - AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_MPEG2_DIGITAL_HDTV }, 0x01, 0xff, ENC, "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, - AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_SDTV }, 0x01, 0xff, ENC, "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, - AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_HDTV }, 0x01, 0xff, ENC, "mpegts_service_type" }, { "hevc_digital_hdtv", "HEVC Digital Television Service.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_HEVC_DIGITAL_HDTV }, 0x01, 0xff, - AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_HEVC_DIGITAL_HDTV }, 0x01, 0xff, ENC, "mpegts_service_type" }, { "mpegts_pmt_start_pid", "Set the first pid of the PMT.", - offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT, - { .i64 = 0x1000 }, 0x0010, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM }, + OFFSET(pmt_start_pid), AV_OPT_TYPE_INT, { .i64 = 0x1000 }, FIRST_OTHER_PID, LAST_OTHER_PID, ENC }, { "mpegts_start_pid", "Set the first pid.", - offsetof(MpegTSWrite, start_pid), AV_OPT_TYPE_INT, - { .i64 = 0x0100 }, 0x0010, 0x0f00, AV_OPT_FLAG_ENCODING_PARAM }, - { "mpegts_m2ts_mode", "Enable m2ts mode.", - offsetof(MpegTSWrite, m2ts_mode), AV_OPT_TYPE_BOOL, - { .i64 = -1 }, -1, 1, AV_OPT_FLAG_ENCODING_PARAM }, - { "muxrate", NULL, - offsetof(MpegTSWrite, mux_rate), AV_OPT_TYPE_INT, - { .i64 = 1 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + OFFSET(start_pid), AV_OPT_TYPE_INT, { .i64 = 0x0100 }, FIRST_OTHER_PID, LAST_OTHER_PID, ENC }, + { "mpegts_m2ts_mode", "Enable m2ts mode.", OFFSET(m2ts_mode), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, ENC }, + { "muxrate", NULL, OFFSET(mux_rate), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, ENC }, { "pes_payload_size", "Minimum PES packet payload in bytes", - offsetof(MpegTSWrite, pes_payload_size), AV_OPT_TYPE_INT, - { .i64 = DEFAULT_PES_PAYLOAD_SIZE }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, - { "mpegts_flags", "MPEG-TS muxing flags", - offsetof(MpegTSWrite, flags), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, 0, INT_MAX, - AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" }, + 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" }, { "resend_headers", "Reemit PAT/PMT before writing the next packet", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_REEMIT_PAT_PMT }, 0, INT_MAX, - AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_REEMIT_PAT_PMT }, 0, INT_MAX, ENC, "mpegts_flags" }, { "latm", "Use LATM packetization for AAC", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_AAC_LATM }, 0, INT_MAX, - AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_AAC_LATM }, 0, INT_MAX, ENC, "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, - AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_PAT_PMT_AT_FRAMES}, 0, INT_MAX, ENC, "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, - AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_SYSTEM_B }, 0, INT_MAX, ENC, "mpegts_flags" }, { "initial_discontinuity", "Mark initial packets as discontinuous", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_DISCONT }, 0, INT_MAX, - AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" }, - // backward compatibility - { "resend_headers", "Reemit PAT/PMT before writing the next packet", - offsetof(MpegTSWrite, reemit_pat_pmt), AV_OPT_TYPE_INT, - { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, - { "mpegts_copyts", "don't offset dts/pts", - offsetof(MpegTSWrite, copyts), AV_OPT_TYPE_BOOL, - { .i64 = -1 }, -1, 1, AV_OPT_FLAG_ENCODING_PARAM }, - { "tables_version", "set PAT, PMT and SDT version", - offsetof(MpegTSWrite, tables_version), AV_OPT_TYPE_INT, - { .i64 = 0 }, 0, 31, AV_OPT_FLAG_ENCODING_PARAM }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_DISCONT }, 0, INT_MAX, ENC, "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 and SDT 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", - offsetof(MpegTSWrite, omit_video_pes_length), AV_OPT_TYPE_BOOL, - { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, + OFFSET(omit_video_pes_length), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, ENC }, { "pcr_period", "PCR retransmission time in milliseconds", - offsetof(MpegTSWrite, pcr_period), AV_OPT_TYPE_INT, - { .i64 = PCR_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + OFFSET(pcr_period_ms), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, ENC }, { "pat_period", "PAT/PMT retransmission time limit in seconds", - offsetof(MpegTSWrite, pat_period), AV_OPT_TYPE_DOUBLE, - { .dbl = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + OFFSET(pat_period_us), AV_OPT_TYPE_DURATION, { .i64 = PAT_RETRANS_TIME * 1000LL }, 0, INT64_MAX, ENC }, { "sdt_period", "SDT retransmission time limit in seconds", - offsetof(MpegTSWrite, sdt_period), AV_OPT_TYPE_DOUBLE, - { .dbl = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + OFFSET(sdt_period_us), AV_OPT_TYPE_DURATION, { .i64 = SDT_RETRANS_TIME * 1000LL }, 0, INT64_MAX, ENC }, { NULL }, }; diff --git a/libavformat/mpjpeg.c b/libavformat/mpjpeg.c index 80f83c58719..0404e86d7fb 100644 --- a/libavformat/mpjpeg.c +++ b/libavformat/mpjpeg.c @@ -34,7 +34,6 @@ static int mpjpeg_write_header(AVFormatContext *s) { MPJPEGContext *mpj = s->priv_data; avio_printf(s->pb, "--%s\r\n", mpj->boundary_tag); - avio_flush(s->pb); return 0; } @@ -50,11 +49,6 @@ static int mpjpeg_write_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -static int mpjpeg_write_trailer(AVFormatContext *s) -{ - return 0; -} - static const AVOption options[] = { { "boundary_tag", "Boundary tag", offsetof(MPJPEGContext, boundary_tag), AV_OPT_TYPE_STRING, {.str = BOUNDARY_TAG}, .flags = AV_OPT_FLAG_ENCODING_PARAM }, { NULL }, @@ -77,7 +71,6 @@ AVOutputFormat ff_mpjpeg_muxer = { .video_codec = AV_CODEC_ID_MJPEG, .write_header = mpjpeg_write_header, .write_packet = mpjpeg_write_packet, - .write_trailer = mpjpeg_write_trailer, .flags = AVFMT_NOTIMESTAMPS, .priv_class = &mpjpeg_muxer_class, }; diff --git a/libavformat/mpjpegdec.c b/libavformat/mpjpegdec.c index e653b5cc93e..df2880412d1 100644 --- a/libavformat/mpjpegdec.c +++ b/libavformat/mpjpegdec.c @@ -113,20 +113,16 @@ static int mpjpeg_read_close(AVFormatContext *s) static int mpjpeg_read_probe(const AVProbeData *p) { - AVIOContext *pb; + AVIOContext pb; int ret = 0; int size = 0; if (p->buf_size < 2 || p->buf[0] != '-' || p->buf[1] != '-') return 0; - pb = avio_alloc_context(p->buf, p->buf_size, 0, NULL, NULL, NULL, NULL); - if (!pb) - return 0; - - ret = (parse_multipart_header(pb, &size, "--", NULL) >= 0) ? AVPROBE_SCORE_MAX : 0; + ffio_init_context(&pb, p->buf, p->buf_size, 0, NULL, NULL, NULL, NULL); - avio_context_free(&pb); + ret = (parse_multipart_header(&pb, &size, "--", NULL) >= 0) ? AVPROBE_SCORE_MAX : 0; return ret; } @@ -271,7 +267,7 @@ static char* mpjpeg_get_boundary(AVIOContext* pb) while (av_isspace(*start)) start++; - if (!av_stristart(start, "boundary=", &start)) { + if (av_stristart(start, "boundary=", &start)) { end = strchr(start, ';'); if (end) len = end - start - 1; @@ -306,8 +302,9 @@ static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt) boundary = mpjpeg_get_boundary(s->pb); } if (boundary != NULL) { - mpjpeg->boundary = boundary; - mpjpeg->searchstr = av_asprintf( "\r\n%s\r\n", boundary ); + mpjpeg->boundary = av_asprintf("--%s", boundary); + mpjpeg->searchstr = av_asprintf("\r\n--%s\r\n", boundary); + av_freep(&boundary); } else { mpjpeg->boundary = av_strdup("--"); mpjpeg->searchstr = av_strdup("\r\n--"); @@ -334,15 +331,11 @@ static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt) int remaining = 0, len; const int read_chunk = 2048; - av_init_packet(pkt); - pkt->data = NULL; - pkt->size = 0; - pkt->pos = avio_tell(s->pb); - /* we may need to return as much as all we've read back to the buffer */ - ffio_ensure_seekback(s->pb, read_chunk); + pkt->pos = avio_tell(s->pb); - while ((ret = av_append_packet(s->pb, pkt, read_chunk - remaining)) >= 0) { + while ((ret = ffio_ensure_seekback(s->pb, read_chunk - remaining)) >= 0 && /* we may need to return as much as all we've read back to the buffer */ + (ret = av_append_packet(s->pb, pkt, read_chunk - remaining)) >= 0) { /* scan the new data */ char *start; @@ -364,8 +357,6 @@ static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt) /* error or EOF occurred */ if (ret == AVERROR_EOF) { ret = pkt->size > 0 ? pkt->size : AVERROR_EOF; - } else { - av_packet_unref(pkt); } } diff --git a/libavformat/mpl2dec.c b/libavformat/mpl2dec.c index 4ae18390f0b..bea258d9e9a 100644 --- a/libavformat/mpl2dec.c +++ b/libavformat/mpl2dec.c @@ -55,7 +55,7 @@ static int mpl2_probe(const AVProbeData *p) return AVPROBE_SCORE_MAX; } -static int read_ts(char **line, int64_t *pts_start, int *duration) +static int read_ts(char **line, int64_t *pts_start, int64_t *duration) { char c; int len; @@ -69,7 +69,10 @@ static int read_ts(char **line, int64_t *pts_start, int *duration) } if (sscanf(*line, "[%"SCNd64"][%"SCNd64"]%c%n", pts_start, &end, &c, &len) >= 3) { - *duration = end - *pts_start; + if (end < *pts_start || end - (uint64_t)*pts_start > INT64_MAX) { + *duration = -1; + } else + *duration = end - *pts_start; *line += len - 1; return 0; } @@ -97,7 +100,7 @@ static int mpl2_read_header(AVFormatContext *s) const int64_t pos = avio_tell(s->pb); int len = ff_get_line(s->pb, line, sizeof(line)); int64_t pts_start; - int duration; + int64_t duration; if (!len) break; @@ -108,8 +111,10 @@ static int mpl2_read_header(AVFormatContext *s) AVPacket *sub; sub = ff_subtitles_queue_insert(&mpl2->q, p, strlen(p), 0); - if (!sub) + if (!sub) { + ff_subtitles_queue_clean(&mpl2->q); return AVERROR(ENOMEM); + } sub->pos = pos; sub->pts = pts_start; sub->duration = duration; diff --git a/libavformat/mpsubdec.c b/libavformat/mpsubdec.c index 4ff49ba3cf5..e7b83a1d85f 100644 --- a/libavformat/mpsubdec.c +++ b/libavformat/mpsubdec.c @@ -27,6 +27,8 @@ #include "internal.h" #include "subtitles.h" +#define TSBASE 10000000 + typedef struct { FFDemuxSubtitlesQueue q; } MPSubContext; @@ -51,21 +53,55 @@ static int mpsub_probe(const AVProbeData *p) return 0; } +static int parse_line(const char *line, int64_t *value, int64_t *value2) +{ + int vi, p1, p2; + + for (vi = 0; vi < 2; vi++) { + long long intval, fracval; + int n = av_sscanf(line, "%lld%n.%lld%n", &intval, &p1, &fracval, &p2); + if (n <= 0 || intval < INT64_MIN / TSBASE || intval > INT64_MAX / TSBASE) + return AVERROR_INVALIDDATA; + + intval *= TSBASE; + + if (n == 2) { + if (fracval < 0) + return AVERROR_INVALIDDATA; + for (;p2 - p1 < 7 + 1; p1--) + fracval *= 10; + for (;p2 - p1 > 7 + 1; p1++) + fracval /= 10; + if (intval > 0) intval += fracval; + else intval -= fracval; + line += p2; + } else + line += p1; + + *value = intval; + + value = value2; + } + + return 0; +} + static int mpsub_read_header(AVFormatContext *s) { MPSubContext *mpsub = s->priv_data; AVStream *st; AVBPrint buf; - AVRational pts_info = (AVRational){ 100, 1 }; // ts based by default + AVRational pts_info = (AVRational){ TSBASE, 1 }; // ts based by default int res = 0; - int multiplier = 100; - double current_pts = 0; + int64_t current_pts = 0; + int i; + int common_factor = 0; av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); while (!avio_feof(s->pb)) { char line[1024]; - double start, duration; + int64_t start, duration; int fps, len = ff_get_line(s->pb, line, sizeof(line)); if (!len) @@ -75,9 +111,8 @@ static int mpsub_read_header(AVFormatContext *s) if (sscanf(line, "FORMAT=%d", &fps) == 1 && fps > 3 && fps < 100) { /* frame based timing */ - pts_info = (AVRational){ fps, 1 }; - multiplier = 1; - } else if (sscanf(line, "%lf %lf", &start, &duration) == 2) { + pts_info = (AVRational){ TSBASE * fps, 1 }; + } else if (parse_line(line, &start, &duration) >= 0) { AVPacket *sub; const int64_t pos = avio_tell(s->pb); @@ -88,17 +123,41 @@ static int mpsub_read_header(AVFormatContext *s) res = AVERROR(ENOMEM); goto end; } - sub->pts = (int64_t)(current_pts + start*multiplier); - sub->duration = (int)(duration * multiplier); - current_pts += (start + duration) * multiplier; + if ( current_pts < 0 && start < INT64_MIN - current_pts + || current_pts > 0 && start > INT64_MAX - current_pts) { + res = AVERROR_INVALIDDATA; + goto end; + } + sub->pts = current_pts + start; + if (duration < 0 || sub->pts > INT64_MAX - duration) { + res = AVERROR_INVALIDDATA; + goto end; + } + sub->duration = duration; + + common_factor = av_gcd(duration, common_factor); + common_factor = av_gcd(sub->pts, common_factor); + + current_pts = sub->pts + duration; sub->pos = pos; } } } + if (common_factor > 1) { + common_factor = av_gcd(pts_info.num, common_factor); + for (i = 0; i < mpsub->q.nb_subs; i++) { + mpsub->q.subs[i].pts /= common_factor; + mpsub->q.subs[i].duration /= common_factor; + } + pts_info.num /= common_factor; + } + st = avformat_new_stream(s, NULL); - if (!st) - return AVERROR(ENOMEM); + if (!st) { + res = AVERROR(ENOMEM); + goto end; + } avpriv_set_pts_info(st, 64, pts_info.den, pts_info.num); st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; st->codecpar->codec_id = AV_CODEC_ID_TEXT; @@ -106,6 +165,9 @@ static int mpsub_read_header(AVFormatContext *s) ff_subtitles_queue_finalize(s, &mpsub->q); end: + if (res < 0) + ff_subtitles_queue_clean(&mpsub->q); + av_bprint_finalize(&buf, NULL); return res; } diff --git a/libavformat/mtv.c b/libavformat/mtv.c index 728f4a4781c..e731d910771 100644 --- a/libavformat/mtv.c +++ b/libavformat/mtv.c @@ -171,6 +171,8 @@ static int mtv_read_header(AVFormatContext *s) st->codecpar->width = mtv->img_width; st->codecpar->height = mtv->img_height; st->codecpar->extradata = av_strdup("BottomUp"); + if (!st->codecpar->extradata) + return AVERROR(ENOMEM); st->codecpar->extradata_size = 9; // audio - mp3 diff --git a/libavformat/mux.c b/libavformat/mux.c index 8ab5ea8c2bd..44d5e5d1c0b 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -20,29 +20,16 @@ */ #include "avformat.h" -#include "avio_internal.h" #include "internal.h" #include "libavcodec/internal.h" -#include "libavcodec/bytestream.h" #include "libavutil/opt.h" #include "libavutil/dict.h" #include "libavutil/pixdesc.h" #include "libavutil/timestamp.h" -#include "metadata.h" -#include "id3v2.h" #include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/internal.h" #include "libavutil/mathematics.h" -#include "libavutil/parseutils.h" -#include "libavutil/time.h" -#include "riff.h" -#include "audiointerleave.h" -#include "url.h" -#include -#if CONFIG_NETWORK -#include "network.h" -#endif /** * @file @@ -357,6 +344,8 @@ FF_ENABLE_DEPRECATION_WARNINGS if (desc && desc->props & AV_CODEC_PROP_REORDER) st->internal->reorder = 1; + st->internal->is_intra_only = ff_is_intra_only(par->codec_id); + if (of->codec_tag) { if ( par->codec_tag && par->codec_id == AV_CODEC_ID_RAWVIDEO @@ -485,6 +474,14 @@ static void flush_if_needed(AVFormatContext *s) } } +static void deinit_muxer(AVFormatContext *s) +{ + if (s->oformat && s->oformat->deinit && s->internal->initialized) + s->oformat->deinit(s); + s->internal->initialized = + s->internal->streams_initialized = 0; +} + int avformat_init_output(AVFormatContext *s, AVDictionary **options) { int ret = 0; @@ -536,19 +533,12 @@ int avformat_write_header(AVFormatContext *s, AVDictionary **options) return streams_already_initialized; fail: - if (s->oformat->deinit) - s->oformat->deinit(s); + deinit_muxer(s); return ret; } #define AV_PKT_FLAG_UNCODED_FRAME 0x2000 -/* Note: using sizeof(AVFrame) from outside lavu is unsafe in general, but - it is only being used internally to this file as a consistency check. - The value is chosen to be very unlikely to appear on its own and to cause - immediate failure if used anywhere as a real size. */ -#define UNCODED_FRAME_PACKET_SIZE (INT_MIN / 3 * 2 + (int)sizeof(AVFrame)) - #if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX FF_DISABLE_DEPRECATION_WARNINGS @@ -643,7 +633,7 @@ static int compute_muxer_pkt_fields(AVFormatContext *s, AVStream *st, AVPacket * switch (st->codecpar->codec_type) { case AVMEDIA_TYPE_AUDIO: frame_size = (pkt->flags & AV_PKT_FLAG_UNCODED_FRAME) ? - ((AVFrame *)pkt->data)->nb_samples : + (*(AVFrame **)pkt->data)->nb_samples : av_get_audio_frame_duration(st->codec, pkt->size); /* HACK/FIXME, we skip the initial 0 size packets as they are most @@ -663,8 +653,7 @@ FF_ENABLE_DEPRECATION_WARNINGS #endif /** - * Make timestamps non negative, move side data from payload to internal struct, call muxer, and restore - * sidedata. + * Shift timestamps and call muxer; the original pts/dts are not kept. * * FIXME: this function should NEVER get undefined pts/dts beside when the * AVFMT_NOTIMESTAMPS is set. @@ -674,10 +663,6 @@ FF_ENABLE_DEPRECATION_WARNINGS static int write_packet(AVFormatContext *s, AVPacket *pkt) { int ret; - int64_t pts_backup, dts_backup; - - pts_backup = pkt->pts; - dts_backup = pkt->dts; // If the timestamp offsetting below is adjusted, adjust // ff_interleaved_peek similarly. @@ -739,10 +724,9 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt) } if ((pkt->flags & AV_PKT_FLAG_UNCODED_FRAME)) { - AVFrame *frame = (AVFrame *)pkt->data; - av_assert0(pkt->size == UNCODED_FRAME_PACKET_SIZE); - ret = s->oformat->write_uncoded_frame(s, pkt->stream_index, &frame, 0); - av_frame_free(&frame); + AVFrame **frame = (AVFrame **)pkt->data; + av_assert0(pkt->size == sizeof(*frame)); + ret = s->oformat->write_uncoded_frame(s, pkt->stream_index, frame, 0); } else { ret = s->oformat->write_packet(s, pkt); } @@ -753,19 +737,14 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt) ret = s->pb->error; } - if (ret < 0) { - pkt->pts = pts_backup; - pkt->dts = dts_backup; - } + if (ret >= 0) + s->streams[pkt->stream_index]->nb_frames++; return ret; } static int check_packet(AVFormatContext *s, AVPacket *pkt) { - if (!pkt) - return 0; - if (pkt->stream_index < 0 || pkt->stream_index >= s->nb_streams) { av_log(s, AV_LOG_ERROR, "Invalid packet stream index: %d\n", pkt->stream_index); @@ -780,18 +759,11 @@ static int check_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -static int prepare_input_packet(AVFormatContext *s, AVPacket *pkt) +static int prepare_input_packet(AVFormatContext *s, AVStream *st, AVPacket *pkt) { - int ret; - - ret = check_packet(s, pkt); - if (ret < 0) - return ret; - #if !FF_API_COMPUTE_PKT_FIELDS2 || !FF_API_LAVF_AVCTX /* sanitize the timestamps */ if (!(s->oformat->flags & AVFMT_NOTIMESTAMPS)) { - AVStream *st = s->streams[pkt->stream_index]; /* when there is no reordering (so dts is equal to pts), but * only one of them is set, set the other as well */ @@ -828,120 +800,38 @@ static int prepare_input_packet(AVFormatContext *s, AVPacket *pkt) } } #endif + /* update flags */ + if (st->internal->is_intra_only) + pkt->flags |= AV_PKT_FLAG_KEY; return 0; } -static int do_packet_auto_bsf(AVFormatContext *s, AVPacket *pkt) { - AVStream *st = s->streams[pkt->stream_index]; - int i, ret; - - if (!(s->flags & AVFMT_FLAG_AUTO_BSF)) - return 1; - - if (s->oformat->check_bitstream) { - if (!st->internal->bitstream_checked) { - if ((ret = s->oformat->check_bitstream(s, pkt)) < 0) - return ret; - else if (ret == 1) - st->internal->bitstream_checked = 1; - } - } - - for (i = 0; i < st->internal->nb_bsfcs; i++) { - AVBSFContext *ctx = st->internal->bsfcs[i]; - // TODO: when any bitstream filter requires flushing at EOF, we'll need to - // flush each stream's BSF chain on write_trailer. - if ((ret = av_bsf_send_packet(ctx, pkt)) < 0) { - av_log(ctx, AV_LOG_ERROR, - "Failed to send packet to filter %s for stream %d\n", - ctx->filter->name, pkt->stream_index); - return ret; - } - // TODO: when any automatically-added bitstream filter is generating multiple - // output packets for a single input one, we'll need to call this in a loop - // and write each output packet. - if ((ret = av_bsf_receive_packet(ctx, pkt)) < 0) { - if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) - return 0; - av_log(ctx, AV_LOG_ERROR, - "Failed to receive packet from filter %s for stream %d\n", - ctx->filter->name, pkt->stream_index); - if (s->error_recognition & AV_EF_EXPLODE) - return ret; - return 0; - } - } - return 1; -} - -int av_write_frame(AVFormatContext *s, AVPacket *pkt) -{ - int ret; - - ret = prepare_input_packet(s, pkt); - if (ret < 0) - return ret; - - if (!pkt) { - if (s->oformat->flags & AVFMT_ALLOW_FLUSH) { - ret = s->oformat->write_packet(s, NULL); - flush_if_needed(s); - if (ret >= 0 && s->pb && s->pb->error < 0) - ret = s->pb->error; - return ret; - } - return 1; - } - - ret = do_packet_auto_bsf(s, pkt); - if (ret <= 0) - return ret; - -#if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX - ret = compute_muxer_pkt_fields(s, s->streams[pkt->stream_index], pkt); - - if (ret < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS)) - return ret; -#endif - - ret = write_packet(s, pkt); - if (ret >= 0 && s->pb && s->pb->error < 0) - ret = s->pb->error; - - if (ret >= 0) - s->streams[pkt->stream_index]->nb_frames++; - return ret; -} - #define CHUNK_START 0x1000 int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, - int (*compare)(AVFormatContext *, AVPacket *, AVPacket *)) + int (*compare)(AVFormatContext *, const AVPacket *, const AVPacket *)) { int ret; AVPacketList **next_point, *this_pktl; - AVStream *st = s->streams[pkt->stream_index]; - int chunked = s->max_chunk_size || s->max_chunk_duration; + AVStream *st = s->streams[pkt->stream_index]; + int chunked = s->max_chunk_size || s->max_chunk_duration; - this_pktl = av_mallocz(sizeof(AVPacketList)); - if (!this_pktl) + this_pktl = av_malloc(sizeof(AVPacketList)); + if (!this_pktl) { + av_packet_unref(pkt); return AVERROR(ENOMEM); - if ((pkt->flags & AV_PKT_FLAG_UNCODED_FRAME)) { - av_assert0(pkt->size == UNCODED_FRAME_PACKET_SIZE); - av_assert0(((AVFrame *)pkt->data)->buf); - this_pktl->pkt = *pkt; - pkt->buf = NULL; - pkt->side_data = NULL; - pkt->side_data_elems = 0; - } else { - if ((ret = av_packet_ref(&this_pktl->pkt, pkt)) < 0) { - av_free(this_pktl); - return ret; - } } + if ((ret = av_packet_make_refcounted(pkt)) < 0) { + av_free(this_pktl); + av_packet_unref(pkt); + return ret; + } + + av_packet_move_ref(&this_pktl->pkt, pkt); + pkt = &this_pktl->pkt; - if (s->streams[pkt->stream_index]->last_in_packet_buffer) { + if (st->last_in_packet_buffer) { next_point = &(st->last_in_packet_buffer->next); } else { next_point = &s->internal->packet_buffer; @@ -953,8 +843,8 @@ int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, st->interleaver_chunk_duration += pkt->duration; if ( (s->max_chunk_size && st->interleaver_chunk_size > s->max_chunk_size) || (max && st->interleaver_chunk_duration > max)) { - st->interleaver_chunk_size = 0; - this_pktl->pkt.flags |= CHUNK_START; + st->interleaver_chunk_size = 0; + pkt->flags |= CHUNK_START; if (max && st->interleaver_chunk_duration > max) { int64_t syncoffset = (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)*max/2; int64_t syncto = av_rescale(pkt->dts + syncoffset, 1, max)*max - syncoffset; @@ -965,7 +855,7 @@ int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, } } if (*next_point) { - if (chunked && !(this_pktl->pkt.flags & CHUNK_START)) + if (chunked && !(pkt->flags & CHUNK_START)) goto next_non_null; if (compare(s, &s->internal->packet_buffer_end->pkt, pkt)) { @@ -986,16 +876,13 @@ int ff_interleave_add_packet(AVFormatContext *s, AVPacket *pkt, this_pktl->next = *next_point; - s->streams[pkt->stream_index]->last_in_packet_buffer = - *next_point = this_pktl; - - av_packet_unref(pkt); + st->last_in_packet_buffer = *next_point = this_pktl; return 0; } -static int interleave_compare_dts(AVFormatContext *s, AVPacket *next, - AVPacket *pkt) +static int interleave_compare_dts(AVFormatContext *s, const AVPacket *next, + const AVPacket *pkt) { AVStream *st = s->streams[pkt->stream_index]; AVStream *st2 = s->streams[next->stream_index]; @@ -1138,7 +1025,6 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out, return 1; } else { - av_init_packet(out); return 0; } } @@ -1172,7 +1058,7 @@ int ff_interleaved_peek(AVFormatContext *s, int stream, /** * Interleave an AVPacket correctly so it can be muxed. * @param out the interleaved packet will be output here - * @param in the input packet + * @param in the input packet; will always be blank on return if not NULL * @param flush 1 if no further packets are available as input and all * remaining packets should be output * @return 1 if a packet was output, 0 if no packet could be output, @@ -1181,101 +1067,206 @@ int ff_interleaved_peek(AVFormatContext *s, int stream, static int interleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *in, int flush) { if (s->oformat->interleave_packet) { - int ret = s->oformat->interleave_packet(s, out, in, flush); - if (in) - av_packet_unref(in); - return ret; + return s->oformat->interleave_packet(s, out, in, flush); } else return ff_interleave_packet_per_dts(s, out, in, flush); } -int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt) +static int check_bitstream(AVFormatContext *s, AVStream *st, AVPacket *pkt) { - int ret, flush = 0; + int ret; - ret = prepare_input_packet(s, pkt); - if (ret < 0) - goto fail; + if (!(s->flags & AVFMT_FLAG_AUTO_BSF)) + return 1; - if (pkt) { - AVStream *st = s->streams[pkt->stream_index]; + if (s->oformat->check_bitstream) { + if (!st->internal->bitstream_checked) { + if ((ret = s->oformat->check_bitstream(s, pkt)) < 0) + return ret; + else if (ret == 1) + st->internal->bitstream_checked = 1; + } + } - ret = do_packet_auto_bsf(s, pkt); - if (ret == 0) - return 0; - else if (ret < 0) - goto fail; + return 1; +} - if (s->debug & FF_FDEBUG_TS) - av_log(s, AV_LOG_DEBUG, "av_interleaved_write_frame size:%d dts:%s pts:%s\n", - pkt->size, av_ts2str(pkt->dts), av_ts2str(pkt->pts)); +static int interleaved_write_packet(AVFormatContext *s, AVPacket *pkt, int flush) +{ + for (;; ) { + AVPacket opkt; + int ret = interleave_packet(s, &opkt, pkt, flush); + if (ret <= 0) + return ret; + + pkt = NULL; + + ret = write_packet(s, &opkt); + + av_packet_unref(&opkt); + + if (ret < 0) + return ret; + } +} + +static int write_packet_common(AVFormatContext *s, AVStream *st, AVPacket *pkt, int interleaved) +{ + int ret; + + if (s->debug & FF_FDEBUG_TS) + av_log(s, AV_LOG_DEBUG, "%s size:%d dts:%s pts:%s\n", __FUNCTION__, + pkt->size, av_ts2str(pkt->dts), av_ts2str(pkt->pts)); #if FF_API_COMPUTE_PKT_FIELDS2 && FF_API_LAVF_AVCTX - if ((ret = compute_muxer_pkt_fields(s, st, pkt)) < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS)) - goto fail; + if ((ret = compute_muxer_pkt_fields(s, st, pkt)) < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS)) + return ret; #endif - if (pkt->dts == AV_NOPTS_VALUE && !(s->oformat->flags & AVFMT_NOTIMESTAMPS)) { - ret = AVERROR(EINVAL); - goto fail; - } + if (interleaved) { + if (pkt->dts == AV_NOPTS_VALUE && !(s->oformat->flags & AVFMT_NOTIMESTAMPS)) + return AVERROR(EINVAL); + return interleaved_write_packet(s, pkt, 0); } else { - av_log(s, AV_LOG_TRACE, "av_interleaved_write_frame FLUSH\n"); - flush = 1; + return write_packet(s, pkt); } +} - for (;; ) { - AVPacket opkt; - int ret = interleave_packet(s, &opkt, pkt, flush); - if (pkt) { - memset(pkt, 0, sizeof(*pkt)); - av_init_packet(pkt); - pkt = NULL; - } - if (ret <= 0) //FIXME cleanup needed for ret<0 ? +static int write_packets_from_bsfs(AVFormatContext *s, AVStream *st, AVPacket *pkt, int interleaved) +{ + AVBSFContext *bsfc = st->internal->bsfc; + int ret; + + if ((ret = av_bsf_send_packet(bsfc, pkt)) < 0) { + av_log(s, AV_LOG_ERROR, + "Failed to send packet to filter %s for stream %d\n", + bsfc->filter->name, st->index); + return ret; + } + + do { + ret = av_bsf_receive_packet(bsfc, pkt); + if (ret < 0) { + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + return 0; + av_log(s, AV_LOG_ERROR, "Error applying bitstream filters to an output " + "packet for stream #%d: %s\n", st->index, av_err2str(ret)); + if (!(s->error_recognition & AV_EF_EXPLODE) && ret != AVERROR(ENOMEM)) + continue; return ret; + } + av_packet_rescale_ts(pkt, bsfc->time_base_out, st->time_base); + ret = write_packet_common(s, st, pkt, interleaved); + if (ret >= 0 && !interleaved) // a successful write_packet_common already unrefed pkt for interleaved + av_packet_unref(pkt); + } while (ret >= 0); - ret = write_packet(s, &opkt); - if (ret >= 0) - s->streams[opkt.stream_index]->nb_frames++; + return ret; +} - av_packet_unref(&opkt); +static int write_packets_common(AVFormatContext *s, AVPacket *pkt, int interleaved) +{ + AVStream *st; + int ret = check_packet(s, pkt); + if (ret < 0) + return ret; + st = s->streams[pkt->stream_index]; + + ret = prepare_input_packet(s, st, pkt); + if (ret < 0) + return ret; + + ret = check_bitstream(s, st, pkt); + if (ret < 0) + return ret; + if (st->internal->bsfc) { + return write_packets_from_bsfs(s, st, pkt, interleaved); + } else { + return write_packet_common(s, st, pkt, interleaved); + } +} + +int av_write_frame(AVFormatContext *s, AVPacket *in) +{ + AVPacket local_pkt, *pkt = &local_pkt; + int ret; + + if (!in) { + if (s->oformat->flags & AVFMT_ALLOW_FLUSH) { + ret = s->oformat->write_packet(s, NULL); + flush_if_needed(s); + if (ret >= 0 && s->pb && s->pb->error < 0) + ret = s->pb->error; + return ret; + } + return 1; + } + + if (in->flags & AV_PKT_FLAG_UNCODED_FRAME) { + pkt = in; + } else { + /* We don't own in, so we have to make sure not to modify it. + * The following avoids copying in's data unnecessarily. + * Copying side data is unavoidable as a bitstream filter + * may change it, e.g. free it on errors. */ + pkt->buf = NULL; + pkt->data = in->data; + pkt->size = in->size; + ret = av_packet_copy_props(pkt, in); if (ret < 0) return ret; - if(s->pb && s->pb->error) - return s->pb->error; + if (in->buf) { + pkt->buf = av_buffer_ref(in->buf); + if (!pkt->buf) { + ret = AVERROR(ENOMEM); + goto fail; + } + } } + + ret = write_packets_common(s, pkt, 0/*non-interleaved*/); + fail: + // Uncoded frames using the noninterleaved codepath are also freed here av_packet_unref(pkt); return ret; } -int av_write_trailer(AVFormatContext *s) +int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt) { - int ret, i; + int ret; - for (;; ) { - AVPacket pkt; - ret = interleave_packet(s, &pkt, NULL, 1); + if (pkt) { + ret = write_packets_common(s, pkt, 1/*interleaved*/); if (ret < 0) - goto fail; - if (!ret) - break; - - ret = write_packet(s, &pkt); - if (ret >= 0) - s->streams[pkt.stream_index]->nb_frames++; + av_packet_unref(pkt); + return ret; + } else { + av_log(s, AV_LOG_TRACE, "av_interleaved_write_frame FLUSH\n"); + return interleaved_write_packet(s, NULL, 1/*flush*/); + } +} - av_packet_unref(&pkt); +int av_write_trailer(AVFormatContext *s) +{ + int i, ret1, ret = 0; + AVPacket pkt = {0}; + av_init_packet(&pkt); - if (ret < 0) - goto fail; - if(s->pb && s->pb->error) - goto fail; + for (i = 0; i < s->nb_streams; i++) { + if (s->streams[i]->internal->bsfc) { + ret1 = write_packets_from_bsfs(s, s->streams[i], &pkt, 1/*interleaved*/); + if (ret1 < 0) + av_packet_unref(&pkt); + if (ret >= 0) + ret = ret1; + } } + ret1 = interleaved_write_packet(s, NULL, 1); + if (ret >= 0) + ret = ret1; -fail: if (s->oformat->write_trailer) { if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb) avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_TRAILER); @@ -1286,11 +1277,7 @@ int av_write_trailer(AVFormatContext *s) } } - if (s->oformat->deinit) - s->oformat->deinit(s); - - s->internal->initialized = - s->internal->streams_initialized = 0; + deinit_muxer(s); if (s->pb) avio_flush(s->pb); @@ -1323,18 +1310,10 @@ int ff_write_chained(AVFormatContext *dst, int dst_stream, AVPacket *pkt, local_pkt = *pkt; local_pkt.stream_index = dst_stream; - if (pkt->pts != AV_NOPTS_VALUE) - local_pkt.pts = av_rescale_q(pkt->pts, - src->streams[pkt->stream_index]->time_base, - dst->streams[dst_stream]->time_base); - if (pkt->dts != AV_NOPTS_VALUE) - local_pkt.dts = av_rescale_q(pkt->dts, - src->streams[pkt->stream_index]->time_base, - dst->streams[dst_stream]->time_base); - if (pkt->duration) - local_pkt.duration = av_rescale_q(pkt->duration, - src->streams[pkt->stream_index]->time_base, - dst->streams[dst_stream]->time_base); + + av_packet_rescale_ts(&local_pkt, + src->streams[pkt->stream_index]->time_base, + dst->streams[dst_stream]->time_base); if (interleave) ret = av_interleaved_write_frame(dst, &local_pkt); else ret = av_write_frame(dst, &local_pkt); @@ -1344,22 +1323,45 @@ int ff_write_chained(AVFormatContext *dst, int dst_stream, AVPacket *pkt, return ret; } -static int av_write_uncoded_frame_internal(AVFormatContext *s, int stream_index, - AVFrame *frame, int interleaved) +static void uncoded_frame_free(void *unused, uint8_t *data) +{ + av_frame_free((AVFrame **)data); + av_free(data); +} + +static int write_uncoded_frame_internal(AVFormatContext *s, int stream_index, + AVFrame *frame, int interleaved) { AVPacket pkt, *pktp; av_assert0(s->oformat); - if (!s->oformat->write_uncoded_frame) + if (!s->oformat->write_uncoded_frame) { + av_frame_free(&frame); return AVERROR(ENOSYS); + } if (!frame) { pktp = NULL; } else { + size_t bufsize = sizeof(frame) + AV_INPUT_BUFFER_PADDING_SIZE; + AVFrame **framep = av_mallocz(bufsize); + + if (!framep) + goto fail; pktp = &pkt; av_init_packet(&pkt); - pkt.data = (void *)frame; - pkt.size = UNCODED_FRAME_PACKET_SIZE; + pkt.buf = av_buffer_create((void *)framep, bufsize, + uncoded_frame_free, NULL, 0); + if (!pkt.buf) { + av_free(framep); + fail: + av_frame_free(&frame); + return AVERROR(ENOMEM); + } + *framep = frame; + + pkt.data = (void *)framep; + pkt.size = sizeof(frame); pkt.pts = pkt.dts = frame->pts; pkt.duration = frame->pkt_duration; @@ -1374,13 +1376,13 @@ static int av_write_uncoded_frame_internal(AVFormatContext *s, int stream_index, int av_write_uncoded_frame(AVFormatContext *s, int stream_index, AVFrame *frame) { - return av_write_uncoded_frame_internal(s, stream_index, frame, 0); + return write_uncoded_frame_internal(s, stream_index, frame, 0); } int av_interleaved_write_uncoded_frame(AVFormatContext *s, int stream_index, AVFrame *frame) { - return av_write_uncoded_frame_internal(s, stream_index, frame, 1); + return write_uncoded_frame_internal(s, stream_index, frame, 1); } int av_write_uncoded_frame_query(AVFormatContext *s, int stream_index) diff --git a/libavformat/mvdec.c b/libavformat/mvdec.c index 4f75dcacdbb..d5b400213df 100644 --- a/libavformat/mvdec.c +++ b/libavformat/mvdec.c @@ -211,6 +211,8 @@ static int parse_video_var(AVFormatContext *avctx, AVStream *st, } else if (!strcmp(name, "ORIENTATION")) { if (var_read_int(pb, size) == 1101) { st->codecpar->extradata = av_strdup("BottomUp"); + if (!st->codecpar->extradata) + return AVERROR(ENOMEM); st->codecpar->extradata_size = 9; } } else if (!strcmp(name, "Q_SPATIAL") || !strcmp(name, "Q_TEMPORAL")) { @@ -266,7 +268,7 @@ static void read_index(AVIOContext *pb, AVStream *st) avio_skip(pb, 8); av_add_index_entry(st, pos, timestamp, size, 0, AVINDEX_KEYFRAME); if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - timestamp += size / (st->codecpar->channels * 2); + timestamp += size / (st->codecpar->channels * 2LL); } else { timestamp++; } @@ -353,7 +355,7 @@ static int mv_read_header(AVFormatContext *avctx) avio_skip(pb, 8); av_add_index_entry(ast, pos, timestamp, asize, 0, AVINDEX_KEYFRAME); av_add_index_entry(vst, pos + asize, i, vsize, 0, AVINDEX_KEYFRAME); - timestamp += asize / (ast->codecpar->channels * 2); + timestamp += asize / (ast->codecpar->channels * 2LL); } } else if (!version && avio_rb16(pb) == 3) { avio_skip(pb, 4); @@ -361,6 +363,12 @@ static int mv_read_header(AVFormatContext *avctx) if ((ret = read_table(avctx, NULL, parse_global_var)) < 0) return ret; + if (mv->nb_audio_tracks < 0 || mv->nb_video_tracks < 0 || + (mv->nb_audio_tracks == 0 && mv->nb_video_tracks == 0)) { + av_log(avctx, AV_LOG_ERROR, "Stream count is invalid.\n"); + return AVERROR_INVALIDDATA; + } + if (mv->nb_audio_tracks > 1) { avpriv_request_sample(avctx, "Multiple audio streams support"); return AVERROR_PATCHWELCOME; diff --git a/libavformat/mvi.c b/libavformat/mvi.c index 9f90faf56b8..ff5c08bf511 100644 --- a/libavformat/mvi.c +++ b/libavformat/mvi.c @@ -45,6 +45,7 @@ static int read_header(AVFormatContext *s) AVIOContext *pb = s->pb; AVStream *ast, *vst; unsigned int version, frames_count, msecs_per_frame, player_version; + int ret; ast = avformat_new_stream(s, NULL); if (!ast) @@ -54,8 +55,8 @@ static int read_header(AVFormatContext *s) if (!vst) return AVERROR(ENOMEM); - if (ff_alloc_extradata(vst->codecpar, 2)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(vst->codecpar, 2)) < 0) + return ret; version = avio_r8(pb); vst->codecpar->extradata[0] = avio_r8(pb); diff --git a/libavformat/mxf.c b/libavformat/mxf.c index 451cbcfb2c0..7d154ca9d3e 100644 --- a/libavformat/mxf.c +++ b/libavformat/mxf.c @@ -131,61 +131,38 @@ int ff_mxf_decode_pixel_layout(const char pixel_layout[16], enum AVPixelFormat * return -1; } -static const MXFSamplesPerFrame mxf_spf[] = { - { { 1001, 24000 }, { 2002, 0, 0, 0, 0, 0 } }, // FILM 23.976 - { { 1, 24}, { 2000, 0, 0, 0, 0, 0 } }, // FILM 24 - { { 1001, 30000 }, { 1602, 1601, 1602, 1601, 1602, 0 } }, // NTSC 29.97 - { { 1001, 60000 }, { 801, 801, 800, 801, 801, 0 } }, // NTSC 59.94 - { { 1, 25 }, { 1920, 0, 0, 0, 0, 0 } }, // PAL 25 - { { 1, 50 }, { 960, 0, 0, 0, 0, 0 } }, // PAL 50 - { { 1, 60 }, { 800, 0, 0, 0, 0, 0 } }, -}; - -static const AVRational mxf_time_base[] = { - { 1001, 24000 }, - { 1, 24}, - { 1001, 30000 }, - { 1001, 60000 }, - { 1, 25 }, - { 1, 50 }, - { 1, 60 }, - { 0, 0} -}; - -const MXFSamplesPerFrame *ff_mxf_get_samples_per_frame(AVFormatContext *s, - AVRational time_base) -{ - int idx = av_find_nearest_q_idx(time_base, mxf_time_base); - AVRational diff = av_sub_q(time_base, mxf_time_base[idx]); - - diff.num = FFABS(diff.num); - - if (av_cmp_q(diff, (AVRational){1, 1000}) >= 0) - return NULL; - - if (av_cmp_q(time_base, mxf_time_base[idx])) - av_log(s, AV_LOG_WARNING, - "%d/%d input time base matched %d/%d container time base\n", - time_base.num, time_base.den, - mxf_spf[idx].time_base.num, - mxf_spf[idx].time_base.den); - - return &mxf_spf[idx]; -} - -static const int mxf_content_package_rates[] = { - 3, 2, 7, 13, 4, 10, 12, +/** + * See SMPTE 326M-2000 Section 7.2 Content package rate + * MXFContentPackageRate->rate is bits b5..b0. + */ +static const MXFContentPackageRate mxf_content_package_rates[] = { + { 2, { 1, 24 } }, + { 3, { 1001, 24000 } }, + { 4, { 1, 25 } }, + { 6, { 1, 30 } }, + { 7, { 1001, 30000 } }, + { 8, { 1 , 48 } }, + { 9, { 1001, 48000 } }, + { 10, { 1, 50 } }, + { 12, { 1, 60 } }, + { 13, { 1001, 60000 } }, + { 14, { 1, 72 } }, + { 15, { 1001, 72000 } }, + { 16, { 1, 75 } }, + { 18, { 1, 90 } }, + { 19, { 1001, 90000 } }, + { 20, { 1, 96 } }, + { 21, { 1001, 96000 } }, + { 22, { 1, 100 } }, + { 24, { 1, 120 } }, + { 25, { 1001, 120000} }, + {0} }; int ff_mxf_get_content_package_rate(AVRational time_base) { - int idx = av_find_nearest_q_idx(time_base, mxf_time_base); - AVRational diff = av_sub_q(time_base, mxf_time_base[idx]); - - diff.num = FFABS(diff.num); - - if (av_cmp_q(diff, (AVRational){1, 1000}) >= 0) - return -1; - - return mxf_content_package_rates[idx]; + for (int i = 0; mxf_content_package_rates[i].rate; i++) + if (!av_cmp_q(time_base, mxf_content_package_rates[i].tb)) + return mxf_content_package_rates[i].rate; + return 0; } diff --git a/libavformat/mxf.h b/libavformat/mxf.h index f32124f7722..f2fff2781e8 100644 --- a/libavformat/mxf.h +++ b/libavformat/mxf.h @@ -59,6 +59,11 @@ enum MXFFrameLayout { SegmentedFrame, }; +typedef struct MXFContentPackageRate { + int rate; + AVRational tb; +} MXFContentPackageRate; + typedef struct KLVPacket { UID key; int64_t offset; @@ -82,18 +87,12 @@ typedef struct MXFCodecUL { MXFWrappingIndicatorType wrapping_indicator_type; } MXFCodecUL; -typedef struct { - struct AVRational time_base; - int samples_per_frame[6]; -} MXFSamplesPerFrame; - extern const MXFCodecUL ff_mxf_data_definition_uls[]; extern const MXFCodecUL ff_mxf_codec_uls[]; extern const MXFCodecUL ff_mxf_pixel_format_uls[]; extern const MXFCodecUL ff_mxf_codec_tag_uls[]; int ff_mxf_decode_pixel_layout(const char pixel_layout[16], enum AVPixelFormat *pix_fmt); -const MXFSamplesPerFrame *ff_mxf_get_samples_per_frame(AVFormatContext *s, AVRational time_base); int ff_mxf_get_content_package_rate(AVRational time_base); diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 397f820b3f8..90546d42b3c 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -131,7 +131,7 @@ typedef struct MXFSequence { uint8_t origin; } MXFSequence; -typedef struct MXFTrack { +typedef struct MXFTimecodeComponent { UID uid; enum MXFMetadataSetType type; int drop_frame; @@ -362,8 +362,9 @@ static void mxf_free_metadataset(MXFMetadataSet **ctx, int freectx) default: break; } - if (freectx) - av_freep(ctx); + if (freectx) { + av_freep(ctx); + } } static int64_t klv_decode_ber_length(AVIOContext *pb) @@ -866,6 +867,7 @@ static inline int mxf_read_utf16_string(AVIOContext *pb, int size, char** str, i return AVERROR(EINVAL); buf_size = size + size / 2 + 1; + av_free(*str); *str = av_malloc(buf_size); if (!*str) return AVERROR(ENOMEM); @@ -2017,7 +2019,7 @@ static MXFStructuralComponent* mxf_resolve_sourceclip(MXFContext *mxf, UID *stro static int mxf_parse_package_comments(MXFContext *mxf, AVDictionary **pm, MXFPackage *package) { MXFTaggedValue *tag; - int size, i; + int i; char *key = NULL; for (i = 0; i < package->comment_count; i++) { @@ -2025,12 +2027,10 @@ static int mxf_parse_package_comments(MXFContext *mxf, AVDictionary **pm, MXFPac if (!tag || !tag->name || !tag->value) continue; - size = strlen(tag->name) + 8 + 1; - key = av_mallocz(size); + key = av_asprintf("comment_%s", tag->name); if (!key) return AVERROR(ENOMEM); - snprintf(key, size, "comment_%s", tag->name); av_dict_set(pm, key, tag->value, AV_DICT_DONT_STRDUP_KEY); } return 0; @@ -3169,6 +3169,7 @@ static int mxf_read_header(AVFormatContext *s) if (!mxf_read_sync(s->pb, mxf_header_partition_pack_key, 14)) { av_log(s, AV_LOG_ERROR, "could not find header partition pack key\n"); + //goto fail should not be needed as no metadata sets will have been parsed yet return AVERROR_INVALIDDATA; } avio_seek(s->pb, -14, SEEK_CUR); @@ -3199,7 +3200,8 @@ static int mxf_read_header(AVFormatContext *s) if (!mxf->current_partition) { av_log(mxf->fc, AV_LOG_ERROR, "found essence prior to first PartitionPack\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail; } if (!mxf->current_partition->first_essence_klv.offset) @@ -3305,20 +3307,17 @@ static int mxf_get_next_track_edit_unit(MXFContext *mxf, MXFTrack *track, int64_ static int64_t mxf_compute_sample_count(MXFContext *mxf, AVStream *st, int64_t edit_unit) { - int i, total = 0, size = 0; MXFTrack *track = st->priv_data; AVRational time_base = av_inv_q(track->edit_rate); AVRational sample_rate = av_inv_q(st->time_base); - const MXFSamplesPerFrame *spf = NULL; - int64_t sample_count; // For non-audio sample_count equals current edit unit if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) return edit_unit; - if ((sample_rate.num / sample_rate.den) == 48000) - spf = ff_mxf_get_samples_per_frame(mxf->fc, time_base); - if (!spf) { + if ((sample_rate.num / sample_rate.den) == 48000) { + return av_rescale_q(edit_unit, sample_rate, track->edit_rate); + } else { int remainder = (sample_rate.num * time_base.num) % (time_base.den * sample_rate.den); if (remainder) @@ -3329,20 +3328,6 @@ static int64_t mxf_compute_sample_count(MXFContext *mxf, AVStream *st, sample_rate.num, sample_rate.den); return av_rescale_q(edit_unit, sample_rate, track->edit_rate); } - - while (spf->samples_per_frame[size]) { - total += spf->samples_per_frame[size]; - size++; - } - - av_assert2(size); - - sample_count = (edit_unit / size) * (uint64_t)total; - for (i = 0; i < edit_unit % size; i++) { - sample_count += spf->samples_per_frame[i]; - } - - return sample_count; } /** @@ -3581,6 +3566,7 @@ static int mxf_read_close(AVFormatContext *s) for (i = 0; i < mxf->metadata_sets_count; i++) { mxf_free_metadataset(mxf->metadata_sets + i, 1); } + mxf->metadata_sets_count = 0; av_freep(&mxf->partitions); av_freep(&mxf->metadata_sets); av_freep(&mxf->aesc); diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index 2e54320cf0e..5a3a609bf60 100644 --- a/libavformat/mxfenc.c +++ b/libavformat/mxfenc.c @@ -52,7 +52,6 @@ #include "libavcodec/h264_ps.h" #include "libavcodec/golomb.h" #include "libavcodec/internal.h" -#include "audiointerleave.h" #include "avformat.h" #include "avio_internal.h" #include "internal.h" @@ -72,17 +71,18 @@ typedef struct MXFLocalTagPair { } MXFLocalTagPair; typedef struct MXFIndexEntry { - uint8_t flags; uint64_t offset; unsigned slice_offset; ///< offset of audio slice uint16_t temporal_ref; + uint8_t flags; } MXFIndexEntry; typedef struct MXFStreamContext { - AudioInterleaveContext aic; + int64_t pkt_cnt; ///< pkt counter for muxed packets UID track_essence_element_key; int index; ///< index in mxf_essence_container_uls table const UID *codec_ul; + const UID *container_ul; int order; ///< interleaving order if dts are equal int interlaced; ///< whether picture is interlaced int field_dominance; ///< tff=1, bff=2 @@ -122,29 +122,9 @@ enum ULIndex { INDEX_MPEG2 = 0, INDEX_AES3, INDEX_WAV, - INDEX_D10_625_50_50_VIDEO, - INDEX_D10_625_50_50_AUDIO, - INDEX_D10_525_60_50_VIDEO, - INDEX_D10_525_60_50_AUDIO, - INDEX_D10_625_50_40_VIDEO, - INDEX_D10_625_50_40_AUDIO, - INDEX_D10_525_60_40_VIDEO, - INDEX_D10_525_60_40_AUDIO, - INDEX_D10_625_50_30_VIDEO, - INDEX_D10_625_50_30_AUDIO, - INDEX_D10_525_60_30_VIDEO, - INDEX_D10_525_60_30_AUDIO, + INDEX_D10_VIDEO, + INDEX_D10_AUDIO, INDEX_DV, - INDEX_DV25_525_60, - INDEX_DV25_625_50, - INDEX_DV25_525_60_IEC, - INDEX_DV25_625_50_IEC, - INDEX_DV50_525_60, - INDEX_DV50_625_50, - INDEX_DV100_1080_60, - INDEX_DV100_1080_50, - INDEX_DV100_720_60, - INDEX_DV100_720_50, INDEX_DNXHD, INDEX_JPEG2000, INDEX_H264, @@ -188,118 +168,21 @@ static const MXFContainerEssenceEntry mxf_essence_container_uls[] = { { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x16,0x01,0x01,0x00 }, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, mxf_write_wav_desc }, - // D-10 625/50 PAL 50mb/s + // D-10 Video { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x01,0x01 }, { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x05,0x01,0x01,0x00 }, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x01 }, mxf_write_cdci_desc }, + // D-10 Audio { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x01,0x01 }, { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x06,0x01,0x10,0x00 }, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, mxf_write_generic_sound_desc }, - // D-10 525/60 NTSC 50mb/s - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x02,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x05,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x02 }, - mxf_write_cdci_desc }, - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x02,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x06,0x01,0x10,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, - mxf_write_generic_sound_desc }, - // D-10 625/50 PAL 40mb/s - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x03,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x05,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x03 }, - mxf_write_cdci_desc }, - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x03,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x06,0x01,0x10,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, - mxf_write_generic_sound_desc }, - // D-10 525/60 NTSC 40mb/s - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x04,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x05,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x04 }, - mxf_write_cdci_desc }, - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x04,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x06,0x01,0x10,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, - mxf_write_generic_sound_desc }, - // D-10 625/50 PAL 30mb/s - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x05,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x05,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x05 }, - mxf_write_cdci_desc }, - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x05,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x06,0x01,0x10,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, - mxf_write_generic_sound_desc }, - // D-10 525/60 NTSC 30mb/s - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x06,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x05,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x06 }, - mxf_write_cdci_desc }, - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x06,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x06,0x01,0x10,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 }, - mxf_write_generic_sound_desc }, - // DV Unknown + // DV { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x7F,0x01 }, { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x00,0x00,0x00 }, mxf_write_cdci_desc }, - - // DV25 525/60 - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x40,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x01,0x00 }, - mxf_write_cdci_desc }, - // DV25 625/50 - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x41,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x02,0x00 }, - mxf_write_cdci_desc }, - - // IEC DV25 525/60 - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x01,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x01,0x00 }, - mxf_write_cdci_desc }, - // IEC DV25 625/50 - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x02,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x01,0x02,0x00 }, - mxf_write_cdci_desc }, - - // DV50 525/60 - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x50,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x03,0x00 }, - mxf_write_cdci_desc }, - // DV50 625/50 - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x51,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x04,0x00 }, - mxf_write_cdci_desc }, - // DV100 1080/60 - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x60,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x05,0x00 }, - mxf_write_cdci_desc }, - // DV100 1080/50 - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x61,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x06,0x00 }, - mxf_write_cdci_desc }, - // DV100 720/60 - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x62,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x07,0x00 }, - mxf_write_cdci_desc }, - // DV100 720/50 - { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x63,0x01 }, - { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x18,0x01,0x01,0x00 }, - { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x08,0x00 }, - mxf_write_cdci_desc }, // DNxHD { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x11,0x01,0x00 }, { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 }, @@ -331,6 +214,24 @@ static const MXFContainerEssenceEntry mxf_essence_container_uls[] = { NULL }, }; +static const UID mxf_d10_codec_uls[] = { + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x01 }, // D-10 625/50 PAL 50mb/s + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x02 }, // D-10 525/50 NTSC 50mb/s + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x03 }, // D-10 625/50 PAL 40mb/s + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x04 }, // D-10 525/50 NTSC 40mb/s + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x05 }, // D-10 625/50 PAL 30mb/s + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x01,0x02,0x01,0x06 }, // D-10 525/50 NTSC 30mb/s +}; + +static const UID mxf_d10_container_uls[] = { + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x01,0x01 }, // D-10 625/50 PAL 50mb/s + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x02,0x01 }, // D-10 525/50 NTSC 50mb/s + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x03,0x01 }, // D-10 625/50 PAL 40mb/s + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x04,0x01 }, // D-10 525/50 NTSC 40mb/s + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x05,0x01 }, // D-10 625/50 PAL 30mb/s + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x06,0x01 }, // D-10 525/50 NTSC 30mb/s +}; + typedef struct MXFContext { AVClass *av_class; int64_t footer_partition_offset; @@ -553,15 +454,14 @@ static int klv_ber_length(uint64_t len) static int klv_encode_ber_length(AVIOContext *pb, uint64_t len) { // Determine the best BER size - int size; - if (len < 128) { + int size = klv_ber_length(len); + if (size == 1) { //short form avio_w8(pb, len); return 1; } - size = (av_log2(len) >> 3) + 1; - + size --; // long form avio_w8(pb, 0x80 + size); while(size) { @@ -653,16 +553,6 @@ static void mxf_write_metadata_key(AVIOContext *pb, unsigned int value) avio_wb24(pb, value); } -static void mxf_free(AVFormatContext *s) -{ - int i; - - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - av_freep(&st->priv_data); - } -} - static const MXFCodecUL *mxf_get_data_definition_ul(int type) { const MXFCodecUL *uls = ff_mxf_data_definition_uls; @@ -691,7 +581,7 @@ static void mxf_write_essence_container_refs(AVFormatContext *s) // check first track of essence container type and only write it once if (sc->track_essence_element_key[15] != 0) continue; - avio_write(pb, mxf_essence_container_uls[sc->index].container_ul, 16); + avio_write(pb, *sc->container_ul, 16); if (c->essence_container_count == 1) break; } @@ -1101,7 +991,7 @@ static void mxf_write_multi_descriptor(AVFormatContext *s) ul = multiple_desc_ul; else { MXFStreamContext *sc = s->streams[0]->priv_data; - ul = mxf_essence_container_uls[sc->index].container_ul; + ul = *sc->container_ul; } avio_write(pb, ul, 16); @@ -1145,7 +1035,7 @@ static int64_t mxf_write_generic_desc(AVFormatContext *s, AVStream *st, const UI } mxf_write_local_tag(pb, 16, 0x3004); - avio_write(pb, mxf_essence_container_uls[sc->index].container_ul, 16); + avio_write(pb, *sc->container_ul, 16); return pos; } @@ -1191,7 +1081,7 @@ 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 = (st->codecpar->width +15)/16*16; + int stored_width = 0; int stored_height = (st->codecpar->height+15)/16*16; int display_height; int f1, f2; @@ -1200,6 +1090,15 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID get_trc(transfer_ul, st->codecpar->color_trc); + if (st->codecpar->codec_id == AV_CODEC_ID_DVVIDEO) { + if (st->codecpar->height == 1080) + stored_width = 1920; + else if (st->codecpar->height == 720) + stored_width = 1280; + } + if (!stored_width) + stored_width = (st->codecpar->width+15)/16*16; + mxf_write_local_tag(pb, 4, 0x3203); avio_wb32(pb, stored_width); @@ -1222,7 +1121,7 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID //Sampled width mxf_write_local_tag(pb, 4, 0x3205); - avio_wb32(pb, st->codecpar->width); + avio_wb32(pb, stored_width); //Samples height mxf_write_local_tag(pb, 4, 0x3204); @@ -1237,7 +1136,7 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID avio_wb32(pb, 0); mxf_write_local_tag(pb, 4, 0x3209); - avio_wb32(pb, st->codecpar->width); + avio_wb32(pb, stored_width); if (st->codecpar->height == 608) // PAL + VBI display_height = 576; @@ -1847,7 +1746,7 @@ static void mxf_write_index_table_segment(AVFormatContext *s) avio_wb32(pb, KAG_SIZE); // system item size including klv fill } else { // audio or data track if (!audio_frame_size) { - audio_frame_size = sc->aic.samples[0]*sc->aic.sample_size; + audio_frame_size = sc->frame_size; audio_frame_size += klv_fill_size(audio_frame_size); } avio_w8(pb, 1); @@ -2026,7 +1925,7 @@ static int mxf_write_partition(AVFormatContext *s, int bodysid, } if(key) - avio_flush(pb); + avio_write_marker(pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); return 0; } @@ -2148,13 +2047,38 @@ static int mxf_parse_dnxhd_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt return 1; } +static const struct { + const UID container_ul; + const UID codec_ul; +} mxf_dv_uls[] = { + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x01,0x01 }, // IEC DV25 525/60 + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x01,0x01,0x00 } }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x02,0x01 }, // IEC DV25 626/50 + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x01,0x02,0x00 } }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x40,0x01 }, // DV25 525/60 + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x01,0x00 }, }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x41,0x01 }, // DV25 625/50 + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x02,0x00 }, }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x50,0x01 }, // DV50 525/60 + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x03,0x00 }, }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x51,0x01 }, // DV50 625/50 + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x04,0x00 }, }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x60,0x01 }, // DV100 1080/60 + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x05,0x00 }, }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x61,0x01 }, // DV100 1080/50 + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x06,0x00 }, }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x62,0x01 }, // DV100 720/60 + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x07,0x00 }, }, + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x63,0x01 }, // DV100 720/50 + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x01,0x02,0x02,0x02,0x02,0x08,0x00 }, }, +}; + static int mxf_parse_dv_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt) { MXFContext *mxf = s->priv_data; MXFStreamContext *sc = st->priv_data; uint8_t *vs_pack, *vsc_pack; - int ul_index, stype, pal; - const AVDVProfile *profile; + int apt, ul_index, stype, pal; if (mxf->header_written) return 1; @@ -2163,8 +2087,7 @@ static int mxf_parse_dv_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt) if (pkt->size < 120000) return -1; - profile = av_dv_frame_profile(NULL, pkt->data, pkt->size); - + apt = pkt->data[4] & 0x7; vs_pack = pkt->data + 80*5 + 48; vsc_pack = pkt->data + 80*5 + 53; stype = vs_pack[3] & 0x1f; @@ -2183,28 +2106,29 @@ static int mxf_parse_dv_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt) switch (stype) { case 0x18: // DV100 720p - ul_index = INDEX_DV100_720_50 + pal; + ul_index = 8+pal; if (sc->interlaced) { av_log(s, AV_LOG_ERROR, "source marked as interlaced but codec profile is progressive\n"); sc->interlaced = 0; } break; case 0x14: // DV100 1080i - ul_index = INDEX_DV100_1080_50 + pal; + ul_index = 6+pal; break; case 0x04: // DV50 - ul_index = INDEX_DV50_525_60 + pal; + ul_index = 4+pal; break; default: // DV25 - if (profile && profile->pix_fmt == AV_PIX_FMT_YUV420P && pal) { - ul_index = INDEX_DV25_525_60_IEC + pal; - break; + if (!apt) { // IEC + ul_index = 0+pal; + } else { + ul_index = 2+pal; } - ul_index = INDEX_DV25_525_60 + pal; } - sc->index = ul_index; - sc->codec_ul = &mxf_essence_container_uls[sc->index].codec_ul; + sc->container_ul = &mxf_dv_uls[ul_index].container_ul; + sc->codec_ul = &mxf_dv_uls[ul_index].codec_ul; + sc->frame_size = pkt->size; return 1; @@ -2247,14 +2171,14 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st, { MXFContext *mxf = s->priv_data; MXFStreamContext *sc = st->priv_data; - H264SequenceParameterSet *sps = NULL; + H264SPS seq, *const sps = &seq; GetBitContext gb; const uint8_t *buf = pkt->data; const uint8_t *buf_end = pkt->data + pkt->size; const uint8_t *nal_end; uint32_t state = -1; int extra_size = 512; // support AVC Intra files without SPS/PPS header - int i, frame_size, slice_type, intra_only = 0; + int i, frame_size, slice_type, has_sps = 0, intra_only = 0, ret; for (;;) { buf = avpriv_find_start_code(buf, buf_end, &state); @@ -2269,11 +2193,12 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st, break; nal_end = ff_avc_find_startcode(buf, buf_end); - sps = ff_avc_decode_sps(buf, nal_end - buf); - if (!sps) { + ret = ff_avc_decode_sps(sps, buf, nal_end - buf); + if (ret < 0) { av_log(s, AV_LOG_ERROR, "error parsing sps\n"); return 0; } + has_sps = 1; sc->aspect_ratio.num = st->codecpar->width * sps->sar.num; sc->aspect_ratio.den = st->codecpar->height * sps->sar.den; @@ -2319,7 +2244,7 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st, if (mxf->header_written) return 1; - if (!sps) + if (!has_sps) sc->interlaced = st->codecpar->field_order != AV_FIELD_PROGRESSIVE ? 1 : 0; sc->codec_ul = NULL; frame_size = pkt->size + extra_size; @@ -2336,7 +2261,7 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st, if (sc->interlaced) sc->field_dominance = 1; // top field first is mandatory for AVC Intra break; - } else if (sps && mxf_h264_codec_uls[i].frame_size == 0 && + } else if (has_sps && mxf_h264_codec_uls[i].frame_size == 0 && mxf_h264_codec_uls[i].profile == sps->profile_idc && (mxf_h264_codec_uls[i].intra_only < 0 || mxf_h264_codec_uls[i].intra_only == intra_only)) { @@ -2347,8 +2272,6 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st, } } - av_free(sps); - if (!sc->codec_ul) { av_log(s, AV_LOG_ERROR, "h264 profile not supported\n"); return 0; @@ -2482,17 +2405,28 @@ static void mxf_gen_umid(AVFormatContext *s) mxf->instance_number = seed & 0xFFFFFF; } -static int mxf_init_timecode(AVFormatContext *s, AVStream *st, AVRational rate) +static int mxf_init_timecode(AVFormatContext *s, AVStream *st, AVRational tbc) { MXFContext *mxf = s->priv_data; AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0); + + if (!ff_mxf_get_content_package_rate(tbc)) { + if (s->strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL) { + av_log(s, AV_LOG_ERROR, "Unsupported frame rate %d/%d. Set -strict option to 'unofficial' or lower in order to allow it!\n", tbc.den, tbc.num); + return AVERROR(EINVAL); + } else { + av_log(s, AV_LOG_WARNING, "Unofficial frame rate %d/%d.\n", tbc.den, tbc.num); + } + } + + mxf->timecode_base = (tbc.den + tbc.num/2) / tbc.num; if (!tcr) tcr = av_dict_get(st->metadata, "timecode", NULL, 0); if (tcr) - return av_timecode_init_from_string(&mxf->tc, rate, tcr->value, s); + return av_timecode_init_from_string(&mxf->tc, av_inv_q(tbc), tcr->value, s); else - return av_timecode_init(&mxf->tc, rate, 0, 0, s); + return av_timecode_init(&mxf->tc, av_inv_q(tbc), 0, 0, s); } static int mxf_write_header(AVFormatContext *s) @@ -2500,7 +2434,6 @@ static int mxf_write_header(AVFormatContext *s) MXFContext *mxf = s->priv_data; int i, ret; uint8_t present[FF_ARRAY_ELEMS(mxf_essence_container_uls)] = {0}; - const MXFSamplesPerFrame *spf = NULL; int64_t timestamp = 0; if (!s->nb_streams) @@ -2520,6 +2453,7 @@ static int mxf_write_header(AVFormatContext *s) if (!sc) return AVERROR(ENOMEM); st->priv_data = sc; + sc->index = -1; if (((i == 0) ^ (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) && s->oformat != &ff_mxf_opatom_muxer) { av_log(s, AV_LOG_ERROR, "there must be exactly one video stream and it must be the first one\n"); @@ -2529,7 +2463,7 @@ static int mxf_write_header(AVFormatContext *s) if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(st->codecpar->format); // TODO: should be avg_frame_rate - AVRational rate, tbc = st->time_base; + AVRational tbc = st->time_base; // Default component depth to 8 sc->component_depth = 8; sc->h_chroma_sub_sample = 2; @@ -2553,18 +2487,10 @@ static int mxf_write_header(AVFormatContext *s) case AVCHROMA_LOC_CENTER: sc->color_siting = 3; break; } - mxf->timecode_base = (tbc.den + tbc.num/2) / tbc.num; - spf = ff_mxf_get_samples_per_frame(s, tbc); - if (!spf) { - av_log(s, AV_LOG_ERROR, "Unsupported video frame rate %d/%d\n", - tbc.den, tbc.num); - return AVERROR(EINVAL); - } mxf->content_package_rate = ff_mxf_get_content_package_rate(tbc); - mxf->time_base = spf->time_base; - rate = av_inv_q(mxf->time_base); + mxf->time_base = tbc; avpriv_set_pts_info(st, 64, mxf->time_base.num, mxf->time_base.den); - if((ret = mxf_init_timecode(s, st, rate)) < 0) + if((ret = mxf_init_timecode(s, st, tbc)) < 0) return ret; if (st->codecpar->codec_id == AV_CODEC_ID_MPEG2VIDEO) { @@ -2579,25 +2505,29 @@ static int mxf_write_header(AVFormatContext *s) mxf->cbr_index = 1; if (s->oformat == &ff_mxf_d10_muxer) { + int ntsc = mxf->time_base.den != 25; + int ul_index; + if (st->codecpar->codec_id != AV_CODEC_ID_MPEG2VIDEO) { av_log(s, AV_LOG_ERROR, "error MXF D-10 only support MPEG-2 Video\n"); return AVERROR(EINVAL); } if ((sc->video_bit_rate == 50000000) && (mxf->time_base.den == 25)) { - sc->index = INDEX_D10_625_50_50_VIDEO; - } else if ((sc->video_bit_rate == 49999840 || sc->video_bit_rate == 50000000) && (mxf->time_base.den != 25)) { - sc->index = INDEX_D10_525_60_50_VIDEO; + ul_index = 0; + } else if ((sc->video_bit_rate == 49999840 || sc->video_bit_rate == 50000000) && ntsc) { + ul_index = 1; } else if (sc->video_bit_rate == 40000000) { - if (mxf->time_base.den == 25) sc->index = INDEX_D10_625_50_40_VIDEO; - else sc->index = INDEX_D10_525_60_40_VIDEO; + ul_index = 2+ntsc; } else if (sc->video_bit_rate == 30000000) { - if (mxf->time_base.den == 25) sc->index = INDEX_D10_625_50_30_VIDEO; - else sc->index = INDEX_D10_525_60_30_VIDEO; + ul_index = 4+ntsc; } else { av_log(s, AV_LOG_ERROR, "error MXF D-10 only support 30/40/50 mbit/s\n"); return -1; } + sc->codec_ul = &mxf_d10_codec_uls[ul_index]; + sc->container_ul = &mxf_d10_container_uls[ul_index]; + sc->index = INDEX_D10_VIDEO; sc->signal_standard = 1; sc->color_siting = 0; sc->frame_size = (int64_t)sc->video_bit_rate * @@ -2606,6 +2536,7 @@ static int mxf_write_header(AVFormatContext *s) if (mxf->signal_standard >= 0) sc->signal_standard = mxf->signal_standard; } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + char bsf_arg[32]; if (st->codecpar->sample_rate != 48000) { av_log(s, AV_LOG_ERROR, "only 48khz is implemented\n"); return -1; @@ -2620,8 +2551,9 @@ static int mxf_write_header(AVFormatContext *s) st->codecpar->codec_id != AV_CODEC_ID_PCM_S24LE) { av_log(s, AV_LOG_ERROR, "MXF D-10 only support 16 or 24 bits le audio\n"); } - sc->index = ((MXFStreamContext*)s->streams[0]->priv_data)->index + 1; - sc->frame_size = 4 + 8 * spf[0].samples_per_frame[0] * 4; + sc->index = INDEX_D10_AUDIO; + sc->container_ul = ((MXFStreamContext*)s->streams[0]->priv_data)->container_ul; + sc->frame_size = 4 + 8 * av_rescale_rnd(st->codecpar->sample_rate, mxf->time_base.num, mxf->time_base.den, AV_ROUND_UP) * 4; } else if (s->oformat == &ff_mxf_opatom_muxer) { AVRational tbc = av_inv_q(mxf->audio_edit_rate); @@ -2635,24 +2567,22 @@ static int mxf_write_header(AVFormatContext *s) return AVERROR(EINVAL); } - spf = ff_mxf_get_samples_per_frame(s, tbc); - if (!spf) { - av_log(s, AV_LOG_ERROR, "Unsupported timecode frame rate %d/%d\n", tbc.den, tbc.num); - return AVERROR(EINVAL); - } - mxf->time_base = st->time_base; - if((ret = mxf_init_timecode(s, st, av_inv_q(spf->time_base))) < 0) + if((ret = mxf_init_timecode(s, st, tbc)) < 0) return ret; - mxf->timecode_base = (tbc.den + tbc.num/2) / tbc.num; mxf->edit_unit_byte_count = (av_get_bits_per_sample(st->codecpar->codec_id) * st->codecpar->channels) >> 3; sc->index = INDEX_WAV; } else { mxf->slice_count = 1; - sc->frame_size = (st->codecpar->channels * spf[0].samples_per_frame[0] * - av_get_bits_per_sample(st->codecpar->codec_id)) / 8; + sc->frame_size = st->codecpar->channels * + av_rescale_rnd(st->codecpar->sample_rate, mxf->time_base.num, mxf->time_base.den, AV_ROUND_UP) * + av_get_bits_per_sample(st->codecpar->codec_id) / 8; } + snprintf(bsf_arg, sizeof(bsf_arg), "r=%d/%d", mxf->tc.rate.num, mxf->tc.rate.den); + ret = ff_stream_add_bitstream_filter(st, "pcm_rechunk", bsf_arg); + if (ret < 0) + return ret; } else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) { AVDictionaryEntry *e = av_dict_get(st->metadata, "data_type", NULL, 0); if (e && !strcmp(e->value, "vbi_vanc_smpte_436M")) { @@ -2667,7 +2597,7 @@ static int mxf_write_header(AVFormatContext *s) } } - if (!sc->index) { + if (sc->index == -1) { sc->index = mxf_get_essence_container_ul_index(st->codecpar->codec_id); if (sc->index == -1) { av_log(s, AV_LOG_ERROR, "track %d: could not find essence container ul, " @@ -2676,7 +2606,10 @@ static int mxf_write_header(AVFormatContext *s) } } - sc->codec_ul = &mxf_essence_container_uls[sc->index].codec_ul; + if (!sc->codec_ul) + sc->codec_ul = &mxf_essence_container_uls[sc->index].codec_ul; + if (!sc->container_ul) + sc->container_ul = &mxf_essence_container_uls[sc->index].container_ul; memcpy(sc->track_essence_element_key, mxf_essence_container_uls[sc->index].element_ul, 15); sc->track_essence_element_key[15] = present[sc->index]; @@ -2698,7 +2631,7 @@ static int mxf_write_header(AVFormatContext *s) MXFStreamContext *sc = s->streams[i]->priv_data; // update element count sc->track_essence_element_key[13] = present[sc->index]; - if (!memcmp(sc->track_essence_element_key, mxf_essence_container_uls[15].element_ul, 13)) // DV + if (!memcmp(sc->track_essence_element_key, mxf_essence_container_uls[INDEX_DV].element_ul, 13)) // DV sc->order = (0x15 << 24) | AV_RB32(sc->track_essence_element_key+13); else sc->order = AV_RB32(sc->track_essence_element_key+12); @@ -2716,12 +2649,6 @@ static int mxf_write_header(AVFormatContext *s) return AVERROR(ENOMEM); mxf->timecode_track->index = -1; - if (!spf) - spf = ff_mxf_get_samples_per_frame(s, (AVRational){ 1, 25 }); - - if (ff_audio_interleave_init(s, spf->samples_per_frame, mxf->time_base) < 0) - return -1; - return 0; } @@ -2752,12 +2679,12 @@ static void mxf_write_system_item(AVFormatContext *s) avio_w8(pb, mxf->content_package_rate); // content package rate avio_w8(pb, 0x00); // content package type avio_wb16(pb, 0x00); // channel handle - avio_wb16(pb, (mxf->tc.start + frame) & 0xFFFF); // continuity count, supposed to overflow + avio_wb16(pb, frame & 0xFFFF); // continuity count, supposed to overflow if (mxf->essence_container_count > 1) avio_write(pb, multiple_desc_ul, 16); else { MXFStreamContext *sc = s->streams[0]->priv_data; - avio_write(pb, mxf_essence_container_uls[sc->index].container_ul, 16); + avio_write(pb, *sc->container_ul, 16); } avio_w8(pb, 0); avio_wb64(pb, 0); @@ -2855,7 +2782,6 @@ static int mxf_write_opatom_packet(AVFormatContext *s, AVPacket *pkt, MXFIndexEn mxf->edit_units_count++; avio_write(pb, pkt->data, pkt->size); mxf->body_offset += pkt->size; - avio_flush(pb); return 0; } @@ -2993,8 +2919,6 @@ static int mxf_write_packet(AVFormatContext *s, AVPacket *pkt) mxf->body_offset += 16+4+pkt->size + klv_fill_size(16+4+pkt->size); } - avio_flush(pb); - return 0; } @@ -3029,13 +2953,12 @@ static int mxf_write_footer(AVFormatContext *s) { MXFContext *mxf = s->priv_data; AVIOContext *pb = s->pb; - int i, err = 0; + int i, err; if (!mxf->header_written || (s->oformat == &ff_mxf_opatom_muxer && !mxf->body_partition_offset)) { /* reason could be invalid options/not supported codec/out of memory */ - err = AVERROR_UNKNOWN; - goto end; + return AVERROR_UNKNOWN; } mxf->duration = mxf->last_indexed_edit_unit + mxf->edit_units_count; @@ -3044,10 +2967,10 @@ static int mxf_write_footer(AVFormatContext *s) mxf->footer_partition_offset = avio_tell(pb); if (mxf->edit_unit_byte_count && s->oformat != &ff_mxf_opatom_muxer) { // no need to repeat index if ((err = mxf_write_partition(s, 0, 0, footer_partition_key, 0)) < 0) - goto end; + return err; } else { if ((err = mxf_write_partition(s, 0, 2, footer_partition_key, 0)) < 0) - goto end; + return err; mxf_write_klv_fill(s); mxf_write_index_table_segment(s); } @@ -3060,18 +2983,18 @@ static int mxf_write_footer(AVFormatContext *s) /* rewrite body partition to update lengths */ avio_seek(pb, mxf->body_partition_offset[0], SEEK_SET); if ((err = mxf_write_opatom_body_partition(s)) < 0) - goto end; + return err; } avio_seek(pb, 0, SEEK_SET); if (mxf->edit_unit_byte_count && s->oformat != &ff_mxf_opatom_muxer) { if ((err = mxf_write_partition(s, 1, 2, header_closed_partition_key, 1)) < 0) - goto end; + return err; mxf_write_klv_fill(s); mxf_write_index_table_segment(s); } else { if ((err = mxf_write_partition(s, 0, 0, header_closed_partition_key, 1)) < 0) - goto end; + return err; } // update footer partition offset for (i = 0; i < mxf->body_partitions_count; i++) { @@ -3080,17 +3003,19 @@ static int mxf_write_footer(AVFormatContext *s) } } -end: - ff_audio_interleave_close(s); + return 0; +} + +static void mxf_deinit(AVFormatContext *s) +{ + MXFContext *mxf = s->priv_data; av_freep(&mxf->index_entries); av_freep(&mxf->body_partition_offset); - av_freep(&mxf->timecode_track->priv_data); - av_freep(&mxf->timecode_track); - - mxf_free(s); - - return err < 0 ? err : 0; + if (mxf->timecode_track) { + av_freep(&mxf->timecode_track->priv_data); + av_freep(&mxf->timecode_track); + } } static int mxf_interleave_get_packet(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush) @@ -3143,12 +3068,12 @@ static int mxf_interleave_get_packet(AVFormatContext *s, AVPacket *out, AVPacket return 1; } else { out: - av_init_packet(out); return 0; } } -static int mxf_compare_timestamps(AVFormatContext *s, AVPacket *next, AVPacket *pkt) +static int mxf_compare_timestamps(AVFormatContext *s, const AVPacket *next, + const AVPacket *pkt) { MXFStreamContext *sc = s->streams[pkt ->stream_index]->priv_data; MXFStreamContext *sc2 = s->streams[next->stream_index]->priv_data; @@ -3159,8 +3084,14 @@ static int mxf_compare_timestamps(AVFormatContext *s, AVPacket *next, AVPacket * static int mxf_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush) { - return ff_audio_rechunk_interleave(s, out, pkt, flush, - mxf_interleave_get_packet, mxf_compare_timestamps); + int ret; + if (pkt) { + MXFStreamContext *sc = s->streams[pkt->stream_index]->priv_data; + pkt->pts = pkt->dts = sc->pkt_cnt++; + if ((ret = ff_interleave_add_packet(s, pkt, mxf_compare_timestamps)) < 0) + return ret; + } + return mxf_interleave_get_packet(s, out, NULL, flush); } #define MXF_COMMON_OPTIONS \ @@ -3240,6 +3171,7 @@ AVOutputFormat ff_mxf_muxer = { .write_header = mxf_write_header, .write_packet = mxf_write_packet, .write_trailer = mxf_write_footer, + .deinit = mxf_deinit, .flags = AVFMT_NOTIMESTAMPS, .interleave_packet = mxf_interleave, .priv_class = &mxf_muxer_class, @@ -3255,6 +3187,7 @@ AVOutputFormat ff_mxf_d10_muxer = { .write_header = mxf_write_header, .write_packet = mxf_write_packet, .write_trailer = mxf_write_footer, + .deinit = mxf_deinit, .flags = AVFMT_NOTIMESTAMPS, .interleave_packet = mxf_interleave, .priv_class = &mxf_d10_muxer_class, @@ -3271,6 +3204,7 @@ AVOutputFormat ff_mxf_opatom_muxer = { .write_header = mxf_write_header, .write_packet = mxf_write_packet, .write_trailer = mxf_write_footer, + .deinit = mxf_deinit, .flags = AVFMT_NOTIMESTAMPS, .interleave_packet = mxf_interleave, .priv_class = &mxf_opatom_muxer_class, diff --git a/libavformat/ncdec.c b/libavformat/ncdec.c index bc3d3e82bf5..f2066b485a4 100644 --- a/libavformat/ncdec.c +++ b/libavformat/ncdec.c @@ -83,7 +83,6 @@ static int nc_read_packet(AVFormatContext *s, AVPacket *pkt) ret = av_get_packet(s->pb, pkt, size); if (ret != size) { - if (ret > 0) av_packet_unref(pkt); return AVERROR(EIO); } diff --git a/libavformat/network.c b/libavformat/network.c index 5664455d186..0f5a575f773 100644 --- a/libavformat/network.c +++ b/libavformat/network.c @@ -238,7 +238,7 @@ int ff_accept(int fd, int timeout, URLContext *h) if (ret < 0) return ff_neterrno(); if (ff_socket_nonblock(ret, 1) < 0) - av_log(NULL, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); + av_log(h, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); return ret; } @@ -264,7 +264,7 @@ int ff_listen_connect(int fd, const struct sockaddr *addr, socklen_t optlen; if (ff_socket_nonblock(fd, 1) < 0) - av_log(NULL, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); + av_log(h, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); while ((ret = connect(fd, addr, addrlen))) { ret = ff_neterrno(); diff --git a/libavformat/network.h b/libavformat/network.h index 7f467304a80..71347e815b0 100644 --- a/libavformat/network.h +++ b/libavformat/network.h @@ -50,6 +50,9 @@ #ifndef EINPROGRESS #define EINPROGRESS WSAEINPROGRESS #endif +#ifndef ENOTCONN +#define ENOTCONN WSAENOTCONN +#endif #define getsockopt(a, b, c, d, e) getsockopt(a, b, c, (char*) d, e) #define setsockopt(a, b, c, d, e) setsockopt(a, b, c, (const char*) d, e) diff --git a/libavformat/nsvdec.c b/libavformat/nsvdec.c index 7aa1b605b07..eb26b294508 100644 --- a/libavformat/nsvdec.c +++ b/libavformat/nsvdec.c @@ -211,6 +211,7 @@ static const AVCodecTag nsv_codec_audio_tags[] = { //static int nsv_load_index(AVFormatContext *s); static int nsv_read_chunk(AVFormatContext *s, int fill_header); +static int nsv_read_close(AVFormatContext *s); /* try to find something we recognize, and set the state accordingly */ static int nsv_resync(AVFormatContext *s) @@ -492,25 +493,32 @@ static int nsv_read_header(AVFormatContext *s) nsv->ahead[0].data = nsv->ahead[1].data = NULL; for (i = 0; i < NSV_MAX_RESYNC_TRIES; i++) { - if (nsv_resync(s) < 0) - return -1; + err = nsv_resync(s); + if (err < 0) + goto fail; if (nsv->state == NSV_FOUND_NSVF) { err = nsv_parse_NSVf_header(s); if (err < 0) - return err; + goto fail; } /* we need the first NSVs also... */ if (nsv->state == NSV_FOUND_NSVS) { err = nsv_parse_NSVs_header(s); if (err < 0) - return err; + goto fail; break; /* we just want the first one */ } } - if (s->nb_streams < 1) /* no luck so far */ - return -1; + if (s->nb_streams < 1) { /* no luck so far */ + err = AVERROR_INVALIDDATA; + goto fail; + } + /* now read the first chunk, so we can attempt to decode more info */ err = nsv_read_chunk(s, 1); +fail: + if (err < 0) + nsv_read_close(s); av_log(s, AV_LOG_TRACE, "parsed header\n"); return err; @@ -654,10 +662,8 @@ static int nsv_read_packet(AVFormatContext *s, AVPacket *pkt) /* now pick one of the plates */ for (i = 0; i < 2; i++) { if (nsv->ahead[i].data) { - /* avoid the cost of new_packet + memcpy(->data) */ - memcpy(pkt, &nsv->ahead[i], sizeof(AVPacket)); - nsv->ahead[i].data = NULL; /* we ate that one */ - return pkt->size; + av_packet_move_ref(pkt, &nsv->ahead[i]); + return 0; } } diff --git a/libavformat/nut.c b/libavformat/nut.c index 937f452878d..d6993239a3b 100644 --- a/libavformat/nut.c +++ b/libavformat/nut.c @@ -227,6 +227,8 @@ const AVCodecTag ff_nut_audio_tags[] = { { AV_CODEC_ID_PCM_S24LE, MKTAG('P', 'S', 'D', 24 ) }, { AV_CODEC_ID_PCM_S32BE, MKTAG(32 , 'D', 'S', 'P') }, { AV_CODEC_ID_PCM_S32LE, MKTAG('P', 'S', 'D', 32 ) }, + { AV_CODEC_ID_PCM_S64BE, MKTAG(64 , 'D', 'S', 'P') }, + { AV_CODEC_ID_PCM_S64LE, MKTAG('P', 'S', 'D', 64 ) }, { AV_CODEC_ID_PCM_S8, MKTAG('P', 'S', 'D', 8 ) }, { AV_CODEC_ID_PCM_U16BE, MKTAG(16 , 'D', 'U', 'P') }, { AV_CODEC_ID_PCM_U16LE, MKTAG('P', 'U', 'D', 16 ) }, diff --git a/libavformat/nutdec.c b/libavformat/nutdec.c index 979cb9a031d..3779dce2a88 100644 --- a/libavformat/nutdec.c +++ b/libavformat/nutdec.c @@ -427,8 +427,10 @@ static int decode_stream_header(NUTContext *nut) GET_V(st->codecpar->extradata_size, tmp < (1 << 30)); if (st->codecpar->extradata_size) { - if (ff_get_extradata(s, st->codecpar, bc, st->codecpar->extradata_size) < 0) - return AVERROR(ENOMEM); + ret = ff_get_extradata(s, st->codecpar, bc, + st->codecpar->extradata_size); + if (ret < 0) + return ret; } if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { @@ -1275,13 +1277,13 @@ static int read_seek(AVFormatContext *s, int stream_index, av_assert0(sp); pos2 = sp->back_ptr - 15; } - av_log(NULL, AV_LOG_DEBUG, "SEEKTO: %"PRId64"\n", pos2); + av_log(s, AV_LOG_DEBUG, "SEEKTO: %"PRId64"\n", pos2); pos = find_startcode(s->pb, SYNCPOINT_STARTCODE, pos2); avio_seek(s->pb, pos, SEEK_SET); nut->last_syncpoint_pos = pos; - av_log(NULL, AV_LOG_DEBUG, "SP: %"PRId64"\n", pos); + av_log(s, AV_LOG_DEBUG, "SP: %"PRId64"\n", pos); if (pos2 > pos || pos2 + 15 < pos) - av_log(NULL, AV_LOG_ERROR, "no syncpoint at backptr pos\n"); + av_log(s, AV_LOG_ERROR, "no syncpoint at backptr pos\n"); for (i = 0; i < s->nb_streams; i++) nut->stream[i].skip_until_key_frame = 1; diff --git a/libavformat/nutenc.c b/libavformat/nutenc.c index e9a3bb49db0..6d3bf6c21e2 100644 --- a/libavformat/nutenc.c +++ b/libavformat/nutenc.c @@ -277,11 +277,37 @@ static void build_frame_code(AVFormatContext *s) nut->frame_code['N'].flags = FLAG_INVALID; } +/** + * Get the length in bytes which is needed to store val as v. + */ +static int get_v_length(uint64_t val) +{ + int i = 1; + + while (val >>= 7) + i++; + + return i; +} + +/** + * Put val using a variable number of bytes. + */ +static void put_v(AVIOContext *bc, uint64_t val) +{ + int i = get_v_length(val); + + while (--i > 0) + avio_w8(bc, 128 | (uint8_t)(val >> (7*i))); + + avio_w8(bc, val & 127); +} + static void put_tt(NUTContext *nut, AVRational *time_base, AVIOContext *bc, uint64_t val) { val *= nut->time_base_count; val += time_base - nut->time_base; - ff_put_v(bc, val); + put_v(bc, val); } /** * Store a string as vb. @@ -290,37 +316,34 @@ static void put_str(AVIOContext *bc, const char *string) { size_t len = strlen(string); - ff_put_v(bc, len); + put_v(bc, len); avio_write(bc, string, len); } static void put_s(AVIOContext *bc, int64_t val) { - ff_put_v(bc, 2 * FFABS(val) - (val > 0)); + put_v(bc, 2 * FFABS(val) - (val > 0)); } -//FIXME remove calculate_checksum static void put_packet(NUTContext *nut, AVIOContext *bc, AVIOContext *dyn_bc, - int calculate_checksum, uint64_t startcode) + uint64_t startcode) { uint8_t *dyn_buf = NULL; - int dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf); - int forw_ptr = dyn_size + 4 * calculate_checksum; + int dyn_size = avio_get_dyn_buf(dyn_bc, &dyn_buf); + int forw_ptr = dyn_size + 4; if (forw_ptr > 4096) ffio_init_checksum(bc, ff_crc04C11DB7_update, 0); avio_wb64(bc, startcode); - ff_put_v(bc, forw_ptr); + put_v(bc, forw_ptr); if (forw_ptr > 4096) avio_wl32(bc, ffio_get_checksum(bc)); - if (calculate_checksum) - ffio_init_checksum(bc, ff_crc04C11DB7_update, 0); + ffio_init_checksum(bc, ff_crc04C11DB7_update, 0); avio_write(bc, dyn_buf, dyn_size); - if (calculate_checksum) - avio_wl32(bc, ffio_get_checksum(bc)); + avio_wl32(bc, ffio_get_checksum(bc)); - av_free(dyn_buf); + ffio_reset_dyn_buf(dyn_bc); } static void write_mainheader(NUTContext *nut, AVIOContext *bc) @@ -329,16 +352,16 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc) tmp_head_idx; int64_t tmp_match; - ff_put_v(bc, nut->version); + put_v(bc, nut->version); if (nut->version > 3) - ff_put_v(bc, nut->minor_version = 1); - ff_put_v(bc, nut->avf->nb_streams); - ff_put_v(bc, nut->max_distance); - ff_put_v(bc, nut->time_base_count); + put_v(bc, nut->minor_version = 1); + put_v(bc, nut->avf->nb_streams); + put_v(bc, nut->max_distance); + put_v(bc, nut->time_base_count); for (i = 0; i < nut->time_base_count; i++) { - ff_put_v(bc, nut->time_base[i].num); - ff_put_v(bc, nut->time_base[i].den); + put_v(bc, nut->time_base[i].num); + put_v(bc, nut->time_base[i].den); } tmp_pts = 0; @@ -382,25 +405,25 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc) if (j != tmp_mul - tmp_size) tmp_fields = 6; - ff_put_v(bc, tmp_flags); - ff_put_v(bc, tmp_fields); + put_v(bc, tmp_flags); + put_v(bc, tmp_fields); if (tmp_fields > 0) put_s(bc, tmp_pts); - if (tmp_fields > 1) ff_put_v(bc, tmp_mul); - if (tmp_fields > 2) ff_put_v(bc, tmp_stream); - if (tmp_fields > 3) ff_put_v(bc, tmp_size); - if (tmp_fields > 4) ff_put_v(bc, 0 /*tmp_res*/); - if (tmp_fields > 5) ff_put_v(bc, j); - if (tmp_fields > 6) ff_put_v(bc, tmp_match); - if (tmp_fields > 7) ff_put_v(bc, tmp_head_idx); + if (tmp_fields > 1) put_v(bc, tmp_mul); + if (tmp_fields > 2) put_v(bc, tmp_stream); + if (tmp_fields > 3) put_v(bc, tmp_size); + if (tmp_fields > 4) put_v(bc, 0 /*tmp_res*/); + if (tmp_fields > 5) put_v(bc, j); + if (tmp_fields > 6) put_v(bc, tmp_match); + if (tmp_fields > 7) put_v(bc, tmp_head_idx); } - ff_put_v(bc, nut->header_count - 1); + put_v(bc, nut->header_count - 1); for (i = 1; i < nut->header_count; i++) { - ff_put_v(bc, nut->header_len[i]); + put_v(bc, nut->header_len[i]); avio_write(bc, nut->header[i], nut->header_len[i]); } // flags had been effectively introduced in version 4 if (nut->version > 3) - ff_put_v(bc, nut->flags); + put_v(bc, nut->flags); } static int write_streamheader(AVFormatContext *avctx, AVIOContext *bc, @@ -409,14 +432,14 @@ static int write_streamheader(AVFormatContext *avctx, AVIOContext *bc, NUTContext *nut = avctx->priv_data; AVCodecParameters *par = st->codecpar; - ff_put_v(bc, i); + put_v(bc, i); switch (par->codec_type) { - case AVMEDIA_TYPE_VIDEO: ff_put_v(bc, 0); break; - case AVMEDIA_TYPE_AUDIO: ff_put_v(bc, 1); break; - case AVMEDIA_TYPE_SUBTITLE: ff_put_v(bc, 2); break; - default: ff_put_v(bc, 3); break; + case AVMEDIA_TYPE_VIDEO: put_v(bc, 0); break; + case AVMEDIA_TYPE_AUDIO: put_v(bc, 1); break; + case AVMEDIA_TYPE_SUBTITLE: put_v(bc, 2); break; + default: put_v(bc, 3); break; } - ff_put_v(bc, 4); + put_v(bc, 4); if (par->codec_tag) { avio_wl32(bc, par->codec_tag); @@ -425,34 +448,34 @@ static int write_streamheader(AVFormatContext *avctx, AVIOContext *bc, return AVERROR(EINVAL); } - ff_put_v(bc, nut->stream[i].time_base - nut->time_base); - ff_put_v(bc, nut->stream[i].msb_pts_shift); - ff_put_v(bc, nut->stream[i].max_pts_distance); - ff_put_v(bc, par->video_delay); + put_v(bc, nut->stream[i].time_base - nut->time_base); + put_v(bc, nut->stream[i].msb_pts_shift); + put_v(bc, nut->stream[i].max_pts_distance); + put_v(bc, par->video_delay); avio_w8(bc, 0); /* flags: 0x1 - fixed_fps, 0x2 - index_present */ - ff_put_v(bc, par->extradata_size); + put_v(bc, par->extradata_size); avio_write(bc, par->extradata, par->extradata_size); switch (par->codec_type) { case AVMEDIA_TYPE_AUDIO: - ff_put_v(bc, par->sample_rate); - ff_put_v(bc, 1); - ff_put_v(bc, par->channels); + put_v(bc, par->sample_rate); + put_v(bc, 1); + put_v(bc, par->channels); break; case AVMEDIA_TYPE_VIDEO: - ff_put_v(bc, par->width); - ff_put_v(bc, par->height); + put_v(bc, par->width); + put_v(bc, par->height); if (st->sample_aspect_ratio.num <= 0 || st->sample_aspect_ratio.den <= 0) { - ff_put_v(bc, 0); - ff_put_v(bc, 0); + put_v(bc, 0); + put_v(bc, 0); } else { - ff_put_v(bc, st->sample_aspect_ratio.num); - ff_put_v(bc, st->sample_aspect_ratio.den); + put_v(bc, st->sample_aspect_ratio.num); + put_v(bc, st->sample_aspect_ratio.den); } - ff_put_v(bc, 0); /* csp type -- unknown */ + put_v(bc, 0); /* csp type -- unknown */ break; default: break; @@ -483,12 +506,12 @@ static int write_globalinfo(NUTContext *nut, AVIOContext *bc) while ((t = av_dict_get(s->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) count += add_info(dyn_bc, t->key, t->value); - ff_put_v(bc, 0); //stream_if_plus1 - ff_put_v(bc, 0); //chapter_id - ff_put_v(bc, 0); //timestamp_start - ff_put_v(bc, 0); //length + put_v(bc, 0); //stream_if_plus1 + put_v(bc, 0); //chapter_id + put_v(bc, 0); //timestamp_start + put_v(bc, 0); //length - ff_put_v(bc, count); + put_v(bc, count); dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf); avio_write(bc, dyn_buf, dyn_size); @@ -524,12 +547,12 @@ static int write_streaminfo(NUTContext *nut, AVIOContext *bc, int stream_id) { dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf); if (count) { - ff_put_v(bc, stream_id + 1); //stream_id_plus1 - ff_put_v(bc, 0); //chapter_id - ff_put_v(bc, 0); //timestamp_start - ff_put_v(bc, 0); //length + put_v(bc, stream_id + 1); //stream_id_plus1 + put_v(bc, 0); //chapter_id + put_v(bc, 0); //timestamp_start + put_v(bc, 0); //length - ff_put_v(bc, count); + put_v(bc, count); avio_write(bc, dyn_buf, dyn_size); } @@ -550,15 +573,15 @@ static int write_chapter(NUTContext *nut, AVIOContext *bc, int id) if (ret < 0) return ret; - ff_put_v(bc, 0); // stream_id_plus1 + put_v(bc, 0); // stream_id_plus1 put_s(bc, id + 1); // chapter_id put_tt(nut, nut->chapter[id].time_base, bc, ch->start); // chapter_start - ff_put_v(bc, ch->end - ch->start); // chapter_len + put_v(bc, ch->end - ch->start); // chapter_len while ((t = av_dict_get(ch->metadata, "", t, AV_DICT_IGNORE_SUFFIX))) count += add_info(dyn_bc, t->key, t->value); - ff_put_v(bc, count); + put_v(bc, count); dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf); avio_write(bc, dyn_buf, dyn_size); @@ -575,11 +598,11 @@ static int write_index(NUTContext *nut, AVIOContext *bc) { put_tt(nut, nut->max_pts_tb, bc, nut->max_pts); - ff_put_v(bc, nut->sp_count); + put_v(bc, nut->sp_count); for (i=0; isp_count; i++) { av_tree_find(nut->syncpoints, &dummy, ff_nut_sp_pos_cmp, (void**)next_node); - ff_put_v(bc, (next_node[1]->pos >> 4) - (dummy.pos>>4)); + put_v(bc, (next_node[1]->pos >> 4) - (dummy.pos>>4)); dummy.pos = next_node[1]->pos; } @@ -600,12 +623,12 @@ static int write_index(NUTContext *nut, AVIOContext *bc) { for (; jsp_count && (nus->keyframe_pts[j] != AV_NOPTS_VALUE) == flag; j++) n++; - ff_put_v(bc, 1 + 2*flag + 4*n); + put_v(bc, 1 + 2 * flag + 4 * n); for (k= j - n; k<=j && ksp_count; k++) { if (nus->keyframe_pts[k] == AV_NOPTS_VALUE) continue; av_assert0(nus->keyframe_pts[k] > last_pts); - ff_put_v(bc, nus->keyframe_pts[k] - last_pts); + put_v(bc, nus->keyframe_pts[k] - last_pts); last_pts = nus->keyframe_pts[k]; } } @@ -630,52 +653,44 @@ static int write_headers(AVFormatContext *avctx, AVIOContext *bc) if (ret < 0) return ret; write_mainheader(nut, dyn_bc); - put_packet(nut, bc, dyn_bc, 1, MAIN_STARTCODE); + put_packet(nut, bc, dyn_bc, MAIN_STARTCODE); for (i = 0; i < nut->avf->nb_streams; i++) { - ret = avio_open_dyn_buf(&dyn_bc); - if (ret < 0) - return ret; ret = write_streamheader(avctx, dyn_bc, nut->avf->streams[i], i); - if (ret < 0) - return ret; - put_packet(nut, bc, dyn_bc, 1, STREAM_STARTCODE); + if (ret < 0) { + goto fail; + } + put_packet(nut, bc, dyn_bc, STREAM_STARTCODE); } - ret = avio_open_dyn_buf(&dyn_bc); - if (ret < 0) - return ret; write_globalinfo(nut, dyn_bc); - put_packet(nut, bc, dyn_bc, 1, INFO_STARTCODE); + put_packet(nut, bc, dyn_bc, INFO_STARTCODE); for (i = 0; i < nut->avf->nb_streams; i++) { - ret = avio_open_dyn_buf(&dyn_bc); - if (ret < 0) - return ret; ret = write_streaminfo(nut, dyn_bc, i); - if (ret < 0) - return ret; if (ret > 0) - put_packet(nut, bc, dyn_bc, 1, INFO_STARTCODE); - else - ffio_free_dyn_buf(&dyn_bc); + put_packet(nut, bc, dyn_bc, INFO_STARTCODE); + else if (ret < 0) { + goto fail; + } } for (i = 0; i < nut->avf->nb_chapters; i++) { - ret = avio_open_dyn_buf(&dyn_bc); - if (ret < 0) - return ret; ret = write_chapter(nut, dyn_bc, i); if (ret < 0) { - ffio_free_dyn_buf(&dyn_bc); - return ret; + goto fail; } - put_packet(nut, bc, dyn_bc, 1, INFO_STARTCODE); + put_packet(nut, bc, dyn_bc, INFO_STARTCODE); } nut->last_syncpoint_pos = INT_MIN; nut->header_count++; - return 0; + + ret = 0; +fail: + ffio_free_dyn_buf(&dyn_bc); + + return ret; } static int nut_write_header(AVFormatContext *s) @@ -700,12 +715,8 @@ static int nut_write_header(AVFormatContext *s) nut->chapter = av_calloc(s->nb_chapters, sizeof(*nut->chapter)); nut->time_base= av_calloc(s->nb_streams + s->nb_chapters, sizeof(*nut->time_base)); - if (!nut->stream || !nut->chapter || !nut->time_base) { - av_freep(&nut->stream); - av_freep(&nut->chapter); - av_freep(&nut->time_base); + if (!nut->stream || !nut->chapter || !nut->time_base) return AVERROR(ENOMEM); - } for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; @@ -765,8 +776,6 @@ static int nut_write_header(AVFormatContext *s) if (s->avoid_negative_ts < 0) s->avoid_negative_ts = 1; - avio_flush(bc); - return 0; } @@ -789,11 +798,12 @@ static int get_needed_flags(NUTContext *nut, StreamContext *nus, FrameCode *fc, flags |= FLAG_CHECKSUM; if (FFABS(pkt->pts - nus->last_pts) > nus->max_pts_distance) flags |= FLAG_CHECKSUM; - if (pkt->size < nut->header_len[fc->header_idx] || - (pkt->size > 4096 && fc->header_idx) || - memcmp(pkt->data, nut->header[fc->header_idx], - nut->header_len[fc->header_idx])) - flags |= FLAG_HEADER_IDX; + if (fc->header_idx) + if (pkt->size < nut->header_len[fc->header_idx] || + pkt->size > 4096 || + memcmp(pkt->data, nut->header [fc->header_idx], + nut->header_len[fc->header_idx])) + flags |= FLAG_HEADER_IDX; return flags | (fc->flags & FLAG_CODED); } @@ -878,7 +888,7 @@ static int write_sm_data(AVFormatContext *s, AVIOContext *bc, AVPacket *pkt, int } put_s(dyn_bc, -2); put_str(dyn_bc, "bin"); - ff_put_v(dyn_bc, pkt->side_data[i].size); + put_v(dyn_bc, pkt->side_data[i].size); avio_write(dyn_bc, data, pkt->side_data[i].size); sm_data_count++; break; @@ -893,7 +903,7 @@ static int write_sm_data(AVFormatContext *s, AVIOContext *bc, AVPacket *pkt, int put_str(dyn_bc, "ChannelLayout"); put_s(dyn_bc, -2); put_str(dyn_bc, "u64"); - ff_put_v(bc, 8); + put_v(dyn_bc, 8); avio_write(dyn_bc, data, 8); data+=8; sm_data_count++; } @@ -932,7 +942,7 @@ static int write_sm_data(AVFormatContext *s, AVIOContext *bc, AVPacket *pkt, int } fail: - ff_put_v(bc, sm_data_count); + put_v(bc, sm_data_count); dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf); avio_write(bc, dyn_buf, dyn_size); av_freep(&dyn_buf); @@ -1018,13 +1028,14 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) if (ret < 0) goto fail; put_tt(nut, nus->time_base, dyn_bc, pkt->dts); - ff_put_v(dyn_bc, sp_pos != INT64_MAX ? (nut->last_syncpoint_pos - sp_pos) >> 4 : 0); + put_v(dyn_bc, sp_pos != INT64_MAX ? (nut->last_syncpoint_pos - sp_pos) >> 4 : 0); if (nut->flags & NUT_BROADCAST) { put_tt(nut, nus->time_base, dyn_bc, av_rescale_q(av_gettime(), AV_TIME_BASE_Q, *nus->time_base)); } - put_packet(nut, bc, dyn_bc, 1, SYNCPOINT_STARTCODE); + put_packet(nut, bc, dyn_bc, SYNCPOINT_STARTCODE); + 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) @@ -1075,18 +1086,18 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) continue; if (flags & FLAG_STREAM_ID) - length += ff_get_v_length(pkt->stream_index); + length += get_v_length(pkt->stream_index); if (data_size % fc->size_mul != fc->size_lsb) continue; if (flags & FLAG_SIZE_MSB) - length += ff_get_v_length(data_size / fc->size_mul); + length += get_v_length(data_size / fc->size_mul); if (flags & FLAG_CHECKSUM) length += 4; if (flags & FLAG_CODED_PTS) - length += ff_get_v_length(coded_pts); + length += get_v_length(coded_pts); if ( (flags & FLAG_CODED) && nut->header_len[best_header_idx] > nut->header_len[fc->header_idx] + 1) { @@ -1118,13 +1129,13 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) ffio_init_checksum(bc, ff_crc04C11DB7_update, 0); avio_w8(bc, frame_code); if (flags & FLAG_CODED) { - ff_put_v(bc, (flags ^ needed_flags) & ~(FLAG_CODED)); + put_v(bc, (flags ^ needed_flags) & ~(FLAG_CODED)); flags = needed_flags; } - if (flags & FLAG_STREAM_ID) ff_put_v(bc, pkt->stream_index); - if (flags & FLAG_CODED_PTS) ff_put_v(bc, coded_pts); - if (flags & FLAG_SIZE_MSB ) ff_put_v(bc, data_size / fc->size_mul); - if (flags & FLAG_HEADER_IDX) ff_put_v(bc, header_idx = best_header_idx); + if (flags & FLAG_STREAM_ID) put_v(bc, pkt->stream_index); + if (flags & FLAG_CODED_PTS) put_v(bc, coded_pts); + if (flags & FLAG_SIZE_MSB ) put_v(bc, data_size / fc->size_mul); + if (flags & FLAG_HEADER_IDX) put_v(bc, header_idx = best_header_idx); if (flags & FLAG_CHECKSUM) avio_wl32(bc, ffio_get_checksum(bc)); else ffio_get_checksum(bc); @@ -1170,11 +1181,15 @@ static int nut_write_trailer(AVFormatContext *s) while (nut->header_count < 3) write_headers(s, bc); + if (!nut->sp_count) + return 0; + ret = avio_open_dyn_buf(&dyn_bc); - if (ret >= 0 && nut->sp_count) { + if (ret >= 0) { av_assert1(nut->write_index); // sp_count should be 0 if no index is going to be written write_index(nut, dyn_bc); - put_packet(nut, bc, dyn_bc, 1, INDEX_STARTCODE); + put_packet(nut, bc, dyn_bc, INDEX_STARTCODE); + ffio_free_dyn_buf(&dyn_bc); } return 0; diff --git a/libavformat/nuv.c b/libavformat/nuv.c index a1edbf88dfd..d99770d41d8 100644 --- a/libavformat/nuv.c +++ b/libavformat/nuv.c @@ -74,7 +74,7 @@ static int get_codec_data(AVFormatContext *s, AVIOContext *pb, AVStream *vst, if (!vst && !myth) return 1; // no codec data needed while (!avio_feof(pb)) { - int size, subtype; + int size, subtype, ret; frametype = avio_r8(pb); switch (frametype) { @@ -83,12 +83,8 @@ static int get_codec_data(AVFormatContext *s, AVIOContext *pb, AVStream *vst, avio_skip(pb, 6); size = PKTSIZE(avio_rl32(pb)); if (vst && subtype == 'R') { - if (vst->codecpar->extradata) { - av_freep(&vst->codecpar->extradata); - vst->codecpar->extradata_size = 0; - } - if (ff_get_extradata(NULL, vst->codecpar, pb, size) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_get_extradata(NULL, vst->codecpar, pb, size)) < 0) + return ret; size = 0; if (!myth) return 0; @@ -288,7 +284,6 @@ static int nuv_packet(AVFormatContext *s, AVPacket *pkt) memcpy(pkt->data, hdr, copyhdrsize); ret = avio_read(pb, pkt->data + copyhdrsize, size); if (ret < 0) { - av_packet_unref(pkt); return ret; } if (ret < size) diff --git a/libavformat/oggdec.c b/libavformat/oggdec.c index e815f421348..a456c3df602 100644 --- a/libavformat/oggdec.c +++ b/libavformat/oggdec.c @@ -31,6 +31,7 @@ #include #include "libavutil/avassert.h" #include "libavutil/intreadwrite.h" +#include "avio_internal.h" #include "oggdec.h" #include "avformat.h" #include "internal.h" @@ -41,7 +42,6 @@ static const struct ogg_codec * const ogg_codecs[] = { &ff_skeleton_codec, - &ff_daala_codec, &ff_dirac_codec, &ff_speex_codec, &ff_vorbis_codec, @@ -177,6 +177,7 @@ static int ogg_reset(AVFormatContext *s) if (start_pos <= s->internal->data_offset) { os->lastpts = 0; } + os->start_trimming = 0; os->end_trimming = 0; av_freep(&os->new_metadata); os->new_metadata_size = 0; @@ -205,59 +206,43 @@ static const struct ogg_codec *ogg_find_codec(uint8_t *buf, int size) * situation where a new audio stream spawn (identified with a new serial) and * must replace the previous one (track switch). */ -static int ogg_replace_stream(AVFormatContext *s, uint32_t serial, int nsegs) +static int ogg_replace_stream(AVFormatContext *s, uint32_t serial, char *magic, int page_size, + int probing) { struct ogg *ogg = s->priv_data; struct ogg_stream *os; const struct ogg_codec *codec; int i = 0; - if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) { - uint8_t magic[8]; - int64_t pos = avio_tell(s->pb); - avio_skip(s->pb, nsegs); - avio_read(s->pb, magic, sizeof(magic)); - avio_seek(s->pb, pos, SEEK_SET); - codec = ogg_find_codec(magic, sizeof(magic)); - if (!codec) { - av_log(s, AV_LOG_ERROR, "Cannot identify new stream\n"); - return AVERROR_INVALIDDATA; - } - for (i = 0; i < ogg->nstreams; i++) { - if (ogg->streams[i].codec == codec) - break; - } - if (i >= ogg->nstreams) - return ogg_new_stream(s, serial); - } else if (ogg->nstreams != 1) { + if (ogg->nstreams != 1) { avpriv_report_missing_feature(s, "Changing stream parameters in multistream ogg"); return AVERROR_PATCHWELCOME; } - os = &ogg->streams[i]; - - os->serial = serial; - return i; - -#if 0 - buf = os->buf; - bufsize = os->bufsize; - codec = os->codec; + /* Check for codecs */ + codec = ogg_find_codec(magic, page_size); + if (!codec && !probing) { + av_log(s, AV_LOG_ERROR, "Cannot identify new stream\n"); + return AVERROR_INVALIDDATA; + } - if (!ogg->state || ogg->state->streams[i].private != os->private) - av_freep(&ogg->streams[i].private); + os = &ogg->streams[0]; + if (os->codec != codec) + return AVERROR(EINVAL); - /* Set Ogg stream settings similar to what is done in ogg_new_stream(). We - * also re-use the ogg_stream allocated buffer */ - memset(os, 0, sizeof(*os)); os->serial = serial; - os->bufsize = bufsize; - os->buf = buf; - os->header = -1; os->codec = codec; + os->serial = serial; + os->lastpts = 0; + os->lastdts = 0; + os->start_trimming = 0; + os->end_trimming = 0; + + /* Chained files have extradata as a new packet */ + if (codec == &ff_opus_codec) + os->header = -1; return i; -#endif } static int ogg_new_stream(AVFormatContext *s, uint32_t serial) @@ -302,27 +287,6 @@ static int ogg_new_stream(AVFormatContext *s, uint32_t serial) return idx; } -static int ogg_new_buf(struct ogg *ogg, int idx) -{ - struct ogg_stream *os = ogg->streams + idx; - uint8_t *nb = av_malloc(os->bufsize + AV_INPUT_BUFFER_PADDING_SIZE); - int size = os->bufpos - os->pstart; - - if (!nb) - return AVERROR(ENOMEM); - - if (os->buf) { - memcpy(nb, os->buf + os->pstart, size); - av_free(os->buf); - } - - os->buf = nb; - os->bufpos = size; - os->pstart = 0; - - return 0; -} - static int data_packets_seen(const struct ogg *ogg) { int i; @@ -333,7 +297,21 @@ static int data_packets_seen(const struct ogg *ogg) return 0; } -static int ogg_read_page(AVFormatContext *s, int *sid) +static int buf_realloc(struct ogg_stream *os, int size) +{ + /* Even if invalid guarantee there's enough memory to read the page */ + if (os->bufsize - os->bufpos < size) { + uint8_t *nb = av_realloc(os->buf, 2*os->bufsize + AV_INPUT_BUFFER_PADDING_SIZE); + if (!nb) + return AVERROR(ENOMEM); + os->buf = nb; + os->bufsize *= 2; + } + + return 0; +} + +static int ogg_read_page(AVFormatContext *s, int *sid, int probing) { AVIOContext *bc = s->pb; struct ogg *ogg = s->priv_data; @@ -342,8 +320,13 @@ static int ogg_read_page(AVFormatContext *s, int *sid) int flags, nsegs; uint64_t gp; uint32_t serial; - int size, idx; + uint32_t crc, crc_tmp; + int size = 0, idx; + int64_t version, page_pos; + int64_t start_pos; uint8_t sync[4]; + uint8_t segments[255]; + uint8_t *readout_buf; int sp = 0; ret = avio_read(bc, sync, 4); @@ -377,53 +360,109 @@ static int ogg_read_page(AVFormatContext *s, int *sid) return AVERROR_INVALIDDATA; } - if (avio_r8(bc) != 0) { /* version */ - av_log (s, AV_LOG_ERROR, "ogg page, unsupported version\n"); - return AVERROR_INVALIDDATA; - } + /* 0x4fa9b05f = av_crc(AV_CRC_32_IEEE, 0x0, "OggS", 4) */ + ffio_init_checksum(bc, ff_crc04C11DB7_update, 0x4fa9b05f); - flags = avio_r8(bc); - gp = avio_rl64(bc); - serial = avio_rl32(bc); - avio_skip(bc, 8); /* seq, crc */ - nsegs = avio_r8(bc); + /* To rewind if checksum is bad/check magic on switches - this is the max packet size */ + ffio_ensure_seekback(bc, MAX_PAGE_SIZE); + start_pos = avio_tell(bc); + + version = avio_r8(bc); + flags = avio_r8(bc); + gp = avio_rl64(bc); + serial = avio_rl32(bc); + avio_skip(bc, 4); /* seq */ + + crc_tmp = ffio_get_checksum(bc); + crc = avio_rb32(bc); + crc_tmp = ff_crc04C11DB7_update(crc_tmp, (uint8_t[4]){0}, 4); + ffio_init_checksum(bc, ff_crc04C11DB7_update, crc_tmp); + + nsegs = avio_r8(bc); + page_pos = avio_tell(bc) - 27; + + ret = avio_read(bc, segments, nsegs); + if (ret < nsegs) + return ret < 0 ? ret : AVERROR_EOF; + + for (i = 0; i < nsegs; i++) + size += segments[i]; idx = ogg_find_stream(ogg, serial); + if (idx >= 0) { + os = ogg->streams + idx; + + ret = buf_realloc(os, size); + if (ret < 0) + return ret; + + readout_buf = os->buf + os->bufpos; + } else { + readout_buf = av_malloc(size); + } + + ret = avio_read(bc, readout_buf, size); + if (ret < size) { + if (idx < 0) + av_free(readout_buf); + return ret < 0 ? ret : AVERROR_EOF; + } + + if (crc ^ ffio_get_checksum(bc)) { + av_log(s, AV_LOG_ERROR, "CRC mismatch!\n"); + if (idx < 0) + av_free(readout_buf); + avio_seek(bc, start_pos, SEEK_SET); + *sid = -1; + return 0; + } + + /* Since we're almost sure its a valid packet, checking the version after + * the checksum lets the demuxer be more tolerant */ + if (version) { + av_log(s, AV_LOG_ERROR, "Invalid Ogg vers!\n"); + if (idx < 0) + av_free(readout_buf); + avio_seek(bc, start_pos, SEEK_SET); + *sid = -1; + return 0; + } + + /* CRC is correct so we can be 99% sure there's an actual change here */ if (idx < 0) { if (data_packets_seen(ogg)) - idx = ogg_replace_stream(s, serial, nsegs); + idx = ogg_replace_stream(s, serial, readout_buf, size, probing); else idx = ogg_new_stream(s, serial); if (idx < 0) { av_log(s, AV_LOG_ERROR, "failed to create or replace stream\n"); + av_free(readout_buf); return idx; } - } - os = ogg->streams + idx; - ogg->page_pos = - os->page_pos = avio_tell(bc) - 27; + os = ogg->streams + idx; - if (os->psize > 0) { - ret = ogg_new_buf(ogg, idx); - if (ret < 0) + ret = buf_realloc(os, size); + if (ret < 0) { + av_free(readout_buf); return ret; - } - - ret = avio_read(bc, os->segments, nsegs); - if (ret < nsegs) - return ret < 0 ? ret : AVERROR_EOF; - - os->nsegs = nsegs; - os->segp = 0; + } - size = 0; - for (i = 0; i < nsegs; i++) - size += os->segments[i]; + memcpy(os->buf + os->bufpos, readout_buf, size); + av_free(readout_buf); + } - if (!(flags & OGG_FLAG_BOS)) - os->got_data = 1; + ogg->page_pos = page_pos; + os->page_pos = page_pos; + os->nsegs = nsegs; + os->segp = 0; + os->got_data = !(flags & OGG_FLAG_BOS); + os->bufpos += size; + os->granule = gp; + os->flags = flags; + memcpy(os->segments, segments, nsegs); + memset(os->buf + os->bufpos, 0, AV_INPUT_BUFFER_PADDING_SIZE); if (flags & OGG_FLAG_CONT || os->incomplete) { if (!os->psize) { @@ -443,26 +482,8 @@ static int ogg_read_page(AVFormatContext *s, int *sid) os->sync_pos = os->page_pos; } - if (os->bufsize - os->bufpos < size) { - uint8_t *nb = av_malloc((os->bufsize *= 2) + AV_INPUT_BUFFER_PADDING_SIZE); - if (!nb) - return AVERROR(ENOMEM); - memcpy(nb, os->buf, os->bufpos); - av_free(os->buf); - os->buf = nb; - } - - ret = avio_read(bc, os->buf + os->bufpos, size); - if (ret < size) - return ret < 0 ? ret : AVERROR_EOF; - - os->bufpos += size; - os->granule = gp; - os->flags = flags; - - memset(os->buf + os->bufpos, 0, AV_INPUT_BUFFER_PADDING_SIZE); - if (sid) - *sid = idx; + /* This function is always called with sid != NULL */ + *sid = idx; return 0; } @@ -491,7 +512,7 @@ static int ogg_packet(AVFormatContext *s, int *sid, int *dstart, int *dsize, idx = ogg->curidx; while (idx < 0) { - ret = ogg_read_page(s, &idx); + ret = ogg_read_page(s, &idx, 0); if (ret < 0) return ret; } @@ -642,8 +663,8 @@ static int ogg_get_length(AVFormatContext *s) avio_seek(s->pb, end, SEEK_SET); ogg->page_pos = -1; - while (!ogg_read_page(s, &i)) { - if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 && + while (!ogg_read_page(s, &i, 1)) { + if (i >= 0 && ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 && ogg->streams[i].codec) { s->streams[i]->duration = ogg_gptopts(s, i, ogg->streams[i].granule, NULL); @@ -846,32 +867,29 @@ static int ogg_read_packet(AVFormatContext *s, AVPacket *pkt) pkt->duration = os->pduration; pkt->pos = fpos; - if (os->end_trimming) { + if (os->start_trimming || os->end_trimming) { uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 10); if(!side_data) - goto fail; + return AVERROR(ENOMEM); + AV_WL32(side_data + 0, os->start_trimming); AV_WL32(side_data + 4, os->end_trimming); + os->start_trimming = 0; os->end_trimming = 0; } if (os->new_metadata) { - uint8_t *side_data = av_packet_new_side_data(pkt, - AV_PKT_DATA_METADATA_UPDATE, - os->new_metadata_size); - if(!side_data) - goto fail; + ret = av_packet_add_side_data(pkt, AV_PKT_DATA_METADATA_UPDATE, + os->new_metadata, os->new_metadata_size); + if (ret < 0) + return ret; - memcpy(side_data, os->new_metadata, os->new_metadata_size); - av_freep(&os->new_metadata); + os->new_metadata = NULL; os->new_metadata_size = 0; } return psize; -fail: - av_packet_unref(pkt); - return AVERROR(ENOMEM); } static int64_t ogg_read_timestamp(AVFormatContext *s, int stream_index, diff --git a/libavformat/oggdec.h b/libavformat/oggdec.h index 4a2b6ddee8f..629a1d62628 100644 --- a/libavformat/oggdec.h +++ b/libavformat/oggdec.h @@ -84,6 +84,7 @@ struct ogg_stream { int got_start; int got_data; ///< 1 if the stream got some data (non-initial packets), 0 otherwise int nb_header; ///< set to the number of parsed headers + int start_trimming; ///< set the number of packets to drop from the start int end_trimming; ///< set the number of packets to drop from the end uint8_t *new_metadata; unsigned int new_metadata_size; @@ -114,7 +115,6 @@ struct ogg { #define OGG_NOGRANULE_VALUE (-1ull) extern const struct ogg_codec ff_celt_codec; -extern const struct ogg_codec ff_daala_codec; extern const struct ogg_codec ff_dirac_codec; extern const struct ogg_codec ff_flac_codec; extern const struct ogg_codec ff_ogm_audio_codec; diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c index 06021c4f4ba..f5032759a64 100644 --- a/libavformat/oggenc.c +++ b/libavformat/oggenc.c @@ -99,51 +99,32 @@ static const AVClass flavor ## _muxer_class = {\ .version = LIBAVUTIL_VERSION_INT,\ }; -static void ogg_update_checksum(AVFormatContext *s, AVIOContext *pb, int64_t crc_offset) -{ - int64_t pos = avio_tell(pb); - uint32_t checksum = ffio_get_checksum(pb); - avio_seek(pb, crc_offset, SEEK_SET); - avio_wb32(pb, checksum); - avio_seek(pb, pos, SEEK_SET); -} - -static int ogg_write_page(AVFormatContext *s, OGGPage *page, int extra_flags) +static void ogg_write_page(AVFormatContext *s, OGGPage *page, int extra_flags) { OGGStreamContext *oggstream = s->streams[page->stream_index]->priv_data; - AVIOContext *pb; - int64_t crc_offset; - int ret, size; - uint8_t *buf; - - ret = avio_open_dyn_buf(&pb); - if (ret < 0) - return ret; - ffio_init_checksum(pb, ff_crc04C11DB7_update, 0); - ffio_wfourcc(pb, "OggS"); - avio_w8(pb, 0); - avio_w8(pb, page->flags | extra_flags); - avio_wl64(pb, page->granule); - avio_wl32(pb, oggstream->serial_num); - avio_wl32(pb, oggstream->page_counter++); - crc_offset = avio_tell(pb); - avio_wl32(pb, 0); // crc - avio_w8(pb, page->segments_count); - avio_write(pb, page->segments, page->segments_count); - avio_write(pb, page->data, page->size); - - ogg_update_checksum(s, pb, crc_offset); - avio_flush(pb); - - size = avio_close_dyn_buf(pb, &buf); - if (size < 0) - return size; - - avio_write(s->pb, buf, size); - avio_flush(s->pb); - av_free(buf); + uint8_t buf[4 + 1 + 1 + 8 + 4 + 4 + 4 + 1 + 255], *ptr = buf, *crc_pos; + const AVCRC *crc_table = av_crc_get_table(AV_CRC_32_IEEE); + uint32_t crc; + + bytestream_put_le32(&ptr, MKTAG('O', 'g', 'g', 'S')); + bytestream_put_byte(&ptr, 0); + bytestream_put_byte(&ptr, page->flags | extra_flags); + bytestream_put_le64(&ptr, page->granule); + bytestream_put_le32(&ptr, oggstream->serial_num); + bytestream_put_le32(&ptr, oggstream->page_counter++); + crc_pos = ptr; + bytestream_put_le32(&ptr, 0); + bytestream_put_byte(&ptr, page->segments_count); + bytestream_put_buffer(&ptr, page->segments, page->segments_count); + + crc = av_crc(crc_table, 0, buf, ptr - buf); + crc = av_crc(crc_table, crc, page->data, page->size); + bytestream_put_be32(&crc_pos, crc); + + avio_write(s->pb, buf, ptr - buf); + avio_write(s->pb, page->data, page->size); + avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); oggstream->page_count--; - return 0; } static int ogg_key_granule(OGGStreamContext *oggstream, int64_t granule) @@ -295,8 +276,9 @@ static uint8_t *ogg_write_vorbiscomment(int64_t offset, int bitexact, AVChapter **chapters, unsigned int nb_chapters) { const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT; + AVIOContext pb; int64_t size; - uint8_t *p, *p0; + uint8_t *p; ff_metadata_conv(m, ff_vorbiscomment_metadata_conv, NULL); @@ -306,15 +288,14 @@ static uint8_t *ogg_write_vorbiscomment(int64_t offset, int bitexact, p = av_mallocz(size); if (!p) return NULL; - p0 = p; - p += offset; - ff_vorbiscomment_write(&p, m, vendor, chapters, nb_chapters); + ffio_init_context(&pb, p + offset, size - offset, 1, NULL, NULL, NULL, NULL); + ff_vorbiscomment_write(&pb, *m, vendor, chapters, nb_chapters); if (framing_bit) - bytestream_put_byte(&p, 1); + avio_w8(&pb, 1); *header_len = size; - return p0; + return p; } static int ogg_build_flac_headers(AVCodecParameters *par, @@ -547,7 +528,6 @@ static int ogg_init(AVFormatContext *s) &st->metadata); if (err) { av_log(s, AV_LOG_ERROR, "Error writing FLAC headers\n"); - av_freep(&st->priv_data); return err; } } else if (st->codecpar->codec_id == AV_CODEC_ID_SPEEX) { @@ -556,7 +536,6 @@ static int ogg_init(AVFormatContext *s) &st->metadata); if (err) { av_log(s, AV_LOG_ERROR, "Error writing Speex headers\n"); - av_freep(&st->priv_data); return err; } } else if (st->codecpar->codec_id == AV_CODEC_ID_OPUS) { @@ -565,7 +544,6 @@ static int ogg_init(AVFormatContext *s) &st->metadata, s->chapters, s->nb_chapters); if (err) { av_log(s, AV_LOG_ERROR, "Error writing Opus headers\n"); - av_freep(&st->priv_data); return err; } } else if (st->codecpar->codec_id == AV_CODEC_ID_VP8) { @@ -573,7 +551,6 @@ static int ogg_init(AVFormatContext *s) s->flags & AVFMT_FLAG_BITEXACT); if (err) { av_log(s, AV_LOG_ERROR, "Error writing VP8 headers\n"); - av_freep(&st->priv_data); return err; } } else { @@ -586,7 +563,7 @@ static int ogg_init(AVFormatContext *s) st->codecpar->codec_id == AV_CODEC_ID_VORBIS ? 30 : 42, (const uint8_t**)oggstream->header, oggstream->header_len) < 0) { av_log(s, AV_LOG_ERROR, "Extradata corrupted\n"); - av_freep(&st->priv_data); + oggstream->header[1] = NULL; return AVERROR_INVALIDDATA; } @@ -740,6 +717,8 @@ static int ogg_write_trailer(AVFormatContext *s) static void ogg_free(AVFormatContext *s) { + OGGContext *ogg = s->priv_data; + OGGPageList *p = ogg->page_list; int i; for (i = 0; i < s->nb_streams; i++) { @@ -754,8 +733,14 @@ static void ogg_free(AVFormatContext *s) av_freep(&oggstream->header[0]); } av_freep(&oggstream->header[1]); - av_freep(&st->priv_data); } + + while (p) { + OGGPageList *next = p->next; + av_free(p); + p = next; + } + ogg->page_list = NULL; } #if CONFIG_OGG_MUXER diff --git a/libavformat/oggparsecelt.c b/libavformat/oggparsecelt.c index 9c438a096ae..f7a88af6161 100644 --- a/libavformat/oggparsecelt.c +++ b/libavformat/oggparsecelt.c @@ -37,6 +37,7 @@ static int celt_header(AVFormatContext *s, int idx) AVStream *st = s->streams[idx]; struct oggcelt_private *priv = os->private; uint8_t *p = os->buf + os->pstart; + int ret; if (os->psize == 60 && !memcmp(p, ff_celt_codec.magic, ff_celt_codec.magicsize)) { @@ -48,9 +49,10 @@ static int celt_header(AVFormatContext *s, int idx) priv = av_malloc(sizeof(struct oggcelt_private)); if (!priv) return AVERROR(ENOMEM); - if (ff_alloc_extradata(st->codecpar, 2 * sizeof(uint32_t)) < 0) { + ret = ff_alloc_extradata(st->codecpar, 2 * sizeof(uint32_t)); + if (ret < 0) { av_free(priv); - return AVERROR(ENOMEM); + return ret; } version = AV_RL32(p + 28); /* unused header size field skipped */ diff --git a/libavformat/oggparsedaala.c b/libavformat/oggparsedaala.c deleted file mode 100644 index e944470aca3..00000000000 --- a/libavformat/oggparsedaala.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Ogg Daala parser - * Copyright (C) 2015 Rostislav Pehlivanov - * Copyright (C) 2015 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 - */ - -#include -#include "libavcodec/bytestream.h" -#include "avformat.h" -#include "internal.h" -#include "oggdec.h" - -struct DaalaPixFmtMap { - enum AVPixelFormat ffmpeg_fmt; - int depth; - int planes; - int xdec[4]; - int ydec[4]; -}; - -/* Currently supported formats only */ -static const struct DaalaPixFmtMap list_fmts[] = { - { AV_PIX_FMT_YUV420P, 8, 3, {0, 1, 1, 0}, {0, 1, 1, 0} }, - { AV_PIX_FMT_YUV444P, 8, 3, {0, 0, 0, 0}, {0, 0, 0, 0} } -}; - -typedef struct DaalaInfoHeader { - int init_d; - int fpr; - int gpshift; - int gpmask; - int version_maj; - int version_min; - int version_sub; - int frame_duration; - int keyframe_granule_shift; - struct DaalaPixFmtMap format; -} DaalaInfoHeader; - -static inline int daala_match_pix_fmt(struct DaalaPixFmtMap *fmt) -{ - int i, j; - for (i = 0; i < FF_ARRAY_ELEMS(list_fmts); i++) { - int match = 0; - if (fmt->depth != list_fmts[i].depth) - continue; - if (fmt->planes != list_fmts[i].planes) - continue; - for (j = 0; j < fmt->planes; j++) { - if (fmt->xdec[j] != list_fmts[i].xdec[j]) - continue; - if (fmt->ydec[j] != list_fmts[i].ydec[j]) - continue; - match++; - } - if (match == fmt->planes) - return list_fmts[i].ffmpeg_fmt; - } - return -1; -} - -static int daala_header(AVFormatContext *s, int idx) -{ - int i, err; - uint8_t *cdp; - GetByteContext gb; - AVRational timebase; - struct ogg *ogg = s->priv_data; - struct ogg_stream *os = ogg->streams + idx; - AVStream *st = s->streams[idx]; - int cds = st->codecpar->extradata_size + os->psize + 2; - DaalaInfoHeader *hdr = os->private; - - if (!(os->buf[os->pstart] & 0x80)) - return 0; - - if (!hdr) { - hdr = av_mallocz(sizeof(*hdr)); - if (!hdr) - return AVERROR(ENOMEM); - os->private = hdr; - } - - switch (os->buf[os->pstart]) { - case 0x80: - bytestream2_init(&gb, os->buf + os->pstart, os->psize); - bytestream2_skip(&gb, ff_daala_codec.magicsize); - - hdr->version_maj = bytestream2_get_byte(&gb); - hdr->version_min = bytestream2_get_byte(&gb); - hdr->version_sub = bytestream2_get_byte(&gb); - - st->codecpar->width = bytestream2_get_ne32(&gb); - st->codecpar->height = bytestream2_get_ne32(&gb); - - st->sample_aspect_ratio.num = bytestream2_get_ne32(&gb); - st->sample_aspect_ratio.den = bytestream2_get_ne32(&gb); - - timebase.num = bytestream2_get_ne32(&gb); - timebase.den = bytestream2_get_ne32(&gb); - if (timebase.num < 0 && timebase.den < 0) { - av_log(s, AV_LOG_WARNING, "Invalid timebase, assuming 30 FPS\n"); - timebase.num = 1; - timebase.den = 30; - } - avpriv_set_pts_info(st, 64, timebase.den, timebase.num); - - hdr->frame_duration = bytestream2_get_ne32(&gb); - hdr->gpshift = bytestream2_get_byte(&gb); - if (hdr->gpshift >= 32) { - av_log(s, AV_LOG_ERROR, "Too large gpshift %d (>= 32).\n", - hdr->gpshift); - hdr->gpshift = 0; - return AVERROR_INVALIDDATA; - } - hdr->gpmask = (1U << hdr->gpshift) - 1; - - hdr->format.depth = 8 + 2*(bytestream2_get_byte(&gb)-1); - - hdr->fpr = bytestream2_get_byte(&gb); - - hdr->format.planes = bytestream2_get_byte(&gb); - if (hdr->format.planes > 4) { - av_log(s, AV_LOG_ERROR, - "Invalid number of planes %d in daala pixel format map.\n", - hdr->format.planes); - return AVERROR_INVALIDDATA; - } - for (i = 0; i < hdr->format.planes; i++) { - hdr->format.xdec[i] = bytestream2_get_byte(&gb); - hdr->format.ydec[i] = bytestream2_get_byte(&gb); - } - - if ((st->codecpar->format = daala_match_pix_fmt(&hdr->format)) < 0) - av_log(s, AV_LOG_ERROR, "Unsupported pixel format - %i %i\n", - hdr->format.depth, hdr->format.planes); - - st->codecpar->codec_id = AV_CODEC_ID_DAALA; - st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; - st->need_parsing = AVSTREAM_PARSE_HEADERS; - - hdr->init_d = 1; - break; - case 0x81: - if (!hdr->init_d) - return AVERROR_INVALIDDATA; - ff_vorbis_stream_comment(s, st, - os->buf + os->pstart + ff_daala_codec.magicsize, - os->psize - ff_daala_codec.magicsize); - break; - case 0x82: - if (!hdr->init_d) - return AVERROR_INVALIDDATA; - break; - default: - av_log(s, AV_LOG_ERROR, "Unknown header type %X\n", os->buf[os->pstart]); - return AVERROR_INVALIDDATA; - break; - } - - if ((err = av_reallocp(&st->codecpar->extradata, - cds + AV_INPUT_BUFFER_PADDING_SIZE)) < 0) { - st->codecpar->extradata_size = 0; - return err; - } - - memset(st->codecpar->extradata + cds, 0, AV_INPUT_BUFFER_PADDING_SIZE); - cdp = st->codecpar->extradata + st->codecpar->extradata_size; - *cdp++ = os->psize >> 8; - *cdp++ = os->psize & 0xff; - memcpy(cdp, os->buf + os->pstart, os->psize); - st->codecpar->extradata_size = cds; - - return 1; -} - -static uint64_t daala_gptopts(AVFormatContext *ctx, int idx, uint64_t gp, - int64_t *dts) -{ - uint64_t iframe, pframe; - struct ogg *ogg = ctx->priv_data; - struct ogg_stream *os = ogg->streams + idx; - DaalaInfoHeader *hdr = os->private; - - if (!hdr) - return AV_NOPTS_VALUE; - - iframe = gp >> hdr->gpshift; - pframe = gp & hdr->gpmask; - - if (!pframe) - os->pflags |= AV_PKT_FLAG_KEY; - - if (dts) - *dts = iframe + pframe; - - return iframe + pframe; -} - -static int daala_packet(AVFormatContext *s, int idx) -{ - int seg, duration = 1; - struct ogg *ogg = s->priv_data; - struct ogg_stream *os = ogg->streams + idx; - int64_t pts; - - /* - * first packet handling: here we parse the duration of each packet in the - * first page and compare the total duration to the page granule to find the - * encoder delay and set the first timestamp - */ - - if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS)) { - for (seg = os->segp; seg < os->nsegs; seg++) - if (os->segments[seg] < 255) - duration++; - - pts = daala_gptopts(s, idx, os->granule, NULL); - if (pts != AV_NOPTS_VALUE) - pts -= duration; - os->lastpts = os->lastdts = pts; - if(s->streams[idx]->start_time == AV_NOPTS_VALUE) { - s->streams[idx]->start_time = os->lastpts; - if (s->streams[idx]->duration != AV_NOPTS_VALUE) - s->streams[idx]->duration -= s->streams[idx]->start_time; - } - } - - /* parse packet duration */ - if (os->psize > 0) - os->pduration = 1; - - return 0; -} - -const struct ogg_codec ff_daala_codec = { - .name = "Daala", - .magic = "\200daala", - .magicsize = 6, - .header = daala_header, - .packet = daala_packet, - .gptopts = daala_gptopts, - .granule_is_start = 1, - .nb_header = 3, -}; diff --git a/libavformat/oggparseflac.c b/libavformat/oggparseflac.c index b5f1416a3c5..4e85b05c671 100644 --- a/libavformat/oggparseflac.c +++ b/libavformat/oggparseflac.c @@ -34,7 +34,7 @@ flac_header (AVFormatContext * s, int idx) struct ogg_stream *os = ogg->streams + idx; AVStream *st = s->streams[idx]; GetBitContext gb; - int mdt; + int mdt, ret; if (os->buf[os->pstart] == 0xff) return 0; @@ -50,7 +50,7 @@ flac_header (AVFormatContext * s, int idx) skip_bits_long(&gb, 4*8); /* "FLAC" */ if(get_bits(&gb, 8) != 1) /* unsupported major version */ return -1; - skip_bits_long(&gb, 8 + 16); /* minor version + header count */ + skip_bits(&gb, 8 + 16); /* minor version + header count */ skip_bits_long(&gb, 4*8); /* "fLaC" */ /* METADATA_BLOCK_HEADER */ @@ -61,8 +61,8 @@ flac_header (AVFormatContext * s, int idx) st->codecpar->codec_id = AV_CODEC_ID_FLAC; st->need_parsing = AVSTREAM_PARSE_HEADERS; - if (ff_alloc_extradata(st->codecpar, FLAC_STREAMINFO_SIZE) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, FLAC_STREAMINFO_SIZE)) < 0) + return ret; memcpy(st->codecpar->extradata, streaminfo_start, st->codecpar->extradata_size); samplerate = AV_RB24(st->codecpar->extradata + 10) >> 4; diff --git a/libavformat/oggparseogm.c b/libavformat/oggparseogm.c index b07a5d55ba0..469b2299953 100644 --- a/libavformat/oggparseogm.c +++ b/libavformat/oggparseogm.c @@ -43,6 +43,7 @@ ogm_header(AVFormatContext *s, int idx) uint64_t time_unit; uint64_t spu; uint32_t size; + int ret; bytestream2_init(&p, os->buf + os->pstart, os->psize); if (!(bytestream2_peek_byte(&p) & 1)) @@ -108,9 +109,8 @@ ogm_header(AVFormatContext *s, int idx) size -= 52; if (bytestream2_get_bytes_left(&p) < size) return AVERROR_INVALIDDATA; - av_freep(&st->codecpar->extradata); - if (ff_alloc_extradata(st->codecpar, size) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, size)) < 0) + return ret; bytestream2_get_buffer(&p, st->codecpar->extradata, st->codecpar->extradata_size); } } diff --git a/libavformat/oggparseopus.c b/libavformat/oggparseopus.c index cd34cf23ba6..36d691e9aaf 100644 --- a/libavformat/oggparseopus.c +++ b/libavformat/oggparseopus.c @@ -42,6 +42,7 @@ static int opus_header(AVFormatContext *avf, int idx) AVStream *st = avf->streams[idx]; struct oggopus_private *priv = os->private; uint8_t *packet = os->buf + os->pstart; + int ret; if (!priv) { priv = os->private = av_mallocz(sizeof(*priv)); @@ -58,13 +59,13 @@ static int opus_header(AVFormatContext *avf, int idx) priv->pre_skip = AV_RL16(packet + 10); st->codecpar->initial_padding = priv->pre_skip; + os->start_trimming = priv->pre_skip; /*orig_sample_rate = AV_RL32(packet + 12);*/ /*gain = AV_RL16(packet + 16);*/ /*channel_map = AV_RL8 (packet + 18);*/ - av_freep(&st->codecpar->extradata); - if (ff_alloc_extradata(st->codecpar, os->psize)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, os->psize)) < 0) + return ret; memcpy(st->codecpar->extradata, packet, os->psize); diff --git a/libavformat/oggparsespeex.c b/libavformat/oggparsespeex.c index 27fc99247de..c4fee7e076b 100644 --- a/libavformat/oggparsespeex.c +++ b/libavformat/oggparsespeex.c @@ -46,6 +46,7 @@ static int speex_header(AVFormatContext *s, int idx) { struct speex_params *spxp = os->private; AVStream *st = s->streams[idx]; uint8_t *p = os->buf + os->pstart; + int ret; if (!spxp) { spxp = av_mallocz(sizeof(*spxp)); @@ -92,8 +93,8 @@ static int speex_header(AVFormatContext *s, int idx) { if (frames_per_packet) spxp->packet_size *= frames_per_packet; - if (ff_alloc_extradata(st->codecpar, os->psize) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, os->psize)) < 0) + return ret; memcpy(st->codecpar->extradata, p, st->codecpar->extradata_size); avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); diff --git a/libavformat/oggparsetheora.c b/libavformat/oggparsetheora.c index b0c0edc7a54..d1064e4328d 100644 --- a/libavformat/oggparsetheora.c +++ b/libavformat/oggparsetheora.c @@ -65,7 +65,7 @@ static int theora_header(AVFormatContext *s, int idx) /* 0x80"theora" */ skip_bits_long(&gb, 7 * 8); - thp->version = get_bits_long(&gb, 24); + thp->version = get_bits(&gb, 24); if (thp->version < 0x030100) { av_log(s, AV_LOG_ERROR, "Too old or unsupported Theora (%x)\n", thp->version); @@ -79,8 +79,8 @@ static int theora_header(AVFormatContext *s, int idx) skip_bits(&gb, 100); if (thp->version >= 0x030200) { - int width = get_bits_long(&gb, 24); - int height = get_bits_long(&gb, 24); + int width = get_bits(&gb, 24); + int height = get_bits(&gb, 24); if (width <= st->codecpar->width && width > st->codecpar->width - 16 && height <= st->codecpar->height && height > st->codecpar->height - 16) { st->codecpar->width = width; @@ -99,8 +99,8 @@ static int theora_header(AVFormatContext *s, int idx) } avpriv_set_pts_info(st, 64, timebase.num, timebase.den); - st->sample_aspect_ratio.num = get_bits_long(&gb, 24); - st->sample_aspect_ratio.den = get_bits_long(&gb, 24); + st->sample_aspect_ratio.num = get_bits(&gb, 24); + st->sample_aspect_ratio.den = get_bits(&gb, 24); if (thp->version >= 0x030200) skip_bits_long(&gb, 38); @@ -191,9 +191,9 @@ static int theora_packet(AVFormatContext *s, int idx) pts = theora_gptopts(s, idx, os->granule, NULL); if (pts != AV_NOPTS_VALUE) - pts -= duration; + pts = av_sat_sub64(pts, duration); os->lastpts = os->lastdts = pts; - if(s->streams[idx]->start_time == AV_NOPTS_VALUE) { + if(s->streams[idx]->start_time == AV_NOPTS_VALUE && os->lastpts != AV_NOPTS_VALUE) { s->streams[idx]->start_time = os->lastpts; if (s->streams[idx]->duration > 0) s->streams[idx]->duration -= s->streams[idx]->start_time; diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c index 43f05f928ac..0e8c25c030b 100644 --- a/libavformat/oggparsevorbis.c +++ b/libavformat/oggparsevorbis.c @@ -151,7 +151,7 @@ int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m, * 'METADATA_BLOCK_PICTURE'. This is the preferred and * recommended way of embedding cover art within VorbisComments." */ - if (!strcmp(tt, "METADATA_BLOCK_PICTURE") && parse_picture) { + if (!av_strcasecmp(tt, "METADATA_BLOCK_PICTURE") && parse_picture) { int ret, len = AV_BASE64_DECODE_SIZE(vl); char *pict = av_malloc(len); @@ -165,7 +165,7 @@ int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m, av_freep(&tt); av_freep(&ct); if (ret > 0) - ret = ff_flac_parse_picture(as, pict, ret); + ret = ff_flac_parse_picture(as, pict, ret, 0); av_freep(&pict); if (ret < 0) { av_log(as, AV_LOG_WARNING, "Failed to parse cover art block.\n"); @@ -177,9 +177,8 @@ int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m, av_dict_set(m, tt, ";", AV_DICT_APPEND); } av_dict_set(m, tt, ct, - AV_DICT_DONT_STRDUP_KEY | + AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL | AV_DICT_APPEND); - av_freep(&ct); } } } @@ -288,7 +287,7 @@ static int vorbis_update_metadata(AVFormatContext *s, int idx) os->new_metadata = av_packet_pack_dictionary(st->metadata, &os->new_metadata_size); /* Send an empty dictionary to indicate that metadata has been cleared. */ } else { - os->new_metadata = av_malloc(1); + os->new_metadata = av_mallocz(1); os->new_metadata_size = 0; } @@ -386,7 +385,12 @@ static int vorbis_header(AVFormatContext *s, int idx) } } } else { - int ret = fixup_vorbis_headers(s, priv, &st->codecpar->extradata); + int ret; + + if (priv->vp) + return AVERROR_INVALIDDATA; + + ret = fixup_vorbis_headers(s, priv, &st->codecpar->extradata); if (ret < 0) { st->codecpar->extradata_size = 0; return ret; diff --git a/libavformat/omadec.c b/libavformat/omadec.c index 60cbf3a87fc..5675d86e75e 100644 --- a/libavformat/omadec.c +++ b/libavformat/omadec.c @@ -79,6 +79,13 @@ typedef struct OMAContext { int (*read_packet)(AVFormatContext *s, AVPacket *pkt); } OMAContext; +static int oma_read_close(AVFormatContext *s) +{ + OMAContext *oc = s->priv_data; + av_freep(&oc->av_des); + return 0; +} + static void hex_log(AVFormatContext *s, int level, const char *name, const uint8_t *value, int len) { @@ -217,14 +224,13 @@ static int decrypt_init(AVFormatContext *s, ID3v2ExtraMeta *em, uint8_t *header) av_log(s, AV_LOG_INFO, "File is encrypted\n"); /* find GEOB metadata */ - while (em) { - if (!strcmp(em->tag, "GEOB") && - (geob = em->data) && - (!strcmp(geob->description, "OMG_LSI") || - !strcmp(geob->description, "OMG_BKLSI"))) { + for (; em; em = em->next) { + if (strcmp(em->tag, "GEOB")) + continue; + geob = &em->data.geob; + if (!strcmp(geob->description, "OMG_LSI") || + !strcmp(geob->description, "OMG_BKLSI")) break; - } - em = em->next; } if (!em) { av_log(s, AV_LOG_ERROR, "No encryption header found\n"); @@ -397,17 +403,20 @@ static int oma_read_header(AVFormatContext *s) OMAContext *oc = s->priv_data; ff_id3v2_read(s, ID3v2_EA3_MAGIC, &extra_meta, 0); - if ((ret = ff_id3v2_parse_chapters(s, &extra_meta)) < 0) { + if ((ret = ff_id3v2_parse_chapters(s, extra_meta)) < 0) { ff_id3v2_free_extra_meta(&extra_meta); return ret; } ret = avio_read(s->pb, buf, EA3_HEADER_SIZE); - if (ret < EA3_HEADER_SIZE) + if (ret < EA3_HEADER_SIZE) { + ff_id3v2_free_extra_meta(&extra_meta); return -1; + } if (memcmp(buf, ((const uint8_t[]){'E', 'A', '3'}), 3) || buf[4] != 0 || buf[5] != EA3_HEADER_SIZE) { + ff_id3v2_free_extra_meta(&extra_meta); av_log(s, AV_LOG_ERROR, "Couldn't find the EA3 header !\n"); return AVERROR_INVALIDDATA; } @@ -426,8 +435,10 @@ static int oma_read_header(AVFormatContext *s) codec_params = AV_RB24(&buf[33]); st = avformat_new_stream(s, NULL); - if (!st) - return AVERROR(ENOMEM); + if (!st) { + ret = AVERROR(ENOMEM); + goto fail; + } st->start_time = 0; st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; @@ -442,7 +453,8 @@ static int oma_read_header(AVFormatContext *s) samplerate = ff_oma_srate_tab[(codec_params >> 13) & 7] * 100; if (!samplerate) { av_log(s, AV_LOG_ERROR, "Unsupported sample rate\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail; } if (samplerate != 44100) avpriv_request_sample(s, "Sample rate %d", samplerate); @@ -459,8 +471,8 @@ static int oma_read_header(AVFormatContext *s) /* fake the ATRAC3 extradata * (wav format, makes stream copy to wav work) */ - if (ff_alloc_extradata(st->codecpar, 14)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, 14)) < 0) + goto fail; edata = st->codecpar->extradata; AV_WL16(&edata[0], 1); // always 1 @@ -477,7 +489,8 @@ static int oma_read_header(AVFormatContext *s) if (!channel_id) { av_log(s, AV_LOG_ERROR, "Invalid ATRAC-X channel id: %"PRIu32"\n", channel_id); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail; } st->codecpar->channel_layout = ff_oma_chid_to_native_layout[channel_id - 1]; st->codecpar->channels = ff_oma_chid_to_num_channels[channel_id - 1]; @@ -485,7 +498,8 @@ static int oma_read_header(AVFormatContext *s) samplerate = ff_oma_srate_tab[(codec_params >> 13) & 7] * 100; if (!samplerate) { av_log(s, AV_LOG_ERROR, "Unsupported sample rate\n"); - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail; } st->codecpar->sample_rate = samplerate; st->codecpar->bit_rate = samplerate * framesize / (2048 / 8); @@ -525,12 +539,16 @@ static int oma_read_header(AVFormatContext *s) break; default: av_log(s, AV_LOG_ERROR, "Unsupported codec %d!\n", buf[32]); - return AVERROR(ENOSYS); + ret = AVERROR(ENOSYS); + goto fail; } st->codecpar->block_align = framesize; return 0; +fail: + oma_read_close(s); + return ret; } static int oma_read_packet(AVFormatContext *s, AVPacket *pkt) @@ -592,13 +610,6 @@ static int oma_read_seek(struct AVFormatContext *s, return err; } -static int oma_read_close(AVFormatContext *s) -{ - OMAContext *oc = s->priv_data; - av_free(oc->av_des); - return 0; -} - AVInputFormat ff_oma_demuxer = { .name = "oma", .long_name = NULL_IF_CONFIG_SMALL("Sony OpenMG audio"), diff --git a/libavformat/options.c b/libavformat/options.c index c188c23506a..e14510504f7 100644 --- a/libavformat/options.c +++ b/libavformat/options.c @@ -144,15 +144,17 @@ static void avformat_get_context_defaults(AVFormatContext *s) AVFormatContext *avformat_alloc_context(void) { AVFormatContext *ic; + AVFormatInternal *internal; ic = av_malloc(sizeof(AVFormatContext)); if (!ic) return ic; - avformat_get_context_defaults(ic); - ic->internal = av_mallocz(sizeof(*ic->internal)); - if (!ic->internal) { - avformat_free_context(ic); + internal = av_mallocz(sizeof(*internal)); + if (!internal) { + av_free(ic); return NULL; } + avformat_get_context_defaults(ic); + ic->internal = internal; ic->internal->offset = AV_NOPTS_VALUE; ic->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE; ic->internal->shortest_end = AV_NOPTS_VALUE; diff --git a/libavformat/options_table.h b/libavformat/options_table.h index f2f077b34f9..b4141564c84 100644 --- a/libavformat/options_table.h +++ b/libavformat/options_table.h @@ -82,8 +82,8 @@ static const AVOption avformat_options[] = { {"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 }, 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 }, 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"}, {"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}, @@ -104,13 +104,14 @@ static const AVOption avformat_options[] = { {"disabled", "do not change timestamps", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 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"}, -{"dump_separator", "set information dump field separator", OFFSET(dump_separator), AV_OPT_TYPE_STRING, {.str = ", "}, CHAR_MIN, CHAR_MAX, D|E}, -{"codec_whitelist", "List of decoders that are allowed to be used", OFFSET(codec_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, -{"format_whitelist", "List of demuxers that are allowed to be used", OFFSET(format_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, -{"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, -{"protocol_blacklist", "List of protocols that are not allowed to be used", OFFSET(protocol_blacklist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, +{"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 }, +{"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D }, +{"protocol_blacklist", "List of protocols that are not allowed to be used", OFFSET(protocol_blacklist), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D }, {"max_streams", "maximum number of streams", OFFSET(max_streams), AV_OPT_TYPE_INT, { .i64 = 1000 }, 0, INT_MAX, D }, {"skip_estimate_duration_from_pts", "skip duration calculation in estimate_timings_from_pts", OFFSET(skip_estimate_duration_from_pts), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, D}, +{"max_probe_packets", "Maximum number of packets to probe a codec", OFFSET(max_probe_packets), AV_OPT_TYPE_INT, { .i64 = 2500 }, 0, INT_MAX, D }, {NULL}, }; diff --git a/libavformat/paf.c b/libavformat/paf.c index b3c8e786bc1..a31d01502b2 100644 --- a/libavformat/paf.c +++ b/libavformat/paf.c @@ -194,7 +194,7 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) PAFDemuxContext *p = s->priv_data; AVIOContext *pb = s->pb; uint32_t count, offset; - int size, i; + int size, i, ret; if (p->current_frame >= p->nb_frames) return AVERROR_EOF; @@ -203,8 +203,8 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EOF; if (p->got_audio) { - if (av_new_packet(pkt, p->audio_size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, p->audio_size)) < 0) + return ret; memcpy(pkt->data, p->temp_audio_frame, p->audio_size); pkt->duration = PAF_SOUND_SAMPLES * (p->audio_size / PAF_SOUND_FRAME_SIZE); @@ -244,8 +244,8 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) size = p->video_size - p->frames_offset_table[p->current_frame]; - if (av_new_packet(pkt, size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, size)) < 0) + return ret; pkt->stream_index = 0; pkt->duration = 1; diff --git a/libavformat/pjsdec.c b/libavformat/pjsdec.c index 50b1a48e4ed..e30c23d8304 100644 --- a/libavformat/pjsdec.c +++ b/libavformat/pjsdec.c @@ -55,6 +55,8 @@ static int64_t read_ts(char **line, int *duration) if (sscanf(*line, "%"SCNd64",%"SCNd64, &start, &end) == 2) { *line += strcspn(*line, "\""); *line += !!**line; + if (end < start || end - (uint64_t)start > INT_MAX) + return AV_NOPTS_VALUE; *duration = end - start; return start; } @@ -92,8 +94,10 @@ static int pjs_read_header(AVFormatContext *s) p[strcspn(p, "\"")] = 0; sub = ff_subtitles_queue_insert(&pjs->q, p, strlen(p), 0); - if (!sub) + if (!sub) { + ff_subtitles_queue_clean(&pjs->q); return AVERROR(ENOMEM); + } sub->pos = pos; sub->pts = pts_start; sub->duration = duration; diff --git a/libavformat/pp_bnk.c b/libavformat/pp_bnk.c new file mode 100644 index 00000000000..8364de1fd9c --- /dev/null +++ b/libavformat/pp_bnk.c @@ -0,0 +1,292 @@ +/* + * Pro Pinball Series Soundbank (44c, 22c, 11c, 5c) demuxer. + * + * Copyright (C) 2020 Zane van Iperen (zane@zanevaniperen.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 + */ +#include "avformat.h" +#include "internal.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/avassert.h" +#include "libavutil/internal.h" + +#define PP_BNK_MAX_READ_SIZE 4096 +#define PP_BNK_FILE_HEADER_SIZE 20 +#define PP_BNK_TRACK_SIZE 20 + +typedef struct PPBnkHeader { + uint32_t bank_id; /*< Bank ID, useless for our purposes. */ + uint32_t sample_rate; /*< Sample rate of the contained tracks. */ + uint32_t always1; /*< Unknown, always seems to be 1. */ + uint32_t track_count; /*< Number of tracks in the file. */ + uint32_t flags; /*< Flags. */ +} PPBnkHeader; + +typedef struct PPBnkTrack { + uint32_t id; /*< Track ID. Usually track[i].id == track[i-1].id + 1, but not always */ + uint32_t size; /*< Size of the data in bytes. */ + uint32_t sample_rate; /*< Sample rate. */ + uint32_t always1_1; /*< Unknown, always seems to be 1. */ + uint32_t always1_2; /*< Unknown, always seems to be 1. */ +} PPBnkTrack; + +typedef struct PPBnkCtxTrack { + int64_t data_offset; + uint32_t data_size; + uint32_t bytes_read; +} PPBnkCtxTrack; + +typedef struct PPBnkCtx { + int track_count; + PPBnkCtxTrack *tracks; + uint32_t current_track; +} PPBnkCtx; + +enum { + PP_BNK_FLAG_PERSIST = (1 << 0), /*< This is a large file, keep in memory. */ + PP_BNK_FLAG_MUSIC = (1 << 1), /*< This is music. */ + PP_BNK_FLAG_MASK = (PP_BNK_FLAG_PERSIST | PP_BNK_FLAG_MUSIC) +}; + +static void pp_bnk_parse_header(PPBnkHeader *hdr, const uint8_t *buf) +{ + hdr->bank_id = AV_RL32(buf + 0); + hdr->sample_rate = AV_RL32(buf + 4); + hdr->always1 = AV_RL32(buf + 8); + hdr->track_count = AV_RL32(buf + 12); + hdr->flags = AV_RL32(buf + 16); +} + +static void pp_bnk_parse_track(PPBnkTrack *trk, const uint8_t *buf) +{ + trk->id = AV_RL32(buf + 0); + trk->size = AV_RL32(buf + 4); + trk->sample_rate = AV_RL32(buf + 8); + trk->always1_1 = AV_RL32(buf + 12); + trk->always1_2 = AV_RL32(buf + 16); +} + +static int pp_bnk_probe(const AVProbeData *p) +{ + uint32_t sample_rate = AV_RL32(p->buf + 4); + uint32_t track_count = AV_RL32(p->buf + 12); + uint32_t flags = AV_RL32(p->buf + 16); + + if (track_count == 0 || track_count > INT_MAX) + return 0; + + if ((sample_rate != 5512) && (sample_rate != 11025) && + (sample_rate != 22050) && (sample_rate != 44100)) + return 0; + + /* Check the first track header. */ + if (AV_RL32(p->buf + 28) != sample_rate) + return 0; + + if ((flags & ~PP_BNK_FLAG_MASK) != 0) + return 0; + + return AVPROBE_SCORE_MAX / 4 + 1; +} + +static int pp_bnk_read_header(AVFormatContext *s) +{ + int64_t ret; + AVStream *st; + AVCodecParameters *par; + PPBnkCtx *ctx = s->priv_data; + uint8_t buf[FFMAX(PP_BNK_FILE_HEADER_SIZE, PP_BNK_TRACK_SIZE)]; + PPBnkHeader hdr; + + if ((ret = avio_read(s->pb, buf, PP_BNK_FILE_HEADER_SIZE)) < 0) + return ret; + else if (ret != PP_BNK_FILE_HEADER_SIZE) + return AVERROR(EIO); + + pp_bnk_parse_header(&hdr, buf); + + if (hdr.track_count == 0 || hdr.track_count > INT_MAX) + return AVERROR_INVALIDDATA; + + if (hdr.sample_rate == 0 || hdr.sample_rate > INT_MAX) + return AVERROR_INVALIDDATA; + + if (hdr.always1 != 1) { + avpriv_request_sample(s, "Non-one header value"); + return AVERROR_PATCHWELCOME; + } + + ctx->track_count = hdr.track_count; + + if (!(ctx->tracks = av_malloc_array(hdr.track_count, sizeof(PPBnkCtxTrack)))) + return AVERROR(ENOMEM); + + /* Parse and validate each track. */ + for (int i = 0; i < hdr.track_count; i++) { + PPBnkTrack e; + PPBnkCtxTrack *trk = ctx->tracks + i; + + ret = avio_read(s->pb, buf, PP_BNK_TRACK_SIZE); + if (ret < 0 && ret != AVERROR_EOF) + goto fail; + + /* Short byte-count or EOF, we have a truncated file. */ + if (ret != PP_BNK_TRACK_SIZE) { + av_log(s, AV_LOG_WARNING, "File truncated at %d/%u track(s)\n", + i, hdr.track_count); + ctx->track_count = i; + break; + } + + pp_bnk_parse_track(&e, buf); + + /* The individual sample rates of all tracks must match that of the file header. */ + if (e.sample_rate != hdr.sample_rate) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + if (e.always1_1 != 1 || e.always1_2 != 1) { + avpriv_request_sample(s, "Non-one track header values"); + ret = AVERROR_PATCHWELCOME; + goto fail; + } + + trk->data_offset = avio_tell(s->pb); + trk->data_size = e.size; + trk->bytes_read = 0; + + /* + * Skip over the data to the next stream header. + * Sometimes avio_skip() doesn't detect EOF. If it doesn't, either: + * - the avio_read() above will, or + * - pp_bnk_read_packet() will read a truncated last track. + */ + if ((ret = avio_skip(s->pb, e.size)) == AVERROR_EOF) { + ctx->track_count = i + 1; + av_log(s, AV_LOG_WARNING, + "Track %d has truncated data, assuming track count == %d\n", + i, ctx->track_count); + break; + } else if (ret < 0) { + goto fail; + } + } + + /* File is only a header. */ + if (ctx->track_count == 0) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + /* Build the streams. */ + for (int i = 0; i < ctx->track_count; i++) { + if (!(st = avformat_new_stream(s, NULL))) { + ret = AVERROR(ENOMEM); + goto fail; + } + + par = st->codecpar; + par->codec_type = AVMEDIA_TYPE_AUDIO; + par->codec_id = AV_CODEC_ID_ADPCM_IMA_CUNNING; + par->format = AV_SAMPLE_FMT_S16; + par->channel_layout = AV_CH_LAYOUT_MONO; + par->channels = 1; + par->sample_rate = hdr.sample_rate; + par->bits_per_coded_sample = 4; + par->bits_per_raw_sample = 16; + par->block_align = 1; + par->bit_rate = par->sample_rate * par->bits_per_coded_sample; + + avpriv_set_pts_info(st, 64, 1, par->sample_rate); + st->start_time = 0; + st->duration = ctx->tracks[i].data_size * 2; + } + + return 0; + +fail: + av_freep(&ctx->tracks); + return ret; +} + +static int pp_bnk_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + PPBnkCtx *ctx = s->priv_data; + + /* + * Read a packet from each track, round-robin style. + * This method is nasty, but needed to avoid "Too many packets buffered" errors. + */ + for (int i = 0; i < ctx->track_count; i++, ctx->current_track++) + { + int64_t ret; + int size; + PPBnkCtxTrack *trk; + + ctx->current_track %= ctx->track_count; + + trk = ctx->tracks + ctx->current_track; + + if (trk->bytes_read == trk->data_size) + continue; + + if ((ret = avio_seek(s->pb, trk->data_offset + trk->bytes_read, SEEK_SET)) < 0) + return ret; + else if (ret != trk->data_offset + trk->bytes_read) + return AVERROR(EIO); + + size = FFMIN(trk->data_size - trk->bytes_read, PP_BNK_MAX_READ_SIZE); + + if ((ret = av_get_packet(s->pb, pkt, size)) == AVERROR_EOF) { + /* If we've hit EOF, don't attempt this track again. */ + trk->data_size = trk->bytes_read; + continue; + } else if (ret < 0) { + return ret; + } + + trk->bytes_read += ret; + pkt->flags &= ~AV_PKT_FLAG_CORRUPT; + pkt->stream_index = ctx->current_track++; + pkt->duration = ret * 2; + return 0; + } + + /* If we reach here, we're done. */ + return AVERROR_EOF; +} + +static int pp_bnk_read_close(AVFormatContext *s) +{ + PPBnkCtx *ctx = s->priv_data; + + av_freep(&ctx->tracks); + + return 0; +} + +AVInputFormat ff_pp_bnk_demuxer = { + .name = "pp_bnk", + .long_name = NULL_IF_CONFIG_SMALL("Pro Pinball Series Soundbank"), + .priv_data_size = sizeof(PPBnkCtx), + .read_probe = pp_bnk_probe, + .read_header = pp_bnk_read_header, + .read_packet = pp_bnk_read_packet, + .read_close = pp_bnk_read_close +}; diff --git a/libavformat/prompeg.c b/libavformat/prompeg.c index 9770a916a2b..59faa824bb3 100644 --- a/libavformat/prompeg.c +++ b/libavformat/prompeg.c @@ -291,8 +291,7 @@ static int prompeg_open(URLContext *h, const char *uri, int flags) { } if (s->ttl > 0) { - snprintf(buf, sizeof (buf), "%d", s->ttl); - av_dict_set(&udp_opts, "ttl", buf, 0); + av_dict_set_int(&udp_opts, "ttl", s->ttl, 0); } ff_url_join(buf, sizeof (buf), "udp", NULL, hostname, rtp_port + 2, NULL); @@ -388,7 +387,7 @@ static int prompeg_write(URLContext *h, const uint8_t *buf, int size) { PrompegFec *fec_tmp; uint8_t *bitstring = NULL; int col_idx, col_out_idx, row_idx; - int ret, written = 0; + int ret = 0; if (s->init && ((ret = prompeg_init(h, buf, size)) < 0)) goto end; @@ -404,7 +403,6 @@ static int prompeg_write(URLContext *h, const uint8_t *buf, int size) { if (!s->first || s->packet_idx > 0) { if ((ret = prompeg_write_fec(h, s->fec_row, PROMPEG_FEC_ROW)) < 0) goto end; - written += ret; } memcpy(s->fec_row->bitstring, bitstring, s->bitstring_size); s->fec_row->sn = AV_RB16(buf + 2); @@ -435,7 +433,6 @@ static int prompeg_write(URLContext *h, const uint8_t *buf, int size) { col_out_idx = s->packet_idx / s->d; if ((ret = prompeg_write_fec(h, s->fec_col[col_out_idx], PROMPEG_FEC_COL)) < 0) goto end; - written += ret; } if (++s->packet_idx >= s->packet_idx_max) { @@ -444,7 +441,7 @@ static int prompeg_write(URLContext *h, const uint8_t *buf, int size) { s->first = 0; } - ret = written; + ret = size; end: av_free(bitstring); diff --git a/libavformat/protocols.c b/libavformat/protocols.c index ad95659795e..f1b8eab0fd6 100644 --- a/libavformat/protocols.c +++ b/libavformat/protocols.c @@ -60,6 +60,7 @@ extern const URLProtocol ff_tls_protocol; extern const URLProtocol ff_udp_protocol; extern const URLProtocol ff_udplite_protocol; extern const URLProtocol ff_unix_protocol; +extern const URLProtocol ff_libamqp_protocol; extern const URLProtocol ff_librtmp_protocol; extern const URLProtocol ff_librtmpe_protocol; extern const URLProtocol ff_librtmps_protocol; @@ -68,6 +69,7 @@ extern const URLProtocol ff_librtmpte_protocol; extern const URLProtocol ff_libsrt_protocol; extern const URLProtocol ff_libssh_protocol; extern const URLProtocol ff_libsmbclient_protocol; +extern const URLProtocol ff_libzmq_protocol; #include "libavformat/protocol_list.c" @@ -106,6 +108,16 @@ const char *avio_enum_protocols(void **opaque, int output) return avio_enum_protocols(opaque, output); } +const AVClass *avio_protocol_get_class(const char *name) +{ + int i = 0; + for (i = 0; url_protocols[i]; i++) { + if (!strcmp(url_protocols[i]->name, name)) + return url_protocols[i]->priv_data_class; + } + return NULL; +} + const URLProtocol **ffurl_get_protocols(const char *whitelist, const char *blacklist) { diff --git a/libavformat/psxstr.c b/libavformat/psxstr.c index ddc17e35d2a..678b9f90aca 100644 --- a/libavformat/psxstr.c +++ b/libavformat/psxstr.c @@ -160,7 +160,7 @@ static int str_read_packet(AVFormatContext *s, AVIOContext *pb = s->pb; StrDemuxContext *str = s->priv_data; unsigned char sector[RAW_CD_SECTOR_SIZE]; - int channel; + int channel, ret; AVPacket *pkt; AVStream *st; @@ -213,8 +213,9 @@ static int str_read_packet(AVFormatContext *s, if(pkt->data) av_log(s, AV_LOG_ERROR, "mismatching sector_count\n"); av_packet_unref(pkt); - if (av_new_packet(pkt, sector_count*VIDEO_DATA_CHUNK_SIZE)) - return AVERROR(EIO); + ret = av_new_packet(pkt, sector_count * VIDEO_DATA_CHUNK_SIZE); + if (ret < 0) + return ret; memset(pkt->data, 0, sector_count*VIDEO_DATA_CHUNK_SIZE); pkt->pos= avio_tell(pb) - RAW_CD_SECTOR_SIZE; @@ -267,8 +268,8 @@ static int str_read_packet(AVFormatContext *s, st->start_time = 0; } pkt = ret_pkt; - if (av_new_packet(pkt, 2304)) - return AVERROR(EIO); + if ((ret = av_new_packet(pkt, 2304)) < 0) + return ret; memcpy(pkt->data,sector+24,2304); pkt->stream_index = diff --git a/libavformat/r3d.c b/libavformat/r3d.c index 224bcf780de..7aa0c5a2c38 100644 --- a/libavformat/r3d.c +++ b/libavformat/r3d.c @@ -27,7 +27,6 @@ typedef struct R3DContext { unsigned video_offsets_count; - unsigned *video_offsets; unsigned rdvo_offset; int audio_channels; @@ -118,17 +117,14 @@ static int r3d_read_rdvo(AVFormatContext *s, Atom *atom) int i; r3d->video_offsets_count = (atom->size - 8) / 4; - r3d->video_offsets = av_malloc(atom->size); - if (!r3d->video_offsets) - return AVERROR(ENOMEM); for (i = 0; i < r3d->video_offsets_count; i++) { - r3d->video_offsets[i] = avio_rb32(s->pb); - if (!r3d->video_offsets[i]) { + unsigned video_offset = avio_rb32(s->pb); + if (!video_offset) { r3d->video_offsets_count = i; break; } - av_log(s, AV_LOG_TRACE, "video offset %d: %#x\n", i, r3d->video_offsets[i]); + av_log(s, AV_LOG_TRACE, "video offset %d: %#x\n", i, video_offset); } if (st->avg_frame_rate.num) @@ -400,15 +396,6 @@ static int r3d_seek(AVFormatContext *s, int stream_index, int64_t sample_time, i return 0; } -static int r3d_close(AVFormatContext *s) -{ - R3DContext *r3d = s->priv_data; - - av_freep(&r3d->video_offsets); - - return 0; -} - AVInputFormat ff_r3d_demuxer = { .name = "r3d", .long_name = NULL_IF_CONFIG_SMALL("REDCODE R3D"), @@ -416,6 +403,5 @@ AVInputFormat ff_r3d_demuxer = { .read_probe = r3d_probe, .read_header = r3d_read_header, .read_packet = r3d_read_packet, - .read_close = r3d_close, .read_seek = r3d_seek, }; diff --git a/libavformat/rawdec.c b/libavformat/rawdec.c index 59b49e3f774..10c37c5cb91 100644 --- a/libavformat/rawdec.c +++ b/libavformat/rawdec.c @@ -39,8 +39,8 @@ int ff_raw_read_partial_packet(AVFormatContext *s, AVPacket *pkt) size = raw->raw_packet_size; - if (av_new_packet(pkt, size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, size)) < 0) + return ret; pkt->pos= avio_tell(s->pb); pkt->stream_index = 0; @@ -123,6 +123,8 @@ const AVOption ff_rawvideo_options[] = { { "raw_packet_size", "", OFFSET(raw_packet_size), AV_OPT_TYPE_INT, {.i64 = RAW_PACKET_SIZE }, 1, INT_MAX, DEC}, { NULL }, }; +#undef OFFSET +#define OFFSET(x) offsetof(FFRawDemuxerContext, x) const AVOption ff_raw_options[] = { { "raw_packet_size", "", OFFSET(raw_packet_size), AV_OPT_TYPE_INT, {.i64 = RAW_PACKET_SIZE }, 1, INT_MAX, DEC}, { NULL }, diff --git a/libavformat/rawdec.h b/libavformat/rawdec.h index 85e0790c86e..34c8adcb199 100644 --- a/libavformat/rawdec.h +++ b/libavformat/rawdec.h @@ -95,7 +95,7 @@ static const AVClass name ## _demuxer_class = {\ }; #define FF_DEF_RAWSUB_DEMUXER(shortname, longname, probe, ext, id, flag)\ -FF_RAWVIDEO_DEMUXER_CLASS(shortname)\ +FF_RAWSUB_DEMUXER_CLASS(shortname)\ AVInputFormat ff_ ## shortname ## _demuxer = {\ .name = #shortname,\ .long_name = NULL_IF_CONFIG_SMALL(longname),\ diff --git a/libavformat/redspark.c b/libavformat/redspark.c index f1f2b3156d3..0ce89153119 100644 --- a/libavformat/redspark.c +++ b/libavformat/redspark.c @@ -140,7 +140,6 @@ static int redspark_read_packet(AVFormatContext *s, AVPacket *pkt) ret = av_get_packet(s->pb, pkt, size); if (ret != size) { - av_packet_unref(pkt); return AVERROR(EIO); } diff --git a/libavformat/riff.c b/libavformat/riff.c index 52b0bf8f034..162e2b1bf20 100644 --- a/libavformat/riff.c +++ b/libavformat/riff.c @@ -403,6 +403,7 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'H', '0') }, { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'H', '2') }, { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'L', 'H', '4') }, + { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'Q', 'Y', '0') }, { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'Q', 'Y', '2') }, { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'Q', 'R', 'A') }, { AV_CODEC_ID_UTVIDEO, MKTAG('U', 'Q', 'R', 'G') }, @@ -488,11 +489,17 @@ const AVCodecTag ff_codec_bmp_tags[] = { { AV_CODEC_ID_AGM, MKTAG('A', 'G', 'M', '6') }, { AV_CODEC_ID_AGM, MKTAG('A', 'G', 'M', '7') }, { AV_CODEC_ID_LSCR, MKTAG('L', 'S', 'C', 'R') }, + { AV_CODEC_ID_IMM5, MKTAG('I', 'M', 'M', '5') }, + { AV_CODEC_ID_MVDV, MKTAG('M', 'V', 'D', 'V') }, + { AV_CODEC_ID_MVHA, MKTAG('M', 'V', 'H', 'A') }, + { AV_CODEC_ID_MV30, MKTAG('M', 'V', '3', '0') }, + { AV_CODEC_ID_NOTCHLC, MKTAG('n', 'l', 'c', '1') }, { AV_CODEC_ID_NONE, 0 } }; const AVCodecTag ff_codec_bmp_tags_unofficial[] = { { AV_CODEC_ID_HEVC, MKTAG('H', 'E', 'V', 'C') }, + { AV_CODEC_ID_HEVC, MKTAG('H', '2', '6', '5') }, { AV_CODEC_ID_NONE, 0 } }; @@ -513,7 +520,7 @@ const AVCodecTag ff_codec_wav_tags[] = { { AV_CODEC_ID_ADPCM_IMA_OKI, 0x0010 }, { AV_CODEC_ID_ADPCM_IMA_WAV, 0x0011 }, /* must come after adpcm_ima_wav in this list */ - { AV_CODEC_ID_PCM_ZORK, 0x0011 }, + { AV_CODEC_ID_ADPCM_ZORK, 0x0011 }, { AV_CODEC_ID_ADPCM_IMA_OKI, 0x0017 }, { AV_CODEC_ID_ADPCM_YAMAHA, 0x0020 }, { AV_CODEC_ID_TRUESPEECH, 0x0022 }, @@ -539,6 +546,7 @@ const AVCodecTag ff_codec_wav_tags[] = { { AV_CODEC_ID_AAC, 0x00ff }, { AV_CODEC_ID_G723_1, 0x0111 }, { AV_CODEC_ID_SIPR, 0x0130 }, + { AV_CODEC_ID_ACELP_KELVIN, 0x0135 }, { AV_CODEC_ID_WMAV1, 0x0160 }, { AV_CODEC_ID_WMAV2, 0x0161 }, { AV_CODEC_ID_WMAPRO, 0x0162 }, diff --git a/libavformat/riffdec.c b/libavformat/riffdec.c index 5523b31adce..533bb5a15da 100644 --- a/libavformat/riffdec.c +++ b/libavformat/riffdec.c @@ -145,7 +145,6 @@ int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb, size -= 22; } if (cbSize > 0) { - av_freep(&par->extradata); if (ff_get_extradata(s, par, pb, cbSize) < 0) return AVERROR(ENOMEM); size -= cbSize; @@ -158,7 +157,6 @@ int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb, int nb_streams, i; size -= 4; - av_freep(&par->extradata); if (ff_get_extradata(s, par, pb, size) < 0) return AVERROR(ENOMEM); nb_streams = AV_RL16(par->extradata + 4); @@ -204,7 +202,7 @@ enum AVCodecID ff_wav_codec_get_id(unsigned int tag, int bps) id = ff_get_pcm_codec_id(bps, 1, 0, 0); if (id == AV_CODEC_ID_ADPCM_IMA_WAV && bps == 8) - id = AV_CODEC_ID_PCM_ZORK; + id = AV_CODEC_ID_ADPCM_ZORK; return id; } diff --git a/libavformat/rl2.c b/libavformat/rl2.c index d847d9aaa88..cfde23a945b 100644 --- a/libavformat/rl2.c +++ b/libavformat/rl2.c @@ -127,8 +127,9 @@ static av_cold int rl2_read_header(AVFormatContext *s) if(signature == RLV3_TAG && back_size > 0) st->codecpar->extradata_size += back_size; - if(ff_get_extradata(s, st->codecpar, pb, st->codecpar->extradata_size) < 0) - return AVERROR(ENOMEM); + ret = ff_get_extradata(s, st->codecpar, pb, st->codecpar->extradata_size); + if (ret < 0) + return ret; /** setup audio stream if present */ if(sound_rate){ @@ -171,18 +172,24 @@ static av_cold int rl2_read_header(AVFormatContext *s) /** read offset and size tables */ for(i=0; i < frame_count;i++) { - if (avio_feof(pb)) - return AVERROR_INVALIDDATA; + if (avio_feof(pb)) { + ret = AVERROR_INVALIDDATA; + goto end; + } chunk_size[i] = avio_rl32(pb); } for(i=0; i < frame_count;i++) { - if (avio_feof(pb)) - return AVERROR_INVALIDDATA; + if (avio_feof(pb)) { + ret = AVERROR_INVALIDDATA; + goto end; + } chunk_offset[i] = avio_rl32(pb); } for(i=0; i < frame_count;i++) { - if (avio_feof(pb)) - return AVERROR_INVALIDDATA; + if (avio_feof(pb)) { + ret = AVERROR_INVALIDDATA; + goto end; + } audio_size[i] = avio_rl32(pb) & 0xFFFF; } @@ -203,7 +210,7 @@ static av_cold int rl2_read_header(AVFormatContext *s) ++video_frame_counter; } - +end: av_free(chunk_size); av_free(audio_size); av_free(chunk_offset); @@ -249,7 +256,6 @@ static int rl2_read_packet(AVFormatContext *s, /** fill the packet */ ret = av_get_packet(pb, pkt, sample->size); if(ret != sample->size){ - av_packet_unref(pkt); return AVERROR(EIO); } diff --git a/libavformat/rmdec.c b/libavformat/rmdec.c index c9abd38d337..a36e693ab28 100644 --- a/libavformat/rmdec.c +++ b/libavformat/rmdec.c @@ -87,9 +87,7 @@ static int rm_read_extradata(AVFormatContext *s, AVIOContext *pb, AVCodecParamet av_log(s, AV_LOG_ERROR, "extradata size %u too large\n", size); return -1; } - if (ff_get_extradata(s, par, pb, size) < 0) - return AVERROR(ENOMEM); - return 0; + return ff_get_extradata(s, par, pb, size); } static void rm_read_metadata(AVFormatContext *s, AVIOContext *pb, int wide) @@ -724,8 +722,8 @@ static int rm_sync(AVFormatContext *s, int64_t *timestamp, int *flags, int *stre num = avio_rb16(pb); *timestamp = avio_rb32(pb); - mlti_id = (avio_r8(pb)>>1)-1<<16; - mlti_id = FFMAX(mlti_id, 0); + mlti_id = (avio_r8(pb) >> 1) - 1; + mlti_id = FFMAX(mlti_id, 0) << 16; *flags = avio_r8(pb); /* flags */ } for(i=0;inb_streams;i++) { @@ -783,8 +781,8 @@ static int rm_assemble_video_frame(AVFormatContext *s, AVIOContext *pb, return -1; } rm->remaining_len -= len; - if(av_new_packet(pkt, len + 9) < 0) - return AVERROR(EIO); + if ((ret = av_new_packet(pkt, len + 9)) < 0) + return ret; pkt->data[0] = 0; AV_WL32(pkt->data + 1, 1); AV_WL32(pkt->data + 5, 0); @@ -806,8 +804,8 @@ static int rm_assemble_video_frame(AVFormatContext *s, AVIOContext *pb, vst->slices = ((hdr & 0x3F) << 1) + 1; vst->videobufsize = len2 + 8*vst->slices + 1; av_packet_unref(&vst->pkt); //FIXME this should be output. - if(av_new_packet(&vst->pkt, vst->videobufsize) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(&vst->pkt, vst->videobufsize)) < 0) + return ret; memset(vst->pkt.data, 0, vst->pkt.size); vst->videobufpos = 8*vst->slices + 1; vst->cur_slice = 0; @@ -836,10 +834,7 @@ static int rm_assemble_video_frame(AVFormatContext *s, AVIOContext *pb, if (type == 2 || vst->videobufpos == vst->videobufsize) { vst->pkt.data[0] = vst->cur_slice-1; - *pkt= vst->pkt; - vst->pkt.data= NULL; - vst->pkt.size= 0; - vst->pkt.buf = NULL; + av_packet_move_ref(pkt, &vst->pkt); if(vst->slices != vst->cur_slice) //FIXME find out how to set slices correct from the begin memmove(pkt->data + 1 + 8*vst->cur_slice, pkt->data + 1 + 8*vst->slices, vst->videobufpos - 1 - 8*vst->slices); @@ -1174,7 +1169,7 @@ static int ivr_read_header(AVFormatContext *s) uint8_t key[256], val[256]; AVIOContext *pb = s->pb; AVStream *st; - int64_t pos, offset, temp; + int64_t pos, offset=0, temp; pos = avio_tell(pb); tag = avio_rl32(pb); @@ -1191,6 +1186,8 @@ static int ivr_read_header(AVFormatContext *s) offset = temp; temp = avio_rb64(pb); } + if (offset <= 0) + return AVERROR_INVALIDDATA; avio_skip(pb, offset - avio_tell(pb)); if (avio_r8(pb) != 1) return AVERROR_INVALIDDATA; diff --git a/libavformat/rmenc.c b/libavformat/rmenc.c index 3bff4daf0a7..e137dbc44fe 100644 --- a/libavformat/rmenc.c +++ b/libavformat/rmenc.c @@ -360,7 +360,6 @@ static int rm_write_header(AVFormatContext *s) if (rv10_write_header(s, 0, 0)) return AVERROR_INVALIDDATA; - avio_flush(s->pb); return 0; } diff --git a/libavformat/rpl.c b/libavformat/rpl.c index 6afd3738100..208c50f00ce 100644 --- a/libavformat/rpl.c +++ b/libavformat/rpl.c @@ -338,7 +338,6 @@ static int rpl_read_packet(AVFormatContext *s, AVPacket *pkt) if (ret < 0) return ret; if (ret != frame_size) { - av_packet_unref(pkt); return AVERROR(EIO); } pkt->duration = 1; @@ -355,7 +354,6 @@ static int rpl_read_packet(AVFormatContext *s, AVPacket *pkt) if (ret < 0) return ret; if (ret != index_entry->size) { - av_packet_unref(pkt); return AVERROR(EIO); } diff --git a/libavformat/rsd.c b/libavformat/rsd.c index 396a431f346..e23c8abae5e 100644 --- a/libavformat/rsd.c +++ b/libavformat/rsd.c @@ -97,9 +97,8 @@ static int rsd_read_header(AVFormatContext *s) switch (par->codec_id) { case AV_CODEC_ID_XMA2: par->block_align = 2048; - ff_alloc_extradata(par, 34); - if (!par->extradata) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(par, 34)) < 0) + return ret; memset(par->extradata, 0, 34); break; case AV_CODEC_ID_ADPCM_PSX: diff --git a/libavformat/rsoenc.c b/libavformat/rsoenc.c index e34e2c64e20..beba94b2a7c 100644 --- a/libavformat/rsoenc.c +++ b/libavformat/rsoenc.c @@ -22,6 +22,7 @@ #include "avformat.h" #include "internal.h" +#include "rawenc.h" #include "riff.h" #include "rso.h" @@ -60,14 +61,6 @@ static int rso_write_header(AVFormatContext *s) avio_wb16(pb, par->sample_rate); avio_wb16(pb, 0x0000); /* play mode ? (0x0000 = don't loop) */ - avio_flush(pb); - - return 0; -} - -static int rso_write_packet(AVFormatContext *s, AVPacket *pkt) -{ - avio_write(s->pb, pkt->data, pkt->size); return 0; } @@ -105,7 +98,7 @@ AVOutputFormat ff_rso_muxer = { .audio_codec = AV_CODEC_ID_PCM_U8, .video_codec = AV_CODEC_ID_NONE, .write_header = rso_write_header, - .write_packet = rso_write_packet, + .write_packet = ff_raw_write_packet, .write_trailer = rso_write_trailer, .codec_tag = (const AVCodecTag* const []){ff_codec_rso_tags, 0}, .flags = AVFMT_NOTIMESTAMPS, diff --git a/libavformat/rtmpcrypt.c b/libavformat/rtmpcrypt.c index 253b8ca2ce1..a835ab263f5 100644 --- a/libavformat/rtmpcrypt.c +++ b/libavformat/rtmpcrypt.c @@ -240,7 +240,7 @@ static int rtmpe_close(URLContext *h) RTMPEContext *rt = h->priv_data; ff_dh_free(rt->dh); - ffurl_close(rt->stream); + ffurl_closep(&rt->stream); return 0; } diff --git a/libavformat/rtmphttp.c b/libavformat/rtmphttp.c index ef6146ca864..c920c19ab5b 100644 --- a/libavformat/rtmphttp.c +++ b/libavformat/rtmphttp.c @@ -176,7 +176,7 @@ static int rtmp_http_close(URLContext *h) } av_freep(&rt->out_data); - ffurl_close(rt->stream); + ffurl_closep(&rt->stream); return ret; } diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c index b741e421af9..d9741bc622c 100644 --- a/libavformat/rtmpproto.c +++ b/libavformat/rtmpproto.c @@ -48,7 +48,6 @@ #endif #define APP_MAX_LENGTH 1024 -#define PLAYPATH_MAX_LENGTH 512 #define TCURL_MAX_LENGTH 1024 #define FLASHVER_MAX_LENGTH 64 #define RTMP_PKTDATA_DEFAULT_SIZE 4096 @@ -164,7 +163,7 @@ static int add_tracked_method(RTMPContext *rt, const char *name, int id) if (rt->nb_tracked_methods + 1 > rt->tracked_methods_size) { rt->tracked_methods_size = (rt->nb_tracked_methods + 1) * 2; - if ((err = av_reallocp(&rt->tracked_methods, rt->tracked_methods_size * + if ((err = av_reallocp_array(&rt->tracked_methods, rt->tracked_methods_size, sizeof(*rt->tracked_methods))) < 0) { rt->nb_tracked_methods = 0; rt->tracked_methods_size = 0; @@ -1112,7 +1111,7 @@ static int rtmp_calc_swfhash(URLContext *s) RTMPContext *rt = s->priv_data; uint8_t *in_data = NULL, *out_data = NULL, *swfdata; int64_t in_size; - URLContext *stream; + URLContext *stream = NULL; char swfhash[32]; int swfsize; int ret = 0; @@ -2386,7 +2385,7 @@ static int handle_metadata(RTMPContext *rt, RTMPPacket *pkt) next += size + 3 + 4; } if (p != rt->flv_data + rt->flv_size) { - av_log(NULL, AV_LOG_WARNING, "Incomplete flv packets in " + av_log(rt, AV_LOG_WARNING, "Incomplete flv packets in " "RTMP_PT_METADATA packet\n"); rt->flv_size = p - rt->flv_data; } @@ -2512,7 +2511,7 @@ static int rtmp_close(URLContext *h) free_tracked_methods(rt); av_freep(&rt->flv_data); - ffurl_close(rt->stream); + ffurl_closep(&rt->stream); return ret; } @@ -2746,7 +2745,10 @@ static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **o } if (!rt->playpath) { - rt->playpath = av_malloc(PLAYPATH_MAX_LENGTH); + int max_len = 1; + if (fname) + max_len = strlen(fname) + 5; // add prefix "mp4:" + rt->playpath = av_malloc(max_len); if (!rt->playpath) { ret = AVERROR(ENOMEM); goto fail; @@ -2763,7 +2765,7 @@ static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **o fname[len - 4] = '\0'; rt->playpath[0] = 0; } - av_strlcat(rt->playpath, fname, PLAYPATH_MAX_LENGTH); + av_strlcat(rt->playpath, fname, max_len); } else { rt->playpath[0] = '\0'; } @@ -2822,8 +2824,7 @@ static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **o if (rt->do_reconnect) { int i; - ffurl_close(rt->stream); - rt->stream = NULL; + ffurl_closep(&rt->stream); rt->do_reconnect = 0; rt->nb_invokes = 0; for (i = 0; i < 2; i++) @@ -2880,6 +2881,9 @@ static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **o return 0; fail: + av_freep(&rt->playpath); + av_freep(&rt->tcurl); + av_freep(&rt->flashver); av_dict_free(opts); rtmp_close(s); return ret; diff --git a/libavformat/rtpdec.c b/libavformat/rtpdec.c index e75a34cb933..3d5b200099f 100644 --- a/libavformat/rtpdec.c +++ b/libavformat/rtpdec.c @@ -415,7 +415,6 @@ void ff_rtp_send_punch_packets(URLContext *rtp_handle) avio_wb32(pb, 0); /* Timestamp */ avio_wb32(pb, 0); /* SSRC */ - avio_flush(pb); len = avio_close_dyn_buf(pb, &buf); if ((len > 0) && buf) ffurl_write(rtp_handle, buf, len); @@ -430,7 +429,6 @@ void ff_rtp_send_punch_packets(URLContext *rtp_handle) avio_wb16(pb, 1); /* length in words - 1 */ avio_wb32(pb, 0); /* our own SSRC */ - avio_flush(pb); len = avio_close_dyn_buf(pb, &buf); if ((len > 0) && buf) ffurl_write(rtp_handle, buf, len); diff --git a/libavformat/rtpdec_ac3.c b/libavformat/rtpdec_ac3.c index 56a379f86cc..dd4a4e10541 100644 --- a/libavformat/rtpdec_ac3.c +++ b/libavformat/rtpdec_ac3.c @@ -62,9 +62,9 @@ static int ac3_handle_packet(AVFormatContext *ctx, PayloadContext *data, av_log(ctx, AV_LOG_ERROR, "Invalid AC3 packet data\n"); return AVERROR_INVALIDDATA; } - if (av_new_packet(pkt, len)) { + if ((err = av_new_packet(pkt, len)) < 0) { av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); - return AVERROR(ENOMEM); + return err; } pkt->stream_index = st->index; diff --git a/libavformat/rtpdec_amr.c b/libavformat/rtpdec_amr.c index 35d32228110..988b7bddfd0 100644 --- a/libavformat/rtpdec_amr.c +++ b/libavformat/rtpdec_amr.c @@ -51,7 +51,7 @@ static int amr_handle_packet(AVFormatContext *ctx, PayloadContext *data, { const uint8_t *frame_sizes = NULL; int frames; - int i; + int i, ret; const uint8_t *speech_data; uint8_t *ptr; @@ -93,9 +93,9 @@ static int amr_handle_packet(AVFormatContext *ctx, PayloadContext *data, speech_data = buf + 1 + frames; /* Everything except the codec mode request byte should be output. */ - if (av_new_packet(pkt, len - 1)) { + if ((ret = av_new_packet(pkt, len - 1)) < 0) { av_log(ctx, AV_LOG_ERROR, "Out of memory\n"); - return AVERROR(ENOMEM); + return ret; } pkt->stream_index = st->index; ptr = pkt->data; diff --git a/libavformat/rtpdec_h263.c b/libavformat/rtpdec_h263.c index 9b71ed7efe9..1905b435f8e 100644 --- a/libavformat/rtpdec_h263.c +++ b/libavformat/rtpdec_h263.c @@ -30,7 +30,7 @@ int ff_h263_handle_packet(AVFormatContext *ctx, PayloadContext *data, { uint8_t *ptr; uint16_t header; - int startcode, vrc, picture_header; + int startcode, vrc, picture_header, ret; if (len < 2) { av_log(ctx, AV_LOG_ERROR, "Too short H.263 RTP packet\n"); @@ -73,9 +73,9 @@ int ff_h263_handle_packet(AVFormatContext *ctx, PayloadContext *data, return AVERROR_INVALIDDATA; } - if (av_new_packet(pkt, len + startcode)) { + if ((ret = av_new_packet(pkt, len + startcode)) < 0) { av_log(ctx, AV_LOG_ERROR, "Out of memory\n"); - return AVERROR(ENOMEM); + return ret; } pkt->stream_index = st->index; ptr = pkt->data; diff --git a/libavformat/rtpdec_hevc.c b/libavformat/rtpdec_hevc.c index 5a06b2362cc..f467104ca56 100644 --- a/libavformat/rtpdec_hevc.c +++ b/libavformat/rtpdec_hevc.c @@ -25,6 +25,7 @@ #include "libavcodec/get_bits.h" #include "avformat.h" +#include "internal.h" #include "rtpdec.h" #include "rtpdec_formats.h" @@ -147,15 +148,9 @@ static av_cold int hevc_parse_sdp_line(AVFormatContext *ctx, int st_index, hevc_sdp_parse_fmtp_config); if (hevc_data->vps_size || hevc_data->sps_size || hevc_data->pps_size || hevc_data->sei_size) { - av_freep(&par->extradata); par->extradata_size = hevc_data->vps_size + hevc_data->sps_size + hevc_data->pps_size + hevc_data->sei_size; - par->extradata = av_malloc(par->extradata_size + - AV_INPUT_BUFFER_PADDING_SIZE); - if (!par->extradata) { - ret = AVERROR(ENOMEM); - par->extradata_size = 0; - } else { + if ((ret = ff_alloc_extradata(par, par->extradata_size)) >= 0) { int pos = 0; memcpy(par->extradata + pos, hevc_data->vps, hevc_data->vps_size); pos += hevc_data->vps_size; @@ -164,8 +159,6 @@ static av_cold int hevc_parse_sdp_line(AVFormatContext *ctx, int st_index, memcpy(par->extradata + pos, hevc_data->pps, hevc_data->pps_size); pos += hevc_data->pps_size; memcpy(par->extradata + pos, hevc_data->sei, hevc_data->sei_size); - pos += hevc_data->sei_size; - memset(par->extradata + pos, 0, AV_INPUT_BUFFER_PADDING_SIZE); } av_freep(&hevc_data->vps); diff --git a/libavformat/rtpdec_latm.c b/libavformat/rtpdec_latm.c index 9087d6bec54..104a00af189 100644 --- a/libavformat/rtpdec_latm.c +++ b/libavformat/rtpdec_latm.c @@ -115,9 +115,8 @@ static int parse_fmtp_config(AVStream *st, const char *value) ret = AVERROR_PATCHWELCOME; goto end; } - av_freep(&st->codecpar->extradata); - if (ff_alloc_extradata(st->codecpar, (get_bits_left(&gb) + 7)/8)) { - ret = AVERROR(ENOMEM); + ret = ff_alloc_extradata(st->codecpar, (get_bits_left(&gb) + 7)/8); + if (ret < 0) { goto end; } for (i = 0; i < st->codecpar->extradata_size; i++) diff --git a/libavformat/rtpdec_mpa_robust.c b/libavformat/rtpdec_mpa_robust.c index f4716edf747..c0355edec2e 100644 --- a/libavformat/rtpdec_mpa_robust.c +++ b/libavformat/rtpdec_mpa_robust.c @@ -90,9 +90,9 @@ static int mpa_robust_parse_packet(AVFormatContext *ctx, PayloadContext *data, return AVERROR_INVALIDDATA; } - if (av_new_packet(pkt, adu_size)) { + if ((err = av_new_packet(pkt, adu_size)) < 0) { av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); - return AVERROR(ENOMEM); + return err; } pkt->stream_index = st->index; @@ -120,9 +120,9 @@ static int mpa_robust_parse_packet(AVFormatContext *ctx, PayloadContext *data, if (!continuation && adu_size <= len) { /* One or more complete frames */ - if (av_new_packet(pkt, adu_size)) { + if ((err = av_new_packet(pkt, adu_size)) < 0) { av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); - return AVERROR(ENOMEM); + return err; } pkt->stream_index = st->index; diff --git a/libavformat/rtpdec_mpeg12.c b/libavformat/rtpdec_mpeg12.c index 43d9d5854c4..e640220ebee 100644 --- a/libavformat/rtpdec_mpeg12.c +++ b/libavformat/rtpdec_mpeg12.c @@ -29,6 +29,7 @@ static int mpeg_parse_packet(AVFormatContext *ctx, PayloadContext *data, int flags) { unsigned int h; + int ret; if (len <= 4) return AVERROR_INVALIDDATA; h = AV_RB32(buf); @@ -41,8 +42,8 @@ static int mpeg_parse_packet(AVFormatContext *ctx, PayloadContext *data, buf += 4; len -= 4; } - if (av_new_packet(pkt, len) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, len)) < 0) + return ret; memcpy(pkt->data, buf, len); pkt->stream_index = st->index; return 0; diff --git a/libavformat/rtpdec_mpeg4.c b/libavformat/rtpdec_mpeg4.c index 4f705990845..34c7950bcc5 100644 --- a/libavformat/rtpdec_mpeg4.c +++ b/libavformat/rtpdec_mpeg4.c @@ -70,6 +70,12 @@ typedef struct AttrNameMap { const char *str; uint16_t type; uint32_t offset; + + /** Range for integer values */ + struct Range { + int min; + int max; + } range; } AttrNameMap; /* All known fmtp parameters and the corresponding RTPAttrTypeEnum */ @@ -77,18 +83,24 @@ typedef struct AttrNameMap { #define ATTR_NAME_TYPE_STR 1 static const AttrNameMap attr_names[] = { { "SizeLength", ATTR_NAME_TYPE_INT, - offsetof(PayloadContext, sizelength) }, + offsetof(PayloadContext, sizelength), + {0, 32} }, // SizeLength number of bits used to encode AU-size integer value { "IndexLength", ATTR_NAME_TYPE_INT, - offsetof(PayloadContext, indexlength) }, + offsetof(PayloadContext, indexlength), + {0, 32} }, // IndexLength number of bits used to encode AU-Index integer value { "IndexDeltaLength", ATTR_NAME_TYPE_INT, - offsetof(PayloadContext, indexdeltalength) }, + offsetof(PayloadContext, indexdeltalength), + {0, 32} }, // IndexDeltaLength number of bits to encode AU-Index-delta integer value { "profile-level-id", ATTR_NAME_TYPE_INT, - offsetof(PayloadContext, profile_level_id) }, + offsetof(PayloadContext, profile_level_id), + {INT32_MIN, INT32_MAX} }, // It differs depending on StreamType { "StreamType", ATTR_NAME_TYPE_INT, - offsetof(PayloadContext, streamtype) }, + offsetof(PayloadContext, streamtype), + {0x00, 0x3F} }, // Values from ISO/IEC 14496-1, 'StreamType Values' table { "mode", ATTR_NAME_TYPE_STR, - offsetof(PayloadContext, mode) }, - { NULL, -1, -1 }, + offsetof(PayloadContext, mode), + {0} }, + { NULL, -1, -1, {0} }, }; static void close_context(PayloadContext *data) @@ -100,10 +112,10 @@ static void close_context(PayloadContext *data) static int parse_fmtp_config(AVCodecParameters *par, const char *value) { /* decode the hexa encoded parameter */ - int len = ff_hex_to_data(NULL, value); - av_freep(&par->extradata); - if (ff_alloc_extradata(par, len)) - return AVERROR(ENOMEM); + int len = ff_hex_to_data(NULL, value), ret; + + if ((ret = ff_alloc_extradata(par, len)) < 0) + return ret; ff_hex_to_data(par->extradata, value); return 0; } @@ -289,15 +301,24 @@ static int parse_fmtp(AVFormatContext *s, for (i = 0; attr_names[i].str; ++i) { if (!av_strcasecmp(attr, attr_names[i].str)) { if (attr_names[i].type == ATTR_NAME_TYPE_INT) { - int val = atoi(value); - if (val > 32) { + char *end_ptr = NULL; + long long int val = strtoll(value, &end_ptr, 10); + if (end_ptr == value || end_ptr[0] != '\0') { av_log(s, AV_LOG_ERROR, - "The %s field size is invalid (%d)\n", - attr, val); + "The %s field value is not a valid number: %s\n", + attr, value); return AVERROR_INVALIDDATA; } + if (val < attr_names[i].range.min || + val > attr_names[i].range.max) { + av_log(s, AV_LOG_ERROR, + "fmtp field %s should be in range [%d,%d] (provided value: %lld)", + attr, attr_names[i].range.min, attr_names[i].range.max, val); + return AVERROR_INVALIDDATA; + } + *(int *)((char *)data+ - attr_names[i].offset) = val; + attr_names[i].offset) = (int) val; } else if (attr_names[i].type == ATTR_NAME_TYPE_STR) { char *val = av_strdup(value); if (!val) diff --git a/libavformat/rtpdec_qdm2.c b/libavformat/rtpdec_qdm2.c index fa2b1b9302f..1eec2da5b4c 100644 --- a/libavformat/rtpdec_qdm2.c +++ b/libavformat/rtpdec_qdm2.c @@ -78,6 +78,7 @@ static int qdm2_parse_config(PayloadContext *qdm, AVStream *st, const uint8_t *buf, const uint8_t *end) { const uint8_t *p = buf; + int ret; while (end - p >= 2) { unsigned int item_len = p[0], config_item = p[1]; @@ -104,9 +105,10 @@ static int qdm2_parse_config(PayloadContext *qdm, AVStream *st, case 4: /* stream with extradata */ if (item_len < 30) return AVERROR_INVALIDDATA; - av_freep(&st->codecpar->extradata); - if (ff_alloc_extradata(st->codecpar, 26 + item_len)) { - return AVERROR(ENOMEM); + + ret = ff_alloc_extradata(st->codecpar, 26 + item_len); + if (ret < 0) { + return ret; } AV_WB32(st->codecpar->extradata, 12); memcpy(st->codecpar->extradata + 4, "frma", 4); diff --git a/libavformat/rtpdec_qt.c b/libavformat/rtpdec_qt.c index 77a3ce40be5..740c382d073 100644 --- a/libavformat/rtpdec_qt.c +++ b/libavformat/rtpdec_qt.c @@ -48,13 +48,13 @@ static int qt_rtp_parse_packet(AVFormatContext *s, PayloadContext *qt, GetBitContext gb; int packing_scheme, has_payload_desc, has_packet_info, alen, has_marker_bit = flags & RTP_FLAG_MARKER, - keyframe; + keyframe, ret; if (qt->remaining) { int num = qt->pkt.size / qt->bytes_per_frame; - if (av_new_packet(pkt, qt->bytes_per_frame)) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, qt->bytes_per_frame)) < 0) + return ret; pkt->stream_index = st->index; pkt->flags = qt->pkt.flags; memcpy(pkt->data, @@ -208,8 +208,8 @@ static int qt_rtp_parse_packet(AVFormatContext *s, PayloadContext *qt, alen % qt->bytes_per_frame != 0) return AVERROR_INVALIDDATA; /* wrongly padded */ qt->remaining = (alen / qt->bytes_per_frame) - 1; - if (av_new_packet(pkt, qt->bytes_per_frame)) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, qt->bytes_per_frame)) < 0) + return ret; memcpy(pkt->data, buf + avio_tell(&pb), qt->bytes_per_frame); pkt->flags = keyframe ? AV_PKT_FLAG_KEY : 0; pkt->stream_index = st->index; diff --git a/libavformat/rtpdec_svq3.c b/libavformat/rtpdec_svq3.c index 77164dd6f91..ffe21ac4c4a 100644 --- a/libavformat/rtpdec_svq3.c +++ b/libavformat/rtpdec_svq3.c @@ -58,10 +58,6 @@ static int svq3_parse_packet (AVFormatContext *s, PayloadContext *sv, len -= 2; if (config_packet) { - - av_freep(&st->codecpar->extradata); - st->codecpar->extradata_size = 0; - if (len < 2 || ff_alloc_extradata(st->codecpar, len + 8)) return AVERROR_INVALIDDATA; diff --git a/libavformat/rtpdec_xiph.c b/libavformat/rtpdec_xiph.c index 574508affb6..c2db10dab87 100644 --- a/libavformat/rtpdec_xiph.c +++ b/libavformat/rtpdec_xiph.c @@ -63,7 +63,7 @@ static int xiph_handle_packet(AVFormatContext *ctx, PayloadContext *data, int flags) { - int ident, fragmented, tdt, num_pkts, pkt_len; + int ident, fragmented, tdt, num_pkts, pkt_len, ret; if (!buf) { if (!data->split_buf || data->split_pos + 2 > data->split_buf_len || @@ -77,9 +77,9 @@ static int xiph_handle_packet(AVFormatContext *ctx, PayloadContext *data, av_log(ctx, AV_LOG_ERROR, "Not enough data to return\n"); return AVERROR_INVALIDDATA; } - if (av_new_packet(pkt, pkt_len)) { + if ((ret = av_new_packet(pkt, pkt_len)) < 0) { av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); - return AVERROR(ENOMEM); + return ret; } pkt->stream_index = st->index; memcpy(pkt->data, data->split_buf + data->split_pos, pkt_len); @@ -123,9 +123,9 @@ static int xiph_handle_packet(AVFormatContext *ctx, PayloadContext *data, len -= 6; if (fragmented == 0) { - if (av_new_packet(pkt, pkt_len)) { + if ((ret = av_new_packet(pkt, pkt_len)) < 0) { av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); - return AVERROR(ENOMEM); + return ret; } pkt->stream_index = st->index; memcpy(pkt->data, buf, pkt_len); @@ -228,6 +228,7 @@ parse_packed_headers(AVFormatContext *s, { unsigned num_packed, num_headers, length, length1, length2, extradata_alloc; + int ret; uint8_t *ptr; if (packed_headers_end - packed_headers < 9) { @@ -264,9 +265,9 @@ parse_packed_headers(AVFormatContext *s, * -- AV_INPUT_BUFFER_PADDING_SIZE required */ extradata_alloc = length + length/255 + 3 + AV_INPUT_BUFFER_PADDING_SIZE; - if (ff_alloc_extradata(par, extradata_alloc)) { + if ((ret = ff_alloc_extradata(par, extradata_alloc)) < 0) { av_log(s, AV_LOG_ERROR, "Out of memory\n"); - return AVERROR(ENOMEM); + return ret; } ptr = par->extradata; *ptr++ = 2; diff --git a/libavformat/rtpenc_mpegts.c b/libavformat/rtpenc_mpegts.c index 969dbff3d6c..7d7377db7a5 100644 --- a/libavformat/rtpenc_mpegts.c +++ b/libavformat/rtpenc_mpegts.c @@ -60,6 +60,7 @@ static int rtp_mpegts_write_header(AVFormatContext *s) return AVERROR(ENOMEM); mpegts_ctx->oformat = mpegts_format; mpegts_ctx->max_delay = s->max_delay; + av_dict_copy(&mpegts_ctx->metadata, s->metadata, 0); for (i = 0; i < s->nb_streams; i++) { AVStream* st = avformat_new_stream(mpegts_ctx, NULL); if (!st) @@ -102,10 +103,10 @@ static int rtp_mpegts_write_header(AVFormatContext *s) fail: if (mpegts_ctx) { ffio_free_dyn_buf(&mpegts_ctx->pb); + av_dict_free(&mpegts_ctx->metadata); avformat_free_context(mpegts_ctx); } - if (rtp_ctx) - avformat_free_context(rtp_ctx); + avformat_free_context(rtp_ctx); rtp_mpegts_write_close(s); return ret; } diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c index 1f0a82ac7e5..19e940d01e6 100644 --- a/libavformat/rtpproto.c +++ b/libavformat/rtpproto.c @@ -301,8 +301,7 @@ static int rtp_open(URLContext *h, const char *uri, int flags) goto fail; } if (s->ttl > 0) { - snprintf(buf, sizeof (buf), "%d", s->ttl); - av_dict_set(&fec_opts, "ttl", buf, 0); + av_dict_set_int(&fec_opts, "ttl", s->ttl, 0); } } @@ -363,10 +362,8 @@ static int rtp_open(URLContext *h, const char *uri, int flags) return 0; fail: - if (s->rtp_hd) - ffurl_close(s->rtp_hd); - if (s->rtcp_hd) - ffurl_close(s->rtcp_hd); + ffurl_closep(&s->rtp_hd); + ffurl_closep(&s->rtcp_hd); ffurl_closep(&s->fec_hd); av_free(fec_protocol); av_dict_free(&fec_opts); @@ -506,8 +503,8 @@ static int rtp_close(URLContext *h) ff_ip_reset_filters(&s->filters); - ffurl_close(s->rtp_hd); - ffurl_close(s->rtcp_hd); + ffurl_closep(&s->rtp_hd); + ffurl_closep(&s->rtcp_hd); ffurl_closep(&s->fec_hd); return 0; } diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index c153cac88b1..5d8491b74ba 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -21,6 +21,7 @@ #include "libavutil/avassert.h" #include "libavutil/base64.h" +#include "libavutil/bprint.h" #include "libavutil/avstring.h" #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" @@ -762,9 +763,7 @@ void ff_rtsp_undo_setup(AVFormatContext *s, int send_packets) ff_rtp_parse_close(rtsp_st->transport_priv); } rtsp_st->transport_priv = NULL; - if (rtsp_st->rtp_handle) - ffurl_close(rtsp_st->rtp_handle); - rtsp_st->rtp_handle = NULL; + ffurl_closep(&rtsp_st->rtp_handle); } } @@ -1318,6 +1317,9 @@ static int rtsp_send_cmd_with_content_async(AVFormatContext *s, char buf[4096], *out_buf; char base64buf[AV_BASE64_SIZE(sizeof(buf))]; + if (!rt->rtsp_hd_out) + return AVERROR(ENOTCONN); + /* Add in RTSP headers */ out_buf = buf; rt->seq++; @@ -1613,6 +1615,7 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, char url[1024], namebuf[50], optbuf[20] = ""; struct sockaddr_storage addr; int port, ttl; + AVDictionary *opts = map_to_opts(rt); if (reply->transports[0].destination.ss_family) { addr = reply->transports[0].destination; @@ -1629,8 +1632,11 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); ff_url_join(url, sizeof(url), "rtp", NULL, namebuf, port, "%s", optbuf); - if (ffurl_open_whitelist(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE, - &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL) < 0) { + err = ffurl_open_whitelist(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE, + &s->interrupt_callback, &opts, s->protocol_whitelist, s->protocol_blacklist, NULL); + av_dict_free(&opts); + + if (err < 0) { err = AVERROR_INVALIDDATA; goto fail; } @@ -1658,9 +1664,10 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, void ff_rtsp_close_connections(AVFormatContext *s) { RTSPState *rt = s->priv_data; - if (rt->rtsp_hd_out != rt->rtsp_hd) ffurl_close(rt->rtsp_hd_out); - ffurl_close(rt->rtsp_hd); - rt->rtsp_hd = rt->rtsp_hd_out = NULL; + if (rt->rtsp_hd_out != rt->rtsp_hd) + ffurl_closep(&rt->rtsp_hd_out); + rt->rtsp_hd_out = NULL; + ffurl_closep(&rt->rtsp_hd); } int ff_rtsp_connect(AVFormatContext *s) @@ -2444,7 +2451,7 @@ static int rtp_probe(const AVProbeData *p) static int rtp_read_header(AVFormatContext *s) { uint8_t recvbuf[RTP_MAX_PACKET_LENGTH]; - char host[500], sdp[500]; + char host[500], filters_buf[1000]; int ret, port; URLContext* in = NULL; int payload_type; @@ -2453,6 +2460,8 @@ static int rtp_read_header(AVFormatContext *s) AVIOContext pb; socklen_t addrlen = sizeof(addr); RTSPState *rt = s->priv_data; + const char *p; + AVBPrint sdp; if (!ff_network_init()) return AVERROR(EIO); @@ -2486,8 +2495,7 @@ static int rtp_read_header(AVFormatContext *s) break; } getsockname(ffurl_get_file_handle(in), (struct sockaddr*) &addr, &addrlen); - ffurl_close(in); - in = NULL; + ffurl_closep(&in); par = avcodec_parameters_alloc(); if (!par) { @@ -2510,16 +2518,39 @@ static int rtp_read_header(AVFormatContext *s) av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, NULL, 0, s->url); - snprintf(sdp, sizeof(sdp), - "v=0\r\nc=IN IP%d %s\r\nm=%s %d RTP/AVP %d\r\n", - addr.ss_family == AF_INET ? 4 : 6, host, - par->codec_type == AVMEDIA_TYPE_DATA ? "application" : - par->codec_type == AVMEDIA_TYPE_VIDEO ? "video" : "audio", - port, payload_type); - av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", sdp); + av_bprint_init(&sdp, 0, AV_BPRINT_SIZE_UNLIMITED); + av_bprintf(&sdp, "v=0\r\nc=IN IP%d %s\r\n", + addr.ss_family == AF_INET ? 4 : 6, host); + + p = strchr(s->url, '?'); + if (p) { + static const char filters[][2][8] = { { "sources", "incl" }, + { "block", "excl" } }; + int i; + char *q; + for (i = 0; i < FF_ARRAY_ELEMS(filters); i++) { + if (av_find_info_tag(filters_buf, sizeof(filters_buf), filters[i][0], p)) { + q = filters_buf; + while ((q = strchr(q, ',')) != NULL) + *q = ' '; + av_bprintf(&sdp, "a=source-filter:%s IN IP%d %s %s\r\n", + filters[i][1], + addr.ss_family == AF_INET ? 4 : 6, host, + filters_buf); + } + } + } + + av_bprintf(&sdp, "m=%s %d RTP/AVP %d\r\n", + par->codec_type == AVMEDIA_TYPE_DATA ? "application" : + par->codec_type == AVMEDIA_TYPE_VIDEO ? "video" : "audio", + port, payload_type); + av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", sdp.str); + if (!av_bprint_is_complete(&sdp)) + goto fail_nobuf; avcodec_parameters_free(&par); - ffio_init_context(&pb, sdp, strlen(sdp), 0, NULL, NULL, NULL, NULL); + ffio_init_context(&pb, sdp.str, sdp.len, 0, NULL, NULL, NULL, NULL); s->pb = &pb; /* sdp_read_header initializes this again */ @@ -2529,12 +2560,16 @@ static int rtp_read_header(AVFormatContext *s) ret = sdp_read_header(s); s->pb = NULL; + av_bprint_finalize(&sdp, NULL); return ret; +fail_nobuf: + ret = AVERROR(ENOMEM); + av_log(s, AV_LOG_ERROR, "rtp_read_header(): not enough buffer space for sdp-headers\n"); + av_bprint_finalize(&sdp, NULL); fail: avcodec_parameters_free(&par); - if (in) - ffurl_close(in); + ffurl_closep(&in); ff_network_close(); return ret; } diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c index bd2e8f47f1a..dfa29913bff 100644 --- a/libavformat/rtspdec.c +++ b/libavformat/rtspdec.c @@ -289,9 +289,7 @@ static int rtsp_read_setup(AVFormatContext *s, char* host, char *controlurl) } else { do { AVDictionary *opts = NULL; - char buf[256]; - snprintf(buf, sizeof(buf), "%d", rt->buffer_size); - av_dict_set(&opts, "buffer_size", buf, 0); + av_dict_set_int(&opts, "buffer_size", rt->buffer_size, 0); ff_url_join(url, sizeof(url), "rtp", NULL, host, localport, NULL); av_log(s, AV_LOG_TRACE, "Opening: %s", url); ret = ffurl_open_whitelist(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE, diff --git a/libavformat/s337m.c b/libavformat/s337m.c index 48ab66a6da7..36e1047af82 100644 --- a/libavformat/s337m.c +++ b/libavformat/s337m.c @@ -31,7 +31,7 @@ #define IS_24LE_MARKER(state) ((state & 0xFFFFFFFFFFFF) == MARKER_24LE) #define IS_LE_MARKER(state) (IS_16LE_MARKER(state) || IS_20LE_MARKER(state) || IS_24LE_MARKER(state)) -static int s337m_get_offset_and_codec(AVFormatContext *s, +static int s337m_get_offset_and_codec(void *avc, uint64_t state, int data_type, int data_size, int *offset, enum AVCodecID *codec) @@ -50,8 +50,8 @@ static int s337m_get_offset_and_codec(AVFormatContext *s, } if ((data_type & 0x1F) != 0x1C) { - if (s) - avpriv_report_missing_feature(s, "Data type %#x in SMPTE 337M", data_type & 0x1F); + if (avc) + avpriv_report_missing_feature(avc, "Data type %#x in SMPTE 337M", data_type & 0x1F); return AVERROR_PATCHWELCOME; } @@ -72,8 +72,8 @@ static int s337m_get_offset_and_codec(AVFormatContext *s, *offset = 1601; break; default: - if (s) - avpriv_report_missing_feature(s, "Dolby E data size %d in SMPTE 337M", data_size); + if (avc) + avpriv_report_missing_feature(avc, "Dolby E data size %d in SMPTE 337M", data_size); return AVERROR_PATCHWELCOME; } @@ -174,7 +174,6 @@ static int s337m_read_packet(AVFormatContext *s, AVPacket *pkt) pkt->pos = pos; if (avio_read(pb, pkt->data, pkt->size) < pkt->size) { - av_packet_unref(pkt); return AVERROR_EOF; } @@ -186,7 +185,6 @@ static int s337m_read_packet(AVFormatContext *s, AVPacket *pkt) if (!s->nb_streams) { AVStream *st = avformat_new_stream(s, NULL); if (!st) { - av_packet_unref(pkt); return AVERROR(ENOMEM); } st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; diff --git a/libavformat/samidec.c b/libavformat/samidec.c index fd98393086b..3070ef9bacf 100644 --- a/libavformat/samidec.c +++ b/libavformat/samidec.c @@ -108,6 +108,8 @@ static int sami_read_header(AVFormatContext *s) ff_subtitles_queue_finalize(s, &sami->q); end: + if (res < 0) + ff_subtitles_queue_clean(&sami->q); av_bprint_finalize(&buf, NULL); return res; } diff --git a/libavformat/sapdec.c b/libavformat/sapdec.c index eddeddeaeae..eec73aa2f45 100644 --- a/libavformat/sapdec.c +++ b/libavformat/sapdec.c @@ -54,8 +54,7 @@ static int sap_read_close(AVFormatContext *s) struct SAPState *sap = s->priv_data; if (sap->sdp_ctx) avformat_close_input(&sap->sdp_ctx); - if (sap->ann_fd) - ffurl_close(sap->ann_fd); + ffurl_closep(&sap->ann_fd); av_freep(&sap->sdp); ff_network_close(); return 0; @@ -142,6 +141,10 @@ static int sap_read_header(AVFormatContext *s) } sap->sdp = av_strdup(&recvbuf[pos]); + if (!sap->sdp) { + ret = AVERROR(ENOMEM); + goto fail; + } break; } @@ -221,7 +224,6 @@ static int sap_fetch_packet(AVFormatContext *s, AVPacket *pkt) int i = s->nb_streams; AVStream *st = avformat_new_stream(s, NULL); if (!st) { - av_packet_unref(pkt); return AVERROR(ENOMEM); } st->id = i; diff --git a/libavformat/sapenc.c b/libavformat/sapenc.c index f9afab0c33e..dc1c2104082 100644 --- a/libavformat/sapenc.c +++ b/libavformat/sapenc.c @@ -60,8 +60,7 @@ static int sap_write_close(AVFormatContext *s) } av_freep(&sap->ann); - if (sap->ann_fd) - ffurl_close(sap->ann_fd); + ffurl_closep(&sap->ann_fd); ff_network_close(); return 0; } diff --git a/libavformat/sbgdec.c b/libavformat/sbgdec.c index 4155395da07..de1de271bbe 100644 --- a/libavformat/sbgdec.c +++ b/libavformat/sbgdec.c @@ -1327,7 +1327,7 @@ static int generate_intervals(void *log, struct sbg_script *s, int sample_rate, static int encode_intervals(struct sbg_script *s, AVCodecParameters *par, struct ws_intervals *inter) { - int i, edata_size = 4; + int i, edata_size = 4, ret; uint8_t *edata; for (i = 0; i < inter->nb_inter; i++) { @@ -1336,8 +1336,8 @@ static int encode_intervals(struct sbg_script *s, AVCodecParameters *par, if (edata_size < 0) return AVERROR(ENOMEM); } - if (ff_alloc_extradata(par, edata_size)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(par, edata_size)) < 0) + return ret; edata = par->extradata; #define ADD_EDATA32(v) do { AV_WL32(edata, (v)); edata += 4; } while(0) @@ -1446,6 +1446,7 @@ static av_cold int sbg_read_header(AVFormatContext *avf) static int sbg_read_packet(AVFormatContext *avf, AVPacket *packet) { int64_t ts, end_ts; + int ret; ts = avf->streams[0]->cur_dts; end_ts = ts + avf->streams[0]->codecpar->frame_size; @@ -1454,8 +1455,8 @@ static int sbg_read_packet(AVFormatContext *avf, AVPacket *packet) end_ts); if (end_ts <= ts) return AVERROR_EOF; - if (av_new_packet(packet, 12) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(packet, 12)) < 0) + return ret; packet->dts = packet->pts = ts; packet->duration = end_ts - ts; AV_WL64(packet->data + 0, ts); diff --git a/libavformat/sccdec.c b/libavformat/sccdec.c index 412d8aaf49e..df4c94a6ea3 100644 --- a/libavformat/sccdec.c +++ b/libavformat/sccdec.c @@ -22,6 +22,7 @@ #include "avformat.h" #include "internal.h" #include "subtitles.h" +#include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/intreadwrite.h" @@ -63,6 +64,7 @@ static int scc_read_header(AVFormatContext *s) SCCContext *scc = s->priv_data; AVStream *st = avformat_new_stream(s, NULL); char line[4096], line2[4096]; + int64_t ts_start, ts_end; int count = 0, ret = 0; ptrdiff_t len2, len; uint8_t out[4096]; @@ -77,14 +79,14 @@ static int scc_read_header(AVFormatContext *s) st->codecpar->codec_id = AV_CODEC_ID_EIA_608; while (!ff_text_eof(&tr)) { - const int64_t pos = ff_text_pos(&tr); + int64_t current_pos, next_pos; char *saveptr = NULL, *lline; int hh1, mm1, ss1, fs1, i; int hh2, mm2, ss2, fs2; - int64_t ts_start, ts_end; AVPacket *sub; if (count == 0) { + current_pos = ff_text_pos(&tr); while (!ff_text_eof(&tr)) { len = ff_subtitles_read_line(&tr, line, sizeof(line)); if (len > 13) @@ -94,17 +96,18 @@ static int scc_read_header(AVFormatContext *s) if (!strncmp(line, "Scenarist_SCC V1.0", 18)) continue; - if (sscanf(line, "%d:%d:%d%*[:;]%d", &hh1, &mm1, &ss1, &fs1) != 4) + if (av_sscanf(line, "%d:%d:%d%*[:;]%d", &hh1, &mm1, &ss1, &fs1) != 4) continue; ts_start = (hh1 * 3600LL + mm1 * 60LL + ss1) * 1000LL + fs1 * 33; + next_pos = ff_text_pos(&tr); while (!ff_text_eof(&tr)) { len2 = ff_subtitles_read_line(&tr, line2, sizeof(line2)); if (len2 > 13) break; } - if (sscanf(line2, "%d:%d:%d%*[:;]%d", &hh2, &mm2, &ss2, &fs2) != 4) + if (av_sscanf(line2, "%d:%d:%d%*[:;]%d", &hh2, &mm2, &ss2, &fs2) != 4) continue; ts_end = (hh2 * 3600LL + mm2 * 60LL + ss2) * 1000LL + fs2 * 33; @@ -121,7 +124,7 @@ static int scc_read_header(AVFormatContext *s) if (!ptr) break; - if (sscanf(ptr, "%c%c%c%c", &c1, &c2, &c3, &c4) != 4) + if (av_sscanf(ptr, "%c%c%c%c", &c1, &c2, &c3, &c4) != 4) break; lline = NULL; @@ -133,22 +136,28 @@ static int scc_read_header(AVFormatContext *s) sub = ff_subtitles_queue_insert(&scc->q, out, i, 0); if (!sub) - return AVERROR(ENOMEM); + goto fail; - sub->pos = pos; + sub->pos = current_pos; sub->pts = ts_start; - sub->duration = FFMAX(1200, ts_end - ts_start); + sub->duration = ts_end - ts_start; memmove(line, line2, sizeof(line)); + current_pos = next_pos; line2[0] = 0; - FFSWAP(ptrdiff_t, len, len2); } - if (line[0]) + if (line[0]) { + ts_start = ts_end; + ts_end += 1200; goto try_again; + } ff_subtitles_queue_finalize(s, &scc->q); return ret; +fail: + ff_subtitles_queue_clean(&scc->q); + return AVERROR(ENOMEM); } static int scc_read_packet(AVFormatContext *s, AVPacket *pkt) diff --git a/libavformat/sdp.c b/libavformat/sdp.c index 34e9839b67a..2ce1a62262d 100644 --- a/libavformat/sdp.c +++ b/libavformat/sdp.c @@ -212,7 +212,7 @@ static char *extradata2psets(AVFormatContext *s, AVCodecParameters *par) p += strlen(p); r = r1; } - if (sps && sps_end - sps >= 4) { + if (sps && sps_end - sps >= 4 && p - psets <= MAX_PSET_SIZE - strlen(profile_string) - 7) { memcpy(p, profile_string, strlen(profile_string)); p += strlen(p); ff_data_to_hex(p, sps + 1, 3, 0); diff --git a/libavformat/sdr2.c b/libavformat/sdr2.c index 50abdf9397c..3743d59e586 100644 --- a/libavformat/sdr2.c +++ b/libavformat/sdr2.c @@ -90,12 +90,11 @@ static int sdr2_read_packet(AVFormatContext *s, AVPacket *pkt) avio_skip(s->pb, 30); if (pos == FIRST) { - if (av_new_packet(pkt, next - 52 + 24) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, next - 52 + 24)) < 0) + return ret; memcpy(pkt->data, header, 24); ret = avio_read(s->pb, pkt->data + 24, next - 52); if (ret < 0) { - av_packet_unref(pkt); return ret; } av_shrink_packet(pkt, ret + 24); diff --git a/libavformat/sdsdec.c b/libavformat/sdsdec.c index 9c361cdff2e..c70f5af8494 100644 --- a/libavformat/sdsdec.c +++ b/libavformat/sdsdec.c @@ -43,7 +43,7 @@ static void byte2_read(const uint8_t *src, uint32_t *dst) int i; for (i = 0; i < 120; i += 2) { - unsigned sample = (src[i + 0] << 25) + (src[i + 1] << 18); + unsigned sample = ((unsigned)src[i + 0] << 25) + ((unsigned)src[i + 1] << 18); dst[i / 2] = sample; } @@ -56,7 +56,7 @@ static void byte3_read(const uint8_t *src, uint32_t *dst) for (i = 0; i < 120; i += 3) { unsigned sample; - sample = (src[i + 0] << 25) | (src[i + 1] << 18) | (src[i + 2] << 11); + sample = ((unsigned)src[i + 0] << 25) | ((unsigned)src[i + 1] << 18) | ((unsigned)src[i + 2] << 11); dst[i / 3] = sample; } } @@ -68,7 +68,7 @@ static void byte4_read(const uint8_t *src, uint32_t *dst) for (i = 0; i < 120; i += 4) { unsigned sample; - sample = (src[i + 0] << 25) | (src[i + 1] << 18) | (src[i + 2] << 11) | (src[i + 3] << 4); + sample = ((unsigned)src[i + 0] << 25) | ((unsigned)src[i + 1] << 18) | ((unsigned)src[i + 2] << 11) | ((unsigned)src[i + 3] << 4); dst[i / 4] = sample; } } diff --git a/libavformat/segafilmenc.c b/libavformat/segafilmenc.c index 524230e461c..93c482ef7d8 100644 --- a/libavformat/segafilmenc.c +++ b/libavformat/segafilmenc.c @@ -45,7 +45,6 @@ typedef struct FILMPacket { } FILMPacket; typedef struct FILMOutputContext { - const AVClass *class; int audio_index; int video_index; int64_t stab_pos; @@ -70,7 +69,7 @@ static int film_write_packet_to_header(AVFormatContext *format_context, FILMPack info2 = pkt->duration; /* The top bit being set indicates a key frame */ if (!pkt->keyframe) - info1 |= (1 << 31); + info1 |= 1U << 31; } /* Write the 16-byte sample info packet to the STAB chunk in the header */ @@ -147,10 +146,8 @@ static int get_audio_codec_id(enum AVCodecID codec_id) case AV_CODEC_ID_PCM_S8_PLANAR: case AV_CODEC_ID_PCM_S16BE_PLANAR: return 0; - break; case AV_CODEC_ID_ADPCM_ADX: return 2; - break; default: return -1; } @@ -158,7 +155,6 @@ static int get_audio_codec_id(enum AVCodecID codec_id) static int film_init(AVFormatContext *format_context) { - AVStream *audio = NULL; FILMOutputContext *film = format_context->priv_data; film->audio_index = -1; film->video_index = -1; @@ -174,8 +170,12 @@ static int film_init(AVFormatContext *format_context) av_log(format_context, AV_LOG_ERROR, "Sega FILM allows a maximum of one audio stream.\n"); return AVERROR(EINVAL); } + if (get_audio_codec_id(st->codecpar->codec_id) < 0) { + av_log(format_context, AV_LOG_ERROR, + "Incompatible audio stream format.\n"); + return AVERROR(EINVAL); + } film->audio_index = i; - audio = st; } if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { @@ -183,17 +183,23 @@ static int film_init(AVFormatContext *format_context) av_log(format_context, AV_LOG_ERROR, "Sega FILM allows a maximum of one video stream.\n"); return AVERROR(EINVAL); } + if (st->codecpar->codec_id != AV_CODEC_ID_CINEPAK && + st->codecpar->codec_id != AV_CODEC_ID_RAWVIDEO) { + av_log(format_context, AV_LOG_ERROR, + "Incompatible video stream format.\n"); + return AVERROR(EINVAL); + } + if (st->codecpar->format != AV_PIX_FMT_RGB24) { + av_log(format_context, AV_LOG_ERROR, + "Pixel format must be rgb24.\n"); + return AVERROR(EINVAL); + } film->video_index = i; } - - if (film->video_index == -1) { - av_log(format_context, AV_LOG_ERROR, "No video stream present.\n"); - return AVERROR(EINVAL); - } } - if (audio != NULL && get_audio_codec_id(audio->codecpar->codec_id) < 0) { - av_log(format_context, AV_LOG_ERROR, "Incompatible audio stream format.\n"); + if (film->video_index == -1) { + av_log(format_context, AV_LOG_ERROR, "No video stream present.\n"); return AVERROR(EINVAL); } @@ -203,7 +209,7 @@ static int film_init(AVFormatContext *format_context) static int shift_data(AVFormatContext *format_context, int64_t shift_size) { int ret = 0; - int64_t pos, pos_end = avio_tell(format_context->pb); + int64_t pos, pos_end; uint8_t *buf, *read_buf[2]; int read_buf_id = 0; int read_size[2]; @@ -261,11 +267,9 @@ static int film_write_header(AVFormatContext *format_context) { int ret = 0; int64_t sample_table_size, stabsize, headersize; - int8_t audio_codec; AVIOContext *pb = format_context->pb; FILMOutputContext *film = format_context->priv_data; FILMPacket *prev, *packet; - AVStream *audio = NULL; AVStream *video = NULL; /* Calculate how much we need to reserve for the header; @@ -282,24 +286,6 @@ static int film_write_header(AVFormatContext *format_context) /* Seek back to the beginning to start writing the header now */ avio_seek(pb, 0, SEEK_SET); - if (film->audio_index > -1) - audio = format_context->streams[film->audio_index]; - if (film->video_index > -1) - video = format_context->streams[film->video_index]; - - if (audio != NULL) { - audio_codec = get_audio_codec_id(audio->codecpar->codec_id); - if (audio_codec < 0) { - av_log(format_context, AV_LOG_ERROR, "Incompatible audio stream format.\n"); - return AVERROR(EINVAL); - } - } - - if (video->codecpar->format != AV_PIX_FMT_RGB24) { - av_log(format_context, AV_LOG_ERROR, "Pixel format must be rgb24.\n"); - return AVERROR(EINVAL); - } - /* First, write the FILM header; this is very simple */ ffio_wfourcc(pb, "FILM"); @@ -314,6 +300,8 @@ static int film_write_header(AVFormatContext *format_context) ffio_wfourcc(pb, "FDSC"); avio_wb32(pb, 0x20); /* Size of FDSC chunk */ + video = format_context->streams[film->video_index]; + /* The only two supported codecs; raw video is rare */ switch (video->codecpar->codec_id) { case AV_CODEC_ID_CINEPAK: @@ -322,16 +310,16 @@ static int film_write_header(AVFormatContext *format_context) case AV_CODEC_ID_RAWVIDEO: ffio_wfourcc(pb, "raw "); break; - default: - av_log(format_context, AV_LOG_ERROR, "Incompatible video stream format.\n"); - return AVERROR(EINVAL); } avio_wb32(pb, video->codecpar->height); avio_wb32(pb, video->codecpar->width); avio_w8(pb, 24); /* Bits per pixel - observed to always be 24 */ - if (audio != NULL) { + if (film->audio_index > -1) { + AVStream *audio = format_context->streams[film->audio_index]; + int audio_codec = get_audio_codec_id(audio->codecpar->codec_id); + avio_w8(pb, audio->codecpar->channels); /* Audio channels */ avio_w8(pb, audio->codecpar->bits_per_coded_sample); /* Audio bit depth */ avio_w8(pb, audio_codec); /* Compression - 0 is PCM, 2 is ADX */ @@ -364,8 +352,6 @@ static int film_write_header(AVFormatContext *format_context) avio_wb32(pb, film->packet_count); - avio_flush(pb); - /* Finally, write out each packet's data to the header */ packet = film->start; while (packet != NULL) { @@ -374,15 +360,22 @@ static int film_write_header(AVFormatContext *format_context) packet = packet->next; av_freep(&prev); } + film->start = film->last = NULL; return 0; } -static const AVClass film_muxer_class = { - .class_name = "Sega FILM muxer", - .item_name = av_default_item_name, - .version = LIBAVUTIL_VERSION_INT, -}; +static void film_deinit(AVFormatContext *format_context) +{ + FILMOutputContext *film = format_context->priv_data; + FILMPacket *packet = film->start; + while (packet != NULL) { + FILMPacket *next = packet->next; + av_free(packet); + packet = next; + } + film->start = film->last = NULL; +} AVOutputFormat ff_segafilm_muxer = { .name = "film_cpk", @@ -394,5 +387,5 @@ AVOutputFormat ff_segafilm_muxer = { .init = film_init, .write_trailer = film_write_header, .write_packet = film_write_packet, - .priv_class = &film_muxer_class, + .deinit = film_deinit, }; diff --git a/libavformat/segment.c b/libavformat/segment.c index e3082063d8f..2ff2b5372e8 100644 --- a/libavformat/segment.c +++ b/libavformat/segment.c @@ -75,7 +75,6 @@ typedef struct SegmentContext { ff_const59 AVOutputFormat *oformat; AVFormatContext *avf; char *format; ///< format to use for output segment files - char *format_options_str; ///< format options to use for output segment files AVDictionary *format_options; char *list; ///< filename for the segment list file int list_flags; ///< flags affecting list generation @@ -163,12 +162,11 @@ static int segment_mux_init(AVFormatContext *s) oc->flags = s->flags; for (i = 0; i < s->nb_streams; i++) { - AVStream *st; - AVCodecParameters *ipar, *opar; + AVStream *st, *ist = s->streams[i]; + AVCodecParameters *ipar = ist->codecpar, *opar; if (!(st = avformat_new_stream(oc, NULL))) return AVERROR(ENOMEM); - ipar = s->streams[i]->codecpar; opar = st->codecpar; avcodec_parameters_copy(opar, ipar); if (!oc->oformat->codec_tag || @@ -178,16 +176,17 @@ static int segment_mux_init(AVFormatContext *s) } else { opar->codec_tag = 0; } - st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio; - st->time_base = s->streams[i]->time_base; - st->avg_frame_rate = s->streams[i]->avg_frame_rate; + st->sample_aspect_ratio = ist->sample_aspect_ratio; + st->time_base = ist->time_base; + st->avg_frame_rate = ist->avg_frame_rate; + st->disposition = ist->disposition; #if FF_API_LAVF_AVCTX FF_DISABLE_DEPRECATION_WARNINGS - if (s->streams[i]->codecpar->codec_tag == MKTAG('t','m','c','d')) - st->codec->time_base = s->streams[i]->codec->time_base; + if (ipar->codec_tag == MKTAG('t','m','c','d')) + st->codec->time_base = ist->codec->time_base; FF_ENABLE_DEPRECATION_WARNINGS #endif - av_dict_copy(&st->metadata, s->streams[i]->metadata, 0); + av_dict_copy(&st->metadata, ist->metadata, 0); } return 0; @@ -720,15 +719,6 @@ static int seg_init(AVFormatContext *s) } } - if (seg->format_options_str) { - ret = av_dict_parse_string(&seg->format_options, seg->format_options_str, "=", ":", 0); - if (ret < 0) { - av_log(s, AV_LOG_ERROR, "Could not parse format options list '%s'\n", - seg->format_options_str); - return ret; - } - } - if (seg->list) { if (seg->list_type == LIST_TYPE_UNDEFINED) { if (av_match_ext(seg->list, "csv" )) seg->list_type = LIST_TYPE_CSV; @@ -791,7 +781,7 @@ static int seg_init(AVFormatContext *s) ret = avformat_init_output(oc, &options); if (av_dict_count(options)) { av_log(s, AV_LOG_ERROR, - "Some of the provided format options in '%s' are not recognized\n", seg->format_options_str); + "Some of the provided format options are not recognized\n"); av_dict_free(&options); return AVERROR(EINVAL); } @@ -892,7 +882,6 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) goto calc_times; } memcpy(st->codecpar->extradata, pkt_extradata, pkt_extradata_size); - st->codecpar->extradata_size = pkt_extradata_size; } } @@ -982,7 +971,8 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base)); - ret = ff_write_chained(seg->avf, pkt->stream_index, pkt, s, seg->initial_offset || seg->reset_timestamps); + ret = ff_write_chained(seg->avf, pkt->stream_index, pkt, s, + seg->initial_offset || seg->reset_timestamps || seg->avf->oformat->interleave_packet); fail: if (pkt->stream_index == seg->reference_stream_index) { @@ -1017,7 +1007,6 @@ static int seg_write_trailer(struct AVFormatContext *s) if (seg->list) ff_format_io_close(s, &seg->list_pb); - av_dict_free(&seg->format_options); av_opt_free(seg); av_freep(&seg->times); av_freep(&seg->frames); @@ -1045,10 +1034,8 @@ static int seg_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt) if (ret == 1) { AVStream *st = s->streams[pkt->stream_index]; AVStream *ost = oc->streams[pkt->stream_index]; - st->internal->bsfcs = ost->internal->bsfcs; - st->internal->nb_bsfcs = ost->internal->nb_bsfcs; - ost->internal->bsfcs = NULL; - ost->internal->nb_bsfcs = 0; + st->internal->bsfc = ost->internal->bsfc; + ost->internal->bsfc = NULL; } return ret; } @@ -1058,9 +1045,9 @@ static int seg_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt) #define OFFSET(x) offsetof(SegmentContext, x) #define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "reference_stream", "set reference stream", OFFSET(reference_stream_specifier), AV_OPT_TYPE_STRING, {.str = "auto"}, CHAR_MIN, CHAR_MAX, E }, + { "reference_stream", "set reference stream", OFFSET(reference_stream_specifier), AV_OPT_TYPE_STRING, {.str = "auto"}, 0, 0, E }, { "segment_format", "set container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, - { "segment_format_options", "set list of options for the container format used for the segments", OFFSET(format_options_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, + { "segment_format_options", "set list of options for the container format used for the segments", OFFSET(format_options), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, E }, { "segment_list", "set the segment list filename", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, { "segment_header_filename", "write a single file containing the header", OFFSET(header_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, diff --git a/libavformat/shortendec.c b/libavformat/shortendec.c index a2879dc5a34..f7390b2e628 100644 --- a/libavformat/shortendec.c +++ b/libavformat/shortendec.c @@ -40,12 +40,18 @@ static int shn_probe(const AVProbeData *p) channels = get_ur_golomb_shorten(&gb, 0); blocksize = 256; } else { - int k; + unsigned k; k = get_ur_golomb_shorten(&gb, 2); + if (k > 31) + return 0; internal_ftype = get_ur_golomb_shorten(&gb, k); k = get_ur_golomb_shorten(&gb, 2); + if (k > 31) + return 0; channels = get_ur_golomb_shorten(&gb, k); k = get_ur_golomb_shorten(&gb, 2); + if (k > 31) + return 0; blocksize = get_ur_golomb_shorten(&gb, k); } diff --git a/libavformat/sierravmd.c b/libavformat/sierravmd.c index d586fc6ac02..531fc415316 100644 --- a/libavformat/sierravmd.c +++ b/libavformat/sierravmd.c @@ -127,8 +127,8 @@ static int vmd_read_header(AVFormatContext *s) vst->codecpar->width >>= 1; vst->codecpar->height >>= 1; } - if (ff_alloc_extradata(vst->codecpar, VMD_HEADER_SIZE)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(vst->codecpar, VMD_HEADER_SIZE)) < 0) + return ret; memcpy(vst->codecpar->extradata, vmd->vmd_header, VMD_HEADER_SIZE); } @@ -283,8 +283,9 @@ static int vmd_read_packet(AVFormatContext *s, if(ffio_limit(pb, frame->frame_size) != frame->frame_size) return AVERROR(EIO); - if (av_new_packet(pkt, frame->frame_size + BYTES_PER_FRAME_RECORD)) - return AVERROR(ENOMEM); + ret = av_new_packet(pkt, frame->frame_size + BYTES_PER_FRAME_RECORD); + if (ret < 0) + return ret; pkt->pos= avio_tell(pb); memcpy(pkt->data, frame->frame_record, BYTES_PER_FRAME_RECORD); if(vmd->is_indeo3 && frame->frame_record[0] == 0x02) @@ -294,7 +295,6 @@ static int vmd_read_packet(AVFormatContext *s, frame->frame_size); if (ret != frame->frame_size) { - av_packet_unref(pkt); ret = AVERROR(EIO); } pkt->stream_index = frame->stream_index; diff --git a/libavformat/siff.c b/libavformat/siff.c index 24d5ebb42d4..f6815b2f269 100644 --- a/libavformat/siff.c +++ b/libavformat/siff.c @@ -192,6 +192,7 @@ static int siff_read_header(AVFormatContext *s) static int siff_read_packet(AVFormatContext *s, AVPacket *pkt) { SIFFContext *c = s->priv_data; + int ret; if (c->has_video) { unsigned int size; @@ -213,13 +214,12 @@ static int siff_read_packet(AVFormatContext *s, AVPacket *pkt) size = c->pktsize - c->sndsize - c->gmcsize - 2; size = ffio_limit(s->pb, size); - if (av_new_packet(pkt, size + c->gmcsize + 2) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, size + c->gmcsize + 2)) < 0) + return ret; AV_WL16(pkt->data, c->flags); if (c->gmcsize) memcpy(pkt->data + 2, c->gmc, c->gmcsize); if (avio_read(s->pb, pkt->data + 2 + c->gmcsize, size) != size) { - av_packet_unref(pkt); return AVERROR_INVALIDDATA; } pkt->stream_index = 0; diff --git a/libavformat/smacker.c b/libavformat/smacker.c index b5c858aa9b1..8b1e185817e 100644 --- a/libavformat/smacker.c +++ b/libavformat/smacker.c @@ -25,10 +25,10 @@ #include -#include "libavutil/bswap.h" #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "avio_internal.h" #include "internal.h" #define SMACKER_PAL 0x01 @@ -43,25 +43,12 @@ enum SAudFlags { }; typedef struct SmackerContext { - /* Smacker file header */ - uint32_t magic; - uint32_t width, height; uint32_t frames; - int pts_inc; - uint32_t flags; - uint32_t audio[7]; - uint32_t treesize; - uint32_t mmap_size, mclr_size, full_size, type_size; - uint8_t aflags[7]; - uint32_t rates[7]; - uint32_t pad; /* frame info */ uint32_t *frm_size; uint8_t *frm_flags; /* internal variables */ int cur_frame; - int is_ver4; - int64_t cur_pts; /* current frame for demuxing */ uint8_t pal[768]; int indexes[7]; @@ -74,11 +61,6 @@ typedef struct SmackerContext { int64_t aud_pts[7]; } SmackerContext; -typedef struct SmackerFrame { - int64_t pts; - int stream; -} SmackerFrame; - /* palette used in Smacker */ static const uint8_t smk_pal[64] = { 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, @@ -108,143 +90,144 @@ static int smacker_read_header(AVFormatContext *s) { AVIOContext *pb = s->pb; SmackerContext *smk = s->priv_data; - AVStream *st, *ast[7]; - int i, ret; + AVStream *st; + AVCodecParameters *par; + uint32_t magic, width, height, flags, treesize; + int i, ret, pts_inc; int tbase; /* read and check header */ - smk->magic = avio_rl32(pb); - if (smk->magic != MKTAG('S', 'M', 'K', '2') && smk->magic != MKTAG('S', 'M', 'K', '4')) + magic = avio_rl32(pb); + if (magic != MKTAG('S', 'M', 'K', '2') && magic != MKTAG('S', 'M', 'K', '4')) return AVERROR_INVALIDDATA; - smk->width = avio_rl32(pb); - smk->height = avio_rl32(pb); + width = avio_rl32(pb); + height = avio_rl32(pb); smk->frames = avio_rl32(pb); - smk->pts_inc = (int32_t)avio_rl32(pb); - if (smk->pts_inc > INT_MAX / 100) { - av_log(s, AV_LOG_ERROR, "pts_inc %d is too large\n", smk->pts_inc); + pts_inc = avio_rl32(pb); + if (pts_inc > INT_MAX / 100) { + av_log(s, AV_LOG_ERROR, "pts_inc %d is too large\n", pts_inc); return AVERROR_INVALIDDATA; } - smk->flags = avio_rl32(pb); - if(smk->flags & SMACKER_FLAG_RING_FRAME) + flags = avio_rl32(pb); + if (flags & SMACKER_FLAG_RING_FRAME) smk->frames++; - for(i = 0; i < 7; i++) - smk->audio[i] = avio_rl32(pb); - smk->treesize = avio_rl32(pb); - - if(smk->treesize >= UINT_MAX/4){ // smk->treesize + 16 must not overflow (this check is probably redundant) - av_log(s, AV_LOG_ERROR, "treesize too large\n"); - return AVERROR_INVALIDDATA; - } - -//FIXME remove extradata "rebuilding" - smk->mmap_size = avio_rl32(pb); - smk->mclr_size = avio_rl32(pb); - smk->full_size = avio_rl32(pb); - smk->type_size = avio_rl32(pb); - for(i = 0; i < 7; i++) { - smk->rates[i] = avio_rl24(pb); - smk->aflags[i] = avio_r8(pb); - } - smk->pad = avio_rl32(pb); - /* setup data */ - if(smk->frames > 0xFFFFFF) { + if (smk->frames > 0xFFFFFF) { av_log(s, AV_LOG_ERROR, "Too many frames: %"PRIu32"\n", smk->frames); return AVERROR_INVALIDDATA; } - smk->frm_size = av_malloc_array(smk->frames, sizeof(*smk->frm_size)); - smk->frm_flags = av_malloc(smk->frames); - if (!smk->frm_size || !smk->frm_flags) { - av_freep(&smk->frm_size); - av_freep(&smk->frm_flags); - return AVERROR(ENOMEM); - } - smk->is_ver4 = (smk->magic != MKTAG('S', 'M', 'K', '2')); + avio_skip(pb, 28); /* Unused audio related data */ - /* read frame info */ - for(i = 0; i < smk->frames; i++) { - smk->frm_size[i] = avio_rl32(pb); - } - for(i = 0; i < smk->frames; i++) { - smk->frm_flags[i] = avio_r8(pb); + treesize = avio_rl32(pb); + if (treesize >= UINT_MAX/4) { + // treesize + 16 must not overflow (this check is probably redundant) + av_log(s, AV_LOG_ERROR, "treesize too large\n"); + return AVERROR_INVALIDDATA; } - /* init video codec */ st = avformat_new_stream(s, NULL); if (!st) return AVERROR(ENOMEM); + smk->videoindex = st->index; - st->codecpar->width = smk->width; - st->codecpar->height = smk->height; - st->codecpar->format = AV_PIX_FMT_PAL8; - st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; - st->codecpar->codec_id = AV_CODEC_ID_SMACKVIDEO; - st->codecpar->codec_tag = smk->magic; /* Smacker uses 100000 as internal timebase */ - if(smk->pts_inc < 0) - smk->pts_inc = -smk->pts_inc; + if (pts_inc < 0) + pts_inc = -pts_inc; else - smk->pts_inc *= 100; + pts_inc *= 100; tbase = 100000; - av_reduce(&tbase, &smk->pts_inc, tbase, smk->pts_inc, (1UL<<31)-1); - avpriv_set_pts_info(st, 33, smk->pts_inc, tbase); + av_reduce(&tbase, &pts_inc, tbase, pts_inc, (1UL << 31) - 1); + avpriv_set_pts_info(st, 33, pts_inc, tbase); st->duration = smk->frames; + + /* init video codec */ + par = st->codecpar; + par->width = width; + par->height = height; + par->format = AV_PIX_FMT_PAL8; + par->codec_type = AVMEDIA_TYPE_VIDEO; + par->codec_id = AV_CODEC_ID_SMACKVIDEO; + par->codec_tag = magic; + + if ((ret = ff_alloc_extradata(par, treesize + 16)) < 0) { + av_log(s, AV_LOG_ERROR, + "Cannot allocate %"PRIu32" bytes of extradata\n", + treesize + 16); + return ret; + } + if ((ret = ffio_read_size(pb, par->extradata, 16)) < 0) + return ret; + /* handle possible audio streams */ - for(i = 0; i < 7; i++) { + for (i = 0; i < 7; i++) { + uint32_t rate = avio_rl24(pb); + uint8_t aflag = avio_r8(pb); + smk->indexes[i] = -1; - if (smk->rates[i]) { - ast[i] = avformat_new_stream(s, NULL); - if (!ast[i]) + + if (rate) { + AVStream *ast = avformat_new_stream(s, NULL); + AVCodecParameters *par; + if (!ast) return AVERROR(ENOMEM); - smk->indexes[i] = ast[i]->index; - ast[i]->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; - if (smk->aflags[i] & SMK_AUD_BINKAUD) { - ast[i]->codecpar->codec_id = AV_CODEC_ID_BINKAUDIO_RDFT; - } else if (smk->aflags[i] & SMK_AUD_USEDCT) { - ast[i]->codecpar->codec_id = AV_CODEC_ID_BINKAUDIO_DCT; - } else if (smk->aflags[i] & SMK_AUD_PACKED){ - ast[i]->codecpar->codec_id = AV_CODEC_ID_SMACKAUDIO; - ast[i]->codecpar->codec_tag = MKTAG('S', 'M', 'K', 'A'); + + smk->indexes[i] = ast->index; + par = ast->codecpar; + par->codec_type = AVMEDIA_TYPE_AUDIO; + if (aflag & SMK_AUD_BINKAUD) { + par->codec_id = AV_CODEC_ID_BINKAUDIO_RDFT; + } else if (aflag & SMK_AUD_USEDCT) { + par->codec_id = AV_CODEC_ID_BINKAUDIO_DCT; + } else if (aflag & SMK_AUD_PACKED) { + par->codec_id = AV_CODEC_ID_SMACKAUDIO; + par->codec_tag = MKTAG('S', 'M', 'K', 'A'); } else { - ast[i]->codecpar->codec_id = AV_CODEC_ID_PCM_U8; + par->codec_id = AV_CODEC_ID_PCM_U8; } - if (smk->aflags[i] & SMK_AUD_STEREO) { - ast[i]->codecpar->channels = 2; - ast[i]->codecpar->channel_layout = AV_CH_LAYOUT_STEREO; + if (aflag & SMK_AUD_STEREO) { + par->channels = 2; + par->channel_layout = AV_CH_LAYOUT_STEREO; } else { - ast[i]->codecpar->channels = 1; - ast[i]->codecpar->channel_layout = AV_CH_LAYOUT_MONO; + par->channels = 1; + par->channel_layout = AV_CH_LAYOUT_MONO; } - ast[i]->codecpar->sample_rate = smk->rates[i]; - ast[i]->codecpar->bits_per_coded_sample = (smk->aflags[i] & SMK_AUD_16BITS) ? 16 : 8; - if(ast[i]->codecpar->bits_per_coded_sample == 16 && ast[i]->codecpar->codec_id == AV_CODEC_ID_PCM_U8) - ast[i]->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; - avpriv_set_pts_info(ast[i], 64, 1, ast[i]->codecpar->sample_rate - * ast[i]->codecpar->channels * ast[i]->codecpar->bits_per_coded_sample / 8); + par->sample_rate = rate; + par->bits_per_coded_sample = (aflag & SMK_AUD_16BITS) ? 16 : 8; + if (par->bits_per_coded_sample == 16 && + par->codec_id == AV_CODEC_ID_PCM_U8) + par->codec_id = AV_CODEC_ID_PCM_S16LE; + avpriv_set_pts_info(ast, 64, 1, par->sample_rate * par->channels + * par->bits_per_coded_sample / 8); } } + avio_rl32(pb); /* padding */ - /* load trees to extradata, they will be unpacked by decoder */ - if(ff_alloc_extradata(st->codecpar, smk->treesize + 16)){ - av_log(s, AV_LOG_ERROR, - "Cannot allocate %"PRIu32" bytes of extradata\n", - smk->treesize + 16); + /* setup data */ + smk->frm_size = av_malloc_array(smk->frames, sizeof(*smk->frm_size)); + smk->frm_flags = av_malloc(smk->frames); + if (!smk->frm_size || !smk->frm_flags) { av_freep(&smk->frm_size); av_freep(&smk->frm_flags); return AVERROR(ENOMEM); } - ret = avio_read(pb, st->codecpar->extradata + 16, st->codecpar->extradata_size - 16); - if(ret != st->codecpar->extradata_size - 16){ + + /* read frame info */ + for (i = 0; i < smk->frames; i++) { + smk->frm_size[i] = avio_rl32(pb); + } + for (i = 0; i < smk->frames; i++) { + smk->frm_flags[i] = avio_r8(pb); + } + + /* load trees to extradata, they will be unpacked by decoder */ + ret = avio_read(pb, par->extradata + 16, par->extradata_size - 16); + if (ret != par->extradata_size - 16) { av_freep(&smk->frm_size); av_freep(&smk->frm_flags); return AVERROR(EIO); } - ((int32_t*)st->codecpar->extradata)[0] = av_le2ne32(smk->mmap_size); - ((int32_t*)st->codecpar->extradata)[1] = av_le2ne32(smk->mclr_size); - ((int32_t*)st->codecpar->extradata)[2] = av_le2ne32(smk->full_size); - ((int32_t*)st->codecpar->extradata)[3] = av_le2ne32(smk->type_size); smk->curstream = -1; smk->nextpos = avio_tell(pb); @@ -347,8 +330,8 @@ static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt) } if (frame_size < 0 || frame_size >= INT_MAX/2) return AVERROR_INVALIDDATA; - if (av_new_packet(pkt, frame_size + 769)) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, frame_size + 769)) < 0) + return ret; if(smk->frm_size[smk->cur_frame] & 1) palchange |= 2; pkt->data[0] = palchange; @@ -364,8 +347,8 @@ static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt) } else { if (smk->stream_id[smk->curstream] < 0 || !smk->bufs[smk->curstream]) return AVERROR_INVALIDDATA; - if (av_new_packet(pkt, smk->buf_sizes[smk->curstream])) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, smk->buf_sizes[smk->curstream])) < 0) + return ret; memcpy(pkt->data, smk->bufs[smk->curstream], smk->buf_sizes[smk->curstream]); pkt->size = smk->buf_sizes[smk->curstream]; pkt->stream_index = smk->stream_id[smk->curstream]; diff --git a/libavformat/smjpegdec.c b/libavformat/smjpegdec.c index 5bc04921fe0..a4e1f957ed8 100644 --- a/libavformat/smjpegdec.c +++ b/libavformat/smjpegdec.c @@ -51,6 +51,9 @@ static int smjpeg_read_header(AVFormatContext *s) uint32_t version, htype, hlength, duration; char *comment; + sc->audio_stream_index = + sc->video_stream_index = -1; + avio_skip(pb, 8); // magic version = avio_rb32(pb); if (version) @@ -147,6 +150,8 @@ static int smjpeg_read_packet(AVFormatContext *s, AVPacket *pkt) dtype = avio_rl32(s->pb); switch (dtype) { case SMJPEG_SNDD: + if (sc->audio_stream_index < 0) + return AVERROR_INVALIDDATA; timestamp = avio_rb32(s->pb); size = avio_rb32(s->pb); ret = av_get_packet(s->pb, pkt, size); @@ -155,6 +160,8 @@ static int smjpeg_read_packet(AVFormatContext *s, AVPacket *pkt) pkt->pos = pos; break; case SMJPEG_VIDD: + if (sc->video_stream_index < 0) + return AVERROR_INVALIDDATA; timestamp = avio_rb32(s->pb); size = avio_rb32(s->pb); ret = av_get_packet(s->pb, pkt, size); diff --git a/libavformat/smjpegenc.c b/libavformat/smjpegenc.c index 68a128647e3..c3c1a6346c5 100644 --- a/libavformat/smjpegenc.c +++ b/libavformat/smjpegenc.c @@ -88,7 +88,6 @@ static int smjpeg_write_header(AVFormatContext *s) } avio_wl32(pb, SMJPEG_HEND); - avio_flush(pb); return 0; } diff --git a/libavformat/smoothstreamingenc.c b/libavformat/smoothstreamingenc.c index 1ed19ebb2f2..33bb404f467 100644 --- a/libavformat/smoothstreamingenc.c +++ b/libavformat/smoothstreamingenc.c @@ -99,14 +99,9 @@ static int64_t ism_seek(void *opaque, int64_t offset, int whence) if (whence != SEEK_SET) return AVERROR(ENOSYS); if (os->tail_out) { - if (os->out) { - ffurl_close(os->out); - } - if (os->out2) { - ffurl_close(os->out2); - } + ffurl_closep(&os->out); + ffurl_closep(&os->out2); os->out = os->tail_out; - os->out2 = NULL; os->tail_out = NULL; } if (offset >= os->cur_start_pos) { @@ -175,16 +170,14 @@ static void ism_free(AVFormatContext *s) return; for (i = 0; i < s->nb_streams; i++) { OutputStream *os = &c->streams[i]; - ffurl_close(os->out); - ffurl_close(os->out2); - ffurl_close(os->tail_out); - os->out = os->out2 = os->tail_out = NULL; + ffurl_closep(&os->out); + ffurl_closep(&os->out2); + ffurl_closep(&os->tail_out); if (os->ctx && os->ctx_inited) av_write_trailer(os->ctx); if (os->ctx && os->ctx->pb) avio_context_free(&os->ctx->pb); - if (os->ctx) - avformat_free_context(os->ctx); + avformat_free_context(os->ctx); av_freep(&os->private_str); for (j = 0; j < os->nb_fragments; j++) av_freep(&os->fragments[j]); @@ -333,12 +326,11 @@ static int ism_write_header(AVFormatContext *s) goto fail; } - ctx = avformat_alloc_context(); + os->ctx = ctx = avformat_alloc_context(); if (!ctx || ff_copy_whiteblacklists(ctx, s) < 0) { ret = AVERROR(ENOMEM); goto fail; } - os->ctx = ctx; ctx->oformat = oformat; ctx->interrupt_callback = s->interrupt_callback; @@ -358,12 +350,13 @@ static int ism_write_header(AVFormatContext *s) av_dict_set_int(&opts, "ism_lookahead", c->lookahead_count, 0); av_dict_set(&opts, "movflags", "frag_custom", 0); - if ((ret = avformat_write_header(ctx, &opts)) < 0) { + ret = avformat_write_header(ctx, &opts); + av_dict_free(&opts); + if (ret < 0) { goto fail; } os->ctx_inited = 1; avio_flush(ctx->pb); - av_dict_free(&opts); s->streams[i]->time_base = st->time_base; if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { c->has_video = 1; @@ -465,7 +458,7 @@ static int add_fragment(OutputStream *os, const char *file, const char *infofile Fragment *frag; if (os->nb_fragments >= os->fragments_size) { os->fragments_size = (os->fragments_size + 1) * 2; - if ((err = av_reallocp(&os->fragments, sizeof(*os->fragments) * + if ((err = av_reallocp_array(&os->fragments, sizeof(*os->fragments), os->fragments_size)) < 0) { os->fragments_size = 0; os->nb_fragments = 0; @@ -538,8 +531,7 @@ static int ism_flush(AVFormatContext *s, int final) if (!os->out || os->tail_out) return AVERROR(EIO); - ffurl_close(os->out); - os->out = NULL; + ffurl_closep(&os->out); size = os->tail_pos - os->cur_start_pos; if ((ret = parse_fragment(s, filename, &start_ts, &duration, &moof_size, size)) < 0) break; diff --git a/libavformat/smush.c b/libavformat/smush.c index 20352adf94f..962eb57ab20 100644 --- a/libavformat/smush.c +++ b/libavformat/smush.c @@ -51,6 +51,7 @@ static int smush_read_header(AVFormatContext *ctx) uint32_t magic, nframes, size, subversion, i; uint32_t width = 0, height = 0, got_audio = 0, read = 0; uint32_t sample_rate, channels, palette[256]; + int ret; magic = avio_rb32(pb); avio_skip(pb, 4); // skip movie size @@ -157,8 +158,8 @@ static int smush_read_header(AVFormatContext *ctx) vst->codecpar->height = height; if (!smush->version) { - if (ff_alloc_extradata(vst->codecpar, 1024 + 2)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(vst->codecpar, 1024 + 2)) < 0) + return ret; AV_WL16(vst->codecpar->extradata, subversion); for (i = 0; i < 256; i++) diff --git a/libavformat/soxenc.c b/libavformat/soxenc.c index 7b37bd48484..ce276f88b5d 100644 --- a/libavformat/soxenc.c +++ b/libavformat/soxenc.c @@ -80,8 +80,6 @@ static int sox_write_header(AVFormatContext *s) ffio_fill(pb, 0, comment_size - comment_len); - avio_flush(pb); - return 0; } @@ -101,8 +99,6 @@ static int sox_write_trailer(AVFormatContext *s) } else avio_wb64(pb, num_samples); avio_seek(pb, file_size, SEEK_SET); - - avio_flush(pb); } return 0; diff --git a/libavformat/spdifdec.c b/libavformat/spdifdec.c index d74f58d82ba..1808fa9d65d 100644 --- a/libavformat/spdifdec.c +++ b/libavformat/spdifdec.c @@ -197,15 +197,13 @@ int ff_spdif_read_packet(AVFormatContext *s, AVPacket *pkt) pkt->pos = avio_tell(pb) - BURST_HEADER_SIZE; if (avio_read(pb, pkt->data, pkt->size) < pkt->size) { - av_packet_unref(pkt); return AVERROR_EOF; } ff_spdif_bswap_buf16((uint16_t *)pkt->data, (uint16_t *)pkt->data, pkt->size >> 1); ret = spdif_get_offset_and_codec(s, data_type, pkt->data, &offset, &codec_id); - if (ret) { - av_packet_unref(pkt); + if (ret < 0) { return ret; } @@ -216,7 +214,6 @@ int ff_spdif_read_packet(AVFormatContext *s, AVPacket *pkt) /* first packet, create a stream */ AVStream *st = avformat_new_stream(s, NULL); if (!st) { - av_packet_unref(pkt); return AVERROR(ENOMEM); } st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; diff --git a/libavformat/spdifenc.c b/libavformat/spdifenc.c index 4307942a440..0288872fd37 100644 --- a/libavformat/spdifenc.c +++ b/libavformat/spdifenc.c @@ -1,7 +1,7 @@ /* * IEC 61937 muxer * Copyright (c) 2009 Bartlomiej Wolowiec - * Copyright (c) 2010 Anssi Hannula + * Copyright (c) 2010, 2020 Anssi Hannula * Copyright (c) 2010 Carl Eugen Hoyos * * This file is part of FFmpeg. @@ -69,13 +69,18 @@ typedef struct IEC61937Context { int use_preamble; ///< preamble enabled (disabled for exactly pre-padded DTS) int extra_bswap; ///< extra bswap for payload (for LE DTS => standard BE DTS) - uint8_t *hd_buf; ///< allocated buffer to concatenate hd audio frames - int hd_buf_size; ///< size of the hd audio buffer - int hd_buf_count; ///< number of frames in the hd audio buffer - int hd_buf_filled; ///< amount of bytes in the hd audio buffer + uint8_t *hd_buf[2]; ///< allocated buffers to concatenate hd audio frames + int hd_buf_size; ///< size of the hd audio buffer (eac3, dts4) + int hd_buf_count; ///< number of frames in the hd audio buffer (eac3) + int hd_buf_filled; ///< amount of bytes in the hd audio buffer (eac3, truehd) + int hd_buf_idx; ///< active hd buffer index (truehd) int dtshd_skip; ///< counter used for skipping DTS-HD frames + uint16_t truehd_prev_time; ///< input_timing from the last frame + int truehd_prev_size; ///< previous frame size in bytes, including any MAT codes + int truehd_samples_per_frame; ///< samples per frame for padding calculation + /* AVOptions: */ int dtshd_rate; int dtshd_fallback; @@ -122,11 +127,11 @@ static int spdif_header_eac3(AVFormatContext *s, AVPacket *pkt) if (bsid > 10 && (pkt->data[4] & 0xc0) != 0xc0) /* fscod */ repeat = eac3_repeat[(pkt->data[4] & 0x30) >> 4]; /* numblkscod */ - ctx->hd_buf = av_fast_realloc(ctx->hd_buf, &ctx->hd_buf_size, ctx->hd_buf_filled + pkt->size); - if (!ctx->hd_buf) + ctx->hd_buf[0] = av_fast_realloc(ctx->hd_buf[0], &ctx->hd_buf_size, ctx->hd_buf_filled + pkt->size); + if (!ctx->hd_buf[0]) return AVERROR(ENOMEM); - memcpy(&ctx->hd_buf[ctx->hd_buf_filled], pkt->data, pkt->size); + memcpy(&ctx->hd_buf[0][ctx->hd_buf_filled], pkt->data, pkt->size); ctx->hd_buf_filled += pkt->size; if (++ctx->hd_buf_count < repeat){ @@ -135,7 +140,7 @@ static int spdif_header_eac3(AVFormatContext *s, AVPacket *pkt) } ctx->data_type = IEC61937_EAC3; ctx->pkt_offset = 24576; - ctx->out_buf = ctx->hd_buf; + ctx->out_buf = ctx->hd_buf[0]; ctx->out_bytes = ctx->hd_buf_filled; ctx->length_code = ctx->hd_buf_filled; @@ -228,15 +233,15 @@ static int spdif_header_dts4(AVFormatContext *s, AVPacket *pkt, int core_size, * with some receivers, but the exact requirement is unconfirmed. */ ctx->length_code = FFALIGN(ctx->out_bytes + 0x8, 0x10) - 0x8; - av_fast_malloc(&ctx->hd_buf, &ctx->hd_buf_size, ctx->out_bytes); - if (!ctx->hd_buf) + av_fast_malloc(&ctx->hd_buf[0], &ctx->hd_buf_size, ctx->out_bytes); + if (!ctx->hd_buf[0]) return AVERROR(ENOMEM); - ctx->out_buf = ctx->hd_buf; + ctx->out_buf = ctx->hd_buf[0]; - memcpy(ctx->hd_buf, dtshd_start_code, sizeof(dtshd_start_code)); - AV_WB16(ctx->hd_buf + sizeof(dtshd_start_code), pkt_size); - memcpy(ctx->hd_buf + sizeof(dtshd_start_code) + 2, pkt->data, pkt_size); + memcpy(ctx->hd_buf[0], dtshd_start_code, sizeof(dtshd_start_code)); + AV_WB16(ctx->hd_buf[0] + sizeof(dtshd_start_code), pkt_size); + memcpy(ctx->hd_buf[0] + sizeof(dtshd_start_code) + 2, pkt->data, pkt_size); return 0; } @@ -384,62 +389,175 @@ static int spdif_header_aac(AVFormatContext *s, AVPacket *pkt) /* * It seems Dolby TrueHD frames have to be encapsulated in MAT frames before * they can be encapsulated in IEC 61937. - * Here we encapsulate 24 TrueHD frames in a single MAT frame, padding them - * to achieve constant rate. - * The actual format of a MAT frame is unknown, but the below seems to work. - * However, it seems it is not actually necessary for the 24 TrueHD frames to - * be in an exact alignment with the MAT frame. */ +#define MAT_PKT_OFFSET 61440 #define MAT_FRAME_SIZE 61424 -#define TRUEHD_FRAME_OFFSET 2560 -#define MAT_MIDDLE_CODE_OFFSET -4 + +static const uint8_t mat_start_code[20] = { + 0x07, 0x9E, 0x00, 0x03, 0x84, 0x01, 0x01, 0x01, 0x80, 0x00, 0x56, 0xA5, 0x3B, 0xF4, 0x81, 0x83, + 0x49, 0x80, 0x77, 0xE0, +}; +static const uint8_t mat_middle_code[12] = { + 0xC3, 0xC1, 0x42, 0x49, 0x3B, 0xFA, 0x82, 0x83, 0x49, 0x80, 0x77, 0xE0, +}; +static const uint8_t mat_end_code[16] = { + 0xC3, 0xC2, 0xC0, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x11, +}; + +#define MAT_CODE(position, data) { .pos = position, .code = data, .len = sizeof(data) } + +static const struct { + unsigned int pos; + const uint8_t *code; + unsigned int len; +} mat_codes[] = { + MAT_CODE(0, mat_start_code), + MAT_CODE(30708, mat_middle_code), + MAT_CODE(MAT_FRAME_SIZE - sizeof(mat_end_code), mat_end_code), +}; static int spdif_header_truehd(AVFormatContext *s, AVPacket *pkt) { IEC61937Context *ctx = s->priv_data; - int mat_code_length = 0; - static const char mat_end_code[16] = { 0xC3, 0xC2, 0xC0, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x11 }; - - if (!ctx->hd_buf_count) { - static const char mat_start_code[20] = { 0x07, 0x9E, 0x00, 0x03, 0x84, 0x01, 0x01, 0x01, 0x80, 0x00, 0x56, 0xA5, 0x3B, 0xF4, 0x81, 0x83, 0x49, 0x80, 0x77, 0xE0 }; - mat_code_length = sizeof(mat_start_code) + BURST_HEADER_SIZE; - memcpy(ctx->hd_buf, mat_start_code, sizeof(mat_start_code)); - - } else if (ctx->hd_buf_count == 12) { - static const char mat_middle_code[12] = { 0xC3, 0xC1, 0x42, 0x49, 0x3B, 0xFA, 0x82, 0x83, 0x49, 0x80, 0x77, 0xE0 }; - mat_code_length = sizeof(mat_middle_code) + MAT_MIDDLE_CODE_OFFSET; - memcpy(&ctx->hd_buf[12 * TRUEHD_FRAME_OFFSET - BURST_HEADER_SIZE + MAT_MIDDLE_CODE_OFFSET], - mat_middle_code, sizeof(mat_middle_code)); + uint8_t *hd_buf = ctx->hd_buf[ctx->hd_buf_idx]; + int ratebits; + int padding_remaining = 0; + uint16_t input_timing; + int total_frame_size = pkt->size; + const uint8_t *dataptr = pkt->data; + int data_remaining = pkt->size; + int have_pkt = 0; + int next_code_idx; + + if (pkt->size < 10) + return AVERROR_INVALIDDATA; + + if (AV_RB24(pkt->data + 4) == 0xf8726f) { + /* major sync unit, fetch sample rate */ + if (pkt->data[7] == 0xba) + ratebits = pkt->data[8] >> 4; + else if (pkt->data[7] == 0xbb) + ratebits = pkt->data[9] >> 4; + else + return AVERROR_INVALIDDATA; + + ctx->truehd_samples_per_frame = 40 << (ratebits & 3); + av_log(s, AV_LOG_TRACE, "TrueHD samples per frame: %d\n", + ctx->truehd_samples_per_frame); } - if (pkt->size > TRUEHD_FRAME_OFFSET - mat_code_length) { - /* if such frames exist, we'd need some more complex logic to - * distribute the TrueHD frames in the MAT frame */ - avpriv_request_sample(s, "Too large TrueHD frame of %d bytes", - pkt->size); - return AVERROR_PATCHWELCOME; + if (!ctx->truehd_samples_per_frame) + return AVERROR_INVALIDDATA; + + input_timing = AV_RB16(pkt->data + 2); + if (ctx->truehd_prev_size) { + uint16_t delta_samples = input_timing - ctx->truehd_prev_time; + /* + * One multiple-of-48kHz frame is 1/1200 sec and the IEC 61937 rate + * is 768kHz = 768000*4 bytes/sec. + * The nominal space per frame is therefore + * (768000*4 bytes/sec) * (1/1200 sec) = 2560 bytes. + * For multiple-of-44.1kHz frames: 1/1102.5 sec, 705.6kHz, 2560 bytes. + * + * 2560 is divisible by truehd_samples_per_frame. + */ + int delta_bytes = delta_samples * 2560 / ctx->truehd_samples_per_frame; + + /* padding needed before this frame */ + padding_remaining = delta_bytes - ctx->truehd_prev_size; + + av_log(s, AV_LOG_TRACE, "delta_samples: %"PRIu16", delta_bytes: %d\n", + delta_samples, delta_bytes); + + /* sanity check */ + if (padding_remaining < 0 || padding_remaining >= MAT_FRAME_SIZE / 2) { + avpriv_request_sample(s, "Unusual frame timing: %"PRIu16" => %"PRIu16", %d samples/frame", + ctx->truehd_prev_time, input_timing, ctx->truehd_samples_per_frame); + padding_remaining = 0; + } } - memcpy(&ctx->hd_buf[ctx->hd_buf_count * TRUEHD_FRAME_OFFSET - BURST_HEADER_SIZE + mat_code_length], - pkt->data, pkt->size); - if (ctx->hd_buf_count < 23) { - memset(&ctx->hd_buf[ctx->hd_buf_count * TRUEHD_FRAME_OFFSET - BURST_HEADER_SIZE + mat_code_length + pkt->size], - 0, TRUEHD_FRAME_OFFSET - pkt->size - mat_code_length); - } else { - size_t padding = MAT_FRAME_SIZE - (ctx->hd_buf_count * TRUEHD_FRAME_OFFSET - BURST_HEADER_SIZE + pkt->size); - memset(&ctx->hd_buf[MAT_FRAME_SIZE - padding], 0, padding); + for (next_code_idx = 0; next_code_idx < FF_ARRAY_ELEMS(mat_codes); next_code_idx++) + if (ctx->hd_buf_filled <= mat_codes[next_code_idx].pos) + break; + + if (next_code_idx >= FF_ARRAY_ELEMS(mat_codes)) + return AVERROR_BUG; + + while (padding_remaining || data_remaining || + mat_codes[next_code_idx].pos == ctx->hd_buf_filled) { + + if (mat_codes[next_code_idx].pos == ctx->hd_buf_filled) { + /* time to insert MAT code */ + int code_len = mat_codes[next_code_idx].len; + int code_len_remaining = code_len; + memcpy(hd_buf + mat_codes[next_code_idx].pos, + mat_codes[next_code_idx].code, code_len); + ctx->hd_buf_filled += code_len; + + next_code_idx++; + if (next_code_idx == FF_ARRAY_ELEMS(mat_codes)) { + next_code_idx = 0; + + /* this was the last code, move to the next MAT frame */ + have_pkt = 1; + ctx->out_buf = hd_buf; + ctx->hd_buf_idx ^= 1; + hd_buf = ctx->hd_buf[ctx->hd_buf_idx]; + ctx->hd_buf_filled = 0; + + /* inter-frame gap has to be counted as well, add it */ + code_len_remaining += MAT_PKT_OFFSET - MAT_FRAME_SIZE; + } + + if (padding_remaining) { + /* consider the MAT code as padding */ + int counted_as_padding = FFMIN(padding_remaining, + code_len_remaining); + padding_remaining -= counted_as_padding; + code_len_remaining -= counted_as_padding; + } + /* count the remainder of the code as part of frame size */ + if (code_len_remaining) + total_frame_size += code_len_remaining; + } + + if (padding_remaining) { + int padding_to_insert = FFMIN(mat_codes[next_code_idx].pos - ctx->hd_buf_filled, + padding_remaining); + + memset(hd_buf + ctx->hd_buf_filled, 0, padding_to_insert); + ctx->hd_buf_filled += padding_to_insert; + padding_remaining -= padding_to_insert; + + if (padding_remaining) + continue; /* time to insert MAT code */ + } + + if (data_remaining) { + int data_to_insert = FFMIN(mat_codes[next_code_idx].pos - ctx->hd_buf_filled, + data_remaining); + + memcpy(hd_buf + ctx->hd_buf_filled, dataptr, data_to_insert); + ctx->hd_buf_filled += data_to_insert; + dataptr += data_to_insert; + data_remaining -= data_to_insert; + } } - if (++ctx->hd_buf_count < 24){ + ctx->truehd_prev_size = total_frame_size; + ctx->truehd_prev_time = input_timing; + + av_log(s, AV_LOG_TRACE, "TrueHD frame inserted, total size %d, buffer position %d\n", + total_frame_size, ctx->hd_buf_filled); + + if (!have_pkt) { ctx->pkt_offset = 0; return 0; } - memcpy(&ctx->hd_buf[MAT_FRAME_SIZE - sizeof(mat_end_code)], mat_end_code, sizeof(mat_end_code)); - ctx->hd_buf_count = 0; ctx->data_type = IEC61937_TRUEHD; - ctx->pkt_offset = 61440; - ctx->out_buf = ctx->hd_buf; + ctx->pkt_offset = MAT_PKT_OFFSET; ctx->out_bytes = MAT_FRAME_SIZE; ctx->length_code = MAT_FRAME_SIZE; return 0; @@ -470,9 +588,11 @@ static int spdif_write_header(AVFormatContext *s) case AV_CODEC_ID_TRUEHD: case AV_CODEC_ID_MLP: ctx->header_info = spdif_header_truehd; - ctx->hd_buf = av_malloc(MAT_FRAME_SIZE); - if (!ctx->hd_buf) - return AVERROR(ENOMEM); + for (int i = 0; i < FF_ARRAY_ELEMS(ctx->hd_buf); i++) { + ctx->hd_buf[i] = av_malloc(MAT_FRAME_SIZE); + if (!ctx->hd_buf[i]) + return AVERROR(ENOMEM); + } break; default: avpriv_report_missing_feature(s, "Codec %d", @@ -482,12 +602,12 @@ static int spdif_write_header(AVFormatContext *s) return 0; } -static int spdif_write_trailer(AVFormatContext *s) +static void spdif_deinit(AVFormatContext *s) { IEC61937Context *ctx = s->priv_data; av_freep(&ctx->buffer); - av_freep(&ctx->hd_buf); - return 0; + for (int i = 0; i < FF_ARRAY_ELEMS(ctx->hd_buf); i++) + av_freep(&ctx->hd_buf[i]); } static av_always_inline void spdif_put_16(IEC61937Context *ctx, @@ -560,7 +680,7 @@ AVOutputFormat ff_spdif_muxer = { .video_codec = AV_CODEC_ID_NONE, .write_header = spdif_write_header, .write_packet = spdif_write_packet, - .write_trailer = spdif_write_trailer, + .deinit = spdif_deinit, .flags = AVFMT_NOTIMESTAMPS, .priv_class = &spdif_class, }; diff --git a/libavformat/srtdec.c b/libavformat/srtdec.c index 40d324b44d1..d6ff00ba6d3 100644 --- a/libavformat/srtdec.c +++ b/libavformat/srtdec.c @@ -207,6 +207,8 @@ static int srt_read_header(AVFormatContext *s) ff_subtitles_queue_finalize(s, &srt->q); end: + if (res < 0) + ff_subtitles_queue_clean(&srt->q); av_bprint_finalize(&buf, NULL); return res; } diff --git a/libavformat/srtpproto.c b/libavformat/srtpproto.c index 5e6e5164d75..13e2245015f 100644 --- a/libavformat/srtpproto.c +++ b/libavformat/srtpproto.c @@ -59,8 +59,7 @@ static int srtp_close(URLContext *h) SRTPProtoContext *s = h->priv_data; ff_srtp_free(&s->srtp_out); ff_srtp_free(&s->srtp_in); - ffurl_close(s->rtp_hd); - s->rtp_hd = NULL; + ffurl_closep(&s->rtp_hd); return 0; } diff --git a/libavformat/stldec.c b/libavformat/stldec.c index d6e0713f8c1..fb67407ac56 100644 --- a/libavformat/stldec.c +++ b/libavformat/stldec.c @@ -97,8 +97,10 @@ static int stl_read_header(AVFormatContext *s) if (pts_start != AV_NOPTS_VALUE) { AVPacket *sub; sub = ff_subtitles_queue_insert(&stl->q, p, strlen(p), 0); - if (!sub) + if (!sub) { + ff_subtitles_queue_clean(&stl->q); return AVERROR(ENOMEM); + } sub->pos = pos; sub->pts = pts_start; sub->duration = duration; diff --git a/libavformat/subfile.c b/libavformat/subfile.c index 2f162e0a34d..300672e657d 100644 --- a/libavformat/subfile.c +++ b/libavformat/subfile.c @@ -86,7 +86,7 @@ static int subfile_open(URLContext *h, const char *filename, int flags, return ret; c->pos = c->start; if ((ret = slave_seek(h)) < 0) { - ffurl_close(c->h); + ffurl_closep(&c->h); return ret; } return 0; @@ -95,7 +95,7 @@ static int subfile_open(URLContext *h, const char *filename, int flags, static int subfile_close(URLContext *h) { SubfileContext *c = h->priv_data; - return ffurl_close(c->h); + return ffurl_closep(&c->h); } static int subfile_read(URLContext *h, unsigned char *buf, int size) @@ -116,7 +116,7 @@ static int subfile_read(URLContext *h, unsigned char *buf, int size) static int64_t subfile_seek(URLContext *h, int64_t pos, int whence) { SubfileContext *c = h->priv_data; - int64_t new_pos = -1, end; + int64_t new_pos, end; int ret; if (whence == AVSEEK_SIZE || whence == SEEK_END) { @@ -132,10 +132,10 @@ static int64_t subfile_seek(URLContext *h, int64_t pos, int whence) new_pos = c->start + pos; break; case SEEK_CUR: - new_pos += pos; + new_pos = c->pos + pos; break; case SEEK_END: - new_pos = end + c->pos; + new_pos = end + pos; break; } if (new_pos < c->start) diff --git a/libavformat/subtitles.c b/libavformat/subtitles.c index 659c99d1cf7..ad7f68938e2 100644 --- a/libavformat/subtitles.c +++ b/libavformat/subtitles.c @@ -132,9 +132,10 @@ AVPacket *ff_subtitles_queue_insert(FFDemuxSubtitlesQueue *q, if (!subs) return NULL; q->subs = subs; - sub = &subs[q->nb_subs++]; + sub = &subs[q->nb_subs]; if (av_new_packet(sub, len) < 0) return NULL; + q->nb_subs++; sub->flags |= AV_PKT_FLAG_KEY; sub->pts = sub->dts = 0; memcpy(sub->data, event, len); @@ -194,6 +195,9 @@ void ff_subtitles_queue_finalize(void *log_ctx, FFDemuxSubtitlesQueue *q) { int i; + if (!q->nb_subs) + return; + qsort(q->subs, q->nb_subs, sizeof(*q->subs), q->sort == SUB_SORT_TS_POS ? cmp_pkt_sub_ts_pos : cmp_pkt_sub_pos_ts); @@ -208,11 +212,12 @@ void ff_subtitles_queue_finalize(void *log_ctx, FFDemuxSubtitlesQueue *q) int ff_subtitles_queue_read_packet(FFDemuxSubtitlesQueue *q, AVPacket *pkt) { AVPacket *sub = q->subs + q->current_sub_idx; + int ret; if (q->current_sub_idx == q->nb_subs) return AVERROR_EOF; - if (av_packet_ref(pkt, sub) < 0) { - return AVERROR(ENOMEM); + if ((ret = av_packet_ref(pkt, sub)) < 0) { + return ret; } pkt->dts = pkt->pts; diff --git a/libavformat/subviewer1dec.c b/libavformat/subviewer1dec.c index 1360d9b7d9e..f2eee294a1c 100644 --- a/libavformat/subviewer1dec.c +++ b/libavformat/subviewer1dec.c @@ -77,8 +77,10 @@ static int subviewer1_read_header(AVFormatContext *s) sub->duration = pts_start - sub->pts; } else { sub = ff_subtitles_queue_insert(&subviewer1->q, line, len, 0); - if (!sub) + if (!sub) { + ff_subtitles_queue_clean(&subviewer1->q); return AVERROR(ENOMEM); + } sub->pos = pos; sub->pts = pts_start; sub->duration = -1; diff --git a/libavformat/subviewerdec.c b/libavformat/subviewerdec.c index 06b827b70f1..fdca3a48205 100644 --- a/libavformat/subviewerdec.c +++ b/libavformat/subviewerdec.c @@ -56,11 +56,21 @@ static int read_ts(const char *s, int64_t *start, int *duration) int64_t end; int hh1, mm1, ss1, ms1; int hh2, mm2, ss2, ms2; + int multiplier = 1; + if (sscanf(s, "%u:%u:%u.%2u,%u:%u:%u.%2u", + &hh1, &mm1, &ss1, &ms1, &hh2, &mm2, &ss2, &ms2) == 8) { + multiplier = 10; + } else if (sscanf(s, "%u:%u:%u.%1u,%u:%u:%u.%1u", + &hh1, &mm1, &ss1, &ms1, &hh2, &mm2, &ss2, &ms2) == 8) { + multiplier = 100; + } if (sscanf(s, "%u:%u:%u.%u,%u:%u:%u.%u", &hh1, &mm1, &ss1, &ms1, &hh2, &mm2, &ss2, &ms2) == 8) { - end = (hh2*3600LL + mm2*60LL + ss2) * 100LL + ms2; - *start = (hh1*3600LL + mm1*60LL + ss1) * 100LL + ms1; + ms1 = FFMIN(ms1, 999); + ms2 = FFMIN(ms2, 999); + end = (hh2*3600LL + mm2*60LL + ss2) * 1000LL + ms2 * multiplier; + *start = (hh1*3600LL + mm1*60LL + ss1) * 1000LL + ms1 * multiplier; *duration = end - *start; return 0; } @@ -84,7 +94,7 @@ static int subviewer_read_header(AVFormatContext *s) return res; if (avio_rb24(s->pb) != 0xefbbbf) avio_seek(s->pb, -3, SEEK_CUR); - avpriv_set_pts_info(st, 64, 1, 100); + avpriv_set_pts_info(st, 64, 1, 1000); st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; st->codecpar->codec_id = AV_CODEC_ID_SUBVIEWER; @@ -162,6 +172,8 @@ static int subviewer_read_header(AVFormatContext *s) ff_subtitles_queue_finalize(s, &subviewer->q); end: + if (res < 0) + ff_subtitles_queue_clean(&subviewer->q); av_bprint_finalize(&header, NULL); return res; } diff --git a/libavformat/swfdec.c b/libavformat/swfdec.c index 85bd30404e6..9a0b27bd8c4 100644 --- a/libavformat/swfdec.c +++ b/libavformat/swfdec.c @@ -152,6 +152,8 @@ static int swf_read_header(AVFormatContext *s) swf->zpb->seekable = 0; if (inflateInit(&swf->zstream) != Z_OK) { av_log(s, AV_LOG_ERROR, "Unable to init zlib context\n"); + av_freep(&swf->zbuf_in); + av_freep(&swf->zbuf_out); return AVERROR(EINVAL); } pb = swf->zpb; @@ -397,7 +399,6 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) if (linesize * height > pkt->size) { res = AVERROR_INVALIDDATA; - av_packet_unref(pkt); goto bitmap_end; } @@ -487,7 +488,6 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) if ((res = av_new_packet(pkt, len)) < 0) return res; if (avio_read(pb, pkt->data, 4) != 4) { - av_packet_unref(pkt); return AVERROR_INVALIDDATA; } if (AV_RB32(pkt->data) == 0xffd8ffd9 || @@ -504,7 +504,6 @@ static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) } if (res != pkt->size) { if (res < 0) { - av_packet_unref(pkt); return res; } av_shrink_packet(pkt, res); diff --git a/libavformat/swfenc.c b/libavformat/swfenc.c index f53db0fb2b3..9da4aad9597 100644 --- a/libavformat/swfenc.c +++ b/libavformat/swfenc.c @@ -256,7 +256,7 @@ static int swf_write_header(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "Invalid (too large) frame rate %d/%d\n", rate, rate_base); return AVERROR(EINVAL); } - avio_wl16(pb, (rate * 256) / rate_base); /* frame rate */ + avio_wl16(pb, (rate * 256LL) / rate_base); /* frame rate */ swf->duration_pos = avio_tell(pb); avio_wl16(pb, (uint16_t)(DUMMY_DURATION * (int64_t)rate / rate_base)); /* frame count */ @@ -337,7 +337,6 @@ static int swf_write_header(AVFormatContext *s) put_swf_end_tag(s); } - avio_flush(s->pb); return 0; } diff --git a/libavformat/tedcaptionsdec.c b/libavformat/tedcaptionsdec.c index 5572bfd9316..3255819e77c 100644 --- a/libavformat/tedcaptionsdec.c +++ b/libavformat/tedcaptionsdec.c @@ -275,10 +275,13 @@ static int parse_file(AVIOContext *pb, FFDemuxSubtitlesQueue *subs) static av_cold int tedcaptions_read_header(AVFormatContext *avf) { TEDCaptionsDemuxer *tc = avf->priv_data; - AVStream *st; + AVStream *st = avformat_new_stream(avf, NULL); int ret, i; AVPacket *last; + if (!st) + return AVERROR(ENOMEM); + ret = parse_file(avf->pb, &tc->subs); if (ret < 0) { if (ret == AVERROR_INVALIDDATA) @@ -292,9 +295,6 @@ static av_cold int tedcaptions_read_header(AVFormatContext *avf) tc->subs.subs[i].pts += tc->start_time; last = &tc->subs.subs[tc->subs.nb_subs - 1]; - st = avformat_new_stream(avf, NULL); - if (!st) - return AVERROR(ENOMEM); st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; st->codecpar->codec_id = AV_CODEC_ID_TEXT; avpriv_set_pts_info(st, 64, 1, 1000); diff --git a/libavformat/tee.c b/libavformat/tee.c index 89a4ceb2809..c5c59975e6b 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -56,7 +56,6 @@ typedef struct TeeContext { TeeSlave *slaves; int use_fifo; AVDictionary *fifo_options; - char *fifo_options_str; } TeeContext; static const char *const slave_delim = "|"; @@ -67,8 +66,8 @@ static const char *const slave_select_sep = ","; static const AVOption options[] = { {"use_fifo", "Use fifo pseudo-muxer to separate actual muxers from encoder", OFFSET(use_fifo), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, - {"fifo_options", "fifo pseudo-muxer options", OFFSET(fifo_options_str), - AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM}, + {"fifo_options", "fifo pseudo-muxer options", OFFSET(fifo_options), + AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM}, {NULL} }; @@ -159,7 +158,7 @@ static void close_slaves(AVFormatContext *avf) static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) { int i, ret; - AVDictionary *options = NULL; + AVDictionary *options = NULL, *bsf_options = NULL; AVDictionaryEntry *entry; char *filename; char *format = NULL, *select = NULL, *on_fail = NULL; @@ -186,6 +185,12 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) STEAL_OPTION("onfail", on_fail); STEAL_OPTION("use_fifo", use_fifo); STEAL_OPTION("fifo_options", fifo_options_str); + entry = NULL; + while ((entry = av_dict_get(options, "bsfs", entry, AV_DICT_IGNORE_SUFFIX))) { + /* trim out strlen("bsfs") characters from key */ + av_dict_set(&bsf_options, entry->key + 4, entry->value, 0); + av_dict_set(&options, entry->key, NULL, 0); + } ret = parse_slave_failure_policy_option(on_fail, tee_slave); if (ret < 0) { @@ -290,7 +295,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) goto end; } - ret = ff_format_output_open(avf2, filename, NULL); + ret = ff_format_output_open(avf2, filename, &options); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "Slave '%s': error opening: %s\n", slave, av_err2str(ret)); @@ -311,8 +316,8 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) } entry = NULL; - while (entry = av_dict_get(options, "bsfs", NULL, AV_DICT_IGNORE_SUFFIX)) { - const char *spec = entry->key + strlen("bsfs"); + while (entry = av_dict_get(bsf_options, "", NULL, AV_DICT_IGNORE_SUFFIX)) { + const char *spec = entry->key; if (*spec) { if (strspn(spec, slave_bsfs_spec_sep) != 1) { av_log(avf, AV_LOG_ERROR, @@ -352,7 +357,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) } } - av_dict_set(&options, entry->key, NULL, 0); + av_dict_set(&bsf_options, entry->key, NULL, 0); } for (i = 0; i < avf->nb_streams; i++){ @@ -399,6 +404,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) av_free(select); av_free(on_fail); av_dict_free(&options); + av_dict_free(&bsf_options); av_freep(&tmp_select); return ret; } @@ -468,12 +474,6 @@ static int tee_write_header(AVFormatContext *avf) filename++; } - if (tee->fifo_options_str) { - ret = av_dict_parse_string(&tee->fifo_options, tee->fifo_options_str, "=", ":", 0); - if (ret < 0) - goto fail; - } - if (!(tee->slaves = av_mallocz_array(nb_slaves, sizeof(*tee->slaves)))) { ret = AVERROR(ENOMEM); goto fail; @@ -564,7 +564,6 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt) if (s2 < 0) continue; - memset(&pkt2, 0, sizeof(AVPacket)); if ((ret = av_packet_ref(&pkt2, pkt)) < 0) if (!ret_all) { ret_all = ret; diff --git a/libavformat/tests/.gitignore b/libavformat/tests/.gitignore new file mode 100644 index 00000000000..7ceb7a356b4 --- /dev/null +++ b/libavformat/tests/.gitignore @@ -0,0 +1,7 @@ +/fifo_muxer +/movenc +/noproxy +/rtmpdh +/seek +/srtp +/url diff --git a/libavformat/tests/url.c b/libavformat/tests/url.c index 16231791286..1d961a1b431 100644 --- a/libavformat/tests/url.c +++ b/libavformat/tests/url.c @@ -19,12 +19,13 @@ */ #include "libavformat/url.h" +#include "libavformat/avformat.h" static void test(const char *base, const char *rel) { char buf[200], buf2[200]; ff_make_absolute_url(buf, sizeof(buf), base, rel); - printf("%s\n", buf); + printf("%50s %-20s => %s\n", base, rel, buf); if (base) { /* Test in-buffer replacement */ snprintf(buf2, sizeof(buf2), "%s", base); @@ -36,12 +37,26 @@ static void test(const char *base, const char *rel) } } +static void test2(const char *url) +{ + char proto[64]; + char auth[256]; + char host[256]; + char path[256]; + int port=-1; + + av_url_split(proto, sizeof(proto), auth, sizeof(auth), host, sizeof(host), &port, path, sizeof(path), url); + printf("%-60s => %-15s %-15s %-15s %5d %s\n", url, proto, auth, host, port, path); +} + int main(void) { + printf("Testing ff_make_absolute_url:\n"); test(NULL, "baz"); test("/foo/bar", "baz"); test("/foo/bar", "../baz"); test("/foo/bar", "/baz"); + test("/foo/bar", "../../../baz"); test("http://server/foo/", "baz"); test("http://server/foo/bar", "baz"); test("http://server/foo/", "../baz"); @@ -51,5 +66,21 @@ int main(void) test("http://server/foo/bar?param=value/with/slashes", "/baz"); test("http://server/foo/bar?param&otherparam", "?someparam"); test("http://server/foo/bar", "//other/url"); + test("http://server/foo/bar", "../../../../../other/url"); + test("http://server/foo/bar", "/../../../../../other/url"); + test("http://server/foo/bar", "/test/../../../../../other/url"); + test("http://server/foo/bar", "/test/../../test/../../../other/url"); + + printf("\nTesting av_url_split:\n"); + test2("/foo/bar"); + test2("http://server/foo/"); + test2("http://example.com/foo/bar"); + test2("http://user:pass@localhost:8080/foo/bar/123"); + test2("http://server/foo/bar?param=value/with/slashes"); + test2("https://1l-lh.a.net/i/1LIVE_HDS@179577/master.m3u8"); + test2("ftp://u:p%2B%2F2@ftp.pbt.com/ExportHD.mpg"); + test2("https://key.dns.com?key_id=2&model_id=12345&&access_key="); + test2("http://example.com#tag"); + return 0; } diff --git a/libavformat/thp.c b/libavformat/thp.c index ee5c78b6d3c..bcc3febaa19 100644 --- a/libavformat/thp.c +++ b/libavformat/thp.c @@ -75,6 +75,8 @@ static int thp_read_header(AVFormatContext *s) avio_rb32(pb); /* Max samples. */ thp->fps = av_d2q(av_int2float(avio_rb32(pb)), INT_MAX); + if (thp->fps.den <= 0 || thp->fps.num < 0) + return AVERROR_INVALIDDATA; thp->framecnt = avio_rb32(pb); thp->first_framesz = avio_rb32(pb); pb->maxsize = avio_rb32(pb); @@ -93,6 +95,9 @@ static int thp_read_header(AVFormatContext *s) avio_seek (pb, thp->compoff, SEEK_SET); thp->compcount = avio_rb32(pb); + if (thp->compcount > FF_ARRAY_ELEMS(thp->components)) + return AVERROR_INVALIDDATA; + /* Read the list of component types. */ avio_read(pb, thp->components, 16); @@ -145,6 +150,9 @@ static int thp_read_header(AVFormatContext *s) } } + if (!thp->vst) + return AVERROR_INVALIDDATA; + return 0; } @@ -181,7 +189,6 @@ static int thp_read_packet(AVFormatContext *s, if (ret < 0) return ret; if (ret != size) { - av_packet_unref(pkt); return AVERROR(EIO); } @@ -191,7 +198,6 @@ static int thp_read_packet(AVFormatContext *s, if (ret < 0) return ret; if (ret != thp->audiosize) { - av_packet_unref(pkt); return AVERROR(EIO); } diff --git a/libavformat/tiertexseq.c b/libavformat/tiertexseq.c index a89a0a9d614..d7719e5acb8 100644 --- a/libavformat/tiertexseq.c +++ b/libavformat/tiertexseq.c @@ -273,8 +273,10 @@ static int seq_read_packet(AVFormatContext *s, AVPacket *pkt) /* video packet */ if (seq->current_pal_data_size + seq->current_video_data_size != 0) { - if (av_new_packet(pkt, 1 + seq->current_pal_data_size + seq->current_video_data_size)) - return AVERROR(ENOMEM); + rc = av_new_packet(pkt, 1 + seq->current_pal_data_size + + seq->current_video_data_size); + if (rc < 0) + return rc; pkt->data[0] = 0; if (seq->current_pal_data_size) { diff --git a/libavformat/tls_gnutls.c b/libavformat/tls_gnutls.c index f32bc2821b8..0c4ef34f5fc 100644 --- a/libavformat/tls_gnutls.c +++ b/libavformat/tls_gnutls.c @@ -100,8 +100,7 @@ static int tls_close(URLContext *h) gnutls_deinit(c->session); if (c->cred) gnutls_certificate_free_credentials(c->cred); - if (c->tls_shared.tcp) - ffurl_close(c->tls_shared.tcp); + ffurl_closep(&c->tls_shared.tcp); ff_gnutls_deinit(); return 0; } @@ -183,6 +182,11 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op gnutls_transport_set_ptr(p->session, c->tcp); gnutls_priority_set_direct(p->session, "NORMAL", NULL); do { + if (ff_check_interrupt(&h->interrupt_callback)) { + ret = AVERROR_EXIT; + goto fail; + } + ret = gnutls_handshake(p->session); if (gnutls_error_is_fatal(ret)) { ret = print_tls_error(h, ret); diff --git a/libavformat/tls_libtls.c b/libavformat/tls_libtls.c index ba83b56ffe2..dff7f2d9fb4 100644 --- a/libavformat/tls_libtls.c +++ b/libavformat/tls_libtls.c @@ -44,8 +44,7 @@ static int ff_tls_close(URLContext *h) tls_close(p->ctx); tls_free(p->ctx); } - if (p->tls_shared.tcp) - ffurl_close(p->tls_shared.tcp); + ffurl_closep(&p->tls_shared.tcp); return 0; } diff --git a/libavformat/tls_mbedtls.c b/libavformat/tls_mbedtls.c index 9b80a1e3c74..965adf1be43 100644 --- a/libavformat/tls_mbedtls.c +++ b/libavformat/tls_mbedtls.c @@ -62,6 +62,7 @@ static int tls_close(URLContext *h) mbedtls_ctr_drbg_free(&tls_ctx->ctr_drbg_context); mbedtls_entropy_free(&tls_ctx->entropy_context); + ffurl_closep(&tls_ctx->tls_shared.tcp); return 0; } diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c index 7ae71bdaf36..002197fa76d 100644 --- a/libavformat/tls_openssl.c +++ b/libavformat/tls_openssl.c @@ -48,7 +48,7 @@ typedef struct TLSContext { #endif } TLSContext; -#if HAVE_THREADS +#if HAVE_THREADS && OPENSSL_VERSION_NUMBER < 0x10100000L #include pthread_mutex_t *openssl_mutexes; static void openssl_lock(int mode, int type, const char *file, int line) @@ -70,9 +70,16 @@ int ff_openssl_init(void) { ff_lock_avformat(); if (!openssl_init) { + /* OpenSSL 1.0.2 or below, then you would use SSL_library_init. If you are + * using OpenSSL 1.1.0 or above, then the library will initialize + * itself automatically. + * https://wiki.openssl.org/index.php/Library_Initialization + */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_library_init(); SSL_load_error_strings(); -#if HAVE_THREADS +#endif +#if HAVE_THREADS && OPENSSL_VERSION_NUMBER < 0x10100000L if (!CRYPTO_get_locking_callback()) { int i; openssl_mutexes = av_malloc_array(sizeof(pthread_mutex_t), CRYPTO_num_locks()); @@ -101,7 +108,7 @@ void ff_openssl_deinit(void) ff_lock_avformat(); openssl_init--; if (!openssl_init) { -#if HAVE_THREADS +#if HAVE_THREADS && OPENSSL_VERSION_NUMBER < 0x10100000L if (CRYPTO_get_locking_callback() == openssl_lock) { int i; CRYPTO_set_locking_callback(NULL); @@ -135,8 +142,7 @@ static int tls_close(URLContext *h) } if (c->ctx) SSL_CTX_free(c->ctx); - if (c->tls_shared.tcp) - ffurl_close(c->tls_shared.tcp); + ffurl_closep(&c->tls_shared.tcp); #if OPENSSL_VERSION_NUMBER >= 0x1010000fL if (c->url_bio_method) BIO_meth_free(c->url_bio_method); diff --git a/libavformat/tls_schannel.c b/libavformat/tls_schannel.c index 4f0badcb8dd..4bfaa852284 100644 --- a/libavformat/tls_schannel.c +++ b/libavformat/tls_schannel.c @@ -138,8 +138,7 @@ static int tls_close(URLContext *h) av_freep(&c->dec_buf); c->dec_buf_size = c->dec_buf_offset = 0; - if (c->tls_shared.tcp) - ffurl_close(c->tls_shared.tcp); + ffurl_closep(&c->tls_shared.tcp); return 0; } @@ -392,7 +391,12 @@ static int tls_read(URLContext *h, uint8_t *buf, int len) int size, ret; int min_enc_buf_size = len + SCHANNEL_FREE_BUFFER_SIZE; - if (len <= c->dec_buf_offset) + /* If we have some left-over data from previous network activity, + * return it first in case it is enough. It may contain + * data that is required to know whether this connection + * is still required or not, esp. in case of HTTP keep-alive + * connections. */ + if (c->dec_buf_offset > 0) goto cleanup; if (c->sspi_close_notify) @@ -424,7 +428,7 @@ static int tls_read(URLContext *h, uint8_t *buf, int len) c->enc_buf_offset += ret; } - while (c->enc_buf_offset > 0 && sspi_ret == SEC_E_OK && c->dec_buf_offset < len) { + while (c->enc_buf_offset > 0 && sspi_ret == SEC_E_OK) { /* input buffer */ init_sec_buffer(&inbuf[0], SECBUFFER_DATA, c->enc_buf, c->enc_buf_offset); diff --git a/libavformat/tls_securetransport.c b/libavformat/tls_securetransport.c index 37380541b11..3250b230517 100644 --- a/libavformat/tls_securetransport.c +++ b/libavformat/tls_securetransport.c @@ -251,8 +251,7 @@ static int tls_close(URLContext *h) } if (c->ca_array) CFRelease(c->ca_array); - if (c->tls_shared.tcp) - ffurl_close(c->tls_shared.tcp); + ffurl_closep(&c->tls_shared.tcp); return 0; } diff --git a/libavformat/ttaenc.c b/libavformat/ttaenc.c index d8e1136ead1..becd3e71536 100644 --- a/libavformat/ttaenc.c +++ b/libavformat/ttaenc.c @@ -145,19 +145,25 @@ static int tta_write_trailer(AVFormatContext *s) /* Write Seek table */ crc = ffio_get_checksum(tta->seek_table) ^ UINT32_MAX; avio_wl32(tta->seek_table, crc); - size = avio_close_dyn_buf(tta->seek_table, &ptr); + size = avio_get_dyn_buf(tta->seek_table, &ptr); avio_write(s->pb, ptr, size); - av_free(ptr); /* Write audio data */ tta_queue_flush(s); ff_ape_write_tag(s); - avio_flush(s->pb); return 0; } +static void tta_deinit(AVFormatContext *s) +{ + TTAMuxContext *tta = s->priv_data; + + ffio_free_dyn_buf(&tta->seek_table); + ff_packet_list_free(&tta->queue, &tta->queue_end); +} + AVOutputFormat ff_tta_muxer = { .name = "tta", .long_name = NULL_IF_CONFIG_SMALL("TTA (True Audio)"), @@ -167,6 +173,7 @@ AVOutputFormat ff_tta_muxer = { .audio_codec = AV_CODEC_ID_TTA, .video_codec = AV_CODEC_ID_NONE, .init = tta_init, + .deinit = tta_deinit, .write_header = tta_write_header, .write_packet = tta_write_packet, .write_trailer = tta_write_trailer, diff --git a/libavformat/tty.c b/libavformat/tty.c index 8d48f2c45c1..aed5c888c38 100644 --- a/libavformat/tty.c +++ b/libavformat/tty.c @@ -34,6 +34,13 @@ #include "internal.h" #include "sauce.h" +static int isansicode(int x) +{ + return x == 0x1B || x == 0x0A || x == 0x0D || (x >= 0x20 && x < 0x7f); +} + +static const char tty_extensions[31] = "ans,art,asc,diz,ice,nfo,txt,vt"; + typedef struct TtyDemuxContext { AVClass *class; int chars_per_frame; @@ -42,6 +49,26 @@ typedef struct TtyDemuxContext { AVRational framerate; /**< Set by a private option. */ } TtyDemuxContext; +static int read_probe(const AVProbeData *p) +{ + int cnt = 0; + + if (!p->buf_size) + return 0; + + for (int i = 0; i < 8 && i < p->buf_size; i++) + cnt += !!isansicode(p->buf[i]); + + if (cnt != 8) + return 0; + + for (int i = 8; i < p->buf_size; i++) + cnt += !!isansicode(p->buf[i]); + + return (cnt * 99LL / p->buf_size) * (cnt > 400) * + !!av_match_ext(p->filename, tty_extensions); +} + /** * Parse EFI header */ @@ -129,6 +156,8 @@ static int read_packet(AVFormatContext *avctx, AVPacket *pkt) pkt->size = av_get_packet(avctx->pb, pkt, n); if (pkt->size < 0) return pkt->size; + pkt->stream_index = 0; + pkt->pts = pkt->pos / s->chars_per_frame; pkt->flags |= AV_PKT_FLAG_KEY; return 0; } @@ -153,8 +182,10 @@ AVInputFormat ff_tty_demuxer = { .name = "tty", .long_name = NULL_IF_CONFIG_SMALL("Tele-typewriter"), .priv_data_size = sizeof(TtyDemuxContext), + .read_probe = read_probe, .read_header = read_header, .read_packet = read_packet, - .extensions = "ans,art,asc,diz,ice,nfo,txt,vt", + .extensions = tty_extensions, .priv_class = &tty_demuxer_class, + .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/ty.c b/libavformat/ty.c index bbb2e28a936..738a22e7de6 100644 --- a/libavformat/ty.c +++ b/libavformat/ty.c @@ -454,7 +454,7 @@ static int demux_video(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt) TYDemuxContext *ty = s->priv_data; const int subrec_type = rec_hdr->subrec_type; const int64_t rec_size = rec_hdr->rec_size; - int es_offset1; + int es_offset1, ret; int got_packet = 0; if (subrec_type != 0x02 && subrec_type != 0x0c && @@ -474,8 +474,8 @@ static int demux_video(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt) int size = rec_hdr->rec_size - VIDEO_PES_LENGTH - es_offset1; ty->cur_chunk_pos += VIDEO_PES_LENGTH + es_offset1; - if (av_new_packet(pkt, size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, size)) < 0) + return ret; memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, size); ty->cur_chunk_pos += size; pkt->stream_index = 0; @@ -498,8 +498,8 @@ static int demux_video(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt) } if (!got_packet) { - if (av_new_packet(pkt, rec_size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, rec_size)) < 0) + return ret; memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size); ty->cur_chunk_pos += rec_size; pkt->stream_index = 0; @@ -578,7 +578,7 @@ static int demux_audio(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt) TYDemuxContext *ty = s->priv_data; const int subrec_type = rec_hdr->subrec_type; const int64_t rec_size = rec_hdr->rec_size; - int es_offset1; + int es_offset1, ret; if (subrec_type == 2) { int need = 0; @@ -621,8 +621,8 @@ static int demux_audio(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt) ty->pes_buf_cnt = 0; } - if (av_new_packet(pkt, rec_size - need) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, rec_size - need)) < 0) + return ret; memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size - need); ty->cur_chunk_pos += rec_size - need; pkt->stream_index = 1; @@ -643,8 +643,8 @@ static int demux_audio(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt) } } } else if (subrec_type == 0x03) { - if (av_new_packet(pkt, rec_size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, rec_size)) < 0) + return ret; memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size); ty->cur_chunk_pos += rec_size; pkt->stream_index = 1; @@ -674,15 +674,15 @@ static int demux_audio(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt) } else if (subrec_type == 0x04) { /* SA Audio with no PES Header */ /* ================================================ */ - if (av_new_packet(pkt, rec_size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, rec_size)) < 0) + return ret; memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size); ty->cur_chunk_pos += rec_size; pkt->stream_index = 1; pkt->pts = ty->last_audio_pts; } else if (subrec_type == 0x09) { - if (av_new_packet(pkt, rec_size) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, rec_size)) < 0) + return ret; memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size); ty->cur_chunk_pos += rec_size ; pkt->stream_index = 1; diff --git a/libavformat/udp.c b/libavformat/udp.c index cf73d331e04..ad6992c57db 100644 --- a/libavformat/udp.c +++ b/libavformat/udp.c @@ -61,8 +61,13 @@ #define IPPROTO_UDPLITE 136 #endif +#if HAVE_W32THREADS +#undef HAVE_PTHREAD_CANCEL +#define HAVE_PTHREAD_CANCEL 1 +#endif + #if HAVE_PTHREAD_CANCEL -#include +#include "libavutil/thread.h" #endif #ifndef IPV6_ADD_MEMBERSHIP @@ -71,6 +76,7 @@ #endif #define UDP_TX_BUF_SIZE 32768 +#define UDP_RX_BUF_SIZE 393216 #define UDP_MAX_PKT_SIZE 65536 #define UDP_HEADER_SIZE 8 @@ -274,7 +280,7 @@ static int udp_set_multicast_sources(URLContext *h, } return 0; #else - av_log(NULL, AV_LOG_ERROR, + av_log(h, AV_LOG_ERROR, "Setting multicast sources only supported for IPv4\n"); return AVERROR(EINVAL); #endif @@ -283,7 +289,7 @@ static int udp_set_multicast_sources(URLContext *h, for (i = 0; i < nb_sources; i++) { struct ip_mreq_source mreqs; if (sources[i].ss_family != AF_INET) { - av_log(NULL, AV_LOG_ERROR, "Source/block address %d is of incorrect protocol family\n", i + 1); + av_log(h, AV_LOG_ERROR, "Source/block address %d is of incorrect protocol family\n", i + 1); return AVERROR(EINVAL); } @@ -298,9 +304,9 @@ static int udp_set_multicast_sources(URLContext *h, include ? IP_ADD_SOURCE_MEMBERSHIP : IP_BLOCK_SOURCE, (const void *)&mreqs, sizeof(mreqs)) < 0) { if (include) - ff_log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_ADD_SOURCE_MEMBERSHIP)"); + ff_log_net_error(h, AV_LOG_ERROR, "setsockopt(IP_ADD_SOURCE_MEMBERSHIP)"); else - ff_log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_BLOCK_SOURCE)"); + ff_log_net_error(h, AV_LOG_ERROR, "setsockopt(IP_BLOCK_SOURCE)"); return ff_neterrno(); } } @@ -519,14 +525,12 @@ static void *circular_buffer_task_tx( void *_URLContext) { URLContext *h = _URLContext; UDPContext *s = h->priv_data; - int old_cancelstate; int64_t target_timestamp = av_gettime_relative(); int64_t start_timestamp = av_gettime_relative(); int64_t sent_bits = 0; int64_t burst_interval = s->bitrate ? (s->burst_bits * 1000000 / s->bitrate) : 0; int64_t max_delay = s->bitrate ? ((int64_t)h->max_packet_size * 8 * 1000000 / s->bitrate + 1) : 0; - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate); pthread_mutex_lock(&s->mutex); if (ff_socket_nonblock(s->udp_fd, 0) < 0) { @@ -561,7 +565,6 @@ static void *circular_buffer_task_tx( void *_URLContext) av_fifo_generic_read(s->fifo, s->tmp, len, NULL); pthread_mutex_unlock(&s->mutex); - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelstate); if (s->bitrate) { timestamp = av_gettime_relative(); @@ -607,7 +610,6 @@ static void *circular_buffer_task_tx( void *_URLContext) } } - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate); pthread_mutex_lock(&s->mutex); } @@ -636,7 +638,7 @@ static int udp_open(URLContext *h, const char *uri, int flags) is_output = !(flags & AVIO_FLAG_READ); if (s->buffer_size < 0) - s->buffer_size = is_output ? UDP_TX_BUF_SIZE : UDP_MAX_PKT_SIZE; + s->buffer_size = is_output ? UDP_TX_BUF_SIZE : UDP_RX_BUF_SIZE; if (s->sources) { if (ff_ip_parse_sources(h, s->sources, &s->filters) < 0) @@ -797,7 +799,7 @@ static int udp_open(URLContext *h, const char *uri, int flags) * receiving UDP packets from other sources aimed at the same UDP * port. This fails on windows. This makes sending to the same address * using sendto() fail, so only do it if we're opened in read-only mode. */ - if (s->is_multicast && !(h->flags & AVIO_FLAG_WRITE)) { + if (s->is_multicast && (h->flags & AVIO_FLAG_READ)) { bind_ret = bind(udp_fd,(struct sockaddr *)&s->dest_addr, len); } /* bind to the local address if not multicast or if the multicast @@ -861,7 +863,7 @@ static int udp_open(URLContext *h, const char *uri, int flags) } else { av_log(h, AV_LOG_DEBUG, "end receive buffer size reported is %d\n", tmp); if(tmp < s->buffer_size) - av_log(h, AV_LOG_WARNING, "attempted to set receive buffer to size %d but it only ended up set as %d", s->buffer_size, tmp); + av_log(h, AV_LOG_WARNING, "attempted to set receive buffer to size %d but it only ended up set as %d\n", s->buffer_size, tmp); } /* make the socket non-blocking */ @@ -978,9 +980,10 @@ static int udp_read(URLContext *h, uint8_t *buf, int size) int64_t t = av_gettime() + 100000; struct timespec tv = { .tv_sec = t / 1000000, .tv_nsec = (t % 1000000) * 1000 }; - if (pthread_cond_timedwait(&s->cond, &s->mutex, &tv) < 0) { + int err = pthread_cond_timedwait(&s->cond, &s->mutex, &tv); + if (err) { pthread_mutex_unlock(&s->mutex); - return AVERROR(errno == ETIMEDOUT ? EAGAIN : errno); + return AVERROR(err == ETIMEDOUT ? EAGAIN : err); } nonblock = 1; } @@ -1071,8 +1074,17 @@ static int udp_close(URLContext *h) if (s->thread_started) { int ret; // Cancel only read, as write has been signaled as success to the user - if (h->flags & AVIO_FLAG_READ) + if (h->flags & AVIO_FLAG_READ) { +#ifdef _WIN32 + /* recvfrom() is not a cancellation point for win32, so we shutdown + * the socket and abort pending IO, subsequent recvfrom() calls + * will fail with WSAESHUTDOWN causing the thread to exit. */ + shutdown(s->udp_fd, SD_RECEIVE); + CancelIoEx((HANDLE)(SOCKET)s->udp_fd, NULL); +#else pthread_cancel(s->circular_buffer_thread); +#endif + } ret = pthread_join(s->circular_buffer_thread, NULL); if (ret != 0) av_log(h, AV_LOG_ERROR, "pthread_join(): %s\n", strerror(ret)); diff --git a/libavformat/url.c b/libavformat/url.c index 596fb49cfcf..20463a66749 100644 --- a/libavformat/url.c +++ b/libavformat/url.c @@ -21,6 +21,7 @@ #include "avformat.h" +#include "internal.h" #include "config.h" #include "url.h" #if CONFIG_NETWORK @@ -77,10 +78,55 @@ int ff_url_join(char *str, int size, const char *proto, return strlen(str); } +static void trim_double_dot_url(char *buf, const char *rel, int size) +{ + const char *p = rel; + const char *root = rel; + char tmp_path[MAX_URL_SIZE] = {0, }; + char *sep; + char *node; + + /* Get the path root of the url which start by "://" */ + if (p && (sep = strstr(p, "://"))) { + sep += 3; + root = strchr(sep, '/'); + if (!root) + return; + } + + /* set new current position if the root node is changed */ + p = root; + while (p && (node = strstr(p, ".."))) { + av_strlcat(tmp_path, p, node - p + strlen(tmp_path)); + p = node + 3; + sep = strrchr(tmp_path, '/'); + if (sep) + sep[0] = '\0'; + else + tmp_path[0] = '\0'; + } + + if (!av_stristart(p, "/", NULL) && root != rel) + av_strlcat(tmp_path, "/", size); + + av_strlcat(tmp_path, p, size); + /* start set buf after temp path process. */ + av_strlcpy(buf, rel, root - rel + 1); + + if (!av_stristart(tmp_path, "/", NULL) && root != rel) + av_strlcat(buf, "/", size); + + av_strlcat(buf, tmp_path, size); +} + void ff_make_absolute_url(char *buf, int size, const char *base, const char *rel) { char *sep, *path_query; + char *root, *p; + char tmp_path[MAX_URL_SIZE]; + + memset(tmp_path, 0, sizeof(tmp_path)); /* Absolute path, relative to the current server */ if (base && strstr(base, "://") && rel[0] == '/') { if (base != buf) @@ -99,11 +145,15 @@ void ff_make_absolute_url(char *buf, int size, const char *base, } } av_strlcat(buf, rel, size); + trim_double_dot_url(tmp_path, buf, size); + memset(buf, 0, size); + av_strlcpy(buf, tmp_path, size); return; } /* If rel actually is an absolute url, just copy it */ if (!base || strstr(rel, "://") || rel[0] == '/') { - av_strlcpy(buf, rel, size); + memset(buf, 0, size); + trim_double_dot_url(buf, rel, size); return; } if (base != buf) @@ -117,19 +167,40 @@ void ff_make_absolute_url(char *buf, int size, const char *base, /* Is relative path just a new query part? */ if (rel[0] == '?') { av_strlcat(buf, rel, size); + trim_double_dot_url(tmp_path, buf, size); + memset(buf, 0, size); + av_strlcpy(buf, tmp_path, size); return; } + root = p = buf; + /* Get the path root of the url which start by "://" */ + if (p && strstr(p, "://")) { + sep = strstr(p, "://"); + if (sep) { + sep += 3; + root = strchr(sep, '/'); + if (!root) + return; + } + } + /* Remove the file name from the base url */ sep = strrchr(buf, '/'); + if (sep && sep <= root) + sep = root; + if (sep) sep[1] = '\0'; else buf[0] = '\0'; - while (av_strstart(rel, "../", NULL) && sep) { + while (av_strstart(rel, "..", NULL) && sep) { /* Remove the path delimiter at the end */ - sep[0] = '\0'; - sep = strrchr(buf, '/'); + if (sep > root) { + sep[0] = '\0'; + sep = strrchr(buf, '/'); + } + /* If the next directory name to pop off is "..", break here */ if (!strcmp(sep ? &sep[1] : buf, "..")) { /* Readd the slash we just removed */ @@ -144,6 +215,9 @@ void ff_make_absolute_url(char *buf, int size, const char *base, rel += 3; } av_strlcat(buf, rel, size); + trim_double_dot_url(tmp_path, buf, size); + memset(buf, 0, size); + av_strlcpy(buf, tmp_path, size); } AVIODirEntry *ff_alloc_dir_entry(void) diff --git a/libavformat/urldecode.c b/libavformat/urldecode.c index 283d9126716..5261bcd0cd8 100644 --- a/libavformat/urldecode.c +++ b/libavformat/urldecode.c @@ -32,7 +32,7 @@ #include "libavutil/avstring.h" #include "urldecode.h" -char *ff_urldecode(const char *url) +char *ff_urldecode(const char *url, int decode_plus_sign) { int s = 0, d = 0, url_len = 0; char c; @@ -74,7 +74,7 @@ char *ff_urldecode(const char *url) dest[d++] = c2; dest[d++] = c3; } - } else if (c == '+') { + } else if (c == '+' && decode_plus_sign) { dest[d++] = ' '; } else { dest[d++] = c; diff --git a/libavformat/urldecode.h b/libavformat/urldecode.h index cb81ebc6f79..80b11c34286 100644 --- a/libavformat/urldecode.h +++ b/libavformat/urldecode.h @@ -26,10 +26,11 @@ * in that case the original string is duplicated. * * @param url a string to be decoded. + * @param decode_plus_sign if nonzero plus sign is decoded to space * @return new string with the URL decoded or NULL if decoding failed. * Note that the returned string should be explicitly freed when not * used anymore. */ -char *ff_urldecode(const char *url); +char *ff_urldecode(const char *url, int decode_plus_sign); #endif /* AVFORMAT_URLDECODE_H */ diff --git a/libavformat/utils.c b/libavformat/utils.c index 6c6f4e1bd13..ba8aaebfb77 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -19,7 +19,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include #include #include "config.h" @@ -31,26 +30,22 @@ #include "libavutil/mathematics.h" #include "libavutil/opt.h" #include "libavutil/parseutils.h" -#include "libavutil/pixdesc.h" +#include "libavutil/pixfmt.h" #include "libavutil/thread.h" #include "libavutil/time.h" -#include "libavutil/time_internal.h" #include "libavutil/timestamp.h" #include "libavcodec/bytestream.h" #include "libavcodec/internal.h" #include "libavcodec/raw.h" -#include "audiointerleave.h" #include "avformat.h" #include "avio_internal.h" #include "id3v2.h" #include "internal.h" -#include "metadata.h" #if CONFIG_NETWORK #include "network.h" #endif -#include "riff.h" #include "url.h" #include "libavutil/ffversion.h" @@ -77,7 +72,7 @@ const char *avformat_configuration(void) const char *avformat_license(void) { #define LICENSE_PREFIX "libavformat license: " - return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1; + return &LICENSE_PREFIX FFMPEG_LICENSE[sizeof(LICENSE_PREFIX) - 1]; } int ff_lock_avformat(void) @@ -222,8 +217,9 @@ static const AVCodec *find_probe_decoder(AVFormatContext *s, const AVStream *st, if (codec->capabilities & AV_CODEC_CAP_AVOID_PROBING) { const AVCodec *probe_codec = NULL; - while (probe_codec = av_codec_next(probe_codec)) { - if (probe_codec->id == codec_id && + void *iter = NULL; + while ((probe_codec = av_codec_iterate(&iter))) { + if (probe_codec->id == codec->id && av_codec_is_decoder(probe_codec) && !(probe_codec->capabilities & (AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_EXPERIMENTAL))) { return probe_codec; @@ -268,7 +264,6 @@ int ffio_limit(AVIOContext *s, int size) * Return the number of bytes read or an error. */ static int append_packet_chunked(AVIOContext *s, AVPacket *pkt, int size) { - int64_t orig_pos = pkt->pos; // av_grow_packet might reset pos int orig_size = pkt->size; int ret; @@ -301,7 +296,6 @@ static int append_packet_chunked(AVIOContext *s, AVPacket *pkt, int size) if (size > 0) pkt->flags |= AV_PKT_FLAG_CORRUPT; - pkt->pos = orig_pos; if (!pkt->size) av_packet_unref(pkt); return pkt->size > orig_size ? pkt->size - orig_size : ret; @@ -363,7 +357,7 @@ static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st, int i; av_log(s, AV_LOG_DEBUG, "Probe with size=%d, packets=%d detected %s with score=%d\n", - pd->buf_size, MAX_PROBE_PACKETS - st->probe_packets, + pd->buf_size, s->max_probe_packets - st->probe_packets, fmt->name, score); for (i = 0; fmt_id_type[i].name; i++) { if (!strcmp(fmt->name, fmt_id_type[i].name)) { @@ -460,10 +454,12 @@ int ff_packet_list_put(AVPacketList **packet_buffer, return ret; } } else { - // TODO: Adapt callers in this file so the line below can use - // av_packet_move_ref() to effectively move the reference - // to the list. - pktl->pkt = *pkt; + ret = av_packet_make_refcounted(pkt); + if (ret < 0) { + av_free(pktl); + return ret; + } + av_packet_move_ref(&pktl->pkt, pkt); } if (*packet_buffer) @@ -635,31 +631,26 @@ FF_ENABLE_DEPRECATION_WARNINGS s->metadata = s->internal->id3v2_meta; s->internal->id3v2_meta = NULL; } else if (s->internal->id3v2_meta) { - int level = AV_LOG_WARNING; - if (s->error_recognition & AV_EF_COMPLIANT) - level = AV_LOG_ERROR; - av_log(s, level, "Discarding ID3 tags because more suitable tags were found.\n"); + av_log(s, AV_LOG_WARNING, "Discarding ID3 tags because more suitable tags were found.\n"); av_dict_free(&s->internal->id3v2_meta); - if (s->error_recognition & AV_EF_EXPLODE) - return AVERROR_INVALIDDATA; } if (id3v2_extra_meta) { if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") || !strcmp(s->iformat->name, "tta") || !strcmp(s->iformat->name, "wav")) { - if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0) - goto fail; - if ((ret = ff_id3v2_parse_chapters(s, &id3v2_extra_meta)) < 0) - goto fail; - if ((ret = ff_id3v2_parse_priv(s, &id3v2_extra_meta)) < 0) - goto fail; + if ((ret = ff_id3v2_parse_apic(s, id3v2_extra_meta)) < 0) + goto close; + if ((ret = ff_id3v2_parse_chapters(s, id3v2_extra_meta)) < 0) + goto close; + if ((ret = ff_id3v2_parse_priv(s, id3v2_extra_meta)) < 0) + goto close; } else av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n"); } ff_id3v2_free_extra_meta(&id3v2_extra_meta); if ((ret = avformat_queue_attached_pictures(s)) < 0) - goto fail; + goto close; if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->internal->data_offset) s->internal->data_offset = avio_tell(s->pb); @@ -678,6 +669,9 @@ FF_ENABLE_DEPRECATION_WARNINGS *ps = s; return 0; +close: + if (s->iformat->read_close) + s->iformat->read_close(s); fail: ff_id3v2_free_extra_meta(&id3v2_extra_meta); av_dict_free(&tmp); @@ -833,28 +827,31 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt) int ret, i, err; AVStream *st; + pkt->data = NULL; + pkt->size = 0; + av_init_packet(pkt); + for (;;) { AVPacketList *pktl = s->internal->raw_packet_buffer; + const AVPacket *pkt1; if (pktl) { - *pkt = pktl->pkt; - st = s->streams[pkt->stream_index]; + st = s->streams[pktl->pkt.stream_index]; if (s->internal->raw_packet_buffer_remaining_size <= 0) if ((err = probe_codec(s, st, NULL)) < 0) return err; if (st->request_probe <= 0) { - s->internal->raw_packet_buffer = pktl->next; + ff_packet_list_get(&s->internal->raw_packet_buffer, + &s->internal->raw_packet_buffer_end, pkt); s->internal->raw_packet_buffer_remaining_size += pkt->size; - av_free(pktl); return 0; } } - pkt->data = NULL; - pkt->size = 0; - av_init_packet(pkt); ret = s->iformat->read_packet(s, pkt); if (ret < 0) { + av_packet_unref(pkt); + /* Some demuxers return FFERROR_REDO when they consume data and discard it (ignored streams, junk, extradata). We must re-call the demuxer to get the real packet. */ @@ -873,22 +870,25 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt) } err = av_packet_make_refcounted(pkt); - if (err < 0) + if (err < 0) { + av_packet_unref(pkt); return err; + } - if ((s->flags & AVFMT_FLAG_DISCARD_CORRUPT) && - (pkt->flags & AV_PKT_FLAG_CORRUPT)) { + if (pkt->flags & AV_PKT_FLAG_CORRUPT) { av_log(s, AV_LOG_WARNING, - "Dropped corrupted packet (stream = %d)\n", - pkt->stream_index); - av_packet_unref(pkt); - continue; + "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"); } - if (pkt->stream_index >= (unsigned)s->nb_streams) { - av_log(s, AV_LOG_ERROR, "Invalid stream index %d\n", pkt->stream_index); - continue; - } + av_assert0(pkt->stream_index < (unsigned)s->nb_streams && + "Invalid stream index.\n"); st = s->streams[pkt->stream_index]; @@ -917,11 +917,14 @@ int ff_read_packet(AVFormatContext *s, AVPacket *pkt) err = ff_packet_list_put(&s->internal->raw_packet_buffer, &s->internal->raw_packet_buffer_end, pkt, 0); - if (err) + if (err < 0) { + av_packet_unref(pkt); return err; - s->internal->raw_packet_buffer_remaining_size -= pkt->size; + } + pkt1 = &s->internal->raw_packet_buffer_end->pkt; + s->internal->raw_packet_buffer_remaining_size -= pkt1->size; - if ((err = probe_codec(s, st, pkt)) < 0) + if ((err = probe_codec(s, st, pkt1)) < 0) return err; } } @@ -1008,12 +1011,13 @@ FF_ENABLE_DEPRECATION_WARNINGS } } -static int is_intra_only(enum AVCodecID id) +int ff_is_intra_only(enum AVCodecID id) { const AVCodecDescriptor *d = avcodec_descriptor_get(id); if (!d) return 0; - if (d->type == AVMEDIA_TYPE_VIDEO && !(d->props & AV_CODEC_PROP_INTRA_ONLY)) + if ((d->type == AVMEDIA_TYPE_VIDEO || d->type == AVMEDIA_TYPE_AUDIO) && + !(d->props & AV_CODEC_PROP_INTRA_ONLY)) return 0; return 1; } @@ -1152,7 +1156,7 @@ static void update_initial_timestamps(AVFormatContext *s, int stream_index, if (st->start_time == AV_NOPTS_VALUE && pktl_it->pkt.pts != AV_NOPTS_VALUE) { st->start_time = pktl_it->pkt.pts; if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->sample_rate) - st->start_time += av_rescale_q(st->skip_samples, (AVRational){1, st->codecpar->sample_rate}, st->time_base); + st->start_time = av_sat_add64(st->start_time, av_rescale_q(st->skip_samples, (AVRational){1, st->codecpar->sample_rate}, st->time_base)); } } @@ -1165,12 +1169,12 @@ static void update_initial_timestamps(AVFormatContext *s, int stream_index, st->start_time = pts; } if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->sample_rate) - st->start_time += av_rescale_q(st->skip_samples, (AVRational){1, st->codecpar->sample_rate}, st->time_base); + st->start_time = av_sat_add64(st->start_time, av_rescale_q(st->skip_samples, (AVRational){1, st->codecpar->sample_rate}, st->time_base)); } } static void update_initial_durations(AVFormatContext *s, AVStream *st, - int stream_index, int duration) + int stream_index, int64_t duration) { AVPacketList *pktl = s->internal->packet_buffer ? s->internal->packet_buffer : s->internal->parse_queue; int64_t cur_dts = RELATIVE_TS_BASE; @@ -1300,7 +1304,7 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, } duration = av_mul_q((AVRational) {pkt->duration, 1}, st->time_base); - if (pkt->duration == 0) { + if (pkt->duration <= 0) { ff_compute_frame_duration(s, &num, &den, st, pc, pkt); if (den && num) { duration = (AVRational) {num, den}; @@ -1311,7 +1315,7 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, } } - if (pkt->duration != 0 && (s->internal->packet_buffer || s->internal->parse_queue)) + if (pkt->duration > 0 && (s->internal->packet_buffer || s->internal->parse_queue)) update_initial_durations(s, st, pkt->stream_index, pkt->duration); /* Correct timestamps with byte offset if demuxers only have timestamps @@ -1355,7 +1359,7 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, if (st->last_IP_duration == 0 && (uint64_t)pkt->duration <= INT32_MAX) st->last_IP_duration = pkt->duration; if (pkt->dts != AV_NOPTS_VALUE) - st->cur_dts = pkt->dts + st->last_IP_duration; + st->cur_dts = av_sat_add64(pkt->dts, st->last_IP_duration); if (pkt->dts != AV_NOPTS_VALUE && pkt->pts == AV_NOPTS_VALUE && st->last_IP_duration > 0 && @@ -1371,7 +1375,7 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, * by knowing the future. */ } else if (pkt->pts != AV_NOPTS_VALUE || pkt->dts != AV_NOPTS_VALUE || - pkt->duration ) { + pkt->duration > 0 ) { /* presentation is not delayed : PTS and DTS are the same */ if (pkt->pts == AV_NOPTS_VALUE) @@ -1381,7 +1385,7 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, if (pkt->pts == AV_NOPTS_VALUE) pkt->pts = st->cur_dts; pkt->dts = pkt->pts; - if (pkt->pts != AV_NOPTS_VALUE) + if (pkt->pts != AV_NOPTS_VALUE && duration.num >= 0) st->cur_dts = av_add_stable(st->time_base, pkt->pts, duration, 1); } } @@ -1406,7 +1410,7 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, presentation_delayed, delay, av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts), st->index, st->id); /* update flags */ - if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA || is_intra_only(st->codecpar->codec_id)) + if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA || ff_is_intra_only(st->codecpar->codec_id)) pkt->flags |= AV_PKT_FLAG_KEY; #if FF_API_CONVERGENCE_DURATION FF_DISABLE_DEPRECATION_WARNINGS @@ -1433,31 +1437,30 @@ void ff_packet_list_free(AVPacketList **pkt_buf, AVPacketList **pkt_buf_end) /** * Parse a packet, add all split parts to parse_queue. * - * @param pkt Packet to parse, NULL when flushing the parser at end of stream. + * @param pkt Packet to parse; must not be NULL. + * @param flush Indicates whether to flush. If set, pkt must be blank. */ -static int parse_packet(AVFormatContext *s, AVPacket *pkt, int stream_index) +static int parse_packet(AVFormatContext *s, AVPacket *pkt, + int stream_index, int flush) { - AVPacket out_pkt = { 0 }, flush_pkt = { 0 }; + AVPacket out_pkt; AVStream *st = s->streams[stream_index]; - uint8_t *data = pkt ? pkt->data : NULL; - int size = pkt ? pkt->size : 0; - int ret = 0, got_output = 0; - - if (!pkt) { - av_init_packet(&flush_pkt); - pkt = &flush_pkt; - got_output = 1; - } else if (!size && st->parser->flags & PARSER_FLAG_COMPLETE_FRAMES) { + uint8_t *data = pkt->data; + int size = pkt->size; + int ret = 0, got_output = flush; + + if (size || flush) { + av_init_packet(&out_pkt); + } else if (st->parser->flags & PARSER_FLAG_COMPLETE_FRAMES) { // preserve 0-size sync packets compute_pkt_fields(s, st, st->parser, pkt, AV_NOPTS_VALUE, AV_NOPTS_VALUE); } - while (size > 0 || (pkt == &flush_pkt && got_output)) { + while (size > 0 || (flush && got_output)) { int len; int64_t next_pts = pkt->pts; int64_t next_dts = pkt->dts; - av_init_packet(&out_pkt); len = av_parser_parse2(st->parser, st->internal->avctx, &out_pkt.data, &out_pkt.size, data, size, pkt->pts, pkt->dts, pkt->pos); @@ -1537,7 +1540,7 @@ static int parse_packet(AVFormatContext *s, AVPacket *pkt, int stream_index) } /* end of the stream => close and free the parser */ - if (pkt == &flush_pkt) { + if (flush) { av_parser_close(st->parser); st->parser = NULL; } @@ -1569,17 +1572,14 @@ static int64_t ts_to_samples(AVStream *st, int64_t ts) static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) { - int ret = 0, i, got_packet = 0; + int ret, i, got_packet = 0; AVDictionary *metadata = NULL; - av_init_packet(pkt); - while (!got_packet && !s->internal->parse_queue) { AVStream *st; - AVPacket cur_pkt; /* read next packet */ - ret = ff_read_packet(s, &cur_pkt); + ret = ff_read_packet(s, pkt); if (ret < 0) { if (ret == AVERROR(EAGAIN)) return ret; @@ -1587,14 +1587,14 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) for (i = 0; i < s->nb_streams; i++) { st = s->streams[i]; if (st->parser && st->need_parsing) - parse_packet(s, NULL, st->index); + parse_packet(s, pkt, st->index, 1); } /* all remaining packets are now in parse_queue => * really terminate parsing */ break; } ret = 0; - st = s->streams[cur_pkt.stream_index]; + st = s->streams[pkt->stream_index]; /* update context if required */ if (st->internal->need_context_update) { @@ -1611,38 +1611,42 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) } ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar); - if (ret < 0) + if (ret < 0) { + av_packet_unref(pkt); return ret; + } #if FF_API_LAVF_AVCTX FF_DISABLE_DEPRECATION_WARNINGS /* update deprecated public codec context */ ret = avcodec_parameters_to_context(st->codec, st->codecpar); - if (ret < 0) + if (ret < 0) { + av_packet_unref(pkt); return ret; + } FF_ENABLE_DEPRECATION_WARNINGS #endif st->internal->need_context_update = 0; } - if (cur_pkt.pts != AV_NOPTS_VALUE && - cur_pkt.dts != AV_NOPTS_VALUE && - cur_pkt.pts < cur_pkt.dts) { + if (pkt->pts != AV_NOPTS_VALUE && + pkt->dts != AV_NOPTS_VALUE && + pkt->pts < pkt->dts) { av_log(s, AV_LOG_WARNING, "Invalid timestamps stream=%d, pts=%s, dts=%s, size=%d\n", - cur_pkt.stream_index, - av_ts2str(cur_pkt.pts), - av_ts2str(cur_pkt.dts), - cur_pkt.size); + pkt->stream_index, + av_ts2str(pkt->pts), + av_ts2str(pkt->dts), + pkt->size); } if (s->debug & FF_FDEBUG_TS) av_log(s, AV_LOG_DEBUG, "ff_read_packet stream=%d, pts=%s, dts=%s, size=%d, duration=%"PRId64", flags=%d\n", - cur_pkt.stream_index, - av_ts2str(cur_pkt.pts), - av_ts2str(cur_pkt.dts), - cur_pkt.size, cur_pkt.duration, cur_pkt.flags); + pkt->stream_index, + av_ts2str(pkt->pts), + av_ts2str(pkt->dts), + pkt->size, pkt->duration, pkt->flags); if (st->need_parsing && !st->parser && !(s->flags & AVFMT_FLAG_NOPARSE)) { st->parser = av_parser_init(st->codecpar->codec_id); @@ -1662,7 +1666,6 @@ FF_ENABLE_DEPRECATION_WARNINGS if (!st->need_parsing || !st->parser) { /* no parsing needed: we just output the packet as is */ - *pkt = cur_pkt; compute_pkt_fields(s, st, NULL, pkt, AV_NOPTS_VALUE, AV_NOPTS_VALUE); if ((s->iformat->flags & AVFMT_GENERIC_INDEX) && (pkt->flags & AV_PKT_FLAG_KEY) && pkt->dts != AV_NOPTS_VALUE) { @@ -1672,7 +1675,7 @@ FF_ENABLE_DEPRECATION_WARNINGS } got_packet = 1; } else if (st->discard < AVDISCARD_ALL) { - if ((ret = parse_packet(s, &cur_pkt, cur_pkt.stream_index)) < 0) + if ((ret = parse_packet(s, pkt, pkt->stream_index, 0)) < 0) return ret; st->codecpar->sample_rate = st->internal->avctx->sample_rate; st->codecpar->bit_rate = st->internal->avctx->bit_rate; @@ -1681,15 +1684,12 @@ FF_ENABLE_DEPRECATION_WARNINGS st->codecpar->codec_id = st->internal->avctx->codec_id; } else { /* free packet */ - av_packet_unref(&cur_pkt); + av_packet_unref(pkt); } if (pkt->flags & AV_PKT_FLAG_KEY) st->skip_to_keyframe = 0; if (st->skip_to_keyframe) { - av_packet_unref(&cur_pkt); - if (got_packet) { - *pkt = cur_pkt; - } + av_packet_unref(pkt); got_packet = 0; } } @@ -1762,6 +1762,11 @@ FF_ENABLE_DEPRECATION_WARNINGS av_ts2str(pkt->dts), pkt->size, pkt->duration, pkt->flags); + /* A demuxer might have returned EOF because of an IO error, let's + * propagate this back to the user. */ + if (ret == AVERROR_EOF && s->pb && s->pb->error < 0 && s->pb->error != AVERROR(EAGAIN)) + ret = s->pb->error; + return ret; } @@ -1840,10 +1845,11 @@ int av_read_frame(AVFormatContext *s, AVPacket *pkt) ret = ff_packet_list_put(&s->internal->packet_buffer, &s->internal->packet_buffer_end, - pkt, FF_PACKETLIST_FLAG_REF_PACKET); - av_packet_unref(pkt); - if (ret < 0) + pkt, 0); + if (ret < 0) { + av_packet_unref(pkt); return ret; + } } return_packet: @@ -1938,7 +1944,7 @@ void ff_read_frame_flush(AVFormatContext *s) /* We set the current DTS to an unspecified origin. */ st->cur_dts = AV_NOPTS_VALUE; - st->probe_packets = MAX_PROBE_PACKETS; + st->probe_packets = s->max_probe_packets; for (j = 0; j < MAX_REORDER_DELAY + 1; j++) st->pts_buffer[j] = AV_NOPTS_VALUE; @@ -2096,6 +2102,8 @@ void ff_configure_buffers_for_index(AVFormatContext *s, int64_t time_tolerance) //We could use URLProtocol flags here but as many user applications do not use URLProtocols this would be unreliable const char *proto = avio_find_protocol_name(s->url); + av_assert0(time_tolerance >= 0); + if (!proto) { av_log(s, AV_LOG_INFO, "Protocol name not provided, cannot determine if input is local or " @@ -2123,7 +2131,7 @@ void ff_configure_buffers_for_index(AVFormatContext *s, int64_t time_tolerance) for (; i2 < st2->nb_index_entries; i2++) { AVIndexEntry *e2 = &st2->index_entries[i2]; int64_t e2_pts = av_rescale_q(e2->timestamp, st2->time_base, AV_TIME_BASE_Q); - if (e2_pts - e1_pts < time_tolerance) + if (e2_pts < e1_pts || e2_pts - (uint64_t)e1_pts < time_tolerance) continue; pos_delta = FFMAX(pos_delta, e1->pos - e2->pos); break; @@ -2136,7 +2144,13 @@ void ff_configure_buffers_for_index(AVFormatContext *s, int64_t time_tolerance) /* XXX This could be adjusted depending on protocol*/ if (s->pb->buffer_size < pos_delta && pos_delta < (1<<24)) { av_log(s, AV_LOG_VERBOSE, "Reconfiguring buffers to size %"PRId64"\n", pos_delta); - ffio_set_buf_size(s->pb, pos_delta); + + /* realloc the buffer and the original data will be retained */ + if (ffio_realloc_buf(s->pb, pos_delta)) { + av_log(s, AV_LOG_ERROR, "Realloc buffer fail.\n"); + return; + } + s->pb->short_seek_threshold = FFMAX(s->pb->short_seek_threshold, pos_delta/2); } @@ -2768,7 +2782,7 @@ static void estimate_timings_from_bit_rate(AVFormatContext *ic) st = ic->streams[i]; if ( st->time_base.num <= INT64_MAX / ic->bit_rate && st->duration == AV_NOPTS_VALUE) { - duration = av_rescale(8 * filesize, st->time_base.den, + duration = av_rescale(filesize, 8LL * st->time_base.den, ic->bit_rate * (int64_t) st->time_base.num); st->duration = duration; @@ -2896,9 +2910,9 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset) case AVMEDIA_TYPE_VIDEO: case AVMEDIA_TYPE_AUDIO: if (st->start_time != AV_NOPTS_VALUE || st->first_dts != AV_NOPTS_VALUE) { - av_log(ic, AV_LOG_DEBUG, "stream %d : no PTS found at end of file, duration not set\n", i); + av_log(ic, AV_LOG_WARNING, "stream %d : no PTS found at end of file, duration not set\n", i); } else - av_log(ic, AV_LOG_DEBUG, "stream %d : no TS found at start of file, duration not set\n", i); + av_log(ic, AV_LOG_WARNING, "stream %d : no TS found at start of file, duration not set\n", i); } } } @@ -2918,6 +2932,18 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset) } } +/* 1:1 map to AVDurationEstimationMethod */ +static const char *duration_name[] = { + [AVFMT_DURATION_FROM_PTS] = "pts", + [AVFMT_DURATION_FROM_STREAM] = "stream", + [AVFMT_DURATION_FROM_BITRATE] = "bit rate", +}; + +static const char *duration_estimate_name(enum AVDurationEstimationMethod method) +{ + return duration_name[method]; +} + static void estimate_timings(AVFormatContext *ic, int64_t old_offset) { int64_t file_size; @@ -2940,7 +2966,11 @@ static void estimate_timings(AVFormatContext *ic, int64_t old_offset) /* at least one component has timings - we use them for all * the components */ fill_all_stream_timings(ic); - ic->duration_estimation_method = AVFMT_DURATION_FROM_STREAM; + /* nut demuxer estimate the duration from PTS */ + if(!strcmp(ic->iformat->name, "nut")) + ic->duration_estimation_method = AVFMT_DURATION_FROM_PTS; + else + ic->duration_estimation_method = AVFMT_DURATION_FROM_STREAM; } else { /* less precise: use bitrate info */ estimate_timings_from_bit_rate(ic); @@ -2954,15 +2984,16 @@ static void estimate_timings(AVFormatContext *ic, int64_t old_offset) for (i = 0; i < ic->nb_streams; i++) { st = ic->streams[i]; if (st->time_base.den) - av_log(ic, AV_LOG_TRACE, "stream %d: start_time: %0.3f duration: %0.3f\n", i, - (double) st->start_time * av_q2d(st->time_base), - (double) st->duration * av_q2d(st->time_base)); + av_log(ic, AV_LOG_TRACE, "stream %d: start_time: %s duration: %s\n", i, + av_ts2timestr(st->start_time, &st->time_base), + av_ts2timestr(st->duration, &st->time_base)); } av_log(ic, AV_LOG_TRACE, - "format: start_time: %0.3f duration: %0.3f bitrate=%"PRId64" kb/s\n", - (double) ic->start_time / AV_TIME_BASE, - (double) ic->duration / AV_TIME_BASE, - (int64_t)ic->bit_rate / 1000); + "format: start_time: %s duration: %s (estimate from %s) bitrate=%"PRId64" kb/s\n", + av_ts2timestr(ic->start_time, &AV_TIME_BASE_Q), + av_ts2timestr(ic->duration, &AV_TIME_BASE_Q), + duration_estimate_name(ic->duration_estimation_method), + (int64_t)ic->bit_rate / 1000); } } @@ -3014,8 +3045,8 @@ static int has_codec_parameters(AVStream *st, const char **errmsg_ptr) } /* returns 1 or 0 if or if not decoded data was returned, or a negative error */ -static int try_decode_frame(AVFormatContext *s, AVStream *st, AVPacket *avpkt, - AVDictionary **options) +static int try_decode_frame(AVFormatContext *s, AVStream *st, + const AVPacket *avpkt, AVDictionary **options) { AVCodecContext *avctx = st->internal->avctx; const AVCodec *codec; @@ -3090,6 +3121,8 @@ static int try_decode_frame(AVFormatContext *s, AVStream *st, AVPacket *avpkt, } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { ret = avcodec_decode_subtitle2(avctx, &subtitle, &got_picture, &pkt); + if (got_picture) + avsubtitle_free(&subtitle); if (ret >= 0) pkt.size = 0; } @@ -3507,7 +3540,7 @@ static int extract_extradata_init(AVStream *st) return ret; } -static int extract_extradata(AVStream *st, AVPacket *pkt) +static int extract_extradata(AVStream *st, const AVPacket *pkt) { AVStreamInternal *sti = st->internal; AVPacket *pkt_ref; @@ -3564,13 +3597,28 @@ static int extract_extradata(AVStream *st, AVPacket *pkt) return 0; } +static int add_coded_side_data(AVStream *st, AVCodecContext *avctx) +{ + int i; + + for (i = 0; i < avctx->nb_coded_side_data; i++) { + const AVPacketSideData *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) { int i, count = 0, ret = 0, j; int64_t read_size; AVStream *st; AVCodecContext *avctx; - AVPacket pkt1, *pkt; + AVPacket pkt1; int64_t old_offset = avio_tell(ic->pb); // new streams might appear, no options for those int orig_nb_streams = ic->nb_streams; @@ -3689,6 +3737,7 @@ FF_ENABLE_DEPRECATION_WARNINGS read_size = 0; for (;;) { + const AVPacket *pkt; int analyzed_all_streams; if (ff_check_interrupt(&ic->interrupt_callback)) { ret = AVERROR_EXIT; @@ -3742,18 +3791,18 @@ FF_ENABLE_DEPRECATION_WARNINGS } analyzed_all_streams = 0; if (!missing_streams || !*missing_streams) - if (i == ic->nb_streams) { - analyzed_all_streams = 1; - /* NOTE: If the format has no header, then we need to read some - * packets to get most of the streams, so we cannot stop here. */ - if (!(ic->ctx_flags & AVFMTCTX_NOHEADER)) { - /* If we found the info for all the codecs, we can stop. */ - ret = count; - av_log(ic, AV_LOG_DEBUG, "All info found\n"); - flush_codecs = 0; - break; + if (i == ic->nb_streams) { + analyzed_all_streams = 1; + /* NOTE: If the format has no header, then we need to read some + * packets to get most of the streams, so we cannot stop here. */ + if (!(ic->ctx_flags & AVFMTCTX_NOHEADER)) { + /* If we found the info for all the codecs, we can stop. */ + ret = count; + av_log(ic, AV_LOG_DEBUG, "All info found\n"); + flush_codecs = 0; + break; + } } - } /* We did not get all the codec info, but we read too much data. */ if (read_size >= probesize) { ret = count; @@ -3782,14 +3831,16 @@ FF_ENABLE_DEPRECATION_WARNINGS break; } - pkt = &pkt1; - if (!(ic->flags & AVFMT_FLAG_NOBUFFER)) { ret = ff_packet_list_put(&ic->internal->packet_buffer, &ic->internal->packet_buffer_end, - pkt, 0); + &pkt1, 0); if (ret < 0) - goto find_stream_info_err; + goto unref_then_goto_end; + + pkt = &ic->internal->packet_buffer_end->pkt; + } else { + pkt = &pkt1; } st = ic->streams[pkt->stream_index]; @@ -3800,7 +3851,7 @@ FF_ENABLE_DEPRECATION_WARNINGS if (!st->internal->avctx_inited) { ret = avcodec_parameters_to_context(avctx, st->codecpar); if (ret < 0) - goto find_stream_info_err; + goto unref_then_goto_end; st->internal->avctx_inited = 1; } @@ -3867,7 +3918,7 @@ FF_ENABLE_DEPRECATION_WARNINGS limit, t, pkt->stream_index); if (ic->flags & AVFMT_FLAG_NOBUFFER) - av_packet_unref(pkt); + av_packet_unref(&pkt1); break; } if (pkt->duration) { @@ -3888,7 +3939,7 @@ FF_ENABLE_DEPRECATION_WARNINGS if (!st->internal->avctx->extradata) { ret = extract_extradata(st, pkt); if (ret < 0) - goto find_stream_info_err; + goto unref_then_goto_end; } /* If still no information, we try to open the codec and to @@ -3904,7 +3955,7 @@ FF_ENABLE_DEPRECATION_WARNINGS (options && i < orig_nb_streams) ? &options[i] : NULL); if (ic->flags & AVFMT_FLAG_NOBUFFER) - av_packet_unref(pkt); + av_packet_unref(&pkt1); st->codec_info_nb_frames++; count++; @@ -4017,7 +4068,7 @@ FF_ENABLE_DEPRECATION_WARNINGS if (!st->r_frame_rate.num) { if ( avctx->time_base.den * (int64_t) st->time_base.num - <= avctx->time_base.num * avctx->ticks_per_frame * (int64_t) st->time_base.den) { + <= avctx->time_base.num * avctx->ticks_per_frame * (uint64_t) st->time_base.den) { av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, avctx->time_base.den, (int64_t)avctx->time_base.num * avctx->ticks_per_frame, INT_MAX); } else { @@ -4100,6 +4151,9 @@ FF_ENABLE_DEPRECATION_WARNINGS ret = avcodec_parameters_from_context(st->codecpar, st->internal->avctx); if (ret < 0) goto find_stream_info_err; + ret = add_coded_side_data(st, st->internal->avctx); + if (ret < 0) + goto find_stream_info_err; #if FF_API_LOWRES // The decoder might reduce the video size by the lowres factor. if (st->internal->avctx->lowres && orig_w) { @@ -4164,6 +4218,10 @@ FF_ENABLE_DEPRECATION_WARNINGS av_log(ic, AV_LOG_DEBUG, "After avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d frames:%d\n", avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count, count); return ret; + +unref_then_goto_end: + av_packet_unref(&pkt1); + goto find_stream_info_err; } AVProgram *av_find_program_from_stream(AVFormatContext *ic, AVProgram *last, int s) @@ -4350,10 +4408,7 @@ static void free_stream(AVStream **pst) if (st->internal) { avcodec_free_context(&st->internal->avctx); - for (i = 0; i < st->internal->nb_bsfcs; i++) { - av_bsf_free(&st->internal->bsfcs[i]); - av_freep(&st->internal->bsfcs); - } + av_bsf_free(&st->internal->bsfc); av_freep(&st->internal->priv_pts); av_bsf_free(&st->internal->extract_extradata.bsf); av_packet_free(&st->internal->extract_extradata.pkt); @@ -4397,21 +4452,26 @@ void avformat_free_context(AVFormatContext *s) if (!s) return; + if (s->oformat && s->oformat->deinit && s->internal->initialized) + s->oformat->deinit(s); + av_opt_free(s); if (s->iformat && s->iformat->priv_class && s->priv_data) av_opt_free(s->priv_data); if (s->oformat && s->oformat->priv_class && s->priv_data) av_opt_free(s->priv_data); - for (i = s->nb_streams - 1; i >= 0; i--) - ff_free_stream(s, s->streams[i]); - + for (i = 0; i < s->nb_streams; i++) + free_stream(&s->streams[i]); + s->nb_streams = 0; - for (i = s->nb_programs - 1; i >= 0; i--) { + for (i = 0; i < s->nb_programs; i++) { av_dict_free(&s->programs[i]->metadata); av_freep(&s->programs[i]->stream_index); av_freep(&s->programs[i]); } + s->nb_programs = 0; + av_freep(&s->programs); av_freep(&s->priv_data); while (s->nb_chapters--) { @@ -4527,7 +4587,7 @@ FF_ENABLE_DEPRECATION_WARNINGS st->start_time = AV_NOPTS_VALUE; st->duration = AV_NOPTS_VALUE; st->first_dts = AV_NOPTS_VALUE; - st->probe_packets = MAX_PROBE_PACKETS; + st->probe_packets = s->max_probe_packets; st->pts_wrap_reference = AV_NOPTS_VALUE; st->pts_wrap_behavior = AV_PTS_WRAP_IGNORE; @@ -4734,7 +4794,7 @@ void av_url_split(char *proto, int proto_size, char *hostname, int hostname_size, int *port_ptr, char *path, int path_size, const char *url) { - const char *p, *ls, *ls2, *at, *at2, *col, *brk; + const char *p, *ls, *at, *at2, *col, *brk; if (port_ptr) *port_ptr = -1; @@ -4762,16 +4822,8 @@ void av_url_split(char *proto, int proto_size, } /* separate path from hostname */ - ls = strchr(p, '/'); - ls2 = strchr(p, '?'); - if (!ls) - ls = ls2; - else if (ls && ls2) - ls = FFMIN(ls, ls2); - if (ls) - av_strlcpy(path, ls, path_size); - else - ls = &p[strlen(p)]; // XXX + ls = p + strcspn(p, "/?#"); + av_strlcpy(path, ls, path_size); /* the rest is hostname, use that to parse auth/port */ if (ls != p) { @@ -5407,7 +5459,7 @@ int ff_generate_avci_extradata(AVStream *st) }; const uint8_t *data = NULL; - int size = 0; + int ret, size = 0; if (st->codecpar->width == 1920) { if (st->codecpar->field_order == AV_FIELD_PROGRESSIVE) { @@ -5436,9 +5488,8 @@ int ff_generate_avci_extradata(AVStream *st) if (!size) return 0; - av_freep(&st->codecpar->extradata); - if (ff_alloc_extradata(st->codecpar, size)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, size)) < 0) + return ret; memcpy(st->codecpar->extradata, data, size); return 0; @@ -5456,6 +5507,8 @@ uint8_t *av_stream_get_side_data(const AVStream *st, return st->side_data[i].data; } } + if (size) + *size = 0; return NULL; } @@ -5518,7 +5571,8 @@ int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *a int ret; const AVBitStreamFilter *bsf; AVBSFContext *bsfc; - AVCodecParameters *in_par; + + av_assert0(!st->internal->bsfc); if (!(bsf = av_bsf_get_by_name(name))) { av_log(NULL, AV_LOG_ERROR, "Unknown bitstream filter '%s'\n", name); @@ -5528,15 +5582,8 @@ int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *a if ((ret = av_bsf_alloc(bsf, &bsfc)) < 0) return ret; - if (st->internal->nb_bsfcs) { - in_par = st->internal->bsfcs[st->internal->nb_bsfcs - 1]->par_out; - bsfc->time_base_in = st->internal->bsfcs[st->internal->nb_bsfcs - 1]->time_base_out; - } else { - in_par = st->codecpar; - bsfc->time_base_in = st->time_base; - } - - if ((ret = avcodec_parameters_copy(bsfc->par_in, in_par)) < 0) { + bsfc->time_base_in = st->time_base; + if ((ret = avcodec_parameters_copy(bsfc->par_in, st->codecpar)) < 0) { av_bsf_free(&bsfc); return ret; } @@ -5559,10 +5606,7 @@ int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *a return ret; } - if ((ret = av_dynarray_add_nofree(&st->internal->bsfcs, &st->internal->nb_bsfcs, bsfc))) { - av_bsf_free(&bsfc); - return ret; - } + st->internal->bsfc = bsfc; av_log(NULL, AV_LOG_VERBOSE, "Automatically inserted bitstream filter '%s'; args='%s'\n", diff --git a/libavformat/vc1test.c b/libavformat/vc1test.c index 3c677931fc1..ff57f44b14f 100644 --- a/libavformat/vc1test.c +++ b/libavformat/vc1test.c @@ -51,7 +51,7 @@ static int vc1t_read_header(AVFormatContext *s) { AVIOContext *pb = s->pb; AVStream *st; - int frames; + int frames, ret; uint32_t fps; uint32_t size; @@ -67,8 +67,8 @@ static int vc1t_read_header(AVFormatContext *s) st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; st->codecpar->codec_id = AV_CODEC_ID_WMV3; - if (ff_get_extradata(s, st->codecpar, pb, VC1_EXTRADATA_SIZE) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_get_extradata(s, st->codecpar, pb, VC1_EXTRADATA_SIZE)) < 0) + return ret; avio_skip(pb, size - 4); st->codecpar->height = avio_rl32(pb); diff --git a/libavformat/vc1testenc.c b/libavformat/vc1testenc.c index cf95d1d80d2..1365bdd660e 100644 --- a/libavformat/vc1testenc.c +++ b/libavformat/vc1testenc.c @@ -76,7 +76,6 @@ static int vc1test_write_trailer(AVFormatContext *s) if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) { avio_seek(pb, 0, SEEK_SET); avio_wl24(pb, ctx->frames); - avio_flush(pb); } return 0; } diff --git a/libavformat/version.h b/libavformat/version.h index 22ed534bfb7..13c8a615a87 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -32,7 +32,7 @@ // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) // Also please add any ticket numbers that you believe might be affected here #define LIBAVFORMAT_VERSION_MAJOR 58 -#define LIBAVFORMAT_VERSION_MINOR 29 +#define LIBAVFORMAT_VERSION_MINOR 45 #define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ diff --git a/libavformat/vividas.c b/libavformat/vividas.c index 1ac86a107e2..b0f9f35ac20 100644 --- a/libavformat/vividas.c +++ b/libavformat/vividas.c @@ -52,6 +52,7 @@ typedef struct VIV_AudioSubpacket { typedef struct VividasDemuxContext { int n_sb_blocks; VIV_SB_block *sb_blocks; + int num_audio; uint32_t sb_key; int64_t sb_offset; @@ -164,8 +165,6 @@ static void decode_block(uint8_t *src, uint8_t *dest, unsigned size, } if (s >= 4) { - if (!align) - align = 4; xor_block(src + a2, dest + a2, s & ~3, key, key_ptr); s &= 3; @@ -220,7 +219,7 @@ static uint8_t *read_vblock(AVIOContext *src, uint32_t *size, memcpy(buf, tmp, 4); if (avio_read(src, buf + 4, n) == n) { - decode_block(buf + 4, buf + 4, n, key, k2, align + 4); + decode_block(buf + 4, buf + 4, n, key, k2, align); } else { av_free(buf); buf = NULL; @@ -279,15 +278,13 @@ static uint8_t *read_sb_block(AVIOContext *src, unsigned *size, static int track_header(VividasDemuxContext *viv, AVFormatContext *s, uint8_t *buf, int size) { - int i,j; + int i, j, ret; int64_t off; int val_1; - int num_video, num_audio; - AVIOContext *pb; + int num_video; + AVIOContext pb0, *pb = &pb0; - pb = avio_alloc_context(buf, size, 0, NULL, NULL, NULL, NULL); - if (!pb) - return AVERROR(ENOMEM); + ffio_init_context(pb, buf, size, 0, NULL, NULL, NULL, NULL); ffio_read_varlen(pb); // track_header_len avio_r8(pb); // '1' @@ -297,6 +294,8 @@ static int track_header(VividasDemuxContext *viv, AVFormatContext *s, uint8_t * for (i=0;iid = i; @@ -340,15 +343,17 @@ static int track_header(VividasDemuxContext *viv, AVFormatContext *s, uint8_t * off = avio_tell(pb); off += ffio_read_varlen(pb); // val_10 avio_r8(pb); // '4' - num_audio = avio_r8(pb); + viv->num_audio = avio_r8(pb); avio_seek(pb, off, SEEK_SET); - if (num_audio != 1) - av_log(s, AV_LOG_WARNING, "number of audio tracks %d is not 1\n", num_audio); + if (viv->num_audio != 1) + av_log(s, AV_LOG_WARNING, "number of audio tracks %d is not 1\n", viv->num_audio); - for(i=0;inum_audio;i++) { int q; AVStream *st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); st->id = num_video + i; @@ -380,18 +385,15 @@ static int track_header(VividasDemuxContext *viv, AVFormatContext *s, uint8_t * for (j = 0; j < num_data; j++) { uint64_t len = ffio_read_varlen(pb); if (len > INT_MAX/2 - xd_size) { - av_free(pb); return AVERROR_INVALIDDATA; } data_len[j] = len; xd_size += len; } - st->codecpar->extradata_size = 64 + xd_size + xd_size / 255; - if (ff_alloc_extradata(st->codecpar, st->codecpar->extradata_size)) { - av_free(pb); - return AVERROR(ENOMEM); - } + ret = ff_alloc_extradata(st->codecpar, 64 + xd_size + xd_size / 255); + if (ret < 0) + return ret; p = st->codecpar->extradata; p[0] = 2; @@ -399,7 +401,6 @@ static int track_header(VividasDemuxContext *viv, AVFormatContext *s, uint8_t * for (j = 0; j < num_data - 1; j++) { unsigned delta = av_xiphlacing(&p[offset], data_len[j]); if (delta > data_len[j]) { - av_free(pb); return AVERROR_INVALIDDATA; } offset += delta; @@ -420,52 +421,64 @@ static int track_header(VividasDemuxContext *viv, AVFormatContext *s, uint8_t * } } - av_free(pb); return 0; } -static void track_index(VividasDemuxContext *viv, AVFormatContext *s, uint8_t *buf, unsigned size) +static int track_index(VividasDemuxContext *viv, AVFormatContext *s, uint8_t *buf, unsigned size) { int64_t off; int64_t poff; int maxnp=0; - AVIOContext *pb; + AVIOContext pb0, *pb = &pb0; int i; + int64_t filesize = avio_size(s->pb); - pb = avio_alloc_context(buf, size, 0, NULL, NULL, NULL, NULL); - if (!pb) - return; + ffio_init_context(pb, buf, size, 0, NULL, NULL, NULL, NULL); ffio_read_varlen(pb); // track_index_len avio_r8(pb); // 'c' viv->n_sb_blocks = ffio_read_varlen(pb); + if (viv->n_sb_blocks < 0 || viv->n_sb_blocks > size / 2) + goto error; viv->sb_blocks = av_calloc(viv->n_sb_blocks, sizeof(VIV_SB_block)); if (!viv->sb_blocks) { viv->n_sb_blocks = 0; - av_free(pb); - return; + return AVERROR(ENOMEM); } off = 0; poff = 0; for (i = 0; i < viv->n_sb_blocks; i++) { + uint64_t size_tmp = ffio_read_varlen(pb); + uint64_t n_packets_tmp = ffio_read_varlen(pb); + + if (size_tmp > INT_MAX || n_packets_tmp > INT_MAX) + goto error; + viv->sb_blocks[i].byte_offset = off; viv->sb_blocks[i].packet_offset = poff; - viv->sb_blocks[i].size = ffio_read_varlen(pb); - viv->sb_blocks[i].n_packets = ffio_read_varlen(pb); + viv->sb_blocks[i].size = size_tmp; + viv->sb_blocks[i].n_packets = n_packets_tmp; off += viv->sb_blocks[i].size; poff += viv->sb_blocks[i].n_packets; - if (maxnp < viv->sb_blocks[i].n_packets) maxnp = viv->sb_blocks[i].n_packets; } + if (filesize > 0 && poff > filesize) + goto error; + viv->sb_entries = av_calloc(maxnp, sizeof(VIV_SB_entry)); - av_free(pb); + + return 0; +error: + viv->n_sb_blocks = 0; + av_freep(&viv->sb_blocks); + return AVERROR_INVALIDDATA; } static void load_sb_block(AVFormatContext *s, VividasDemuxContext *viv, unsigned expected_size) @@ -591,8 +604,10 @@ static int viv_read_header(AVFormatContext *s) buf = read_vblock(pb, &v, key, &k2, v); if (!buf) return AVERROR(EIO); - track_index(viv, s, buf, v); + ret = track_index(viv, s, buf, v); av_free(buf); + if (ret < 0) + return ret; viv->sb_offset = avio_tell(pb); if (viv->n_sb_blocks > 0) { @@ -631,7 +646,7 @@ static int viv_read_packet(AVFormatContext *s, pkt->stream_index = 1; astream = s->streams[pkt->stream_index]; - pkt->pts = av_rescale(viv->audio_sample, astream->time_base.den, astream->time_base.num) / astream->codecpar->sample_rate; + pkt->pts = av_rescale_q(viv->audio_sample, av_make_q(1, astream->codecpar->sample_rate), astream->time_base); viv->audio_sample += viv->audio_subpackets[viv->current_audio_subpacket].pcm_bytes / 2 / astream->codecpar->channels; pkt->flags |= AV_PKT_FLAG_KEY; viv->current_audio_subpacket++; @@ -656,6 +671,9 @@ static int viv_read_packet(AVFormatContext *s, if (viv->sb_entries[viv->current_sb_entry].flag == 0) { uint64_t v_size = ffio_read_varlen(pb); + if (!viv->num_audio) + return AVERROR_INVALIDDATA; + ffio_read_varlen(pb); if (v_size > INT_MAX) return AVERROR_INVALIDDATA; diff --git a/libavformat/vivo.c b/libavformat/vivo.c index 9a07c43849f..fb58aa61785 100644 --- a/libavformat/vivo.c +++ b/libavformat/vivo.c @@ -36,6 +36,7 @@ typedef struct VivoContext { int type; int sequence; int length; + int duration; uint8_t text[1024 + 1]; } VivoContext; @@ -59,9 +60,10 @@ static int vivo_probe(const AVProbeData *p) if (c & 0x80 || length > 1024 || length < 21) return 0; - if (memcmp(buf, "\r\nVersion:Vivo/", 15)) + buf += 2; + if (memcmp(buf, "Version:Vivo/", 13)) return 0; - buf += 15; + buf += 13; if (*buf < '0' || *buf > '2') return 0; @@ -231,6 +233,12 @@ static int vivo_read_header(AVFormatContext *s) ast->codecpar->bits_per_coded_sample = 8; ast->codecpar->block_align = 24; ast->codecpar->bit_rate = 6400; + } else { + ast->codecpar->codec_id = AV_CODEC_ID_SIREN; + ast->codecpar->bits_per_coded_sample = 16; + ast->codecpar->block_align = 40; + ast->codecpar->bit_rate = 6400; + vivo->duration = 320; } ast->start_time = 0; @@ -246,7 +254,7 @@ static int vivo_read_packet(AVFormatContext *s, AVPacket *pkt) VivoContext *vivo = s->priv_data; AVIOContext *pb = s->pb; unsigned old_sequence = vivo->sequence, old_type = vivo->type; - int stream_index, ret = 0; + int stream_index, duration, ret = 0; restart: @@ -262,10 +270,12 @@ static int vivo_read_packet(AVFormatContext *s, AVPacket *pkt) case 1: case 2: // video stream_index = 0; + duration = 1; break; case 3: case 4: // audio stream_index = 1; + duration = vivo->duration; break; default: av_log(s, AV_LOG_ERROR, "unknown packet type %d\n", vivo->type); @@ -273,32 +283,29 @@ static int vivo_read_packet(AVFormatContext *s, AVPacket *pkt) } if ((ret = av_get_packet(pb, pkt, vivo->length)) < 0) - goto fail; + return ret; // get next packet header if ((ret = vivo_get_packet_header(s)) < 0) - goto fail; + return ret; while (vivo->sequence == old_sequence && (((vivo->type - 1) >> 1) == ((old_type - 1) >> 1))) { if (avio_feof(pb)) { - ret = AVERROR_EOF; - break; + return AVERROR_EOF; } if ((ret = av_append_packet(pb, pkt, vivo->length)) < 0) - break; + return ret; // get next packet header if ((ret = vivo_get_packet_header(s)) < 0) - break; + return ret; } pkt->stream_index = stream_index; + pkt->duration = duration; -fail: - if (ret < 0) - av_packet_unref(pkt); return ret; } diff --git a/libavformat/vorbiscomment.c b/libavformat/vorbiscomment.c index fb5c655a23f..a929634cc04 100644 --- a/libavformat/vorbiscomment.c +++ b/libavformat/vorbiscomment.c @@ -19,10 +19,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "avio.h" #include "avformat.h" #include "metadata.h" #include "vorbiscomment.h" -#include "libavcodec/bytestream.h" #include "libavutil/dict.h" /** @@ -38,7 +38,7 @@ const AVMetadataConv ff_vorbiscomment_metadata_conv[] = { { 0 } }; -int64_t ff_vorbiscomment_length(AVDictionary *m, const char *vendor_string, +int64_t ff_vorbiscomment_length(const AVDictionary *m, const char *vendor_string, AVChapter **chapters, unsigned int nb_chapters) { int64_t len = 8; @@ -62,31 +62,31 @@ int64_t ff_vorbiscomment_length(AVDictionary *m, const char *vendor_string, return len; } -int ff_vorbiscomment_write(uint8_t **p, AVDictionary **m, +int ff_vorbiscomment_write(AVIOContext *pb, const AVDictionary *m, const char *vendor_string, AVChapter **chapters, unsigned int nb_chapters) { int cm_count = 0; - bytestream_put_le32(p, strlen(vendor_string)); - bytestream_put_buffer(p, vendor_string, strlen(vendor_string)); + avio_wl32(pb, strlen(vendor_string)); + avio_write(pb, vendor_string, strlen(vendor_string)); if (chapters && nb_chapters) { for (int i = 0; i < nb_chapters; i++) { cm_count += av_dict_count(chapters[i]->metadata) + 1; } } - if (*m) { - int count = av_dict_count(*m) + cm_count; + if (m) { + int count = av_dict_count(m) + cm_count; AVDictionaryEntry *tag = NULL; - bytestream_put_le32(p, count); - while ((tag = av_dict_get(*m, "", tag, AV_DICT_IGNORE_SUFFIX))) { + avio_wl32(pb, count); + while ((tag = av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX))) { int64_t len1 = strlen(tag->key); int64_t len2 = strlen(tag->value); if (len1+1+len2 > UINT32_MAX) return AVERROR(EINVAL); - bytestream_put_le32(p, len1+1+len2); - bytestream_put_buffer(p, tag->key, len1); - bytestream_put_byte(p, '='); - bytestream_put_buffer(p, tag->value, len2); + avio_wl32(pb, len1 + 1 + len2); + avio_write(pb, tag->key, len1); + avio_w8(pb, '='); + avio_write(pb, tag->value, len2); } for (int i = 0; i < nb_chapters; i++) { AVChapter *chp = chapters[i]; @@ -101,11 +101,11 @@ int ff_vorbiscomment_write(uint8_t **p, AVDictionary **m, s = s % 60; snprintf(chapter_number, sizeof(chapter_number), "%03d", i); snprintf(chapter_time, sizeof(chapter_time), "%02d:%02d:%02d.%03d", h, m, s, ms); - bytestream_put_le32(p, 10+1+12); - bytestream_put_buffer(p, "CHAPTER", 7); - bytestream_put_buffer(p, chapter_number, 3); - bytestream_put_byte(p, '='); - bytestream_put_buffer(p, chapter_time, 12); + avio_wl32(pb, 10 + 1 + 12); + avio_write(pb, "CHAPTER", 7); + avio_write(pb, chapter_number, 3); + avio_w8(pb, '='); + avio_write(pb, chapter_time, 12); tag = NULL; while ((tag = av_dict_get(chapters[i]->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { @@ -113,18 +113,18 @@ int ff_vorbiscomment_write(uint8_t **p, AVDictionary **m, int64_t len2 = strlen(tag->value); if (len1+1+len2+10 > UINT32_MAX) return AVERROR(EINVAL); - bytestream_put_le32(p, 10+len1+1+len2); - bytestream_put_buffer(p, "CHAPTER", 7); - bytestream_put_buffer(p, chapter_number, 3); + avio_wl32(pb, 10 + len1 + 1 + len2); + avio_write(pb, "CHAPTER", 7); + avio_write(pb, chapter_number, 3); if (!strcmp(tag->key, "title")) - bytestream_put_buffer(p, "NAME", 4); + avio_write(pb, "NAME", 4); else - bytestream_put_buffer(p, tag->key, len1); - bytestream_put_byte(p, '='); - bytestream_put_buffer(p, tag->value, len2); + avio_write(pb, tag->key, len1); + avio_w8(pb, '='); + avio_write(pb, tag->value, len2); } } } else - bytestream_put_le32(p, 0); + avio_wl32(pb, 0); return 0; } diff --git a/libavformat/vorbiscomment.h b/libavformat/vorbiscomment.h index 4ff3dd6c278..7cacd0b2a09 100644 --- a/libavformat/vorbiscomment.h +++ b/libavformat/vorbiscomment.h @@ -34,22 +34,21 @@ * For no string, set to an empty string. * @return The length in bytes. */ -int64_t ff_vorbiscomment_length(AVDictionary *m, const char *vendor_string, +int64_t ff_vorbiscomment_length(const AVDictionary *m, const char *vendor_string, AVChapter **chapters, unsigned int nb_chapters); /** - * Write a VorbisComment into a buffer. The buffer, p, must have enough - * data to hold the whole VorbisComment. The minimum size required can be - * obtained by passing the same AVDictionary and vendor_string to + * Write a VorbisComment into an AVIOContext. The output size can be obtained + * in advance by passing the same chapters, AVDictionary and vendor_string to * ff_vorbiscomment_length() * - * @param p The buffer in which to write. + * @param pb The AVIOContext to write the output. * @param m The metadata struct to write. * @param vendor_string The vendor string to write. * @param chapters The chapters to write. * @param nb_chapters The number of chapters to write. */ -int ff_vorbiscomment_write(uint8_t **p, AVDictionary **m, +int ff_vorbiscomment_write(AVIOContext *pb, const AVDictionary *m, const char *vendor_string, AVChapter **chapters, unsigned int nb_chapters); diff --git a/libavformat/vpk.c b/libavformat/vpk.c index 255d6030b0d..b1df4e0dfd9 100644 --- a/libavformat/vpk.c +++ b/libavformat/vpk.c @@ -24,6 +24,7 @@ #include "internal.h" typedef struct VPKDemuxContext { + unsigned data_start; unsigned block_count; unsigned current_block; unsigned last_block_size; @@ -70,6 +71,7 @@ static int vpk_read_header(AVFormatContext *s) if (offset < avio_tell(s->pb)) return AVERROR_INVALIDDATA; avio_skip(s->pb, offset - avio_tell(s->pb)); + vpk->data_start = offset; avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); return 0; @@ -85,6 +87,7 @@ static int vpk_read_packet(AVFormatContext *s, AVPacket *pkt) if (vpk->current_block == vpk->block_count) { unsigned size = vpk->last_block_size / par->channels; unsigned skip = (par->block_align - vpk->last_block_size) / par->channels; + uint64_t pos = avio_tell(s->pb); ret = av_new_packet(pkt, vpk->last_block_size); if (ret < 0) @@ -93,11 +96,10 @@ static int vpk_read_packet(AVFormatContext *s, AVPacket *pkt) ret = avio_read(s->pb, pkt->data + i * size, size); avio_skip(s->pb, skip); if (ret != size) { - av_packet_unref(pkt); - ret = AVERROR(EIO); - break; + return AVERROR(EIO); } } + pkt->pos = pos; pkt->stream_index = 0; } else if (vpk->current_block < vpk->block_count) { ret = av_get_packet(s->pb, pkt, par->block_align); @@ -109,6 +111,26 @@ static int vpk_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } +static int vpk_read_seek(AVFormatContext *s, int stream_index, + int64_t timestamp, int flags) +{ + AVStream *st = s->streams[stream_index]; + AVCodecParameters *par = st->codecpar; + VPKDemuxContext *vpk = s->priv_data; + int samples_per_block; + int64_t ret = 0; + + samples_per_block = av_get_audio_frame_duration2(par, par->block_align); + timestamp /= samples_per_block; + ret = avio_seek(s->pb, vpk->data_start + timestamp * par->block_align, SEEK_SET); + if (ret < 0) + return ret; + + vpk->current_block = timestamp; + ff_update_cur_dts(s, st, timestamp * samples_per_block); + return 0; +} + AVInputFormat ff_vpk_demuxer = { .name = "vpk", .long_name = NULL_IF_CONFIG_SMALL("Sony PS2 VPK"), @@ -116,5 +138,6 @@ AVInputFormat ff_vpk_demuxer = { .read_probe = vpk_probe, .read_header = vpk_read_header, .read_packet = vpk_read_packet, + .read_seek = vpk_read_seek, .extensions = "vpk", }; diff --git a/libavformat/vplayerdec.c b/libavformat/vplayerdec.c index e3e7b4efb82..ca23ec76ac5 100644 --- a/libavformat/vplayerdec.c +++ b/libavformat/vplayerdec.c @@ -83,8 +83,10 @@ static int vplayer_read_header(AVFormatContext *s) AVPacket *sub; sub = ff_subtitles_queue_insert(&vplayer->q, p, strlen(p), 0); - if (!sub) + if (!sub) { + ff_subtitles_queue_clean(&vplayer->q); return AVERROR(ENOMEM); + } sub->pos = pos; sub->pts = pts_start; sub->duration = -1; diff --git a/libavformat/vqf.c b/libavformat/vqf.c index 755849bac74..617a9706f43 100644 --- a/libavformat/vqf.c +++ b/libavformat/vqf.c @@ -97,7 +97,7 @@ static int vqf_read_header(AVFormatContext *s) int rate_flag = -1; int header_size; int read_bitrate = 0; - int size; + int size, ret; uint8_t comm_chunk[12]; if (!st) @@ -222,8 +222,8 @@ static int vqf_read_header(AVFormatContext *s) avpriv_set_pts_info(st, 64, size, st->codecpar->sample_rate); /* put first 12 bytes of COMM chunk in extradata */ - if (ff_alloc_extradata(st->codecpar, 12)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, 12)) < 0) + return ret; memcpy(st->codecpar->extradata, comm_chunk, 12); ff_metadata_conv_ctx(s, NULL, vqf_metadata_conv); @@ -237,8 +237,8 @@ static int vqf_read_packet(AVFormatContext *s, AVPacket *pkt) int ret; int size = (c->frame_bit_len - c->remaining_bits + 7)>>3; - if (av_new_packet(pkt, size+2) < 0) - return AVERROR(EIO); + if ((ret = av_new_packet(pkt, size + 2)) < 0) + return ret; pkt->pos = avio_tell(s->pb); pkt->stream_index = 0; @@ -249,7 +249,6 @@ static int vqf_read_packet(AVFormatContext *s, AVPacket *pkt) ret = avio_read(s->pb, pkt->data+2, size); if (ret != size) { - av_packet_unref(pkt); return AVERROR(EIO); } diff --git a/libavformat/wavdec.c b/libavformat/wavdec.c index 684efd97f9a..c35966f9705 100644 --- a/libavformat/wavdec.c +++ b/libavformat/wavdec.c @@ -181,7 +181,7 @@ static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream **st) static int wav_parse_xma2_tag(AVFormatContext *s, int64_t size, AVStream **st) { AVIOContext *pb = s->pb; - int version, num_streams, i, channels = 0; + int version, num_streams, i, channels = 0, ret; if (size < 36) return AVERROR_INVALIDDATA; @@ -220,9 +220,8 @@ static int wav_parse_xma2_tag(AVFormatContext *s, int64_t size, AVStream **st) avpriv_set_pts_info(*st, 64, 1, (*st)->codecpar->sample_rate); avio_seek(pb, -size, SEEK_CUR); - av_freep(&(*st)->codecpar->extradata); - if (ff_get_extradata(s, (*st)->codecpar, pb, size) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_get_extradata(s, (*st)->codecpar, pb, size)) < 0) + return ret; return 0; } @@ -473,9 +472,9 @@ static int wav_read_header(AVFormatContext *s) vst->codecpar->codec_id = AV_CODEC_ID_SMVJPEG; vst->codecpar->width = avio_rl24(pb); vst->codecpar->height = avio_rl24(pb); - if (ff_alloc_extradata(vst->codecpar, 4)) { + if ((ret = ff_alloc_extradata(vst->codecpar, 4)) < 0) { av_log(s, AV_LOG_ERROR, "Could not allocate extradata.\n"); - return AVERROR(ENOMEM); + return ret; } size = avio_rl24(pb); wav->smv_data_ofs = avio_tell(pb) + (size - 5) * 3; @@ -508,9 +507,9 @@ static int wav_read_header(AVFormatContext *s) ID3v2ExtraMeta *id3v2_extra_meta = NULL; ff_id3v2_read_dict(pb, &s->internal->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); if (id3v2_extra_meta) { - ff_id3v2_parse_apic(s, &id3v2_extra_meta); - ff_id3v2_parse_chapters(s, &id3v2_extra_meta); - ff_id3v2_parse_priv(s, &id3v2_extra_meta); + ff_id3v2_parse_apic(s, id3v2_extra_meta); + ff_id3v2_parse_chapters(s, id3v2_extra_meta); + ff_id3v2_parse_priv(s, id3v2_extra_meta); } ff_id3v2_free_extra_meta(&id3v2_extra_meta); } @@ -590,6 +589,8 @@ static int wav_read_header(AVFormatContext *s) } else if (st->codecpar->codec_id == AV_CODEC_ID_XMA1 || st->codecpar->codec_id == AV_CODEC_ID_XMA2) { st->codecpar->block_align = 2048; + } else if (st->codecpar->codec_id == AV_CODEC_ID_ADPCM_MS && st->codecpar->channels > 2) { + st->codecpar->block_align *= st->codecpar->channels; } ff_metadata_conv_ctx(s, NULL, wav_metadata_conv); diff --git a/libavformat/wavenc.c b/libavformat/wavenc.c index 159119d6936..1027f107eed 100644 --- a/libavformat/wavenc.c +++ b/libavformat/wavenc.c @@ -141,7 +141,7 @@ static void bwf_write_bext_chunk(AVFormatContext *s) ff_end_tag(s->pb, bext); } -static av_cold void peak_free_buffers(AVFormatContext *s) +static av_cold void wav_deinit(AVFormatContext *s) { WAVMuxContext *wav = s->priv_data; @@ -185,7 +185,6 @@ static av_cold int peak_init_writer(AVFormatContext *s) nomem: av_log(s, AV_LOG_ERROR, "Out of memory\n"); - peak_free_buffers(s); return AVERROR(ENOMEM); } @@ -362,8 +361,6 @@ static int wav_write_header(AVFormatContext *s) wav->data = ff_start_tag(pb, "data"); } - avio_flush(pb); - return 0; } @@ -414,17 +411,13 @@ static int wav_write_trailer(AVFormatContext *s) int rf64 = 0; int ret = 0; - avio_flush(pb); - if (s->pb->seekable & AVIO_SEEKABLE_NORMAL) { if (wav->write_peak != PEAK_ONLY && avio_tell(pb) - wav->data < UINT32_MAX) { ff_end_tag(pb, wav->data); - avio_flush(pb); } if (wav->write_peak && wav->peak_output) { ret = peak_write_chunk(s); - avio_flush(pb); } /* update file size */ @@ -436,17 +429,14 @@ static int wav_write_trailer(AVFormatContext *s) avio_seek(pb, 4, SEEK_SET); avio_wl32(pb, (uint32_t)(file_size - 8)); avio_seek(pb, file_size, SEEK_SET); - - avio_flush(pb); } else { av_log(s, AV_LOG_ERROR, "Filesize %"PRId64" invalid for wav, output file will be broken\n", file_size); } - - number_of_samples = av_rescale(wav->maxpts - wav->minpts + wav->last_duration, - s->streams[0]->codecpar->sample_rate * (int64_t)s->streams[0]->time_base.num, - s->streams[0]->time_base.den); + number_of_samples = av_rescale_q(wav->maxpts - wav->minpts + wav->last_duration, + s->streams[0]->time_base, + av_make_q(1, s->streams[0]->codecpar->sample_rate)); if(s->streams[0]->codecpar->codec_tag != 0x01) { /* Update num_samps in fact chunk */ @@ -457,7 +447,6 @@ static int wav_write_trailer(AVFormatContext *s) } else { avio_wl32(pb, number_of_samples); avio_seek(pb, file_size, SEEK_SET); - avio_flush(pb); } } @@ -481,13 +470,9 @@ static int wav_write_trailer(AVFormatContext *s) avio_wl32(pb, -1); avio_seek(pb, file_size, SEEK_SET); - avio_flush(pb); } } - if (wav->write_peak) - peak_free_buffers(s); - return ret; } @@ -527,6 +512,7 @@ AVOutputFormat ff_wav_muxer = { .write_header = wav_write_header, .write_packet = wav_write_packet, .write_trailer = wav_write_trailer, + .deinit = wav_deinit, .flags = AVFMT_TS_NONSTRICT, .codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, 0 }, .priv_class = &wav_muxer_class, @@ -610,7 +596,6 @@ static int w64_write_trailer(AVFormatContext *s) } avio_seek(pb, file_size, SEEK_SET); - avio_flush(pb); } return 0; diff --git a/libavformat/wc3movie.c b/libavformat/wc3movie.c index 27f5eb19626..65770077773 100644 --- a/libavformat/wc3movie.c +++ b/libavformat/wc3movie.c @@ -130,8 +130,10 @@ static int wc3_read_header(AVFormatContext *s) buffer = av_malloc(size+1); if (!buffer) return AVERROR(ENOMEM); - if ((ret = avio_read(pb, buffer, size)) != size) + if ((ret = avio_read(pb, buffer, size)) != size) { + av_freep(&buffer); return AVERROR(EIO); + } buffer[size] = 0; av_dict_set(&s->metadata, "title", buffer, AV_DICT_DONT_STRDUP_VAL); diff --git a/libavformat/webm_chunk.c b/libavformat/webm_chunk.c index 4e2ce21a797..f1bee5fa9ff 100644 --- a/libavformat/webm_chunk.c +++ b/libavformat/webm_chunk.c @@ -24,90 +24,130 @@ * chunk, followed by data chunks where each Cluster is written out as a Chunk. */ -#include -#include - #include "avformat.h" #include "avio.h" #include "avio_internal.h" #include "internal.h" -#include "libavutil/avassert.h" #include "libavutil/log.h" #include "libavutil/opt.h" -#include "libavutil/avstring.h" -#include "libavutil/parseutils.h" #include "libavutil/mathematics.h" -#include "libavutil/time.h" -#include "libavutil/time_internal.h" -#include "libavutil/timestamp.h" #define MAX_FILENAME_SIZE 1024 typedef struct WebMChunkContext { const AVClass *class; - int chunk_start_index; char *header_filename; int chunk_duration; int chunk_index; char *http_method; uint64_t duration_written; int64_t prev_pts; - ff_const59 AVOutputFormat *oformat; AVFormatContext *avf; + int header_written; } WebMChunkContext; -static int chunk_mux_init(AVFormatContext *s) +static int webm_chunk_init(AVFormatContext *s) { WebMChunkContext *wc = s->priv_data; + ff_const59 AVOutputFormat *oformat; AVFormatContext *oc; + AVStream *st, *ost = s->streams[0]; + AVDictionary *dict = NULL; int ret; - ret = avformat_alloc_output_context2(&wc->avf, wc->oformat, NULL, NULL); + // DASH Streams can only have one track per file. + if (s->nb_streams != 1) + return AVERROR(EINVAL); + + if (!wc->header_filename) { + av_log(s, AV_LOG_ERROR, "No header filename provided\n"); + return AVERROR(EINVAL); + } + + wc->prev_pts = AV_NOPTS_VALUE; + + oformat = av_guess_format("webm", s->url, "video/webm"); + if (!oformat) + return AVERROR_MUXER_NOT_FOUND; + + ret = avformat_alloc_output_context2(&wc->avf, oformat, NULL, NULL); if (ret < 0) return ret; oc = wc->avf; - oc->interrupt_callback = s->interrupt_callback; - oc->max_delay = s->max_delay; - av_dict_copy(&oc->metadata, s->metadata, 0); + ff_format_set_url(oc, wc->header_filename); + wc->header_filename = NULL; + + oc->interrupt_callback = s->interrupt_callback; + oc->max_delay = s->max_delay; + oc->flags = s->flags & ~AVFMT_FLAG_FLUSH_PACKETS; + oc->strict_std_compliance = s->strict_std_compliance; + oc->avoid_negative_ts = s->avoid_negative_ts; - *(const AVClass**)oc->priv_data = oc->oformat->priv_class; - av_opt_set_defaults(oc->priv_data); - av_opt_set_int(oc->priv_data, "dash", 1, 0); - av_opt_set_int(oc->priv_data, "cluster_time_limit", wc->chunk_duration, 0); - av_opt_set_int(oc->priv_data, "live", 1, 0); + oc->flush_packets = 0; + + if ((ret = av_dict_copy(&oc->metadata, s->metadata, 0)) < 0) + return ret; + + if (!(st = avformat_new_stream(oc, NULL))) + return AVERROR(ENOMEM); + + if ((ret = avcodec_parameters_copy(st->codecpar, ost->codecpar)) < 0 || + (ret = av_dict_copy(&st->metadata, ost->metadata, 0)) < 0) + return ret; - oc->streams = s->streams; - oc->nb_streams = s->nb_streams; + st->sample_aspect_ratio = ost->sample_aspect_ratio; + st->disposition = ost->disposition; + avpriv_set_pts_info(st, ost->pts_wrap_bits, ost->time_base.num, + ost->time_base.den); + + if (wc->http_method) + if ((ret = av_dict_set(&dict, "method", wc->http_method, 0)) < 0) + return ret; + ret = s->io_open(s, &oc->pb, oc->url, AVIO_FLAG_WRITE, &dict); + av_dict_free(&dict); + if (ret < 0) + return ret; + oc->pb->seekable = 0; + + if ((ret = av_dict_set_int(&dict, "dash", 1, 0)) < 0 || + (ret = av_dict_set_int(&dict, "cluster_time_limit", + wc->chunk_duration, 0)) < 0 || + (ret = av_dict_set_int(&dict, "live", 1, 0)) < 0) + goto fail; + + ret = avformat_init_output(oc, &dict); +fail: + av_dict_free(&dict); + if (ret < 0) + return ret; + + // Copy the timing info back to the original stream + // so that the timestamps of the packets are directly usable + avpriv_set_pts_info(ost, st->pts_wrap_bits, st->time_base.num, + st->time_base.den); + + // This ensures that the timestamps will already be properly shifted + // when the packets arrive here, so we don't need to shift again. + s->avoid_negative_ts = oc->avoid_negative_ts; + s->internal->avoid_negative_ts_use_pts = + oc->internal->avoid_negative_ts_use_pts; + oc->avoid_negative_ts = 0; return 0; } -static int get_chunk_filename(AVFormatContext *s, int is_header, char filename[MAX_FILENAME_SIZE]) +static int get_chunk_filename(AVFormatContext *s, char filename[MAX_FILENAME_SIZE]) { WebMChunkContext *wc = s->priv_data; - AVFormatContext *oc = wc->avf; if (!filename) { return AVERROR(EINVAL); } - if (is_header) { - int len; - if (!wc->header_filename) { - av_log(oc, AV_LOG_ERROR, "No header filename provided\n"); - return AVERROR(EINVAL); - } - len = av_strlcpy(filename, wc->header_filename, MAX_FILENAME_SIZE); - if (len >= MAX_FILENAME_SIZE) { - av_log(oc, AV_LOG_ERROR, "Header filename too long\n"); - return AVERROR(EINVAL); - } - } else { - if (av_get_frame_filename(filename, MAX_FILENAME_SIZE, - s->url, wc->chunk_index - 1) < 0) { - av_log(oc, AV_LOG_ERROR, "Invalid chunk filename template '%s'\n", s->url); - return AVERROR(EINVAL); - } + if (av_get_frame_filename(filename, MAX_FILENAME_SIZE, + s->url, wc->chunk_index - 1) < 0) { + av_log(s, AV_LOG_ERROR, "Invalid chunk filename template '%s'\n", s->url); + return AVERROR(EINVAL); } return 0; } @@ -115,49 +155,14 @@ static int get_chunk_filename(AVFormatContext *s, int is_header, char filename[M static int webm_chunk_write_header(AVFormatContext *s) { WebMChunkContext *wc = s->priv_data; - AVFormatContext *oc = NULL; + AVFormatContext *oc = wc->avf; int ret; - int i; - AVDictionary *options = NULL; - char oc_filename[MAX_FILENAME_SIZE]; - char *oc_url; - - // DASH Streams can only have either one track per file. - if (s->nb_streams != 1) { return AVERROR_INVALIDDATA; } - - wc->chunk_index = wc->chunk_start_index; - wc->oformat = av_guess_format("webm", s->url, "video/webm"); - if (!wc->oformat) - return AVERROR_MUXER_NOT_FOUND; - wc->prev_pts = AV_NOPTS_VALUE; - ret = chunk_mux_init(s); - if (ret < 0) - return ret; - oc = wc->avf; - ret = get_chunk_filename(s, 1, oc_filename); - if (ret < 0) - return ret; - oc_url = av_strdup(oc_filename); - if (!oc_url) - return AVERROR(ENOMEM); - ff_format_set_url(oc, oc_url); - if (wc->http_method) - av_dict_set(&options, "method", wc->http_method, 0); - ret = s->io_open(s, &oc->pb, oc->url, AVIO_FLAG_WRITE, &options); - av_dict_free(&options); - if (ret < 0) - return ret; - - oc->pb->seekable = 0; - ret = oc->oformat->write_header(oc); + ret = avformat_write_header(oc, NULL); + ff_format_io_close(s, &oc->pb); + wc->header_written = 1; if (ret < 0) return ret; - ff_format_io_close(s, &oc->pb); - for (i = 0; i < s->nb_streams; i++) { - // ms precision is the de-facto standard timescale for mkv files. - avpriv_set_pts_info(s->streams[i], 64, 1, 1000); - } return 0; } @@ -190,21 +195,22 @@ static int chunk_end(AVFormatContext *s, int flush) if (flush) // Flush the cluster in WebM muxer. - oc->oformat->write_packet(oc, NULL); + av_write_frame(oc, NULL); buffer_size = avio_close_dyn_buf(oc->pb, &buffer); oc->pb = NULL; - ret = get_chunk_filename(s, 0, filename); + ret = get_chunk_filename(s, filename); if (ret < 0) goto fail; if (wc->http_method) - av_dict_set(&options, "method", wc->http_method, 0); + if ((ret = av_dict_set(&options, "method", wc->http_method, 0)) < 0) + goto fail; ret = s->io_open(s, &pb, filename, AVIO_FLAG_WRITE, &options); + av_dict_free(&options); if (ret < 0) goto fail; avio_write(pb, buffer, buffer_size); ff_format_io_close(s, &pb); fail: - av_dict_free(&options); av_free(buffer); return (ret < 0) ? ret : 0; } @@ -237,9 +243,8 @@ static int webm_chunk_write_packet(AVFormatContext *s, AVPacket *pkt) } } - ret = oc->oformat->write_packet(oc, pkt); - - return ret; + // We only have one stream, so use the non-interleaving av_write_frame. + return av_write_frame(oc, pkt); } static int webm_chunk_write_trailer(AVFormatContext *s) @@ -251,27 +256,38 @@ static int webm_chunk_write_trailer(AVFormatContext *s) if (!oc->pb) { ret = chunk_start(s); if (ret < 0) - goto fail; + return ret; } - oc->oformat->write_trailer(oc); - ret = chunk_end(s, 0); -fail: - oc->streams = NULL; - oc->nb_streams = 0; - avformat_free_context(oc); - return ret; + ret = av_write_trailer(oc); + if (ret < 0) + return ret; + return chunk_end(s, 0); +} + +static void webm_chunk_deinit(AVFormatContext *s) +{ + WebMChunkContext *wc = s->priv_data; + + if (!wc->avf) + return; + + if (wc->header_written) + ffio_free_dyn_buf(&wc->avf->pb); + else + ff_format_io_close(s, &wc->avf->pb); + avformat_free_context(wc->avf); + wc->avf = NULL; } #define OFFSET(x) offsetof(WebMChunkContext, x) static const AVOption options[] = { - { "chunk_start_index", "start index of the chunk", OFFSET(chunk_start_index), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, - { "header", "filename of the header where the initialization data will be written", OFFSET(header_filename), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM }, + { "chunk_start_index", "start index of the chunk", OFFSET(chunk_index), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { "header", "filename of the header where the initialization data will be written", OFFSET(header_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM }, { "audio_chunk_duration", "duration of each chunk in milliseconds", OFFSET(chunk_duration), AV_OPT_TYPE_INT, {.i64 = 5000}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, { "method", "set the HTTP method", OFFSET(http_method), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM }, { NULL }, }; -#if CONFIG_WEBM_CHUNK_MUXER static const AVClass webm_chunk_class = { .class_name = "WebM Chunk Muxer", .item_name = av_default_item_name, @@ -287,9 +303,10 @@ AVOutputFormat ff_webm_chunk_muxer = { .flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_NEEDNUMBER | AVFMT_TS_NONSTRICT, .priv_data_size = sizeof(WebMChunkContext), + .init = webm_chunk_init, .write_header = webm_chunk_write_header, .write_packet = webm_chunk_write_packet, .write_trailer = webm_chunk_write_trailer, + .deinit = webm_chunk_deinit, .priv_class = &webm_chunk_class, }; -#endif diff --git a/libavformat/webmdashenc.c b/libavformat/webmdashenc.c index 26b87273048..eb286cab996 100644 --- a/libavformat/webmdashenc.c +++ b/libavformat/webmdashenc.c @@ -31,7 +31,6 @@ #include #include "avformat.h" -#include "avio_internal.h" #include "matroska.h" #include "libavutil/avstring.h" @@ -57,22 +56,11 @@ typedef struct WebMDashMuxContext { char *utc_timing_url; double time_shift_buffer_depth; int minimum_update_period; - int debug_mode; } WebMDashMuxContext; static const char *get_codec_name(int codec_id) { - switch (codec_id) { - case AV_CODEC_ID_VP8: - return "vp8"; - case AV_CODEC_ID_VP9: - return "vp9"; - case AV_CODEC_ID_VORBIS: - return "vorbis"; - case AV_CODEC_ID_OPUS: - return "opus"; - } - return NULL; + return avcodec_descriptor_get(codec_id)->name; } static double get_duration(AVFormatContext *s) @@ -114,7 +102,7 @@ static int write_header(AVFormatContext *s) if (!strftime(gmt_iso, 21, "%Y-%m-%dT%H:%M:%SZ", gmt)) { return AVERROR_UNKNOWN; } - if (w->debug_mode) { + if (s->flags & AVFMT_FLAG_BITEXACT) { av_strlcpy(gmt_iso, "", 1); } avio_printf(s->pb, " availabilityStartTime=\"%s\"\n", gmt_iso); @@ -286,7 +274,6 @@ static int parse_filename(char *filename, char **representation_id, char **initialization_pattern, char **media_pattern) { char *underscore_pos = NULL; char *period_pos = NULL; - char *temp_pos = NULL; char *filename_str = av_strdup(filename); int ret = 0; @@ -294,16 +281,12 @@ static int parse_filename(char *filename, char **representation_id, ret = AVERROR(ENOMEM); goto end; } - temp_pos = av_stristr(filename_str, "_"); - while (temp_pos) { - underscore_pos = temp_pos + 1; - temp_pos = av_stristr(temp_pos + 1, "_"); - } + underscore_pos = strrchr(filename_str, '_'); if (!underscore_pos) { ret = AVERROR_INVALIDDATA; goto end; } - period_pos = av_stristr(underscore_pos, "."); + period_pos = strchr(++underscore_pos, '.'); if (!period_pos) { ret = AVERROR_INVALIDDATA; goto end; @@ -437,18 +420,6 @@ static int write_adaptation_set(AVFormatContext *s, int as_index) return 0; } -static int to_integer(char *p, int len) -{ - int ret; - char *q = av_malloc(sizeof(char) * len); - if (!q) - return AVERROR(ENOMEM); - av_strlcpy(q, p, len); - ret = atoi(q); - av_free(q); - return ret; -} - static int parse_adaptation_sets(AVFormatContext *s) { WebMDashMuxContext *w = s->priv_data; @@ -461,10 +432,16 @@ static int parse_adaptation_sets(AVFormatContext *s) } // syntax id=0,streams=0,1,2 id=1,streams=3,4 and so on state = new_set; - while (p < w->adaptation_sets + strlen(w->adaptation_sets)) { - if (*p == ' ') + while (1) { + if (*p == '\0') { + if (state == new_set) + break; + else + return AVERROR(EINVAL); + } else if (state == new_set && *p == ' ') { + p++; continue; - else if (state == new_set && !strncmp(p, "id=", 3)) { + } else if (state == new_set && !strncmp(p, "id=", 3)) { void *mem = av_realloc(w->as, sizeof(*w->as) * (w->nb_as + 1)); const char *comma; if (mem == NULL) @@ -489,17 +466,18 @@ static int parse_adaptation_sets(AVFormatContext *s) state = parsing_streams; } else if (state == parsing_streams) { struct AdaptationSet *as = &w->as[w->nb_as - 1]; - q = p; - while (*q != '\0' && *q != ',' && *q != ' ') q++; - as->streams = av_realloc(as->streams, sizeof(*as->streams) * ++as->nb_streams); - if (as->streams == NULL) - return AVERROR(ENOMEM); - as->streams[as->nb_streams - 1] = to_integer(p, q - p + 1); - if (as->streams[as->nb_streams - 1] < 0 || - as->streams[as->nb_streams - 1] >= s->nb_streams) { + int64_t num; + int ret = av_reallocp_array(&as->streams, ++as->nb_streams, + sizeof(*as->streams)); + if (ret < 0) + return ret; + num = strtoll(p, &q, 10); + if (!av_isdigit(*p) || (*q != ' ' && *q != '\0' && *q != ',') || + num < 0 || num >= s->nb_streams) { av_log(s, AV_LOG_ERROR, "Invalid value for 'streams' in adapation_sets.\n"); return AVERROR(EINVAL); } + as->streams[as->nb_streams - 1] = num; if (*q == '\0') break; if (*q == ' ') state = new_set; p = ++q; @@ -516,6 +494,14 @@ static int webm_dash_manifest_write_header(AVFormatContext *s) double start = 0.0; int ret; WebMDashMuxContext *w = s->priv_data; + + for (unsigned i = 0; i < s->nb_streams; i++) { + enum AVCodecID codec_id = s->streams[i]->codecpar->codec_id; + if (codec_id != AV_CODEC_ID_VP8 && codec_id != AV_CODEC_ID_VP9 && + codec_id != AV_CODEC_ID_VORBIS && codec_id != AV_CODEC_ID_OPUS) + return AVERROR(EINVAL); + } + ret = parse_adaptation_sets(s); if (ret < 0) { goto fail; @@ -550,16 +536,9 @@ static int webm_dash_manifest_write_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EOF; } -static int webm_dash_manifest_write_trailer(AVFormatContext *s) -{ - free_adaptation_sets(s); - return 0; -} - #define OFFSET(x) offsetof(WebMDashMuxContext, x) 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 }, - { "debug_mode", "[private option - users should never set this]. Create deterministic output", OFFSET(debug_mode), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, { "live", "create a live stream manifest", OFFSET(is_live), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, { "chunk_start_index", "start index of the chunk", OFFSET(chunk_start_index), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, { "chunk_duration_ms", "duration of each chunk (in milliseconds)", OFFSET(chunk_duration), AV_OPT_TYPE_INT, {.i64 = 1000}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, @@ -569,7 +548,6 @@ static const AVOption options[] = { { NULL }, }; -#if CONFIG_WEBM_DASH_MANIFEST_MUXER static const AVClass webm_dash_class = { .class_name = "WebM DASH Manifest muxer", .item_name = av_default_item_name, @@ -585,7 +563,5 @@ AVOutputFormat ff_webm_dash_manifest_muxer = { .priv_data_size = sizeof(WebMDashMuxContext), .write_header = webm_dash_manifest_write_header, .write_packet = webm_dash_manifest_write_packet, - .write_trailer = webm_dash_manifest_write_trailer, .priv_class = &webm_dash_class, }; -#endif diff --git a/libavformat/webvttdec.c b/libavformat/webvttdec.c index 52579c5ed2c..bd3d45b3829 100644 --- a/libavformat/webvttdec.c +++ b/libavformat/webvttdec.c @@ -60,7 +60,7 @@ static int64_t read_ts(const char *s) static int webvtt_read_header(AVFormatContext *s) { WebVTTContext *webvtt = s->priv_data; - AVBPrint header, cue; + AVBPrint cue; int res = 0; AVStream *st = avformat_new_stream(s, NULL); @@ -71,7 +71,6 @@ static int webvtt_read_header(AVFormatContext *s) st->codecpar->codec_id = AV_CODEC_ID_WEBVTT; st->disposition |= webvtt->kind; - av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED); av_bprint_init(&cue, 0, AV_BPRINT_SIZE_UNLIMITED); for (;;) { @@ -165,8 +164,9 @@ static int webvtt_read_header(AVFormatContext *s) ff_subtitles_queue_finalize(s, &webvtt->q); end: + if (res < 0) + ff_subtitles_queue_clean(&webvtt->q); av_bprint_finalize(&cue, NULL); - av_bprint_finalize(&header, NULL); return res; } diff --git a/libavformat/webvttenc.c b/libavformat/webvttenc.c index 61b7f546229..cbd989dcb6c 100644 --- a/libavformat/webvttenc.c +++ b/libavformat/webvttenc.c @@ -57,7 +57,6 @@ static int webvtt_write_header(AVFormatContext *ctx) avpriv_set_pts_info(s, 64, 1, 1000); avio_printf(pb, "WEBVTT\n"); - avio_flush(pb); return 0; } diff --git a/libavformat/westwood_vqa.c b/libavformat/westwood_vqa.c index c21a3e31f65..a0db854b1cf 100644 --- a/libavformat/westwood_vqa.c +++ b/libavformat/westwood_vqa.c @@ -85,7 +85,7 @@ static int wsvqa_read_header(AVFormatContext *s) uint8_t scratch[VQA_PREAMBLE_SIZE]; uint32_t chunk_tag; uint32_t chunk_size; - int fps; + int fps, ret; /* initialize the video decoder stream */ st = avformat_new_stream(s, NULL); @@ -101,8 +101,8 @@ static int wsvqa_read_header(AVFormatContext *s) avio_seek(pb, 20, SEEK_SET); /* the VQA header needs to go to the decoder */ - if (ff_get_extradata(s, st->codecpar, pb, VQA_HEADER_SIZE) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_get_extradata(s, st->codecpar, pb, VQA_HEADER_SIZE)) < 0) + return ret; header = st->codecpar->extradata; st->codecpar->width = AV_RL16(&header[6]); st->codecpar->height = AV_RL16(&header[8]); @@ -214,8 +214,8 @@ static int wsvqa_read_packet(AVFormatContext *s, break; case SND2_TAG: st->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_WS; - if (ff_alloc_extradata(st->codecpar, 2)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, 2)) < 0) + return ret; AV_WL16(st->codecpar->extradata, wsvqa->version); break; } diff --git a/libavformat/wtvdec.c b/libavformat/wtvdec.c index 706e8ca38dd..83f510b92f1 100644 --- a/libavformat/wtvdec.c +++ b/libavformat/wtvdec.c @@ -71,7 +71,7 @@ static int wtvfile_read_packet(void *opaque, uint8_t *buf, int buf_size) { WtvFile *wf = opaque; AVIOContext *pb = wf->pb_filesystem; - int nread = 0; + int nread = 0, n = 0; if (wf->error || pb->error) return -1; @@ -80,7 +80,6 @@ static int wtvfile_read_packet(void *opaque, uint8_t *buf, int buf_size) buf_size = FFMIN(buf_size, wf->length - wf->position); while(nread < buf_size) { - int n; int remaining_in_sector = (1 << wf->sector_bits) - (wf->position & ((1 << wf->sector_bits) - 1)); int read_request = FFMIN(buf_size - nread, remaining_in_sector); @@ -100,7 +99,7 @@ static int wtvfile_read_packet(void *opaque, uint8_t *buf, int buf_size) } } } - return nread; + return nread ? nread : n; } /** @@ -290,7 +289,7 @@ static AVIOContext * wtvfile_open2(AVFormatContext *s, const uint8_t *buf, int b buf += dir_length; } - return 0; + return NULL; } #define wtvfile_open(s, buf, buf_size, filename) \ @@ -904,10 +903,10 @@ static int parse_chunks(AVFormatContext *s, int mode, int64_t seekts, int *len_p wtv->last_valid_pts = wtv->pts; if (wtv->epoch == AV_NOPTS_VALUE || wtv->pts < wtv->epoch) wtv->epoch = wtv->pts; - if (mode == SEEK_TO_PTS && wtv->pts >= seekts) { - avio_skip(pb, WTV_PAD8(len) - consumed); - return 0; - } + if (mode == SEEK_TO_PTS && wtv->pts >= seekts) { + avio_skip(pb, WTV_PAD8(len) - consumed); + return 0; + } } } } else if (!ff_guidcmp(g, ff_data_guid)) { @@ -993,8 +992,10 @@ static int read_header(AVFormatContext *s) } ret = parse_chunks(s, SEEK_TO_DATA, 0, 0); - if (ret < 0) + if (ret < 0) { + wtvfile_close(wtv->pb); return ret; + } avio_seek(wtv->pb, -32, SEEK_CUR); timeline_pos = avio_tell(s->pb); // save before opening another file diff --git a/libavformat/wtvenc.c b/libavformat/wtvenc.c index 4a68b8133f9..498bc640195 100644 --- a/libavformat/wtvenc.c +++ b/libavformat/wtvenc.c @@ -823,8 +823,6 @@ static int write_trailer(AVFormatContext *s) avio_seek(pb, 0x5c, SEEK_SET); avio_wl32(pb, file_end_pos >> WTV_SECTOR_BITS); - avio_flush(pb); - av_free(wctx->sp_pairs); av_free(wctx->st_pairs); av_packet_unref(&wctx->thumbnail); diff --git a/libavformat/wvdec.c b/libavformat/wvdec.c index 649791d1511..b9fc6a59f92 100644 --- a/libavformat/wvdec.c +++ b/libavformat/wvdec.c @@ -79,7 +79,7 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) { WVContext *wc = ctx->priv_data; int ret; - int rate, bpp, chan; + int rate, rate_x, bpp, chan; uint32_t chmask, flags; wc->pos = avio_tell(pb); @@ -98,11 +98,6 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) return ret; } - if (wc->header.flags & WV_DSD) { - avpriv_report_missing_feature(ctx, "WV DSD"); - return AVERROR_PATCHWELCOME; - } - if (wc->header.version < 0x402 || wc->header.version > 0x410) { avpriv_report_missing_feature(ctx, "WV version 0x%03X", wc->header.version); @@ -115,7 +110,8 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) return 0; // parse flags flags = wc->header.flags; - bpp = ((flags & 3) + 1) << 3; + rate_x = (flags & WV_DSD) ? 4 : 1; + bpp = (flags & WV_DSD) ? 0 : ((flags & 3) + 1) << 3; chan = 1 + !(flags & WV_MONO); chmask = flags & WV_MONO ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO; rate = wv_rates[(flags >> 23) & 0xF]; @@ -124,7 +120,7 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) chan = wc->chan; chmask = wc->chmask; } - if ((rate == -1 || !chan) && !wc->block_parsed) { + if ((rate == -1 || !chan || flags & WV_DSD) && !wc->block_parsed) { int64_t block_end = avio_tell(pb) + wc->header.blocksize; if (!(pb->seekable & AVIO_SEEKABLE_NORMAL)) { av_log(ctx, AV_LOG_ERROR, @@ -177,6 +173,16 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) return AVERROR_INVALIDDATA; } break; + case 0xE: + if (size <= 1) { + av_log(ctx, AV_LOG_ERROR, + "Invalid DSD block\n"); + return AVERROR_INVALIDDATA; + } + rate_x = 1U << (avio_r8(pb) & 0x1f); + if (size) + avio_skip(pb, size-1); + break; case 0x27: rate = avio_rl24(pb); break; @@ -200,7 +206,7 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) if (!wc->chmask) wc->chmask = chmask; if (!wc->rate) - wc->rate = rate; + wc->rate = rate * rate_x; if (flags && bpp != wc->bpp) { av_log(ctx, AV_LOG_ERROR, @@ -214,10 +220,10 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) chan, wc->chan); return AVERROR_INVALIDDATA; } - if (flags && rate != -1 && rate != wc->rate) { + if (flags && rate != -1 && !(flags & WV_DSD) && rate * rate_x != wc->rate) { av_log(ctx, AV_LOG_ERROR, "Sampling rate differ, this block: %i, header block: %i\n", - rate, wc->rate); + rate * rate_x, wc->rate); return AVERROR_INVALIDDATA; } return 0; @@ -244,6 +250,9 @@ static int wv_read_header(AVFormatContext *s) st = avformat_new_stream(s, NULL); if (!st) return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, 2)) < 0) + return ret; + AV_WL16(st->codecpar->extradata, wc->header.version); st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; st->codecpar->codec_id = AV_CODEC_ID_WAVPACK; st->codecpar->channels = wc->chan; @@ -282,30 +291,26 @@ static int wv_read_packet(AVFormatContext *s, AVPacket *pkt) } pos = wc->pos; - if (av_new_packet(pkt, wc->header.blocksize + WV_HEADER_SIZE) < 0) - return AVERROR(ENOMEM); + if ((ret = av_new_packet(pkt, wc->header.blocksize + WV_HEADER_SIZE)) < 0) + return ret; memcpy(pkt->data, wc->block_header, WV_HEADER_SIZE); ret = avio_read(s->pb, pkt->data + WV_HEADER_SIZE, wc->header.blocksize); if (ret != wc->header.blocksize) { - av_packet_unref(pkt); return AVERROR(EIO); } while (!(wc->header.flags & WV_FLAG_FINAL_BLOCK)) { if ((ret = wv_read_block_header(s, s->pb)) < 0) { - av_packet_unref(pkt); return ret; } off = pkt->size; if ((ret = av_grow_packet(pkt, WV_HEADER_SIZE + wc->header.blocksize)) < 0) { - av_packet_unref(pkt); return ret; } memcpy(pkt->data + off, wc->block_header, WV_HEADER_SIZE); ret = avio_read(s->pb, pkt->data + off + WV_HEADER_SIZE, wc->header.blocksize); if (ret != wc->header.blocksize) { - av_packet_unref(pkt); return (ret < 0) ? ret : AVERROR_EOF; } } diff --git a/libavformat/xmv.c b/libavformat/xmv.c index 7f12956458c..0c69d267de2 100644 --- a/libavformat/xmv.c +++ b/libavformat/xmv.c @@ -397,8 +397,6 @@ static int xmv_process_packet_header(AVFormatContext *s) av_assert0(xmv->video.stream_index < s->nb_streams); if (vst->codecpar->extradata_size < 4) { - av_freep(&vst->codecpar->extradata); - if ((ret = ff_alloc_extradata(vst->codecpar, 4)) < 0) return ret; } diff --git a/libavformat/xwma.c b/libavformat/xwma.c index b0844492961..5a57caa8411 100644 --- a/libavformat/xwma.c +++ b/libavformat/xwma.c @@ -60,16 +60,16 @@ static int xwma_read_header(AVFormatContext *s) /* check RIFF header */ tag = avio_rl32(pb); if (tag != MKTAG('R', 'I', 'F', 'F')) - return -1; + return AVERROR_INVALIDDATA; avio_rl32(pb); /* file size */ tag = avio_rl32(pb); if (tag != MKTAG('X', 'W', 'M', 'A')) - return -1; + return AVERROR_INVALIDDATA; /* parse fmt header */ tag = avio_rl32(pb); if (tag != MKTAG('f', 'm', 't', ' ')) - return -1; + return AVERROR_INVALIDDATA; size = avio_rl32(pb); st = avformat_new_stream(s, NULL); if (!st) @@ -130,15 +130,15 @@ static int xwma_read_header(AVFormatContext *s) avpriv_request_sample(s, "Unexpected extradata (%d bytes)", st->codecpar->extradata_size); } else if (st->codecpar->codec_id == AV_CODEC_ID_WMAPRO) { - if (ff_alloc_extradata(st->codecpar, 18)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, 18)) < 0) + return ret; memset(st->codecpar->extradata, 0, st->codecpar->extradata_size); st->codecpar->extradata[ 0] = st->codecpar->bits_per_coded_sample; st->codecpar->extradata[14] = 224; } else { - if (ff_alloc_extradata(st->codecpar, 6)) - return AVERROR(ENOMEM); + if ((ret = ff_alloc_extradata(st->codecpar, 6)) < 0) + return ret; memset(st->codecpar->extradata, 0, st->codecpar->extradata_size); /* setup extradata with our experimentally obtained value */ diff --git a/libavformat/yop.c b/libavformat/yop.c index f9ead026a25..0d8d9f2ff7c 100644 --- a/libavformat/yop.c +++ b/libavformat/yop.c @@ -125,14 +125,11 @@ static int yop_read_packet(AVFormatContext *s, AVPacket *pkt) yop->video_packet.stream_index = 1; if (yop->video_packet.data) { - *pkt = yop->video_packet; - yop->video_packet.data = NULL; - yop->video_packet.buf = NULL; - yop->video_packet.size = 0; + av_packet_move_ref(pkt, &yop->video_packet); pkt->data[0] = yop->odd_frame; pkt->flags |= AV_PKT_FLAG_KEY; yop->odd_frame ^= 1; - return pkt->size; + return 0; } ret = av_new_packet(&yop->video_packet, yop->frame_size - yop->audio_block_length); @@ -166,7 +163,7 @@ static int yop_read_packet(AVFormatContext *s, AVPacket *pkt) av_shrink_packet(&yop->video_packet, yop->palette_size + ret); // Arbitrarily return the audio data first - return yop->audio_block_length; + return 0; err_out: av_packet_unref(&yop->video_packet); diff --git a/libavformat/yuv4mpegdec.c b/libavformat/yuv4mpegdec.c index fc2f0ca0543..980de7d145c 100644 --- a/libavformat/yuv4mpegdec.c +++ b/libavformat/yuv4mpegdec.c @@ -26,7 +26,7 @@ #include "yuv4mpeg.h" /* Header size increased to allow room for optional flags */ -#define MAX_YUV4_HEADER 80 +#define MAX_YUV4_HEADER 96 #define MAX_FRAME_HEADER 80 static int yuv4_read_header(AVFormatContext *s) @@ -53,10 +53,14 @@ static int yuv4_read_header(AVFormatContext *s) break; } } - if (i == MAX_YUV4_HEADER) - return -1; - if (strncmp(header, Y4M_MAGIC, strlen(Y4M_MAGIC))) - return -1; + if (i == MAX_YUV4_HEADER) { + av_log(s, AV_LOG_ERROR, "Header too large.\n"); + return AVERROR(EINVAL); + } + if (strncmp(header, Y4M_MAGIC, strlen(Y4M_MAGIC))) { + av_log(s, AV_LOG_ERROR, "Invalid magic number for yuv4mpeg.\n"); + return AVERROR(EINVAL); + } header_end = &header[i + 1]; // Include space for (tokstart = &header[strlen(Y4M_MAGIC) + 1]; @@ -120,9 +124,7 @@ static int yuv4_read_header(AVFormatContext *s) } else if (strncmp("422", tokstart, 3) == 0) { pix_fmt = AV_PIX_FMT_YUV422P; } else if (strncmp("444alpha", tokstart, 8) == 0 ) { - av_log(s, AV_LOG_ERROR, "Cannot handle 4:4:4:4 " - "YUV4MPEG stream.\n"); - return -1; + pix_fmt = AV_PIX_FMT_YUVA444P; } else if (strncmp("444", tokstart, 3) == 0) { pix_fmt = AV_PIX_FMT_YUV444P; } else if (strncmp("mono16", tokstart, 6) == 0) { @@ -138,7 +140,7 @@ static int yuv4_read_header(AVFormatContext *s) } else { av_log(s, AV_LOG_ERROR, "YUV4MPEG stream contains an unknown " "pixel format.\n"); - return -1; + return AVERROR_INVALIDDATA; } while (tokstart < header_end && *tokstart != 0x20) tokstart++; @@ -236,7 +238,7 @@ static int yuv4_read_header(AVFormatContext *s) if (width == -1 || height == -1) { av_log(s, AV_LOG_ERROR, "YUV4MPEG has invalid header.\n"); - return -1; + return AVERROR_INVALIDDATA; } if (pix_fmt == AV_PIX_FMT_NONE) { @@ -310,7 +312,6 @@ static int yuv4_read_packet(AVFormatContext *s, AVPacket *pkt) if (ret < 0) return ret; else if (ret != s->packet_size - Y4M_FRAME_MAGIC_LEN) { - av_packet_unref(pkt); return s->pb->eof_reached ? AVERROR_EOF : AVERROR(EIO); } pkt->stream_index = 0; @@ -326,6 +327,8 @@ static int yuv4_read_seek(AVFormatContext *s, int stream_index, if (flags & AVSEEK_FLAG_BACKWARD) pts = FFMAX(0, pts - 1); + if (pts < 0) + return -1; pos = pts * s->packet_size; if (avio_seek(s->pb, pos + s->internal->data_offset, SEEK_SET) < 0) diff --git a/libavresample/tests/.gitignore b/libavresample/tests/.gitignore new file mode 100644 index 00000000000..1e15871d548 --- /dev/null +++ b/libavresample/tests/.gitignore @@ -0,0 +1 @@ +/avresample diff --git a/libavutil/.gitignore b/libavutil/.gitignore new file mode 100644 index 00000000000..4dc74667725 --- /dev/null +++ b/libavutil/.gitignore @@ -0,0 +1,2 @@ +/avconfig.h +/ffversion.h diff --git a/libavutil/Makefile b/libavutil/Makefile index 8a7a44e4b5e..9b08372eb29 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -23,6 +23,7 @@ HEADERS = adler32.h \ des.h \ dict.h \ display.h \ + dovi_meta.h \ downmix_info.h \ encryption_info.h \ error.h \ @@ -40,9 +41,11 @@ HEADERS = adler32.h \ hwcontext_dxva2.h \ hwcontext_qsv.h \ hwcontext_mediacodec.h \ + hwcontext_opencl.h \ hwcontext_vaapi.h \ hwcontext_videotoolbox.h \ hwcontext_vdpau.h \ + hwcontext_vulkan.h \ imgutils.h \ intfloat.h \ intreadwrite.h \ @@ -77,6 +80,7 @@ HEADERS = adler32.h \ tree.h \ twofish.h \ version.h \ + video_enc_params.h \ xtea.h \ tea.h \ tx.h \ @@ -110,6 +114,7 @@ OBJS = adler32.o \ des.o \ dict.o \ display.o \ + dovi_meta.o \ downmix_info.o \ encryption_info.o \ error.o \ @@ -161,6 +166,11 @@ OBJS = adler32.o \ xtea.o \ tea.o \ tx.o \ + tx_float.o \ + tx_double.o \ + tx_int32.o \ + video_enc_params.o \ + OBJS-$(CONFIG_CUDA) += hwcontext_cuda.o OBJS-$(CONFIG_D3D11VA) += hwcontext_d3d11va.o @@ -173,6 +183,7 @@ OBJS-$(CONFIG_QSV) += hwcontext_qsv.o OBJS-$(CONFIG_VAAPI) += hwcontext_vaapi.o OBJS-$(CONFIG_VIDEOTOOLBOX) += hwcontext_videotoolbox.o OBJS-$(CONFIG_VDPAU) += hwcontext_vdpau.o +OBJS-$(CONFIG_VULKAN) += hwcontext_vulkan.o OBJS += $(COMPAT_OBJS:%=../compat/%) @@ -189,6 +200,7 @@ SKIPHEADERS-$(CONFIG_OPENCL) += hwcontext_opencl.h SKIPHEADERS-$(CONFIG_VAAPI) += hwcontext_vaapi.h SKIPHEADERS-$(CONFIG_VIDEOTOOLBOX) += hwcontext_videotoolbox.h SKIPHEADERS-$(CONFIG_VDPAU) += hwcontext_vdpau.h +SKIPHEADERS-$(CONFIG_VULKAN) += hwcontext_vulkan.h TESTPROGS = adler32 \ aes \ diff --git a/libavutil/aarch64/asm.S b/libavutil/aarch64/asm.S index 5c329430fde..d1fa72b3c65 100644 --- a/libavutil/aarch64/asm.S +++ b/libavutil/aarch64/asm.S @@ -32,6 +32,10 @@ # define FUNC # #endif +#ifndef __has_feature +# define __has_feature(x) 0 +#endif + .macro function name, export=0, align=2 .macro endfunc ELF .size \name, . - \name @@ -94,7 +98,11 @@ ELF .size \name, . - \name add \rd, \rd, :lo12:\val+(\offset) .endif #elif CONFIG_PIC +# if __has_feature(hwaddress_sanitizer) + adrp \rd, :pg_hi21_nc:\val+(\offset) +# else adrp \rd, \val+(\offset) +# endif add \rd, \rd, :lo12:\val+(\offset) #else ldr \rd, =\val+\offset @@ -104,3 +112,6 @@ ELF .size \name, . - \name #define GLUE(a, b) a ## b #define JOIN(a, b) GLUE(a, b) #define X(s) JOIN(EXTERN_ASM, s) + +#define x18 do_not_use_x18 +#define w18 do_not_use_w18 diff --git a/libavutil/attributes.h b/libavutil/attributes.h index ced108aa2c7..5cb9fe34528 100644 --- a/libavutil/attributes.h +++ b/libavutil/attributes.h @@ -34,6 +34,12 @@ # define AV_GCC_VERSION_AT_MOST(x,y) 0 #endif +#ifdef __has_builtin +# define AV_HAS_BUILTIN(x) __has_builtin(x) +#else +# define AV_HAS_BUILTIN(x) 0 +#endif + #ifndef av_always_inline #if AV_GCC_VERSION_AT_LEAST(3,1) # define av_always_inline __attribute__((always_inline)) inline diff --git a/libavutil/avsscanf.c b/libavutil/avsscanf.c index 1c85412fd4d..850c117940e 100644 --- a/libavutil/avsscanf.c +++ b/libavutil/avsscanf.c @@ -229,9 +229,9 @@ static long long scanexp(FFFILE *f, int pok) return LLONG_MIN; } for (x=0; c-'0'<10U && x #include +#include "avassert.h" #include "buffer_internal.h" #include "common.h" #include "mem.h" @@ -43,8 +44,7 @@ AVBufferRef *av_buffer_create(uint8_t *data, int size, atomic_init(&buf->refcount, 1); - if (flags & AV_BUFFER_FLAG_READONLY) - buf->flags |= BUFFER_FLAG_READONLY; + buf->flags = flags; ref = av_mallocz(sizeof(*ref)); if (!ref) { @@ -116,7 +116,7 @@ static void buffer_replace(AVBufferRef **dst, AVBufferRef **src) } else av_freep(dst); - if (atomic_fetch_add_explicit(&b->refcount, -1, memory_order_acq_rel) == 1) { + if (atomic_fetch_sub_explicit(&b->refcount, 1, memory_order_acq_rel) == 1) { b->free(b->opaque, b->data); av_freep(&b); } @@ -184,14 +184,14 @@ int av_buffer_realloc(AVBufferRef **pbuf, int size) return AVERROR(ENOMEM); } - buf->buffer->flags |= BUFFER_FLAG_REALLOCATABLE; + buf->buffer->flags_internal |= BUFFER_FLAG_REALLOCATABLE; *pbuf = buf; return 0; } else if (buf->size == size) return 0; - if (!(buf->buffer->flags & BUFFER_FLAG_REALLOCATABLE) || + if (!(buf->buffer->flags_internal & BUFFER_FLAG_REALLOCATABLE) || !av_buffer_is_writable(buf) || buf->data != buf->buffer->data) { /* cannot realloc, allocate a new reallocable buffer and copy data */ AVBufferRef *new = NULL; @@ -228,6 +228,7 @@ AVBufferPool *av_buffer_pool_init2(int size, void *opaque, pool->size = size; pool->opaque = opaque; pool->alloc2 = alloc; + pool->alloc = av_buffer_alloc; // fallback pool->pool_free = pool_free; atomic_init(&pool->refcount, 1); @@ -281,7 +282,7 @@ void av_buffer_pool_uninit(AVBufferPool **ppool) pool = *ppool; *ppool = NULL; - if (atomic_fetch_add_explicit(&pool->refcount, -1, memory_order_acq_rel) == 1) + if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1) buffer_pool_free(pool); } @@ -298,7 +299,7 @@ static void pool_release_buffer(void *opaque, uint8_t *data) pool->pool = buf; ff_mutex_unlock(&pool->mutex); - if (atomic_fetch_add_explicit(&pool->refcount, -1, memory_order_acq_rel) == 1) + if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1) buffer_pool_free(pool); } @@ -309,6 +310,8 @@ static AVBufferRef *pool_alloc_buffer(AVBufferPool *pool) BufferPoolEntry *buf; AVBufferRef *ret; + av_assert0(pool->alloc || pool->alloc2); + ret = pool->alloc2 ? pool->alloc2(pool->opaque, pool->size) : pool->alloc(pool->size); if (!ret) @@ -355,3 +358,10 @@ AVBufferRef *av_buffer_pool_get(AVBufferPool *pool) return ret; } + +void *av_buffer_pool_buffer_get_opaque(AVBufferRef *ref) +{ + BufferPoolEntry *buf = ref->buffer->opaque; + av_assert0(buf); + return buf->opaque; +} diff --git a/libavutil/buffer.h b/libavutil/buffer.h index 73b6bd0b148..c0f3f6cc9ab 100644 --- a/libavutil/buffer.h +++ b/libavutil/buffer.h @@ -254,12 +254,13 @@ AVBufferPool *av_buffer_pool_init(int size, AVBufferRef* (*alloc)(int size)); * @param size size of each buffer in this pool * @param opaque arbitrary user data used by the allocator * @param alloc a function that will be used to allocate new buffers when the - * pool is empty. + * pool is empty. May be NULL, then the default allocator will be + * used (av_buffer_alloc()). * @param pool_free a function that will be called immediately before the pool * is freed. I.e. after av_buffer_pool_uninit() is called * by the caller and all the frames are returned to the pool * and freed. It is intended to uninitialize the user opaque - * data. + * data. May be NULL. * @return newly created buffer pool on success, NULL on error. */ AVBufferPool *av_buffer_pool_init2(int size, void *opaque, @@ -284,6 +285,19 @@ void av_buffer_pool_uninit(AVBufferPool **pool); */ AVBufferRef *av_buffer_pool_get(AVBufferPool *pool); +/** + * Query the original opaque parameter of an allocated buffer in the pool. + * + * @param ref a buffer reference to a buffer returned by av_buffer_pool_get. + * @return the opaque parameter set by the buffer allocator function of the + * buffer pool. + * + * @note the opaque parameter of ref is used by the buffer pool implementation, + * therefore you have to use this function to access the original opaque + * parameter of an allocated buffer. + */ +void *av_buffer_pool_buffer_get_opaque(AVBufferRef *ref); + /** * @} */ diff --git a/libavutil/buffer_internal.h b/libavutil/buffer_internal.h index 54b67047e5e..70d2615a063 100644 --- a/libavutil/buffer_internal.h +++ b/libavutil/buffer_internal.h @@ -25,14 +25,10 @@ #include "buffer.h" #include "thread.h" -/** - * The buffer is always treated as read-only. - */ -#define BUFFER_FLAG_READONLY (1 << 0) /** * The buffer was av_realloc()ed, so it is reallocatable. */ -#define BUFFER_FLAG_REALLOCATABLE (1 << 1) +#define BUFFER_FLAG_REALLOCATABLE (1 << 0) struct AVBuffer { uint8_t *data; /**< data described by this buffer */ @@ -54,9 +50,14 @@ struct AVBuffer { void *opaque; /** - * A combination of BUFFER_FLAG_* + * A combination of AV_BUFFER_FLAG_* */ int flags; + + /** + * A combination of BUFFER_FLAG_* + */ + int flags_internal; }; typedef struct BufferPoolEntry { diff --git a/libavutil/common.h b/libavutil/common.h index 8db02911705..92b721a59c4 100644 --- a/libavutil/common.h +++ b/libavutil/common.h @@ -53,7 +53,7 @@ //rounded division & shift #define RSHIFT(a,b) ((a) > 0 ? ((a) + ((1<<(b))>>1))>>(b) : ((a) + ((1<<(b))>>1)-1)>>(b)) /* assume b>0 */ -#define ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b)) +#define ROUNDED_DIV(a,b) (((a)>=0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b)) /* Fast a/(1<=0 and b>=0 */ #define AV_CEIL_RSHIFT(a,b) (!av_builtin_constant_p(b) ? -((-(a)) >> (b)) \ : ((a) + (1<<(b)) - 1) >> (b)) @@ -240,7 +240,7 @@ static av_always_inline av_const unsigned av_clip_uintp2_c(int a, int p) */ static av_always_inline av_const unsigned av_mod_uintp2_c(unsigned a, unsigned p) { - return a & ((1 << p) - 1); + return a & ((1U << p) - 1); } /** @@ -291,6 +291,46 @@ static av_always_inline int av_sat_dsub32_c(int a, int b) return av_sat_sub32(a, av_sat_add32(b, b)); } +/** + * Add two signed 64-bit values with saturation. + * + * @param a one value + * @param b another value + * @return sum with signed saturation + */ +static av_always_inline int64_t av_sat_add64_c(int64_t a, int64_t b) { +#if (!defined(__INTEL_COMPILER) && AV_GCC_VERSION_AT_LEAST(5,1)) || AV_HAS_BUILTIN(__builtin_add_overflow) + int64_t tmp; + return !__builtin_add_overflow(a, b, &tmp) ? tmp : (tmp < 0 ? INT64_MAX : INT64_MIN); +#else + if (b >= 0 && a >= INT64_MAX - b) + return INT64_MAX; + if (b <= 0 && a <= INT64_MIN - b) + return INT64_MIN; + return a + b; +#endif +} + +/** + * Subtract two signed 64-bit values with saturation. + * + * @param a one value + * @param b another value + * @return difference with signed saturation + */ +static av_always_inline int64_t av_sat_sub64_c(int64_t a, int64_t b) { +#if (!defined(__INTEL_COMPILER) && AV_GCC_VERSION_AT_LEAST(5,1)) || AV_HAS_BUILTIN(__builtin_sub_overflow) + int64_t tmp; + return !__builtin_sub_overflow(a, b, &tmp) ? tmp : (tmp < 0 ? INT64_MAX : INT64_MIN); +#else + if (b <= 0 && a >= INT64_MAX + b) + return INT64_MAX; + if (b >= 0 && a <= INT64_MIN + b) + return INT64_MIN; + return a - b; +#endif +} + /** * Clip a float value into the amin-amax range. * @param a value to clip @@ -331,7 +371,7 @@ static av_always_inline av_const double av_clipd_c(double a, double amin, double */ static av_always_inline av_const int av_ceil_log2_c(int x) { - return av_log2((x - 1) << 1); + return av_log2((x - 1U) << 1); } /** @@ -373,7 +413,9 @@ static av_always_inline av_const int av_parity_c(uint32_t v) * @param GET_BYTE Expression reading one byte from the input. * Evaluated up to 7 times (4 for the currently * assigned Unicode range). With a memory buffer - * input, this could be *ptr++. + * input, this could be *ptr++, or if you want to make sure + * that *ptr stops at the end of a NULL terminated string then + * *ptr ? *ptr++ : 0 * @param ERROR Expression to be evaluated on invalid input, * typically a goto statement. * @@ -387,11 +429,11 @@ static av_always_inline av_const int av_parity_c(uint32_t v) {\ uint32_t top = (val & 128) >> 1;\ if ((val & 0xc0) == 0x80 || val >= 0xFE)\ - ERROR\ + {ERROR}\ while (val & top) {\ - int tmp= (GET_BYTE) - 128;\ + unsigned int tmp = (GET_BYTE) - 128;\ if(tmp>>6)\ - ERROR\ + {ERROR}\ val= (val<<6) + tmp;\ top <<= 5;\ }\ @@ -408,13 +450,13 @@ static av_always_inline av_const int av_parity_c(uint32_t v) * typically a goto statement. */ #define GET_UTF16(val, GET_16BIT, ERROR)\ - val = GET_16BIT;\ + val = (GET_16BIT);\ {\ unsigned int hi = val - 0xD800;\ if (hi < 0x800) {\ - val = GET_16BIT - 0xDC00;\ + val = (GET_16BIT) - 0xDC00;\ if (val > 0x3FFU || hi > 0x3FFU)\ - ERROR\ + {ERROR}\ val += (hi<<10) + 0x10000;\ }\ }\ @@ -543,6 +585,12 @@ static av_always_inline av_const int av_parity_c(uint32_t v) #ifndef av_sat_dsub32 # define av_sat_dsub32 av_sat_dsub32_c #endif +#ifndef av_sat_add64 +# define av_sat_add64 av_sat_add64_c +#endif +#ifndef av_sat_sub64 +# define av_sat_sub64 av_sat_sub64_c +#endif #ifndef av_clipf # define av_clipf av_clipf_c #endif diff --git a/libavutil/dict.c b/libavutil/dict.c index 0ea71386e5a..9d3d96c58b6 100644 --- a/libavutil/dict.c +++ b/libavutil/dict.c @@ -103,8 +103,8 @@ int av_dict_set(AVDictionary **pm, const char *key, const char *value, av_free(tag->key); *tag = m->elems[--m->count]; } else if (copy_value) { - AVDictionaryEntry *tmp = av_realloc(m->elems, - (m->count + 1) * sizeof(*m->elems)); + AVDictionaryEntry *tmp = av_realloc_array(m->elems, + m->count + 1, sizeof(*m->elems)); if (!tmp) goto err_out; m->elems = tmp; diff --git a/libavutil/dovi_meta.c b/libavutil/dovi_meta.c new file mode 100644 index 00000000000..7bd08f6c540 --- /dev/null +++ b/libavutil/dovi_meta.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 Jun Zhao + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 "dovi_meta.h" +#include "mem.h" + +AVDOVIDecoderConfigurationRecord *av_dovi_alloc(size_t *size) +{ + AVDOVIDecoderConfigurationRecord *dovi = + av_mallocz(sizeof(AVDOVIDecoderConfigurationRecord)); + if (!dovi) + return NULL; + + if (size) + *size = sizeof(*dovi); + + return dovi; +} diff --git a/libavutil/dovi_meta.h b/libavutil/dovi_meta.h new file mode 100644 index 00000000000..299911d4347 --- /dev/null +++ b/libavutil/dovi_meta.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 Vacing Fang + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 + * DOVI configuration + */ + + +#ifndef AVUTIL_DOVI_META_H +#define AVUTIL_DOVI_META_H + +#include +#include + +/* + * DOVI configuration + * ref: dolby-vision-bitstreams-within-the-iso-base-media-file-format-v2.1.2 + dolby-vision-bitstreams-in-mpeg-2-transport-stream-multiplex-v1.2 + * @code + * uint8_t dv_version_major, the major version number that the stream complies with + * uint8_t dv_version_minor, the minor version number that the stream complies with + * uint8_t dv_profile, the Dolby Vision profile + * uint8_t dv_level, the Dolby Vision level + * uint8_t rpu_present_flag + * uint8_t el_present_flag + * uint8_t bl_present_flag + * uint8_t dv_bl_signal_compatibility_id + * @endcode + * + * @note The struct must be allocated with av_dovi_alloc() and + * its size is not a part of the public ABI. + */ +typedef struct AVDOVIDecoderConfigurationRecord { + uint8_t dv_version_major; + uint8_t dv_version_minor; + uint8_t dv_profile; + uint8_t dv_level; + uint8_t rpu_present_flag; + uint8_t el_present_flag; + uint8_t bl_present_flag; + uint8_t dv_bl_signal_compatibility_id; +} AVDOVIDecoderConfigurationRecord; + +/** + * Allocate a AVDOVIDecoderConfigurationRecord structure and initialize its + * fields to default values. + * + * @return the newly allocated struct or NULL on failure + */ +AVDOVIDecoderConfigurationRecord *av_dovi_alloc(size_t *size); + +#endif /* AVUTIL_DOVI_META_H */ diff --git a/libavutil/encryption_info.c b/libavutil/encryption_info.c index 812c7047769..dd3fa71a44c 100644 --- a/libavutil/encryption_info.c +++ b/libavutil/encryption_info.c @@ -331,8 +331,10 @@ uint8_t *av_encryption_init_info_add_side_data(const AVEncryptionInitInfo *info, memcpy(cur_buffer, cur_info->key_ids[i], cur_info->key_id_size); cur_buffer += cur_info->key_id_size; } - memcpy(cur_buffer, cur_info->data, cur_info->data_size); - cur_buffer += cur_info->data_size; + if (cur_info->data_size > 0) { + memcpy(cur_buffer, cur_info->data, cur_info->data_size); + cur_buffer += cur_info->data_size; + } } return buffer; diff --git a/libavutil/eval.c b/libavutil/eval.c index 5da9a6d83b9..d527f6a9d04 100644 --- a/libavutil/eval.c +++ b/libavutil/eval.c @@ -163,10 +163,11 @@ struct AVExpr { e_last, e_st, e_while, e_taylor, e_root, e_floor, e_ceil, e_trunc, e_round, e_sqrt, e_not, e_random, e_hypot, e_gcd, e_if, e_ifnot, e_print, e_bitand, e_bitor, e_between, e_clip, e_atan2, e_lerp, + e_sgn, } type; double value; // is sign in other types + int const_index; union { - int const_index; double (*func0)(double); double (*func1)(void *, double); double (*func2)(void *, double, double); @@ -184,7 +185,7 @@ static double eval_expr(Parser *p, AVExpr *e) { switch (e->type) { case e_value: return e->value; - case e_const: return e->value * p->const_values[e->a.const_index]; + case e_const: return e->value * p->const_values[e->const_index]; case e_func0: return e->value * e->a.func0(eval_expr(p, e->param[0])); case e_func1: return e->value * e->a.func1(p->opaque, eval_expr(p, e->param[0])); case e_func2: return e->value * e->a.func2(p->opaque, eval_expr(p, e->param[0]), eval_expr(p, e->param[1])); @@ -197,6 +198,7 @@ static double eval_expr(Parser *p, AVExpr *e) case e_ceil : return e->value * ceil (eval_expr(p, e->param[0])); case e_trunc: return e->value * trunc(eval_expr(p, e->param[0])); case e_round: return e->value * round(eval_expr(p, e->param[0])); + case e_sgn: return e->value * FFDIFFSIGN(eval_expr(p, e->param[0]), 0); case e_sqrt: return e->value * sqrt (eval_expr(p, e->param[0])); case e_not: return e->value * (eval_expr(p, e->param[0]) == 0); case e_if: return e->value * (eval_expr(p, e->param[0]) ? eval_expr(p, e->param[1]) : @@ -365,7 +367,7 @@ static int parse_primary(AVExpr **e, Parser *p) if (strmatch(p->s, p->const_names[i])) { p->s+= strlen(p->const_names[i]); d->type = e_const; - d->a.const_index = i; + d->const_index = i; *e = d; return 0; } @@ -470,11 +472,13 @@ static int parse_primary(AVExpr **e, Parser *p) else if (strmatch(next, "clip" )) d->type = e_clip; else if (strmatch(next, "atan2" )) d->type = e_atan2; else if (strmatch(next, "lerp" )) d->type = e_lerp; + else if (strmatch(next, "sgn" )) d->type = e_sgn; else { for (i=0; p->func1_names && p->func1_names[i]; i++) { if (strmatch(next, p->func1_names[i])) { d->a.func1 = p->funcs1[i]; d->type = e_func1; + d->const_index = i; *e = d; return 0; } @@ -484,6 +488,7 @@ static int parse_primary(AVExpr **e, Parser *p) if (strmatch(next, p->func2_names[i])) { d->a.func2 = p->funcs2[i]; d->type = e_func2; + d->const_index = i; *e = d; return 0; } @@ -657,6 +662,7 @@ static int verify_expr(AVExpr *e) case e_sqrt: case e_not: case e_random: + case e_sgn: return verify_expr(e->param[0]) && !e->param[1]; case e_print: return verify_expr(e->param[0]) @@ -731,6 +737,32 @@ int av_expr_parse(AVExpr **expr, const char *s, return ret; } +static int expr_count(AVExpr *e, unsigned *counter, int size, int type) +{ + int i; + + if (!e || !counter || !size) + return AVERROR(EINVAL); + + for (i = 0; e->type != type && i < 3 && e->param[i]; i++) + expr_count(e->param[i], counter, size, type); + + if (e->type == type && e->const_index < size) + counter[e->const_index]++; + + return 0; +} + +int av_expr_count_vars(AVExpr *e, unsigned *counter, int size) +{ + return expr_count(e, counter, size, e_const); +} + +int av_expr_count_func(AVExpr *e, unsigned *counter, int size, int arg) +{ + return expr_count(e, counter, size, ((int[]){e_const, e_func1, e_func2})[arg]); +} + double av_expr_eval(AVExpr *e, const double *const_values, void *opaque) { Parser p = { 0 }; diff --git a/libavutil/eval.h b/libavutil/eval.h index dacd22b96e6..068c62cdab0 100644 --- a/libavutil/eval.h +++ b/libavutil/eval.h @@ -86,6 +86,30 @@ int av_expr_parse(AVExpr **expr, const char *s, */ double av_expr_eval(AVExpr *e, const double *const_values, void *opaque); +/** + * Track the presence of variables and their number of occurrences in a parsed expression + * + * @param counter a zero-initialized array where the count of each variable will be stored + * @param size size of array + * @return 0 on success, a negative value indicates that no expression or array was passed + * or size was zero + */ +int av_expr_count_vars(AVExpr *e, unsigned *counter, int size); + +/** + * Track the presence of user provided functions and their number of occurrences + * in a parsed expression. + * + * @param counter a zero-initialized array where the count of each function will be stored + * if you passed 5 functions with 2 arguments to av_expr_parse() + * then for arg=2 this will use upto 5 entries. + * @param size size of array + * @param arg number of arguments the counted functions have + * @return 0 on success, a negative value indicates that no expression or array was passed + * or size was zero + */ +int av_expr_count_func(AVExpr *e, unsigned *counter, int size, int arg); + /** * Free a parsed expression previously created with av_expr_parse(). */ diff --git a/libavutil/file.c b/libavutil/file.c index d946085b280..f228b723ec7 100644 --- a/libavutil/file.c +++ b/libavutil/file.c @@ -60,6 +60,7 @@ int av_file_map(const char *filename, uint8_t **bufptr, size_t *size, off_t off_size; char errbuf[128]; *bufptr = NULL; + *size = 0; if (fd < 0) { err = AVERROR(errno); @@ -97,6 +98,7 @@ int av_file_map(const char *filename, uint8_t **bufptr, size_t *size, av_strerror(err, errbuf, sizeof(errbuf)); av_log(&file_log_ctx, AV_LOG_ERROR, "Error occurred in mmap(): %s\n", errbuf); close(fd); + *size = 0; return err; } *bufptr = ptr; @@ -108,6 +110,7 @@ int av_file_map(const char *filename, uint8_t **bufptr, size_t *size, if (!mh) { av_log(&file_log_ctx, AV_LOG_ERROR, "Error occurred in CreateFileMapping()\n"); close(fd); + *size = 0; return -1; } @@ -116,6 +119,7 @@ int av_file_map(const char *filename, uint8_t **bufptr, size_t *size, if (!ptr) { av_log(&file_log_ctx, AV_LOG_ERROR, "Error occurred in MapViewOfFile()\n"); close(fd); + *size = 0; return -1; } @@ -126,6 +130,7 @@ int av_file_map(const char *filename, uint8_t **bufptr, size_t *size, if (!*bufptr) { av_log(&file_log_ctx, AV_LOG_ERROR, "Memory allocation error occurred\n"); close(fd); + *size = 0; return AVERROR(ENOMEM); } read(fd, *bufptr, *size); @@ -138,7 +143,7 @@ int av_file_map(const char *filename, uint8_t **bufptr, size_t *size, void av_file_unmap(uint8_t *bufptr, size_t size) { - if (!size) + if (!size || !bufptr) return; #if HAVE_MMAP munmap(bufptr, size); diff --git a/libavutil/frame.c b/libavutil/frame.c index dcf1fc3d17b..2e952edd297 100644 --- a/libavutil/frame.c +++ b/libavutil/frame.c @@ -25,6 +25,7 @@ #include "imgutils.h" #include "mem.h" #include "samplefmt.h" +#include "hwcontext.h" #if FF_API_FRAME_GET_SET MAKE_ACCESSORS(AVFrame, frame, int64_t, best_effort_timestamp) @@ -460,7 +461,7 @@ int av_frame_ref(AVFrame *dst, const AVFrame *src) /* duplicate the frame data if it's not refcounted */ if (!src->buf[0]) { - ret = av_frame_get_buffer(dst, 32); + ret = av_frame_get_buffer(dst, 0); if (ret < 0) return ret; @@ -626,7 +627,11 @@ int av_frame_make_writable(AVFrame *frame) tmp.channels = frame->channels; tmp.channel_layout = frame->channel_layout; tmp.nb_samples = frame->nb_samples; - ret = av_frame_get_buffer(&tmp, 32); + + if (frame->hw_frames_ctx) + ret = av_hwframe_get_buffer(frame->hw_frames_ctx, &tmp, 0); + else + ret = av_frame_get_buffer(&tmp, 0); if (ret < 0) return ret; @@ -752,6 +757,9 @@ static int frame_copy_video(AVFrame *dst, const AVFrame *src) dst->height < src->height) return AVERROR(EINVAL); + if (src->hw_frames_ctx || dst->hw_frames_ctx) + return av_hwframe_transfer_data(dst, src, 0); + planes = av_pix_fmt_count_planes(dst->format); for (i = 0; i < planes; i++) if (!dst->data[i] || !src->data[i]) @@ -806,7 +814,7 @@ void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type) { int i; - for (i = 0; i < frame->nb_side_data; i++) { + for (i = frame->nb_side_data - 1; i >= 0; i--) { AVFrameSideData *sd = frame->side_data[i]; if (sd->type == type) { free_side_data(&frame->side_data[i]); @@ -842,6 +850,7 @@ const char *av_frame_side_data_name(enum AVFrameSideDataType type) #endif case AV_FRAME_DATA_DYNAMIC_HDR_PLUS: return "HDR Dynamic Metadata SMPTE2094-40 (HDR10+)"; case AV_FRAME_DATA_REGIONS_OF_INTEREST: return "Regions Of Interest"; + case AV_FRAME_DATA_VIDEO_ENC_PARAMS: return "Video encoding parameters"; } return NULL; } diff --git a/libavutil/frame.h b/libavutil/frame.h index 5d3231e7bb6..fc67db0f6c1 100644 --- a/libavutil/frame.h +++ b/libavutil/frame.h @@ -179,6 +179,11 @@ enum AVFrameSideDataType { * array element is implied by AVFrameSideData.size / AVRegionOfInterest.self_size. */ AV_FRAME_DATA_REGIONS_OF_INTEREST, + + /** + * Encoding parameters for a video frame, as described by AVVideoEncParams. + */ + AV_FRAME_DATA_VIDEO_ENC_PARAMS, }; enum AVActiveFormatDescription { @@ -920,8 +925,7 @@ AVFrameSideData *av_frame_get_side_data(const AVFrame *frame, enum AVFrameSideDataType type); /** - * If side data of the supplied type exists in the frame, free it and remove it - * from the frame. + * Remove and free all side data instances of the given type. */ void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type); diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c index f1e404ab201..d13d0f7c9bc 100644 --- a/libavutil/hwcontext.c +++ b/libavutil/hwcontext.c @@ -58,6 +58,9 @@ static const HWContextType * const hw_table[] = { #endif #if CONFIG_MEDIACODEC &ff_hwcontext_type_mediacodec, +#endif +#if CONFIG_VULKAN + &ff_hwcontext_type_vulkan, #endif NULL, }; @@ -73,6 +76,7 @@ static const char *const hw_type_names[] = { [AV_HWDEVICE_TYPE_VDPAU] = "vdpau", [AV_HWDEVICE_TYPE_VIDEOTOOLBOX] = "videotoolbox", [AV_HWDEVICE_TYPE_MEDIACODEC] = "mediacodec", + [AV_HWDEVICE_TYPE_VULKAN] = "vulkan", }; enum AVHWDeviceType av_hwdevice_find_type_by_name(const char *name) @@ -418,7 +422,7 @@ static int transfer_data_alloc(AVFrame *dst, const AVFrame *src, int flags) frame_tmp->width = ctx->width; frame_tmp->height = ctx->height; - ret = av_frame_get_buffer(frame_tmp, 32); + ret = av_frame_get_buffer(frame_tmp, 0); if (ret < 0) goto fail; @@ -444,21 +448,54 @@ int av_hwframe_transfer_data(AVFrame *dst, const AVFrame *src, int flags) if (!dst->buf[0]) return transfer_data_alloc(dst, src, flags); - if (src->hw_frames_ctx) { - ctx = (AVHWFramesContext*)src->hw_frames_ctx->data; + /* + * Hardware -> Hardware Transfer. + * Unlike Software -> Hardware or Hardware -> Software, the transfer + * function could be provided by either the src or dst, depending on + * the specific combination of hardware. + */ + if (src->hw_frames_ctx && dst->hw_frames_ctx) { + AVHWFramesContext *src_ctx = + (AVHWFramesContext*)src->hw_frames_ctx->data; + AVHWFramesContext *dst_ctx = + (AVHWFramesContext*)dst->hw_frames_ctx->data; + + if (src_ctx->internal->source_frames) { + av_log(src_ctx, AV_LOG_ERROR, + "A device with a derived frame context cannot be used as " + "the source of a HW -> HW transfer."); + return AVERROR(ENOSYS); + } - ret = ctx->internal->hw_type->transfer_data_from(ctx, dst, src); - if (ret < 0) - return ret; - } else if (dst->hw_frames_ctx) { - ctx = (AVHWFramesContext*)dst->hw_frames_ctx->data; + if (dst_ctx->internal->source_frames) { + av_log(src_ctx, AV_LOG_ERROR, + "A device with a derived frame context cannot be used as " + "the destination of a HW -> HW transfer."); + return AVERROR(ENOSYS); + } - ret = ctx->internal->hw_type->transfer_data_to(ctx, dst, src); + ret = src_ctx->internal->hw_type->transfer_data_from(src_ctx, dst, src); + if (ret == AVERROR(ENOSYS)) + ret = dst_ctx->internal->hw_type->transfer_data_to(dst_ctx, dst, src); if (ret < 0) return ret; - } else - return AVERROR(ENOSYS); + } else { + if (src->hw_frames_ctx) { + ctx = (AVHWFramesContext*)src->hw_frames_ctx->data; + ret = ctx->internal->hw_type->transfer_data_from(ctx, dst, src); + if (ret < 0) + return ret; + } else if (dst->hw_frames_ctx) { + ctx = (AVHWFramesContext*)dst->hw_frames_ctx->data; + + ret = ctx->internal->hw_type->transfer_data_to(ctx, dst, src); + if (ret < 0) + return ret; + } else { + return AVERROR(ENOSYS); + } + } return 0; } @@ -520,6 +557,8 @@ int av_hwframe_get_buffer(AVBufferRef *hwframe_ref, AVFrame *frame, int flags) return ret; } + frame->extended_data = frame->data; + return 0; } @@ -604,9 +643,10 @@ int av_hwdevice_ctx_create(AVBufferRef **pdevice_ref, enum AVHWDeviceType type, return ret; } -int av_hwdevice_ctx_create_derived(AVBufferRef **dst_ref_ptr, - enum AVHWDeviceType type, - AVBufferRef *src_ref, int flags) +int av_hwdevice_ctx_create_derived_opts(AVBufferRef **dst_ref_ptr, + enum AVHWDeviceType type, + AVBufferRef *src_ref, + AVDictionary *options, int flags) { AVBufferRef *dst_ref = NULL, *tmp_ref; AVHWDeviceContext *dst_ctx, *tmp_ctx; @@ -639,6 +679,7 @@ int av_hwdevice_ctx_create_derived(AVBufferRef **dst_ref_ptr, if (dst_ctx->internal->hw_type->device_derive) { ret = dst_ctx->internal->hw_type->device_derive(dst_ctx, tmp_ctx, + options, flags); if (ret == 0) { dst_ctx->internal->source_device = av_buffer_ref(src_ref); @@ -670,6 +711,14 @@ int av_hwdevice_ctx_create_derived(AVBufferRef **dst_ref_ptr, return ret; } +int av_hwdevice_ctx_create_derived(AVBufferRef **dst_ref_ptr, + enum AVHWDeviceType type, + AVBufferRef *src_ref, int flags) +{ + return av_hwdevice_ctx_create_derived_opts(dst_ref_ptr, type, src_ref, + NULL, flags); +} + static void ff_hwframe_unmap(void *opaque, uint8_t *data) { HWMapDescriptor *hwmap = (HWMapDescriptor*)data; diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h index f5a4b623877..04d19d89c2b 100644 --- a/libavutil/hwcontext.h +++ b/libavutil/hwcontext.h @@ -36,6 +36,7 @@ enum AVHWDeviceType { AV_HWDEVICE_TYPE_DRM, AV_HWDEVICE_TYPE_OPENCL, AV_HWDEVICE_TYPE_MEDIACODEC, + AV_HWDEVICE_TYPE_VULKAN, }; typedef struct AVHWDeviceInternal AVHWDeviceInternal; @@ -327,6 +328,26 @@ int av_hwdevice_ctx_create_derived(AVBufferRef **dst_ctx, enum AVHWDeviceType type, AVBufferRef *src_ctx, int flags); +/** + * Create a new device of the specified type from an existing device. + * + * This function performs the same action as av_hwdevice_ctx_create_derived, + * however, it is able to set options for the new device to be derived. + * + * @param dst_ctx On success, a reference to the newly-created + * AVHWDeviceContext. + * @param type The type of the new device to create. + * @param src_ctx A reference to an existing AVHWDeviceContext which will be + * used to create the new device. + * @param options Options for the new device to create, same format as in + * av_hwdevice_ctx_create. + * @param flags Currently unused; should be set to zero. + * @return Zero on success, a negative AVERROR code on failure. + */ +int av_hwdevice_ctx_create_derived_opts(AVBufferRef **dst_ctx, + enum AVHWDeviceType type, + AVBufferRef *src_ctx, + AVDictionary *options, int flags); /** * Allocate an AVHWFramesContext tied to a given device context. diff --git a/libavutil/hwcontext_cuda.c b/libavutil/hwcontext_cuda.c index cca39e9fc75..718a449b6e6 100644 --- a/libavutil/hwcontext_cuda.c +++ b/libavutil/hwcontext_cuda.c @@ -21,6 +21,9 @@ #include "hwcontext.h" #include "hwcontext_internal.h" #include "hwcontext_cuda_internal.h" +#if CONFIG_VULKAN +#include "hwcontext_vulkan.h" +#endif #include "cuda_check.h" #include "mem.h" #include "pixdesc.h" @@ -36,12 +39,16 @@ typedef struct CUDAFramesContext { static const enum AVPixelFormat supported_formats[] = { AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_P010, AV_PIX_FMT_P016, AV_PIX_FMT_YUV444P16, AV_PIX_FMT_0RGB32, AV_PIX_FMT_0BGR32, +#if CONFIG_VULKAN + AV_PIX_FMT_VULKAN, +#endif }; #define CHECK_CU(x) FF_CUDA_CHECK_DL(device_ctx, cu, x) @@ -194,8 +201,8 @@ static int cuda_transfer_get_formats(AVHWFramesContext *ctx, return 0; } -static int cuda_transfer_data_from(AVHWFramesContext *ctx, AVFrame *dst, - const AVFrame *src) +static int cuda_transfer_data(AVHWFramesContext *ctx, AVFrame *dst, + const AVFrame *src) { CUDAFramesContext *priv = ctx->internal->priv; AVHWDeviceContext *device_ctx = ctx->device_ctx; @@ -205,65 +212,45 @@ static int cuda_transfer_data_from(AVHWFramesContext *ctx, AVFrame *dst, CUcontext dummy; int i, ret; + if ((src->hw_frames_ctx && ((AVHWFramesContext*)src->hw_frames_ctx->data)->format != AV_PIX_FMT_CUDA) || + (dst->hw_frames_ctx && ((AVHWFramesContext*)dst->hw_frames_ctx->data)->format != AV_PIX_FMT_CUDA)) + return AVERROR(ENOSYS); + ret = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx)); if (ret < 0) return ret; for (i = 0; i < FF_ARRAY_ELEMS(src->data) && src->data[i]; i++) { CUDA_MEMCPY2D cpy = { - .srcMemoryType = CU_MEMORYTYPE_DEVICE, - .dstMemoryType = CU_MEMORYTYPE_HOST, - .srcDevice = (CUdeviceptr)src->data[i], - .dstHost = dst->data[i], .srcPitch = src->linesize[i], .dstPitch = dst->linesize[i], .WidthInBytes = FFMIN(src->linesize[i], dst->linesize[i]), - .Height = src->height >> (i ? priv->shift_height : 0), + .Height = src->height >> ((i == 0 || i == 3) ? 0 : priv->shift_height), }; + if (src->hw_frames_ctx) { + cpy.srcMemoryType = CU_MEMORYTYPE_DEVICE; + cpy.srcDevice = (CUdeviceptr)src->data[i]; + } else { + cpy.srcMemoryType = CU_MEMORYTYPE_HOST; + cpy.srcHost = src->data[i]; + } + + if (dst->hw_frames_ctx) { + cpy.dstMemoryType = CU_MEMORYTYPE_DEVICE; + cpy.dstDevice = (CUdeviceptr)dst->data[i]; + } else { + cpy.dstMemoryType = CU_MEMORYTYPE_HOST; + cpy.dstHost = dst->data[i]; + } + ret = CHECK_CU(cu->cuMemcpy2DAsync(&cpy, hwctx->stream)); if (ret < 0) goto exit; } - ret = CHECK_CU(cu->cuStreamSynchronize(hwctx->stream)); - if (ret < 0) - goto exit; - -exit: - CHECK_CU(cu->cuCtxPopCurrent(&dummy)); - - return 0; -} - -static int cuda_transfer_data_to(AVHWFramesContext *ctx, AVFrame *dst, - const AVFrame *src) -{ - CUDAFramesContext *priv = ctx->internal->priv; - AVHWDeviceContext *device_ctx = ctx->device_ctx; - AVCUDADeviceContext *hwctx = device_ctx->hwctx; - CudaFunctions *cu = hwctx->internal->cuda_dl; - - CUcontext dummy; - int i, ret; - - ret = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx)); - if (ret < 0) - return ret; - - for (i = 0; i < FF_ARRAY_ELEMS(src->data) && src->data[i]; i++) { - CUDA_MEMCPY2D cpy = { - .srcMemoryType = CU_MEMORYTYPE_HOST, - .dstMemoryType = CU_MEMORYTYPE_DEVICE, - .srcHost = src->data[i], - .dstDevice = (CUdeviceptr)dst->data[i], - .srcPitch = src->linesize[i], - .dstPitch = dst->linesize[i], - .WidthInBytes = FFMIN(src->linesize[i], dst->linesize[i]), - .Height = src->height >> (i ? priv->shift_height : 0), - }; - - ret = CHECK_CU(cu->cuMemcpy2DAsync(&cpy, hwctx->stream)); + if (!dst->hw_frames_ctx) { + ret = CHECK_CU(cu->cuStreamSynchronize(hwctx->stream)); if (ret < 0) goto exit; } @@ -280,10 +267,16 @@ static void cuda_device_uninit(AVHWDeviceContext *device_ctx) if (hwctx->internal) { CudaFunctions *cu = hwctx->internal->cuda_dl; + if (hwctx->internal->is_allocated && hwctx->cuda_ctx) { - CHECK_CU(cu->cuCtxDestroy(hwctx->cuda_ctx)); + if (hwctx->internal->flags & AV_CUDA_USE_PRIMARY_CONTEXT) + CHECK_CU(cu->cuDevicePrimaryCtxRelease(hwctx->internal->cuda_device)); + else + CHECK_CU(cu->cuCtxDestroy(hwctx->cuda_ctx)); + hwctx->cuda_ctx = NULL; } + cuda_free_functions(&hwctx->internal->cuda_dl); } @@ -316,14 +309,62 @@ static int cuda_device_init(AVHWDeviceContext *ctx) return ret; } +static int cuda_context_init(AVHWDeviceContext *device_ctx, int flags) { + AVCUDADeviceContext *hwctx = device_ctx->hwctx; + CudaFunctions *cu; + CUcontext dummy; + int ret, dev_active = 0; + unsigned int dev_flags = 0; + + const unsigned int desired_flags = CU_CTX_SCHED_BLOCKING_SYNC; + + cu = hwctx->internal->cuda_dl; + + hwctx->internal->flags = flags; + + if (flags & AV_CUDA_USE_PRIMARY_CONTEXT) { + ret = CHECK_CU(cu->cuDevicePrimaryCtxGetState(hwctx->internal->cuda_device, + &dev_flags, &dev_active)); + if (ret < 0) + return ret; + + if (dev_active && dev_flags != desired_flags) { + av_log(device_ctx, AV_LOG_ERROR, "Primary context already active with incompatible flags.\n"); + return AVERROR(ENOTSUP); + } else if (dev_flags != desired_flags) { + ret = CHECK_CU(cu->cuDevicePrimaryCtxSetFlags(hwctx->internal->cuda_device, + desired_flags)); + if (ret < 0) + return ret; + } + + ret = CHECK_CU(cu->cuDevicePrimaryCtxRetain(&hwctx->cuda_ctx, + hwctx->internal->cuda_device)); + if (ret < 0) + return ret; + } else { + ret = CHECK_CU(cu->cuCtxCreate(&hwctx->cuda_ctx, desired_flags, + hwctx->internal->cuda_device)); + if (ret < 0) + return ret; + + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + } + + hwctx->internal->is_allocated = 1; + + // Setting stream to NULL will make functions automatically use the default CUstream + hwctx->stream = NULL; + + return 0; +} + static int cuda_device_create(AVHWDeviceContext *device_ctx, const char *device, AVDictionary *opts, int flags) { AVCUDADeviceContext *hwctx = device_ctx->hwctx; CudaFunctions *cu; - CUdevice cu_device; - CUcontext dummy; int ret, device_idx = 0; if (device) @@ -338,20 +379,98 @@ static int cuda_device_create(AVHWDeviceContext *device_ctx, if (ret < 0) goto error; - ret = CHECK_CU(cu->cuDeviceGet(&cu_device, device_idx)); + ret = CHECK_CU(cu->cuDeviceGet(&hwctx->internal->cuda_device, device_idx)); if (ret < 0) goto error; - ret = CHECK_CU(cu->cuCtxCreate(&hwctx->cuda_ctx, CU_CTX_SCHED_BLOCKING_SYNC, cu_device)); + ret = cuda_context_init(device_ctx, flags); if (ret < 0) goto error; - // Setting stream to NULL will make functions automatically use the default CUstream - hwctx->stream = NULL; + return 0; - CHECK_CU(cu->cuCtxPopCurrent(&dummy)); +error: + cuda_device_uninit(device_ctx); + return AVERROR_UNKNOWN; +} - hwctx->internal->is_allocated = 1; +static int cuda_device_derive(AVHWDeviceContext *device_ctx, + AVHWDeviceContext *src_ctx, AVDictionary *opts, + int flags) { + AVCUDADeviceContext *hwctx = device_ctx->hwctx; + CudaFunctions *cu; + const char *src_uuid = NULL; + int ret, i, device_count; + +#if CONFIG_VULKAN + VkPhysicalDeviceIDProperties vk_idp = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES, + }; +#endif + + switch (src_ctx->type) { +#if CONFIG_VULKAN + case AV_HWDEVICE_TYPE_VULKAN: { + AVVulkanDeviceContext *vkctx = src_ctx->hwctx; + VkPhysicalDeviceProperties2 vk_dev_props = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + .pNext = &vk_idp, + }; + vkGetPhysicalDeviceProperties2(vkctx->phys_dev, &vk_dev_props); + src_uuid = vk_idp.deviceUUID; + break; + } +#endif + default: + return AVERROR(ENOSYS); + } + + if (!src_uuid) { + av_log(device_ctx, AV_LOG_ERROR, + "Failed to get UUID of source device.\n"); + goto error; + } + + if (cuda_device_init(device_ctx) < 0) + goto error; + + cu = hwctx->internal->cuda_dl; + + ret = CHECK_CU(cu->cuInit(0)); + if (ret < 0) + goto error; + + ret = CHECK_CU(cu->cuDeviceGetCount(&device_count)); + if (ret < 0) + goto error; + + hwctx->internal->cuda_device = -1; + for (i = 0; i < device_count; i++) { + CUdevice dev; + CUuuid uuid; + + ret = CHECK_CU(cu->cuDeviceGet(&dev, i)); + if (ret < 0) + goto error; + + ret = CHECK_CU(cu->cuDeviceGetUuid(&uuid, dev)); + if (ret < 0) + goto error; + + if (memcmp(src_uuid, uuid.bytes, sizeof (uuid.bytes)) == 0) { + hwctx->internal->cuda_device = dev; + break; + } + } + + if (hwctx->internal->cuda_device == -1) { + av_log(device_ctx, AV_LOG_ERROR, "Could not derive CUDA device.\n"); + goto error; + } + + ret = cuda_context_init(device_ctx, flags); + if (ret < 0) + goto error; return 0; @@ -368,14 +487,15 @@ const HWContextType ff_hwcontext_type_cuda = { .frames_priv_size = sizeof(CUDAFramesContext), .device_create = cuda_device_create, + .device_derive = cuda_device_derive, .device_init = cuda_device_init, .device_uninit = cuda_device_uninit, .frames_get_constraints = cuda_frames_get_constraints, .frames_init = cuda_frames_init, .frames_get_buffer = cuda_get_buffer, .transfer_get_formats = cuda_transfer_get_formats, - .transfer_data_to = cuda_transfer_data_to, - .transfer_data_from = cuda_transfer_data_from, + .transfer_data_to = cuda_transfer_data, + .transfer_data_from = cuda_transfer_data, .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, AV_PIX_FMT_NONE }, }; diff --git a/libavutil/hwcontext_cuda.h b/libavutil/hwcontext_cuda.h index 81a0552cabb..cefbe0ceab6 100644 --- a/libavutil/hwcontext_cuda.h +++ b/libavutil/hwcontext_cuda.h @@ -49,4 +49,21 @@ typedef struct AVCUDADeviceContext { * AVHWFramesContext.hwctx is currently not used */ +/** + * @defgroup hwcontext_cuda Device context creation flags + * + * Flags for av_hwdevice_ctx_create. + * + * @{ + */ + +/** + * Use primary device context instead of creating a new one. + */ +#define AV_CUDA_USE_PRIMARY_CONTEXT (1 << 0) + +/** + * @} + */ + #endif /* AVUTIL_HWCONTEXT_CUDA_H */ diff --git a/libavutil/hwcontext_cuda_internal.h b/libavutil/hwcontext_cuda_internal.h index e1bc6ff3504..d5633c58d56 100644 --- a/libavutil/hwcontext_cuda_internal.h +++ b/libavutil/hwcontext_cuda_internal.h @@ -31,6 +31,8 @@ struct AVCUDADeviceContextInternal { CudaFunctions *cuda_dl; int is_allocated; + CUdevice cuda_device; + int flags; }; #endif /* AVUTIL_HWCONTEXT_CUDA_INTERNAL_H */ diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c index 6670c475797..c8ae58f908a 100644 --- a/libavutil/hwcontext_d3d11va.c +++ b/libavutil/hwcontext_d3d11va.c @@ -39,6 +39,7 @@ #include "pixdesc.h" #include "pixfmt.h" #include "thread.h" +#include "compat/w32dlfcn.h" typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory); @@ -55,8 +56,8 @@ static av_cold void load_functions(void) // from too many LoadLibrary calls. HANDLE d3dlib, dxgilib; - d3dlib = LoadLibrary("d3d11.dll"); - dxgilib = LoadLibrary("dxgi.dll"); + d3dlib = dlopen("d3d11.dll", 0); + dxgilib = dlopen("dxgi.dll", 0); if (!d3dlib || !dxgilib) return; diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h index 77dc47ddd6e..e6266494ac7 100644 --- a/libavutil/hwcontext_internal.h +++ b/libavutil/hwcontext_internal.h @@ -67,7 +67,8 @@ typedef struct HWContextType { int (*device_create)(AVHWDeviceContext *ctx, const char *device, AVDictionary *opts, int flags); int (*device_derive)(AVHWDeviceContext *dst_ctx, - AVHWDeviceContext *src_ctx, int flags); + AVHWDeviceContext *src_ctx, + AVDictionary *opts, int flags); int (*device_init)(AVHWDeviceContext *ctx); void (*device_uninit)(AVHWDeviceContext *ctx); @@ -172,5 +173,6 @@ extern const HWContextType ff_hwcontext_type_vaapi; extern const HWContextType ff_hwcontext_type_vdpau; extern const HWContextType ff_hwcontext_type_videotoolbox; extern const HWContextType ff_hwcontext_type_mediacodec; +extern const HWContextType ff_hwcontext_type_vulkan; #endif /* AVUTIL_HWCONTEXT_INTERNAL_H */ diff --git a/libavutil/hwcontext_opencl.c b/libavutil/hwcontext_opencl.c index 41fdfe96f1a..cd8638abbb9 100644 --- a/libavutil/hwcontext_opencl.c +++ b/libavutil/hwcontext_opencl.c @@ -1194,7 +1194,7 @@ static int opencl_filter_drm_arm_device(AVHWDeviceContext *hwdev, #endif static int opencl_device_derive(AVHWDeviceContext *hwdev, - AVHWDeviceContext *src_ctx, + AVHWDeviceContext *src_ctx, AVDictionary *opts, int flags) { int err; @@ -1207,16 +1207,16 @@ static int opencl_device_derive(AVHWDeviceContext *hwdev, // Surface mapping works via DRM PRIME fds with no special // initialisation required in advance. This just finds the // Beignet ICD by name. - AVDictionary *opts = NULL; + AVDictionary *selector_opts = NULL; - err = av_dict_set(&opts, "platform_vendor", "Intel", 0); + err = av_dict_set(&selector_opts, "platform_vendor", "Intel", 0); if (err >= 0) - err = av_dict_set(&opts, "platform_version", "beignet", 0); + err = av_dict_set(&selector_opts, "platform_version", "beignet", 0); if (err >= 0) { OpenCLDeviceSelector selector = { .platform_index = -1, .device_index = 0, - .context = opts, + .context = selector_opts, .enumerate_platforms = &opencl_enumerate_platforms, .filter_platform = &opencl_filter_platform, .enumerate_devices = &opencl_enumerate_devices, @@ -1224,7 +1224,7 @@ static int opencl_device_derive(AVHWDeviceContext *hwdev, }; err = opencl_device_create_internal(hwdev, &selector, NULL); } - av_dict_free(&opts); + av_dict_free(&selector_opts); } break; #endif diff --git a/libavutil/hwcontext_qsv.c b/libavutil/hwcontext_qsv.c index 59e4ed91571..35a944f8f8a 100644 --- a/libavutil/hwcontext_qsv.c +++ b/libavutil/hwcontext_qsv.c @@ -44,6 +44,10 @@ #include "pixdesc.h" #include "time.h" +#define QSV_VERSION_ATLEAST(MAJOR, MINOR) \ + (MFX_VERSION_MAJOR > (MAJOR) || \ + MFX_VERSION_MAJOR == (MAJOR) && MFX_VERSION_MINOR >= (MINOR)) + typedef struct QSVDevicePriv { AVBufferRef *child_device_ctx; } QSVDevicePriv; @@ -103,6 +107,14 @@ static const struct { { AV_PIX_FMT_BGRA, MFX_FOURCC_RGB4 }, { AV_PIX_FMT_P010, MFX_FOURCC_P010 }, { AV_PIX_FMT_PAL8, MFX_FOURCC_P8 }, +#if CONFIG_VAAPI + { AV_PIX_FMT_YUYV422, + MFX_FOURCC_YUY2 }, +#if QSV_VERSION_ATLEAST(1, 27) + { AV_PIX_FMT_Y210, + MFX_FOURCC_Y210 }, +#endif +#endif }; static uint32_t qsv_fourcc_from_pix_fmt(enum AVPixelFormat pix_fmt) @@ -773,7 +785,19 @@ static int map_frame_to_surface(const AVFrame *frame, mfxFrameSurface1 *surface) surface->Data.R = frame->data[0] + 2; surface->Data.A = frame->data[0] + 3; break; +#if CONFIG_VAAPI + case AV_PIX_FMT_YUYV422: + surface->Data.Y = frame->data[0]; + surface->Data.U = frame->data[0] + 1; + surface->Data.V = frame->data[0] + 3; + break; + case AV_PIX_FMT_Y210: + surface->Data.Y16 = (mfxU16 *)frame->data[0]; + surface->Data.U16 = (mfxU16 *)frame->data[0] + 1; + surface->Data.V16 = (mfxU16 *)frame->data[0] + 3; + break; +#endif default: return MFX_ERR_UNSUPPORTED; } @@ -898,7 +922,7 @@ static int qsv_transfer_data_to(AVHWFramesContext *ctx, AVFrame *dst, tmp_frame.format = src->format; tmp_frame.width = FFALIGN(src->width, 16); tmp_frame.height = FFALIGN(src->height, 16); - ret = av_frame_get_buffer(&tmp_frame, 32); + ret = av_frame_get_buffer(&tmp_frame, 0); if (ret < 0) return ret; @@ -1180,11 +1204,6 @@ static int qsv_device_derive_from_child(AVHWDeviceContext *ctx, goto fail; } - ret = MFXQueryVersion(hwctx->session,&ver); - if (ret == MFX_ERR_NONE) { - av_log(ctx, AV_LOG_VERBOSE, "MFX compile/runtime API: %d.%d/%d.%d\n", - MFX_VERSION_MAJOR, MFX_VERSION_MINOR, ver.Major, ver.Minor); - } return 0; fail: @@ -1194,7 +1213,8 @@ static int qsv_device_derive_from_child(AVHWDeviceContext *ctx, } static int qsv_device_derive(AVHWDeviceContext *ctx, - AVHWDeviceContext *child_device_ctx, int flags) + AVHWDeviceContext *child_device_ctx, + AVDictionary *opts, int flags) { return qsv_device_derive_from_child(ctx, MFX_IMPL_HARDWARE_ANY, child_device_ctx, flags); @@ -1240,6 +1260,8 @@ static int qsv_device_create(AVHWDeviceContext *ctx, const char *device, ret = av_hwdevice_ctx_create(&priv->child_device_ctx, child_device_type, e ? e->value : NULL, child_device_opts, 0); + + av_dict_free(&child_device_opts); if (ret < 0) return ret; diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c index cf117640f28..5c4f5de04ec 100644 --- a/libavutil/hwcontext_vaapi.c +++ b/libavutil/hwcontext_vaapi.c @@ -116,6 +116,9 @@ static const VAAPIFormatDescriptor vaapi_format_map[] = { #endif MAP(UYVY, YUV422, UYVY422, 0), MAP(YUY2, YUV422, YUYV422, 0), +#ifdef VA_FOURCC_Y210 + MAP(Y210, YUV422_10, Y210, 0), +#endif MAP(411P, YUV411, YUV411P, 0), MAP(422V, YUV422, YUV440P, 0), MAP(444P, YUV444, YUV444P, 0), @@ -1621,13 +1624,15 @@ static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device, } static int vaapi_device_derive(AVHWDeviceContext *ctx, - AVHWDeviceContext *src_ctx, int flags) + AVHWDeviceContext *src_ctx, + AVDictionary *opts, int flags) { #if HAVE_VAAPI_DRM if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) { AVDRMDeviceContext *src_hwctx = src_ctx->hwctx; VADisplay *display; VAAPIDevicePriv *priv; + int fd; if (src_hwctx->fd < 0) { av_log(ctx, AV_LOG_ERROR, "DRM instance requires an associated " @@ -1635,17 +1640,56 @@ static int vaapi_device_derive(AVHWDeviceContext *ctx, return AVERROR(EINVAL); } +#if CONFIG_LIBDRM + { + int node_type = drmGetNodeTypeFromFd(src_hwctx->fd); + char *render_node; + if (node_type < 0) { + av_log(ctx, AV_LOG_ERROR, "DRM instance fd does not appear " + "to refer to a DRM device.\n"); + return AVERROR(EINVAL); + } + if (node_type == DRM_NODE_RENDER) { + fd = src_hwctx->fd; + } else { + render_node = drmGetRenderDeviceNameFromFd(src_hwctx->fd); + if (!render_node) { + av_log(ctx, AV_LOG_ERROR, "Failed to find a render node " + "matching the DRM device.\n"); + return AVERROR(ENODEV); + } + fd = open(render_node, O_RDWR); + if (fd < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to open render node %s" + "matching the DRM device.\n", render_node); + free(render_node); + return AVERROR(errno); + } + av_log(ctx, AV_LOG_VERBOSE, "Using render node %s in place " + "of non-render DRM device.\n", render_node); + free(render_node); + } + } +#else + fd = src_hwctx->fd; +#endif + priv = av_mallocz(sizeof(*priv)); if (!priv) return AVERROR(ENOMEM); - // Inherits the fd from the source context, which will close it. - priv->drm_fd = -1; + if (fd == src_hwctx->fd) { + // The fd is inherited from the source context and we are holding + // a reference to that, we don't want to close it from here. + priv->drm_fd = -1; + } else { + priv->drm_fd = fd; + } ctx->user_opaque = priv; ctx->free = &vaapi_device_free; - display = vaGetDisplayDRM(src_hwctx->fd); + display = vaGetDisplayDRM(fd); if (!display) { av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from " "DRM device.\n"); diff --git a/libavutil/hwcontext_videotoolbox.c b/libavutil/hwcontext_videotoolbox.c index 6eac2c0774d..bded9873fee 100644 --- a/libavutil/hwcontext_videotoolbox.c +++ b/libavutil/hwcontext_videotoolbox.c @@ -34,16 +34,19 @@ static const struct { uint32_t cv_fmt; + bool full_range; enum AVPixelFormat pix_fmt; } cv_pix_fmts[] = { - { kCVPixelFormatType_420YpCbCr8Planar, AV_PIX_FMT_YUV420P }, - { kCVPixelFormatType_422YpCbCr8, AV_PIX_FMT_UYVY422 }, - { kCVPixelFormatType_32BGRA, AV_PIX_FMT_BGRA }, + { kCVPixelFormatType_420YpCbCr8Planar, false, AV_PIX_FMT_YUV420P }, + { kCVPixelFormatType_422YpCbCr8, false, AV_PIX_FMT_UYVY422 }, + { kCVPixelFormatType_32BGRA, false, AV_PIX_FMT_BGRA }, #ifdef kCFCoreFoundationVersionNumber10_7 - { kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, AV_PIX_FMT_NV12 }, + { kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, false, AV_PIX_FMT_NV12 }, + { kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, true, AV_PIX_FMT_NV12 }, #endif #if HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE - { kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange, AV_PIX_FMT_P010 }, + { kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange, false, AV_PIX_FMT_P010 }, + { kCVPixelFormatType_420YpCbCr10BiPlanarFullRange, true, AV_PIX_FMT_P010 }, #endif }; @@ -58,10 +61,15 @@ enum AVPixelFormat av_map_videotoolbox_format_to_pixfmt(uint32_t cv_fmt) } uint32_t av_map_videotoolbox_format_from_pixfmt(enum AVPixelFormat pix_fmt) +{ + return av_map_videotoolbox_format_from_pixfmt2(pix_fmt, false); +} + +uint32_t av_map_videotoolbox_format_from_pixfmt2(enum AVPixelFormat pix_fmt, bool full_range) { int i; for (i = 0; i < FF_ARRAY_ELEMS(cv_pix_fmts); i++) { - if (cv_pix_fmts[i].pix_fmt == pix_fmt) + if (cv_pix_fmts[i].pix_fmt == pix_fmt && cv_pix_fmts[i].full_range == full_range) return cv_pix_fmts[i].cv_fmt; } return 0; diff --git a/libavutil/hwcontext_videotoolbox.h b/libavutil/hwcontext_videotoolbox.h index 380918d92ee..5074d79e68d 100644 --- a/libavutil/hwcontext_videotoolbox.h +++ b/libavutil/hwcontext_videotoolbox.h @@ -51,4 +51,10 @@ enum AVPixelFormat av_map_videotoolbox_format_to_pixfmt(uint32_t cv_fmt); */ uint32_t av_map_videotoolbox_format_from_pixfmt(enum AVPixelFormat pix_fmt); +/** + * Same as av_map_videotoolbox_format_from_pixfmt function, but can map and + * return full range pixel formats via a flag. + */ +uint32_t av_map_videotoolbox_format_from_pixfmt2(enum AVPixelFormat pix_fmt, bool full_range); + #endif /* AVUTIL_HWCONTEXT_VIDEOTOOLBOX_H */ diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c new file mode 100644 index 00000000000..5e51d0390f2 --- /dev/null +++ b/libavutil/hwcontext_vulkan.c @@ -0,0 +1,3329 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 "pixdesc.h" +#include "avstring.h" +#include "imgutils.h" +#include "hwcontext.h" +#include "hwcontext_internal.h" +#include "hwcontext_vulkan.h" + +#if CONFIG_LIBDRM +#include +#include +#include +#include "hwcontext_drm.h" +#if CONFIG_VAAPI +#include +#include "hwcontext_vaapi.h" +#endif +#endif + +#if CONFIG_CUDA +#include "hwcontext_cuda_internal.h" +#include "cuda_check.h" +#define CHECK_CU(x) FF_CUDA_CHECK_DL(cuda_cu, cu, x) +#endif + +typedef struct VulkanQueueCtx { + VkFence fence; + VkQueue queue; + int was_synchronous; + + /* Buffer dependencies */ + AVBufferRef **buf_deps; + int nb_buf_deps; + int buf_deps_alloc_size; +} VulkanQueueCtx; + +typedef struct VulkanExecCtx { + VkCommandPool pool; + VkCommandBuffer *bufs; + VulkanQueueCtx *queues; + int nb_queues; + int cur_queue_idx; +} VulkanExecCtx; + +typedef struct VulkanDevicePriv { + /* Properties */ + VkPhysicalDeviceProperties2 props; + VkPhysicalDeviceMemoryProperties mprops; + VkPhysicalDeviceExternalMemoryHostPropertiesEXT hprops; + + /* Queues */ + uint32_t qfs[3]; + int num_qfs; + + /* Debug callback */ + VkDebugUtilsMessengerEXT debug_ctx; + + /* Extensions */ + uint64_t extensions; + + /* Settings */ + int use_linear_images; + + /* Nvidia */ + int dev_is_nvidia; +} VulkanDevicePriv; + +typedef struct VulkanFramesPriv { + /* Image conversions */ + VulkanExecCtx conv_ctx; + + /* Image transfers */ + VulkanExecCtx upload_ctx; + VulkanExecCtx download_ctx; +} VulkanFramesPriv; + +typedef struct AVVkFrameInternal { +#if CONFIG_CUDA + /* Importing external memory into cuda is really expensive so we keep the + * memory imported all the time */ + AVBufferRef *cuda_fc_ref; /* Need to keep it around for uninit */ + CUexternalMemory ext_mem[AV_NUM_DATA_POINTERS]; + CUmipmappedArray cu_mma[AV_NUM_DATA_POINTERS]; + CUarray cu_array[AV_NUM_DATA_POINTERS]; + CUexternalSemaphore cu_sem[AV_NUM_DATA_POINTERS]; +#endif +} AVVkFrameInternal; + +#define GET_QUEUE_COUNT(hwctx, graph, comp, tx) ( \ + graph ? hwctx->nb_graphics_queues : \ + comp ? (hwctx->nb_comp_queues ? \ + hwctx->nb_comp_queues : hwctx->nb_graphics_queues) : \ + tx ? (hwctx->nb_tx_queues ? hwctx->nb_tx_queues : \ + (hwctx->nb_comp_queues ? \ + hwctx->nb_comp_queues : hwctx->nb_graphics_queues)) : \ + 0 \ +) + +#define VK_LOAD_PFN(inst, name) PFN_##name pfn_##name = (PFN_##name) \ + vkGetInstanceProcAddr(inst, #name) + +#define DEFAULT_USAGE_FLAGS (VK_IMAGE_USAGE_SAMPLED_BIT | \ + VK_IMAGE_USAGE_STORAGE_BIT | \ + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | \ + VK_IMAGE_USAGE_TRANSFER_DST_BIT) + +#define ADD_VAL_TO_LIST(list, count, val) \ + do { \ + list = av_realloc_array(list, sizeof(*list), ++count); \ + if (!list) { \ + err = AVERROR(ENOMEM); \ + goto fail; \ + } \ + list[count - 1] = av_strdup(val); \ + if (!list[count - 1]) { \ + err = AVERROR(ENOMEM); \ + goto fail; \ + } \ + } while(0) + +static const struct { + enum AVPixelFormat pixfmt; + const VkFormat vkfmts[3]; +} vk_pixfmt_map[] = { + { AV_PIX_FMT_GRAY8, { VK_FORMAT_R8_UNORM } }, + { AV_PIX_FMT_GRAY16, { VK_FORMAT_R16_UNORM } }, + { AV_PIX_FMT_GRAYF32, { VK_FORMAT_R32_SFLOAT } }, + + { AV_PIX_FMT_NV12, { VK_FORMAT_R8_UNORM, VK_FORMAT_R8G8_UNORM } }, + { AV_PIX_FMT_P010, { VK_FORMAT_R16_UNORM, VK_FORMAT_R16G16_UNORM } }, + { AV_PIX_FMT_P016, { VK_FORMAT_R16_UNORM, VK_FORMAT_R16G16_UNORM } }, + + { AV_PIX_FMT_YUV420P, { VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UNORM } }, + { AV_PIX_FMT_YUV422P, { VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UNORM } }, + { AV_PIX_FMT_YUV444P, { VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UNORM } }, + + { AV_PIX_FMT_YUV420P16, { VK_FORMAT_R16_UNORM, VK_FORMAT_R16_UNORM, VK_FORMAT_R16_UNORM } }, + { AV_PIX_FMT_YUV422P16, { VK_FORMAT_R16_UNORM, VK_FORMAT_R16_UNORM, VK_FORMAT_R16_UNORM } }, + { AV_PIX_FMT_YUV444P16, { VK_FORMAT_R16_UNORM, VK_FORMAT_R16_UNORM, VK_FORMAT_R16_UNORM } }, + + { AV_PIX_FMT_ABGR, { VK_FORMAT_A8B8G8R8_UNORM_PACK32 } }, + { AV_PIX_FMT_BGRA, { VK_FORMAT_B8G8R8A8_UNORM } }, + { AV_PIX_FMT_RGBA, { VK_FORMAT_R8G8B8A8_UNORM } }, + { AV_PIX_FMT_RGB24, { VK_FORMAT_R8G8B8_UNORM } }, + { AV_PIX_FMT_BGR24, { VK_FORMAT_B8G8R8_UNORM } }, + { AV_PIX_FMT_RGB48, { VK_FORMAT_R16G16B16_UNORM } }, + { AV_PIX_FMT_RGBA64, { VK_FORMAT_R16G16B16A16_UNORM } }, + { AV_PIX_FMT_RGB565, { VK_FORMAT_R5G6B5_UNORM_PACK16 } }, + { AV_PIX_FMT_BGR565, { VK_FORMAT_B5G6R5_UNORM_PACK16 } }, + { AV_PIX_FMT_BGR0, { VK_FORMAT_B8G8R8A8_UNORM } }, + { AV_PIX_FMT_0BGR, { VK_FORMAT_A8B8G8R8_UNORM_PACK32 } }, + { AV_PIX_FMT_RGB0, { VK_FORMAT_R8G8B8A8_UNORM } }, + + { AV_PIX_FMT_GBRPF32, { VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32_SFLOAT } }, +}; + +const VkFormat *av_vkfmt_from_pixfmt(enum AVPixelFormat p) +{ + for (enum AVPixelFormat i = 0; i < FF_ARRAY_ELEMS(vk_pixfmt_map); i++) + if (vk_pixfmt_map[i].pixfmt == p) + return vk_pixfmt_map[i].vkfmts; + return NULL; +} + +static int pixfmt_is_supported(AVVulkanDeviceContext *hwctx, enum AVPixelFormat p, + int linear) +{ + const VkFormat *fmt = av_vkfmt_from_pixfmt(p); + int planes = av_pix_fmt_count_planes(p); + + if (!fmt) + return 0; + + for (int i = 0; i < planes; i++) { + VkFormatFeatureFlags flags; + VkFormatProperties2 prop = { + .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, + }; + vkGetPhysicalDeviceFormatProperties2(hwctx->phys_dev, fmt[i], &prop); + flags = linear ? prop.formatProperties.linearTilingFeatures : + prop.formatProperties.optimalTilingFeatures; + if (!(flags & DEFAULT_USAGE_FLAGS)) + return 0; + } + + return 1; +} + +enum VulkanExtensions { + EXT_EXTERNAL_DMABUF_MEMORY = 1ULL << 0, /* VK_EXT_external_memory_dma_buf */ + EXT_DRM_MODIFIER_FLAGS = 1ULL << 1, /* VK_EXT_image_drm_format_modifier */ + EXT_EXTERNAL_FD_MEMORY = 1ULL << 2, /* VK_KHR_external_memory_fd */ + EXT_EXTERNAL_FD_SEM = 1ULL << 3, /* VK_KHR_external_semaphore_fd */ + EXT_EXTERNAL_HOST_MEMORY = 1ULL << 4, /* VK_EXT_external_memory_host */ + + EXT_NO_FLAG = 1ULL << 63, +}; + +typedef struct VulkanOptExtension { + const char *name; + uint64_t flag; +} VulkanOptExtension; + +static const VulkanOptExtension optional_instance_exts[] = { + /* For future use */ +}; + +static const VulkanOptExtension optional_device_exts[] = { + { VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, EXT_EXTERNAL_FD_MEMORY, }, + { VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME, EXT_EXTERNAL_DMABUF_MEMORY, }, + { VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME, EXT_DRM_MODIFIER_FLAGS, }, + { VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, EXT_EXTERNAL_FD_SEM, }, + { VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, EXT_EXTERNAL_HOST_MEMORY, }, +}; + +/* Converts return values to strings */ +static const char *vk_ret2str(VkResult res) +{ +#define CASE(VAL) case VAL: return #VAL + switch (res) { + CASE(VK_SUCCESS); + CASE(VK_NOT_READY); + CASE(VK_TIMEOUT); + CASE(VK_EVENT_SET); + CASE(VK_EVENT_RESET); + CASE(VK_INCOMPLETE); + CASE(VK_ERROR_OUT_OF_HOST_MEMORY); + CASE(VK_ERROR_OUT_OF_DEVICE_MEMORY); + CASE(VK_ERROR_INITIALIZATION_FAILED); + CASE(VK_ERROR_DEVICE_LOST); + CASE(VK_ERROR_MEMORY_MAP_FAILED); + CASE(VK_ERROR_LAYER_NOT_PRESENT); + CASE(VK_ERROR_EXTENSION_NOT_PRESENT); + CASE(VK_ERROR_FEATURE_NOT_PRESENT); + CASE(VK_ERROR_INCOMPATIBLE_DRIVER); + CASE(VK_ERROR_TOO_MANY_OBJECTS); + CASE(VK_ERROR_FORMAT_NOT_SUPPORTED); + CASE(VK_ERROR_FRAGMENTED_POOL); + CASE(VK_ERROR_SURFACE_LOST_KHR); + CASE(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); + CASE(VK_SUBOPTIMAL_KHR); + CASE(VK_ERROR_OUT_OF_DATE_KHR); + CASE(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR); + CASE(VK_ERROR_VALIDATION_FAILED_EXT); + CASE(VK_ERROR_INVALID_SHADER_NV); + CASE(VK_ERROR_OUT_OF_POOL_MEMORY); + CASE(VK_ERROR_INVALID_EXTERNAL_HANDLE); + CASE(VK_ERROR_NOT_PERMITTED_EXT); + CASE(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT); + CASE(VK_ERROR_INVALID_DEVICE_ADDRESS_EXT); + CASE(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT); + default: return "Unknown error"; + } +#undef CASE +} + +static VkBool32 vk_dbg_callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT *data, + void *priv) +{ + int l; + AVHWDeviceContext *ctx = priv; + + switch (severity) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: l = AV_LOG_VERBOSE; break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: l = AV_LOG_INFO; break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: l = AV_LOG_WARNING; break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: l = AV_LOG_ERROR; break; + default: l = AV_LOG_DEBUG; break; + } + + av_log(ctx, l, "%s\n", data->pMessage); + for (int i = 0; i < data->cmdBufLabelCount; i++) + av_log(ctx, l, "\t%i: %s\n", i, data->pCmdBufLabels[i].pLabelName); + + return 0; +} + +static int check_extensions(AVHWDeviceContext *ctx, int dev, AVDictionary *opts, + const char * const **dst, uint32_t *num, int debug) +{ + const char *tstr; + const char **extension_names = NULL; + VulkanDevicePriv *p = ctx->internal->priv; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + int err = 0, found, extensions_found = 0; + + const char *mod; + int optional_exts_num; + uint32_t sup_ext_count; + char *user_exts_str = NULL; + AVDictionaryEntry *user_exts; + VkExtensionProperties *sup_ext; + const VulkanOptExtension *optional_exts; + + if (!dev) { + mod = "instance"; + optional_exts = optional_instance_exts; + optional_exts_num = FF_ARRAY_ELEMS(optional_instance_exts); + user_exts = av_dict_get(opts, "instance_extensions", NULL, 0); + if (user_exts) { + user_exts_str = av_strdup(user_exts->value); + if (!user_exts_str) { + err = AVERROR(ENOMEM); + goto fail; + } + } + vkEnumerateInstanceExtensionProperties(NULL, &sup_ext_count, NULL); + sup_ext = av_malloc_array(sup_ext_count, sizeof(VkExtensionProperties)); + if (!sup_ext) + return AVERROR(ENOMEM); + vkEnumerateInstanceExtensionProperties(NULL, &sup_ext_count, sup_ext); + } else { + mod = "device"; + optional_exts = optional_device_exts; + optional_exts_num = FF_ARRAY_ELEMS(optional_device_exts); + user_exts = av_dict_get(opts, "device_extensions", NULL, 0); + if (user_exts) { + user_exts_str = av_strdup(user_exts->value); + if (!user_exts_str) { + err = AVERROR(ENOMEM); + goto fail; + } + } + vkEnumerateDeviceExtensionProperties(hwctx->phys_dev, NULL, + &sup_ext_count, NULL); + sup_ext = av_malloc_array(sup_ext_count, sizeof(VkExtensionProperties)); + if (!sup_ext) + return AVERROR(ENOMEM); + vkEnumerateDeviceExtensionProperties(hwctx->phys_dev, NULL, + &sup_ext_count, sup_ext); + } + + for (int i = 0; i < optional_exts_num; i++) { + tstr = optional_exts[i].name; + found = 0; + for (int j = 0; j < sup_ext_count; j++) { + if (!strcmp(tstr, sup_ext[j].extensionName)) { + found = 1; + break; + } + } + if (!found) + continue; + + av_log(ctx, AV_LOG_VERBOSE, "Using %s extension \"%s\"\n", mod, tstr); + p->extensions |= optional_exts[i].flag; + ADD_VAL_TO_LIST(extension_names, extensions_found, tstr); + } + + if (debug && !dev) { + tstr = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + found = 0; + for (int j = 0; j < sup_ext_count; j++) { + if (!strcmp(tstr, sup_ext[j].extensionName)) { + found = 1; + break; + } + } + if (found) { + av_log(ctx, AV_LOG_VERBOSE, "Using %s extension \"%s\"\n", mod, tstr); + ADD_VAL_TO_LIST(extension_names, extensions_found, tstr); + } else { + av_log(ctx, AV_LOG_ERROR, "Debug extension \"%s\" not found!\n", + tstr); + err = AVERROR(EINVAL); + goto fail; + } + } + + if (user_exts_str) { + char *save, *token = av_strtok(user_exts_str, "+", &save); + while (token) { + found = 0; + for (int j = 0; j < sup_ext_count; j++) { + if (!strcmp(token, sup_ext[j].extensionName)) { + found = 1; + break; + } + } + if (found) { + av_log(ctx, AV_LOG_VERBOSE, "Using %s extension \"%s\"\n", mod, token); + ADD_VAL_TO_LIST(extension_names, extensions_found, token); + } else { + av_log(ctx, AV_LOG_WARNING, "%s extension \"%s\" not found, excluding.\n", + mod, token); + } + token = av_strtok(NULL, "+", &save); + } + } + + *dst = extension_names; + *num = extensions_found; + + av_free(user_exts_str); + av_free(sup_ext); + return 0; + +fail: + if (extension_names) + for (int i = 0; i < extensions_found; i++) + av_free((void *)extension_names[i]); + av_free(extension_names); + av_free(user_exts_str); + av_free(sup_ext); + return err; +} + +/* Creates a VkInstance */ +static int create_instance(AVHWDeviceContext *ctx, AVDictionary *opts) +{ + int err = 0; + VkResult ret; + VulkanDevicePriv *p = ctx->internal->priv; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + AVDictionaryEntry *debug_opt = av_dict_get(opts, "debug", NULL, 0); + const int debug_mode = debug_opt && strtol(debug_opt->value, NULL, 10); + VkApplicationInfo application_info = { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pEngineName = "libavutil", + .apiVersion = VK_API_VERSION_1_1, + .engineVersion = VK_MAKE_VERSION(LIBAVUTIL_VERSION_MAJOR, + LIBAVUTIL_VERSION_MINOR, + LIBAVUTIL_VERSION_MICRO), + }; + VkInstanceCreateInfo inst_props = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pApplicationInfo = &application_info, + }; + + /* Check for present/missing extensions */ + err = check_extensions(ctx, 0, opts, &inst_props.ppEnabledExtensionNames, + &inst_props.enabledExtensionCount, debug_mode); + if (err < 0) + return err; + + if (debug_mode) { + static const char *layers[] = { "VK_LAYER_KHRONOS_validation" }; + inst_props.ppEnabledLayerNames = layers; + inst_props.enabledLayerCount = FF_ARRAY_ELEMS(layers); + } + + /* Try to create the instance */ + ret = vkCreateInstance(&inst_props, hwctx->alloc, &hwctx->inst); + + /* Check for errors */ + if (ret != VK_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Instance creation failure: %s\n", + vk_ret2str(ret)); + for (int i = 0; i < inst_props.enabledExtensionCount; i++) + av_free((void *)inst_props.ppEnabledExtensionNames[i]); + av_free((void *)inst_props.ppEnabledExtensionNames); + return AVERROR_EXTERNAL; + } + + if (debug_mode) { + VkDebugUtilsMessengerCreateInfoEXT dbg = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, + .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, + .pfnUserCallback = vk_dbg_callback, + .pUserData = ctx, + }; + VK_LOAD_PFN(hwctx->inst, vkCreateDebugUtilsMessengerEXT); + + pfn_vkCreateDebugUtilsMessengerEXT(hwctx->inst, &dbg, + hwctx->alloc, &p->debug_ctx); + } + + hwctx->enabled_inst_extensions = inst_props.ppEnabledExtensionNames; + hwctx->nb_enabled_inst_extensions = inst_props.enabledExtensionCount; + + return 0; +} + +typedef struct VulkanDeviceSelection { + uint8_t uuid[VK_UUID_SIZE]; /* Will use this first unless !has_uuid */ + int has_uuid; + const char *name; /* Will use this second unless NULL */ + uint32_t pci_device; /* Will use this third unless 0x0 */ + uint32_t vendor_id; /* Last resort to find something deterministic */ + int index; /* Finally fall back to index */ +} VulkanDeviceSelection; + +static const char *vk_dev_type(enum VkPhysicalDeviceType type) +{ + switch (type) { + case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: return "integrated"; + case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: return "discrete"; + case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: return "virtual"; + case VK_PHYSICAL_DEVICE_TYPE_CPU: return "software"; + default: return "unknown"; + } +} + +/* Finds a device */ +static int find_device(AVHWDeviceContext *ctx, VulkanDeviceSelection *select) +{ + int err = 0, choice = -1; + uint32_t num; + VkResult ret; + VkPhysicalDevice *devices = NULL; + VkPhysicalDeviceIDProperties *idp = NULL; + VkPhysicalDeviceProperties2 *prop = NULL; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + + ret = vkEnumeratePhysicalDevices(hwctx->inst, &num, NULL); + if (ret != VK_SUCCESS || !num) { + av_log(ctx, AV_LOG_ERROR, "No devices found: %s!\n", vk_ret2str(ret)); + return AVERROR(ENODEV); + } + + devices = av_malloc_array(num, sizeof(VkPhysicalDevice)); + if (!devices) + return AVERROR(ENOMEM); + + ret = vkEnumeratePhysicalDevices(hwctx->inst, &num, devices); + if (ret != VK_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed enumerating devices: %s\n", + vk_ret2str(ret)); + err = AVERROR(ENODEV); + goto end; + } + + prop = av_mallocz_array(num, sizeof(*prop)); + if (!prop) { + err = AVERROR(ENOMEM); + goto end; + } + + idp = av_mallocz_array(num, sizeof(*idp)); + if (!idp) { + err = AVERROR(ENOMEM); + goto end; + } + + av_log(ctx, AV_LOG_VERBOSE, "GPU listing:\n"); + for (int i = 0; i < num; i++) { + idp[i].sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES; + prop[i].sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + prop[i].pNext = &idp[i]; + + vkGetPhysicalDeviceProperties2(devices[i], &prop[i]); + av_log(ctx, AV_LOG_VERBOSE, " %d: %s (%s) (0x%x)\n", i, + prop[i].properties.deviceName, + vk_dev_type(prop[i].properties.deviceType), + prop[i].properties.deviceID); + } + + if (select->has_uuid) { + for (int i = 0; i < num; i++) { + if (!strncmp(idp[i].deviceUUID, select->uuid, VK_UUID_SIZE)) { + choice = i; + goto end; + } + } + av_log(ctx, AV_LOG_ERROR, "Unable to find device by given UUID!\n"); + err = AVERROR(ENODEV); + goto end; + } else if (select->name) { + av_log(ctx, AV_LOG_VERBOSE, "Requested device: %s\n", select->name); + for (int i = 0; i < num; i++) { + if (strstr(prop[i].properties.deviceName, select->name)) { + choice = i; + goto end; + } + } + av_log(ctx, AV_LOG_ERROR, "Unable to find device \"%s\"!\n", + select->name); + err = AVERROR(ENODEV); + goto end; + } else if (select->pci_device) { + av_log(ctx, AV_LOG_VERBOSE, "Requested device: 0x%x\n", select->pci_device); + for (int i = 0; i < num; i++) { + if (select->pci_device == prop[i].properties.deviceID) { + choice = i; + goto end; + } + } + av_log(ctx, AV_LOG_ERROR, "Unable to find device with PCI ID 0x%x!\n", + select->pci_device); + err = AVERROR(EINVAL); + goto end; + } else if (select->vendor_id) { + av_log(ctx, AV_LOG_VERBOSE, "Requested vendor: 0x%x\n", select->vendor_id); + for (int i = 0; i < num; i++) { + if (select->vendor_id == prop[i].properties.vendorID) { + choice = i; + goto end; + } + } + av_log(ctx, AV_LOG_ERROR, "Unable to find device with Vendor ID 0x%x!\n", + select->vendor_id); + err = AVERROR(ENODEV); + goto end; + } else { + if (select->index < num) { + choice = select->index; + goto end; + } + av_log(ctx, AV_LOG_ERROR, "Unable to find device with index %i!\n", + select->index); + err = AVERROR(ENODEV); + goto end; + } + +end: + if (choice > -1) + hwctx->phys_dev = devices[choice]; + + av_free(devices); + av_free(prop); + av_free(idp); + + return err; +} + +static int search_queue_families(AVHWDeviceContext *ctx, VkDeviceCreateInfo *cd) +{ + uint32_t num; + float *weights; + VkQueueFamilyProperties *qs = NULL; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + int graph_index = -1, comp_index = -1, tx_index = -1; + VkDeviceQueueCreateInfo *pc = (VkDeviceQueueCreateInfo *)cd->pQueueCreateInfos; + + /* First get the number of queue families */ + vkGetPhysicalDeviceQueueFamilyProperties(hwctx->phys_dev, &num, NULL); + if (!num) { + av_log(ctx, AV_LOG_ERROR, "Failed to get queues!\n"); + return AVERROR_EXTERNAL; + } + + /* Then allocate memory */ + qs = av_malloc_array(num, sizeof(VkQueueFamilyProperties)); + if (!qs) + return AVERROR(ENOMEM); + + /* Finally retrieve the queue families */ + vkGetPhysicalDeviceQueueFamilyProperties(hwctx->phys_dev, &num, qs); + +#define SEARCH_FLAGS(expr, out) \ + for (int i = 0; i < num; i++) { \ + const VkQueueFlagBits flags = qs[i].queueFlags; \ + if (expr) { \ + out = i; \ + break; \ + } \ + } + + SEARCH_FLAGS(flags & VK_QUEUE_GRAPHICS_BIT, graph_index) + + SEARCH_FLAGS((flags & VK_QUEUE_COMPUTE_BIT) && (i != graph_index), + comp_index) + + SEARCH_FLAGS((flags & VK_QUEUE_TRANSFER_BIT) && (i != graph_index) && + (i != comp_index), tx_index) + +#undef SEARCH_FLAGS +#define ADD_QUEUE(fidx, graph, comp, tx) \ + av_log(ctx, AV_LOG_VERBOSE, "Using queue family %i (total queues: %i) for %s%s%s\n", \ + fidx, qs[fidx].queueCount, graph ? "graphics " : "", \ + comp ? "compute " : "", tx ? "transfers " : ""); \ + av_log(ctx, AV_LOG_VERBOSE, " QF %i flags: %s%s%s%s\n", fidx, \ + ((qs[fidx].queueFlags) & VK_QUEUE_GRAPHICS_BIT) ? "(graphics) " : "", \ + ((qs[fidx].queueFlags) & VK_QUEUE_COMPUTE_BIT) ? "(compute) " : "", \ + ((qs[fidx].queueFlags) & VK_QUEUE_TRANSFER_BIT) ? "(transfers) " : "", \ + ((qs[fidx].queueFlags) & VK_QUEUE_SPARSE_BINDING_BIT) ? "(sparse) " : ""); \ + pc[cd->queueCreateInfoCount].queueFamilyIndex = fidx; \ + pc[cd->queueCreateInfoCount].queueCount = qs[fidx].queueCount; \ + weights = av_malloc(qs[fidx].queueCount * sizeof(float)); \ + pc[cd->queueCreateInfoCount].pQueuePriorities = weights; \ + if (!weights) \ + goto fail; \ + for (int i = 0; i < qs[fidx].queueCount; i++) \ + weights[i] = 1.0f; \ + cd->queueCreateInfoCount++; + + ADD_QUEUE(graph_index, 1, comp_index < 0, tx_index < 0 && comp_index < 0) + hwctx->queue_family_index = graph_index; + hwctx->queue_family_comp_index = graph_index; + hwctx->queue_family_tx_index = graph_index; + hwctx->nb_graphics_queues = qs[graph_index].queueCount; + + if (comp_index != -1) { + ADD_QUEUE(comp_index, 0, 1, tx_index < 0) + hwctx->queue_family_tx_index = comp_index; + hwctx->queue_family_comp_index = comp_index; + hwctx->nb_comp_queues = qs[comp_index].queueCount; + } + + if (tx_index != -1) { + ADD_QUEUE(tx_index, 0, 0, 1) + hwctx->queue_family_tx_index = tx_index; + hwctx->nb_tx_queues = qs[tx_index].queueCount; + } + +#undef ADD_QUEUE + av_free(qs); + + return 0; + +fail: + av_freep(&pc[0].pQueuePriorities); + av_freep(&pc[1].pQueuePriorities); + av_freep(&pc[2].pQueuePriorities); + av_free(qs); + + return AVERROR(ENOMEM); +} + +static int create_exec_ctx(AVHWFramesContext *hwfc, VulkanExecCtx *cmd, + int queue_family_index, int num_queues) +{ + VkResult ret; + AVVulkanDeviceContext *hwctx = hwfc->device_ctx->hwctx; + + VkCommandPoolCreateInfo cqueue_create = { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = queue_family_index, + }; + VkCommandBufferAllocateInfo cbuf_create = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = num_queues, + }; + + cmd->nb_queues = num_queues; + + cmd->queues = av_mallocz(num_queues * sizeof(*cmd->queues)); + if (!cmd->queues) + return AVERROR(ENOMEM); + + cmd->bufs = av_mallocz(num_queues * sizeof(*cmd->bufs)); + if (!cmd->bufs) + return AVERROR(ENOMEM); + + /* Create command pool */ + ret = vkCreateCommandPool(hwctx->act_dev, &cqueue_create, + hwctx->alloc, &cmd->pool); + if (ret != VK_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Command pool creation failure: %s\n", + vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + cbuf_create.commandPool = cmd->pool; + + /* Allocate command buffer */ + ret = vkAllocateCommandBuffers(hwctx->act_dev, &cbuf_create, cmd->bufs); + if (ret != VK_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Command buffer alloc failure: %s\n", + vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + for (int i = 0; i < num_queues; i++) { + VulkanQueueCtx *q = &cmd->queues[i]; + vkGetDeviceQueue(hwctx->act_dev, queue_family_index, i, &q->queue); + q->was_synchronous = 1; + } + + return 0; +} + +static void free_exec_ctx(AVHWFramesContext *hwfc, VulkanExecCtx *cmd) +{ + AVVulkanDeviceContext *hwctx = hwfc->device_ctx->hwctx; + + /* Make sure all queues have finished executing */ + for (int i = 0; i < cmd->nb_queues; i++) { + VulkanQueueCtx *q = &cmd->queues[i]; + + if (q->fence && !q->was_synchronous) { + vkWaitForFences(hwctx->act_dev, 1, &q->fence, VK_TRUE, UINT64_MAX); + vkResetFences(hwctx->act_dev, 1, &q->fence); + } + + /* Free the fence */ + if (q->fence) + vkDestroyFence(hwctx->act_dev, q->fence, hwctx->alloc); + + /* Free buffer dependencies */ + for (int j = 0; j < q->nb_buf_deps; j++) + av_buffer_unref(&q->buf_deps[j]); + av_free(q->buf_deps); + } + + if (cmd->bufs) + vkFreeCommandBuffers(hwctx->act_dev, cmd->pool, cmd->nb_queues, cmd->bufs); + if (cmd->pool) + vkDestroyCommandPool(hwctx->act_dev, cmd->pool, hwctx->alloc); + + av_freep(&cmd->bufs); + av_freep(&cmd->queues); +} + +static VkCommandBuffer get_buf_exec_ctx(AVHWFramesContext *hwfc, VulkanExecCtx *cmd) +{ + return cmd->bufs[cmd->cur_queue_idx]; +} + +static void unref_exec_ctx_deps(AVHWFramesContext *hwfc, VulkanExecCtx *cmd) +{ + VulkanQueueCtx *q = &cmd->queues[cmd->cur_queue_idx]; + + for (int j = 0; j < q->nb_buf_deps; j++) + av_buffer_unref(&q->buf_deps[j]); + q->nb_buf_deps = 0; +} + +static int wait_start_exec_ctx(AVHWFramesContext *hwfc, VulkanExecCtx *cmd) +{ + VkResult ret; + AVVulkanDeviceContext *hwctx = hwfc->device_ctx->hwctx; + VulkanQueueCtx *q = &cmd->queues[cmd->cur_queue_idx]; + + VkCommandBufferBeginInfo cmd_start = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + + /* Create the fence and don't wait for it initially */ + if (!q->fence) { + VkFenceCreateInfo fence_spawn = { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + }; + ret = vkCreateFence(hwctx->act_dev, &fence_spawn, hwctx->alloc, + &q->fence); + if (ret != VK_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Failed to queue frame fence: %s\n", + vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + } else if (!q->was_synchronous) { + vkWaitForFences(hwctx->act_dev, 1, &q->fence, VK_TRUE, UINT64_MAX); + vkResetFences(hwctx->act_dev, 1, &q->fence); + } + + /* Discard queue dependencies */ + unref_exec_ctx_deps(hwfc, cmd); + + ret = vkBeginCommandBuffer(cmd->bufs[cmd->cur_queue_idx], &cmd_start); + if (ret != VK_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Unable to init command buffer: %s\n", + vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + return 0; +} + +static int add_buf_dep_exec_ctx(AVHWFramesContext *hwfc, VulkanExecCtx *cmd, + AVBufferRef * const *deps, int nb_deps) +{ + AVBufferRef **dst; + VulkanQueueCtx *q = &cmd->queues[cmd->cur_queue_idx]; + + if (!deps || !nb_deps) + return 0; + + dst = av_fast_realloc(q->buf_deps, &q->buf_deps_alloc_size, + (q->nb_buf_deps + nb_deps) * sizeof(*dst)); + if (!dst) + goto err; + + q->buf_deps = dst; + + for (int i = 0; i < nb_deps; i++) { + q->buf_deps[q->nb_buf_deps] = av_buffer_ref(deps[i]); + if (!q->buf_deps[q->nb_buf_deps]) + goto err; + q->nb_buf_deps++; + } + + return 0; + +err: + unref_exec_ctx_deps(hwfc, cmd); + return AVERROR(ENOMEM); +} + +static int submit_exec_ctx(AVHWFramesContext *hwfc, VulkanExecCtx *cmd, + VkSubmitInfo *s_info, int synchronous) +{ + VkResult ret; + VulkanQueueCtx *q = &cmd->queues[cmd->cur_queue_idx]; + + ret = vkEndCommandBuffer(cmd->bufs[cmd->cur_queue_idx]); + if (ret != VK_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Unable to finish command buffer: %s\n", + vk_ret2str(ret)); + unref_exec_ctx_deps(hwfc, cmd); + return AVERROR_EXTERNAL; + } + + s_info->pCommandBuffers = &cmd->bufs[cmd->cur_queue_idx]; + s_info->commandBufferCount = 1; + + ret = vkQueueSubmit(q->queue, 1, s_info, q->fence); + if (ret != VK_SUCCESS) { + unref_exec_ctx_deps(hwfc, cmd); + return AVERROR_EXTERNAL; + } + + q->was_synchronous = synchronous; + + if (synchronous) { + AVVulkanDeviceContext *hwctx = hwfc->device_ctx->hwctx; + vkWaitForFences(hwctx->act_dev, 1, &q->fence, VK_TRUE, UINT64_MAX); + vkResetFences(hwctx->act_dev, 1, &q->fence); + unref_exec_ctx_deps(hwfc, cmd); + } else { /* Rotate queues */ + cmd->cur_queue_idx = (cmd->cur_queue_idx + 1) % cmd->nb_queues; + } + + return 0; +} + +static void vulkan_device_free(AVHWDeviceContext *ctx) +{ + VulkanDevicePriv *p = ctx->internal->priv; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + + vkDestroyDevice(hwctx->act_dev, hwctx->alloc); + + if (p->debug_ctx) { + VK_LOAD_PFN(hwctx->inst, vkDestroyDebugUtilsMessengerEXT); + pfn_vkDestroyDebugUtilsMessengerEXT(hwctx->inst, p->debug_ctx, + hwctx->alloc); + } + + vkDestroyInstance(hwctx->inst, hwctx->alloc); + + for (int i = 0; i < hwctx->nb_enabled_inst_extensions; i++) + av_free((void *)hwctx->enabled_inst_extensions[i]); + av_free((void *)hwctx->enabled_inst_extensions); + + for (int i = 0; i < hwctx->nb_enabled_dev_extensions; i++) + av_free((void *)hwctx->enabled_dev_extensions[i]); + av_free((void *)hwctx->enabled_dev_extensions); +} + +static int vulkan_device_create_internal(AVHWDeviceContext *ctx, + VulkanDeviceSelection *dev_select, + AVDictionary *opts, int flags) +{ + int err = 0; + VkResult ret; + AVDictionaryEntry *opt_d; + VulkanDevicePriv *p = ctx->internal->priv; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + VkPhysicalDeviceFeatures dev_features = { 0 }; + VkDeviceQueueCreateInfo queue_create_info[3] = { + { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, }, + { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, }, + { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, }, + }; + + VkDeviceCreateInfo dev_info = { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = &hwctx->device_features, + .pQueueCreateInfos = queue_create_info, + .queueCreateInfoCount = 0, + }; + + hwctx->device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + ctx->free = vulkan_device_free; + + /* Create an instance if not given one */ + if ((err = create_instance(ctx, opts))) + goto end; + + /* Find a device (if not given one) */ + if ((err = find_device(ctx, dev_select))) + goto end; + + vkGetPhysicalDeviceFeatures(hwctx->phys_dev, &dev_features); +#define COPY_FEATURE(DST, NAME) (DST).features.NAME = dev_features.NAME; + COPY_FEATURE(hwctx->device_features, shaderImageGatherExtended) + COPY_FEATURE(hwctx->device_features, fragmentStoresAndAtomics) + COPY_FEATURE(hwctx->device_features, vertexPipelineStoresAndAtomics) + COPY_FEATURE(hwctx->device_features, shaderInt64) +#undef COPY_FEATURE + + /* Search queue family */ + if ((err = search_queue_families(ctx, &dev_info))) + goto end; + + if ((err = check_extensions(ctx, 1, opts, &dev_info.ppEnabledExtensionNames, + &dev_info.enabledExtensionCount, 0))) { + av_free((void *)queue_create_info[0].pQueuePriorities); + av_free((void *)queue_create_info[1].pQueuePriorities); + av_free((void *)queue_create_info[2].pQueuePriorities); + goto end; + } + + ret = vkCreateDevice(hwctx->phys_dev, &dev_info, hwctx->alloc, + &hwctx->act_dev); + + av_free((void *)queue_create_info[0].pQueuePriorities); + av_free((void *)queue_create_info[1].pQueuePriorities); + av_free((void *)queue_create_info[2].pQueuePriorities); + + if (ret != VK_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Device creation failure: %s\n", + vk_ret2str(ret)); + for (int i = 0; i < dev_info.enabledExtensionCount; i++) + av_free((void *)dev_info.ppEnabledExtensionNames[i]); + av_free((void *)dev_info.ppEnabledExtensionNames); + err = AVERROR_EXTERNAL; + goto end; + } + + /* Tiled images setting, use them by default */ + opt_d = av_dict_get(opts, "linear_images", NULL, 0); + if (opt_d) + p->use_linear_images = strtol(opt_d->value, NULL, 10); + + hwctx->enabled_dev_extensions = dev_info.ppEnabledExtensionNames; + hwctx->nb_enabled_dev_extensions = dev_info.enabledExtensionCount; + +end: + return err; +} + +static int vulkan_device_init(AVHWDeviceContext *ctx) +{ + uint32_t queue_num; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + VulkanDevicePriv *p = ctx->internal->priv; + + /* Set device extension flags */ + for (int i = 0; i < hwctx->nb_enabled_dev_extensions; i++) { + for (int j = 0; j < FF_ARRAY_ELEMS(optional_device_exts); j++) { + if (!strcmp(hwctx->enabled_dev_extensions[i], + optional_device_exts[j].name)) { + av_log(ctx, AV_LOG_VERBOSE, "Using device extension %s\n", + hwctx->enabled_dev_extensions[i]); + p->extensions |= optional_device_exts[j].flag; + break; + } + } + } + + p->props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + p->props.pNext = &p->hprops; + p->hprops.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT; + + vkGetPhysicalDeviceProperties2(hwctx->phys_dev, &p->props); + av_log(ctx, AV_LOG_VERBOSE, "Using device: %s\n", + p->props.properties.deviceName); + av_log(ctx, AV_LOG_VERBOSE, "Alignments:\n"); + av_log(ctx, AV_LOG_VERBOSE, " optimalBufferCopyRowPitchAlignment: %li\n", + p->props.properties.limits.optimalBufferCopyRowPitchAlignment); + av_log(ctx, AV_LOG_VERBOSE, " minMemoryMapAlignment: %li\n", + p->props.properties.limits.minMemoryMapAlignment); + if (p->extensions & EXT_EXTERNAL_HOST_MEMORY) + av_log(ctx, AV_LOG_VERBOSE, " minImportedHostPointerAlignment: %li\n", + p->hprops.minImportedHostPointerAlignment); + + p->dev_is_nvidia = (p->props.properties.vendorID == 0x10de); + + vkGetPhysicalDeviceQueueFamilyProperties(hwctx->phys_dev, &queue_num, NULL); + if (!queue_num) { + av_log(ctx, AV_LOG_ERROR, "Failed to get queues!\n"); + return AVERROR_EXTERNAL; + } + +#define CHECK_QUEUE(type, n) \ +if (n >= queue_num) { \ + av_log(ctx, AV_LOG_ERROR, "Invalid %s queue index %i (device has %i queues)!\n", \ + type, n, queue_num); \ + return AVERROR(EINVAL); \ +} + + CHECK_QUEUE("graphics", hwctx->queue_family_index) + CHECK_QUEUE("upload", hwctx->queue_family_tx_index) + CHECK_QUEUE("compute", hwctx->queue_family_comp_index) + +#undef CHECK_QUEUE + + p->qfs[p->num_qfs++] = hwctx->queue_family_index; + if ((hwctx->queue_family_tx_index != hwctx->queue_family_index) && + (hwctx->queue_family_tx_index != hwctx->queue_family_comp_index)) + p->qfs[p->num_qfs++] = hwctx->queue_family_tx_index; + if ((hwctx->queue_family_comp_index != hwctx->queue_family_index) && + (hwctx->queue_family_comp_index != hwctx->queue_family_tx_index)) + p->qfs[p->num_qfs++] = hwctx->queue_family_comp_index; + + /* Get device capabilities */ + vkGetPhysicalDeviceMemoryProperties(hwctx->phys_dev, &p->mprops); + + return 0; +} + +static int vulkan_device_create(AVHWDeviceContext *ctx, const char *device, + AVDictionary *opts, int flags) +{ + VulkanDeviceSelection dev_select = { 0 }; + if (device && device[0]) { + char *end = NULL; + dev_select.index = strtol(device, &end, 10); + if (end == device) { + dev_select.index = 0; + dev_select.name = device; + } + } + + return vulkan_device_create_internal(ctx, &dev_select, opts, flags); +} + +static int vulkan_device_derive(AVHWDeviceContext *ctx, + AVHWDeviceContext *src_ctx, + AVDictionary *opts, int flags) +{ + av_unused VulkanDeviceSelection dev_select = { 0 }; + + /* If there's only one device on the system, then even if its not covered + * by the following checks (e.g. non-PCIe ARM GPU), having an empty + * dev_select will mean it'll get picked. */ + switch(src_ctx->type) { +#if CONFIG_LIBDRM +#if CONFIG_VAAPI + case AV_HWDEVICE_TYPE_VAAPI: { + AVVAAPIDeviceContext *src_hwctx = src_ctx->hwctx; + + const char *vendor = vaQueryVendorString(src_hwctx->display); + if (!vendor) { + av_log(ctx, AV_LOG_ERROR, "Unable to get device info from VAAPI!\n"); + return AVERROR_EXTERNAL; + } + + if (strstr(vendor, "Intel")) + dev_select.vendor_id = 0x8086; + if (strstr(vendor, "AMD")) + dev_select.vendor_id = 0x1002; + + return vulkan_device_create_internal(ctx, &dev_select, opts, flags); + } +#endif + case AV_HWDEVICE_TYPE_DRM: { + AVDRMDeviceContext *src_hwctx = src_ctx->hwctx; + + drmDevice *drm_dev_info; + int err = drmGetDevice(src_hwctx->fd, &drm_dev_info); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Unable to get device info from DRM fd!\n"); + return AVERROR_EXTERNAL; + } + + if (drm_dev_info->bustype == DRM_BUS_PCI) + dev_select.pci_device = drm_dev_info->deviceinfo.pci->device_id; + + drmFreeDevice(&drm_dev_info); + + return vulkan_device_create_internal(ctx, &dev_select, opts, flags); + } +#endif +#if CONFIG_CUDA + case AV_HWDEVICE_TYPE_CUDA: { + AVHWDeviceContext *cuda_cu = src_ctx; + AVCUDADeviceContext *src_hwctx = src_ctx->hwctx; + AVCUDADeviceContextInternal *cu_internal = src_hwctx->internal; + CudaFunctions *cu = cu_internal->cuda_dl; + + int ret = CHECK_CU(cu->cuDeviceGetUuid((CUuuid *)&dev_select.uuid, + cu_internal->cuda_device)); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Unable to get UUID from CUDA!\n"); + return AVERROR_EXTERNAL; + } + + dev_select.has_uuid = 1; + + return vulkan_device_create_internal(ctx, &dev_select, opts, flags); + } +#endif + default: + return AVERROR(ENOSYS); + } +} + +static int vulkan_frames_get_constraints(AVHWDeviceContext *ctx, + const void *hwconfig, + AVHWFramesConstraints *constraints) +{ + int count = 0; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + VulkanDevicePriv *p = ctx->internal->priv; + + for (enum AVPixelFormat i = 0; i < AV_PIX_FMT_NB; i++) + count += pixfmt_is_supported(hwctx, i, p->use_linear_images); + +#if CONFIG_CUDA + if (p->dev_is_nvidia) + count++; +#endif + + constraints->valid_sw_formats = av_malloc_array(count + 1, + sizeof(enum AVPixelFormat)); + if (!constraints->valid_sw_formats) + return AVERROR(ENOMEM); + + count = 0; + for (enum AVPixelFormat i = 0; i < AV_PIX_FMT_NB; i++) + if (pixfmt_is_supported(hwctx, i, p->use_linear_images)) + constraints->valid_sw_formats[count++] = i; + +#if CONFIG_CUDA + if (p->dev_is_nvidia) + constraints->valid_sw_formats[count++] = AV_PIX_FMT_CUDA; +#endif + constraints->valid_sw_formats[count++] = AV_PIX_FMT_NONE; + + constraints->min_width = 0; + constraints->min_height = 0; + constraints->max_width = p->props.properties.limits.maxImageDimension2D; + constraints->max_height = p->props.properties.limits.maxImageDimension2D; + + constraints->valid_hw_formats = av_malloc_array(2, sizeof(enum AVPixelFormat)); + if (!constraints->valid_hw_formats) + return AVERROR(ENOMEM); + + constraints->valid_hw_formats[0] = AV_PIX_FMT_VULKAN; + constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE; + + return 0; +} + +static int alloc_mem(AVHWDeviceContext *ctx, VkMemoryRequirements *req, + VkMemoryPropertyFlagBits req_flags, const void *alloc_extension, + VkMemoryPropertyFlagBits *mem_flags, VkDeviceMemory *mem) +{ + VkResult ret; + int index = -1; + VulkanDevicePriv *p = ctx->internal->priv; + AVVulkanDeviceContext *dev_hwctx = ctx->hwctx; + VkMemoryAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = alloc_extension, + .allocationSize = req->size, + }; + + /* The vulkan spec requires memory types to be sorted in the "optimal" + * order, so the first matching type we find will be the best/fastest one */ + for (int i = 0; i < p->mprops.memoryTypeCount; i++) { + /* The memory type must be supported by the requirements (bitfield) */ + if (!(req->memoryTypeBits & (1 << i))) + continue; + + /* The memory type flags must include our properties */ + if ((p->mprops.memoryTypes[i].propertyFlags & req_flags) != req_flags) + continue; + + /* Found a suitable memory type */ + index = i; + break; + } + + if (index < 0) { + av_log(ctx, AV_LOG_ERROR, "No memory type found for flags 0x%x\n", + req_flags); + return AVERROR(EINVAL); + } + + alloc_info.memoryTypeIndex = index; + + ret = vkAllocateMemory(dev_hwctx->act_dev, &alloc_info, + dev_hwctx->alloc, mem); + if (ret != VK_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory: %s\n", + vk_ret2str(ret)); + return AVERROR(ENOMEM); + } + + *mem_flags |= p->mprops.memoryTypes[index].propertyFlags; + + return 0; +} + +static void vulkan_free_internal(AVVkFrameInternal *internal) +{ + if (!internal) + return; + +#if CONFIG_CUDA + if (internal->cuda_fc_ref) { + AVHWFramesContext *cuda_fc = (AVHWFramesContext *)internal->cuda_fc_ref->data; + int planes = av_pix_fmt_count_planes(cuda_fc->sw_format); + AVHWDeviceContext *cuda_cu = cuda_fc->device_ctx; + AVCUDADeviceContext *cuda_dev = cuda_cu->hwctx; + AVCUDADeviceContextInternal *cu_internal = cuda_dev->internal; + CudaFunctions *cu = cu_internal->cuda_dl; + + for (int i = 0; i < planes; i++) { + if (internal->cu_sem[i]) + CHECK_CU(cu->cuDestroyExternalSemaphore(internal->cu_sem[i])); + if (internal->cu_mma[i]) + CHECK_CU(cu->cuMipmappedArrayDestroy(internal->cu_mma[i])); + if (internal->ext_mem[i]) + CHECK_CU(cu->cuDestroyExternalMemory(internal->ext_mem[i])); + } + + av_buffer_unref(&internal->cuda_fc_ref); + } +#endif + + av_free(internal); +} + +static void vulkan_frame_free(void *opaque, uint8_t *data) +{ + AVVkFrame *f = (AVVkFrame *)data; + AVHWFramesContext *hwfc = opaque; + AVVulkanDeviceContext *hwctx = hwfc->device_ctx->hwctx; + int planes = av_pix_fmt_count_planes(hwfc->sw_format); + + vulkan_free_internal(f->internal); + + for (int i = 0; i < planes; i++) { + vkDestroyImage(hwctx->act_dev, f->img[i], hwctx->alloc); + vkFreeMemory(hwctx->act_dev, f->mem[i], hwctx->alloc); + vkDestroySemaphore(hwctx->act_dev, f->sem[i], hwctx->alloc); + } + + av_free(f); +} + +static int alloc_bind_mem(AVHWFramesContext *hwfc, AVVkFrame *f, + void *alloc_pnext, size_t alloc_pnext_stride) +{ + int err; + VkResult ret; + AVHWDeviceContext *ctx = hwfc->device_ctx; + VulkanDevicePriv *p = ctx->internal->priv; + const int planes = av_pix_fmt_count_planes(hwfc->sw_format); + VkBindImageMemoryInfo bind_info[AV_NUM_DATA_POINTERS] = { { 0 } }; + + AVVulkanDeviceContext *hwctx = ctx->hwctx; + + for (int i = 0; i < planes; i++) { + int use_ded_mem; + VkImageMemoryRequirementsInfo2 req_desc = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, + .image = f->img[i], + }; + VkMemoryDedicatedAllocateInfo ded_alloc = { + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, + .pNext = (void *)(((uint8_t *)alloc_pnext) + i*alloc_pnext_stride), + }; + VkMemoryDedicatedRequirements ded_req = { + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, + }; + VkMemoryRequirements2 req = { + .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, + .pNext = &ded_req, + }; + + vkGetImageMemoryRequirements2(hwctx->act_dev, &req_desc, &req); + + if (f->tiling == VK_IMAGE_TILING_LINEAR) + req.memoryRequirements.size = FFALIGN(req.memoryRequirements.size, + p->props.properties.limits.minMemoryMapAlignment); + + /* In case the implementation prefers/requires dedicated allocation */ + use_ded_mem = ded_req.prefersDedicatedAllocation | + ded_req.requiresDedicatedAllocation; + if (use_ded_mem) + ded_alloc.image = f->img[i]; + + /* Allocate memory */ + if ((err = alloc_mem(ctx, &req.memoryRequirements, + f->tiling == VK_IMAGE_TILING_LINEAR ? + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT : + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + use_ded_mem ? &ded_alloc : (void *)ded_alloc.pNext, + &f->flags, &f->mem[i]))) + return err; + + f->size[i] = req.memoryRequirements.size; + bind_info[i].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO; + bind_info[i].image = f->img[i]; + bind_info[i].memory = f->mem[i]; + } + + /* Bind the allocated memory to the images */ + ret = vkBindImageMemory2(hwctx->act_dev, planes, bind_info); + if (ret != VK_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed to bind memory: %s\n", + vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + return 0; +} + +enum PrepMode { + PREP_MODE_WRITE, + PREP_MODE_RO_SHADER, + PREP_MODE_EXTERNAL_EXPORT, +}; + +static int prepare_frame(AVHWFramesContext *hwfc, VulkanExecCtx *ectx, + AVVkFrame *frame, enum PrepMode pmode) +{ + int err; + uint32_t dst_qf; + VkImageLayout new_layout; + VkAccessFlags new_access; + const int planes = av_pix_fmt_count_planes(hwfc->sw_format); + + VkImageMemoryBarrier img_bar[AV_NUM_DATA_POINTERS] = { 0 }; + + VkSubmitInfo s_info = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pSignalSemaphores = frame->sem, + .signalSemaphoreCount = planes, + }; + + VkPipelineStageFlagBits wait_st[AV_NUM_DATA_POINTERS]; + for (int i = 0; i < planes; i++) + wait_st[i] = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + + switch (pmode) { + case PREP_MODE_WRITE: + new_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + new_access = VK_ACCESS_TRANSFER_WRITE_BIT; + dst_qf = VK_QUEUE_FAMILY_IGNORED; + break; + case PREP_MODE_RO_SHADER: + new_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + new_access = VK_ACCESS_TRANSFER_READ_BIT; + dst_qf = VK_QUEUE_FAMILY_IGNORED; + break; + case PREP_MODE_EXTERNAL_EXPORT: + new_layout = VK_IMAGE_LAYOUT_GENERAL; + new_access = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; + dst_qf = VK_QUEUE_FAMILY_EXTERNAL_KHR; + s_info.pWaitSemaphores = frame->sem; + s_info.pWaitDstStageMask = wait_st; + s_info.waitSemaphoreCount = planes; + break; + } + + if ((err = wait_start_exec_ctx(hwfc, ectx))) + return err; + + /* Change the image layout to something more optimal for writes. + * This also signals the newly created semaphore, making it usable + * for synchronization */ + for (int i = 0; i < planes; i++) { + img_bar[i].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + img_bar[i].srcAccessMask = 0x0; + img_bar[i].dstAccessMask = new_access; + img_bar[i].oldLayout = frame->layout[i]; + img_bar[i].newLayout = new_layout; + img_bar[i].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + img_bar[i].dstQueueFamilyIndex = dst_qf; + img_bar[i].image = frame->img[i]; + img_bar[i].subresourceRange.levelCount = 1; + img_bar[i].subresourceRange.layerCount = 1; + img_bar[i].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + + frame->layout[i] = img_bar[i].newLayout; + frame->access[i] = img_bar[i].dstAccessMask; + } + + vkCmdPipelineBarrier(get_buf_exec_ctx(hwfc, ectx), + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, 0, NULL, 0, NULL, planes, img_bar); + + return submit_exec_ctx(hwfc, ectx, &s_info, 0); +} + +static int create_frame(AVHWFramesContext *hwfc, AVVkFrame **frame, + VkImageTiling tiling, VkImageUsageFlagBits usage, + void *create_pnext) +{ + int err; + VkResult ret; + AVHWDeviceContext *ctx = hwfc->device_ctx; + VulkanDevicePriv *p = ctx->internal->priv; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + enum AVPixelFormat format = hwfc->sw_format; + const VkFormat *img_fmts = av_vkfmt_from_pixfmt(format); + const int planes = av_pix_fmt_count_planes(format); + + VkExportSemaphoreCreateInfo ext_sem_info = { + .sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, + .handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, + }; + + VkSemaphoreCreateInfo sem_spawn = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = p->extensions & EXT_EXTERNAL_FD_SEM ? &ext_sem_info : NULL, + }; + + AVVkFrame *f = av_vk_frame_alloc(); + if (!f) { + av_log(ctx, AV_LOG_ERROR, "Unable to allocate memory for AVVkFrame!\n"); + return AVERROR(ENOMEM); + } + + /* Create the images */ + for (int i = 0; i < planes; i++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(format); + int w = hwfc->width; + int h = hwfc->height; + const int p_w = i > 0 ? AV_CEIL_RSHIFT(w, desc->log2_chroma_w) : w; + const int p_h = i > 0 ? AV_CEIL_RSHIFT(h, desc->log2_chroma_h) : h; + + VkImageCreateInfo image_create_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = create_pnext, + .imageType = VK_IMAGE_TYPE_2D, + .format = img_fmts[i], + .extent.width = p_w, + .extent.height = p_h, + .extent.depth = 1, + .mipLevels = 1, + .arrayLayers = 1, + .flags = VK_IMAGE_CREATE_ALIAS_BIT, + .tiling = tiling, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .usage = usage, + .samples = VK_SAMPLE_COUNT_1_BIT, + .pQueueFamilyIndices = p->qfs, + .queueFamilyIndexCount = p->num_qfs, + .sharingMode = p->num_qfs > 1 ? VK_SHARING_MODE_CONCURRENT : + VK_SHARING_MODE_EXCLUSIVE, + }; + + ret = vkCreateImage(hwctx->act_dev, &image_create_info, + hwctx->alloc, &f->img[i]); + if (ret != VK_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Image creation failure: %s\n", + vk_ret2str(ret)); + err = AVERROR(EINVAL); + goto fail; + } + + /* Create semaphore */ + ret = vkCreateSemaphore(hwctx->act_dev, &sem_spawn, + hwctx->alloc, &f->sem[i]); + if (ret != VK_SUCCESS) { + av_log(hwctx, AV_LOG_ERROR, "Failed to create semaphore: %s\n", + vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + f->layout[i] = image_create_info.initialLayout; + f->access[i] = 0x0; + } + + f->flags = 0x0; + f->tiling = tiling; + + *frame = f; + return 0; + +fail: + vulkan_frame_free(hwfc, (uint8_t *)f); + return err; +} + +/* Checks if an export flag is enabled, and if it is ORs it with *iexp */ +static void try_export_flags(AVHWFramesContext *hwfc, + VkExternalMemoryHandleTypeFlags *comp_handle_types, + VkExternalMemoryHandleTypeFlagBits *iexp, + VkExternalMemoryHandleTypeFlagBits exp) +{ + VkResult ret; + AVVulkanFramesContext *hwctx = hwfc->hwctx; + AVVulkanDeviceContext *dev_hwctx = hwfc->device_ctx->hwctx; + VkExternalImageFormatProperties eprops = { + .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR, + }; + VkImageFormatProperties2 props = { + .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, + .pNext = &eprops, + }; + VkPhysicalDeviceExternalImageFormatInfo enext = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, + .handleType = exp, + }; + VkPhysicalDeviceImageFormatInfo2 pinfo = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, + .pNext = !exp ? NULL : &enext, + .format = av_vkfmt_from_pixfmt(hwfc->sw_format)[0], + .type = VK_IMAGE_TYPE_2D, + .tiling = hwctx->tiling, + .usage = hwctx->usage, + .flags = VK_IMAGE_CREATE_ALIAS_BIT, + }; + + ret = vkGetPhysicalDeviceImageFormatProperties2(dev_hwctx->phys_dev, + &pinfo, &props); + if (ret == VK_SUCCESS) { + *iexp |= exp; + *comp_handle_types |= eprops.externalMemoryProperties.compatibleHandleTypes; + } +} + +static AVBufferRef *vulkan_pool_alloc(void *opaque, int size) +{ + int err; + AVVkFrame *f; + AVBufferRef *avbuf = NULL; + AVHWFramesContext *hwfc = opaque; + AVVulkanFramesContext *hwctx = hwfc->hwctx; + VulkanDevicePriv *p = hwfc->device_ctx->internal->priv; + VulkanFramesPriv *fp = hwfc->internal->priv; + VkExportMemoryAllocateInfo eminfo[AV_NUM_DATA_POINTERS]; + VkExternalMemoryHandleTypeFlags e = 0x0; + + VkExternalMemoryImageCreateInfo eiinfo = { + .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, + .pNext = hwctx->create_pnext, + }; + + if (p->extensions & EXT_EXTERNAL_FD_MEMORY) + try_export_flags(hwfc, &eiinfo.handleTypes, &e, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT); + + if (p->extensions & EXT_EXTERNAL_DMABUF_MEMORY) + try_export_flags(hwfc, &eiinfo.handleTypes, &e, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT); + + for (int i = 0; i < av_pix_fmt_count_planes(hwfc->sw_format); i++) { + eminfo[i].sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO; + eminfo[i].pNext = hwctx->alloc_pnext[i]; + eminfo[i].handleTypes = e; + } + + err = create_frame(hwfc, &f, hwctx->tiling, hwctx->usage, + eiinfo.handleTypes ? &eiinfo : NULL); + if (err) + return NULL; + + err = alloc_bind_mem(hwfc, f, eminfo, sizeof(*eminfo)); + if (err) + goto fail; + + err = prepare_frame(hwfc, &fp->conv_ctx, f, PREP_MODE_WRITE); + if (err) + goto fail; + + avbuf = av_buffer_create((uint8_t *)f, sizeof(AVVkFrame), + vulkan_frame_free, hwfc, 0); + if (!avbuf) + goto fail; + + return avbuf; + +fail: + vulkan_frame_free(hwfc, (uint8_t *)f); + return NULL; +} + +static void vulkan_frames_uninit(AVHWFramesContext *hwfc) +{ + VulkanFramesPriv *fp = hwfc->internal->priv; + + free_exec_ctx(hwfc, &fp->conv_ctx); + free_exec_ctx(hwfc, &fp->upload_ctx); + free_exec_ctx(hwfc, &fp->download_ctx); +} + +static int vulkan_frames_init(AVHWFramesContext *hwfc) +{ + int err; + AVVkFrame *f; + AVVulkanFramesContext *hwctx = hwfc->hwctx; + VulkanFramesPriv *fp = hwfc->internal->priv; + AVVulkanDeviceContext *dev_hwctx = hwfc->device_ctx->hwctx; + VulkanDevicePriv *p = hwfc->device_ctx->internal->priv; + + /* Default pool flags */ + hwctx->tiling = hwctx->tiling ? hwctx->tiling : p->use_linear_images ? + VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL; + + if (!hwctx->usage) + hwctx->usage = DEFAULT_USAGE_FLAGS; + + err = create_exec_ctx(hwfc, &fp->conv_ctx, + dev_hwctx->queue_family_comp_index, + GET_QUEUE_COUNT(dev_hwctx, 0, 1, 0)); + if (err) + goto fail; + + err = create_exec_ctx(hwfc, &fp->upload_ctx, + dev_hwctx->queue_family_tx_index, + GET_QUEUE_COUNT(dev_hwctx, 0, 0, 1)); + if (err) + goto fail; + + err = create_exec_ctx(hwfc, &fp->download_ctx, + dev_hwctx->queue_family_tx_index, 1); + if (err) + goto fail; + + /* Test to see if allocation will fail */ + err = create_frame(hwfc, &f, hwctx->tiling, hwctx->usage, + hwctx->create_pnext); + if (err) + goto fail; + + vulkan_frame_free(hwfc, (uint8_t *)f); + + /* If user did not specify a pool, hwfc->pool will be set to the internal one + * in hwcontext.c just after this gets called */ + if (!hwfc->pool) { + hwfc->internal->pool_internal = av_buffer_pool_init2(sizeof(AVVkFrame), + hwfc, vulkan_pool_alloc, + NULL); + if (!hwfc->internal->pool_internal) { + err = AVERROR(ENOMEM); + goto fail; + } + } + + return 0; + +fail: + free_exec_ctx(hwfc, &fp->conv_ctx); + free_exec_ctx(hwfc, &fp->upload_ctx); + free_exec_ctx(hwfc, &fp->download_ctx); + + return err; +} + +static int vulkan_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame) +{ + frame->buf[0] = av_buffer_pool_get(hwfc->pool); + if (!frame->buf[0]) + return AVERROR(ENOMEM); + + frame->data[0] = frame->buf[0]->data; + frame->format = AV_PIX_FMT_VULKAN; + frame->width = hwfc->width; + frame->height = hwfc->height; + + return 0; +} + +static int vulkan_transfer_get_formats(AVHWFramesContext *hwfc, + enum AVHWFrameTransferDirection dir, + enum AVPixelFormat **formats) +{ + enum AVPixelFormat *fmts = av_malloc_array(2, sizeof(*fmts)); + if (!fmts) + return AVERROR(ENOMEM); + + fmts[0] = hwfc->sw_format; + fmts[1] = AV_PIX_FMT_NONE; + + *formats = fmts; + return 0; +} + +typedef struct VulkanMapping { + AVVkFrame *frame; + int flags; +} VulkanMapping; + +static void vulkan_unmap_frame(AVHWFramesContext *hwfc, HWMapDescriptor *hwmap) +{ + VulkanMapping *map = hwmap->priv; + AVVulkanDeviceContext *hwctx = hwfc->device_ctx->hwctx; + const int planes = av_pix_fmt_count_planes(hwfc->sw_format); + + /* Check if buffer needs flushing */ + if ((map->flags & AV_HWFRAME_MAP_WRITE) && + !(map->frame->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { + VkResult ret; + VkMappedMemoryRange flush_ranges[AV_NUM_DATA_POINTERS] = { { 0 } }; + + for (int i = 0; i < planes; i++) { + flush_ranges[i].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + flush_ranges[i].memory = map->frame->mem[i]; + flush_ranges[i].size = VK_WHOLE_SIZE; + } + + ret = vkFlushMappedMemoryRanges(hwctx->act_dev, planes, + flush_ranges); + if (ret != VK_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Failed to flush memory: %s\n", + vk_ret2str(ret)); + } + } + + for (int i = 0; i < planes; i++) + vkUnmapMemory(hwctx->act_dev, map->frame->mem[i]); + + av_free(map); +} + +static int vulkan_map_frame_to_mem(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) +{ + VkResult ret; + int err, mapped_mem_count = 0; + AVVkFrame *f = (AVVkFrame *)src->data[0]; + AVVulkanDeviceContext *hwctx = hwfc->device_ctx->hwctx; + const int planes = av_pix_fmt_count_planes(hwfc->sw_format); + + VulkanMapping *map = av_mallocz(sizeof(VulkanMapping)); + if (!map) + return AVERROR(EINVAL); + + if (src->format != AV_PIX_FMT_VULKAN) { + av_log(hwfc, AV_LOG_ERROR, "Cannot map from pixel format %s!\n", + av_get_pix_fmt_name(src->format)); + err = AVERROR(EINVAL); + goto fail; + } + + if (!(f->flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) || + !(f->tiling == VK_IMAGE_TILING_LINEAR)) { + av_log(hwfc, AV_LOG_ERROR, "Unable to map frame, not host visible " + "and linear!\n"); + err = AVERROR(EINVAL); + goto fail; + } + + dst->width = src->width; + dst->height = src->height; + + for (int i = 0; i < planes; i++) { + ret = vkMapMemory(hwctx->act_dev, f->mem[i], 0, + VK_WHOLE_SIZE, 0, (void **)&dst->data[i]); + if (ret != VK_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Failed to map image memory: %s\n", + vk_ret2str(ret)); + err = AVERROR_EXTERNAL; + goto fail; + } + mapped_mem_count++; + } + + /* Check if the memory contents matter */ + if (((flags & AV_HWFRAME_MAP_READ) || !(flags & AV_HWFRAME_MAP_OVERWRITE)) && + !(f->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { + VkMappedMemoryRange map_mem_ranges[AV_NUM_DATA_POINTERS] = { { 0 } }; + for (int i = 0; i < planes; i++) { + map_mem_ranges[i].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + map_mem_ranges[i].size = VK_WHOLE_SIZE; + map_mem_ranges[i].memory = f->mem[i]; + } + + ret = vkInvalidateMappedMemoryRanges(hwctx->act_dev, planes, + map_mem_ranges); + if (ret != VK_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Failed to invalidate memory: %s\n", + vk_ret2str(ret)); + err = AVERROR_EXTERNAL; + goto fail; + } + } + + for (int i = 0; i < planes; i++) { + VkImageSubresource sub = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + }; + VkSubresourceLayout layout; + vkGetImageSubresourceLayout(hwctx->act_dev, f->img[i], &sub, &layout); + dst->linesize[i] = layout.rowPitch; + } + + map->frame = f; + map->flags = flags; + + err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, + &vulkan_unmap_frame, map); + if (err < 0) + goto fail; + + return 0; + +fail: + for (int i = 0; i < mapped_mem_count; i++) + vkUnmapMemory(hwctx->act_dev, f->mem[i]); + + av_free(map); + return err; +} + +#if CONFIG_LIBDRM +static void vulkan_unmap_from(AVHWFramesContext *hwfc, HWMapDescriptor *hwmap) +{ + VulkanMapping *map = hwmap->priv; + AVVulkanDeviceContext *hwctx = hwfc->device_ctx->hwctx; + const int planes = av_pix_fmt_count_planes(hwfc->sw_format); + + for (int i = 0; i < planes; i++) { + vkDestroyImage(hwctx->act_dev, map->frame->img[i], hwctx->alloc); + vkFreeMemory(hwctx->act_dev, map->frame->mem[i], hwctx->alloc); + vkDestroySemaphore(hwctx->act_dev, map->frame->sem[i], hwctx->alloc); + } + + av_freep(&map->frame); +} + +static const struct { + uint32_t drm_fourcc; + VkFormat vk_format; +} vulkan_drm_format_map[] = { + { DRM_FORMAT_R8, VK_FORMAT_R8_UNORM }, + { DRM_FORMAT_R16, VK_FORMAT_R16_UNORM }, + { DRM_FORMAT_GR88, VK_FORMAT_R8G8_UNORM }, + { DRM_FORMAT_RG88, VK_FORMAT_R8G8_UNORM }, + { DRM_FORMAT_GR1616, VK_FORMAT_R16G16_UNORM }, + { DRM_FORMAT_RG1616, VK_FORMAT_R16G16_UNORM }, + { DRM_FORMAT_ARGB8888, VK_FORMAT_B8G8R8A8_UNORM }, + { DRM_FORMAT_XRGB8888, VK_FORMAT_B8G8R8A8_UNORM }, + { DRM_FORMAT_ABGR8888, VK_FORMAT_R8G8B8A8_UNORM }, + { DRM_FORMAT_XBGR8888, VK_FORMAT_R8G8B8A8_UNORM }, +}; + +static inline VkFormat drm_to_vulkan_fmt(uint32_t drm_fourcc) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(vulkan_drm_format_map); i++) + if (vulkan_drm_format_map[i].drm_fourcc == drm_fourcc) + return vulkan_drm_format_map[i].vk_format; + return VK_FORMAT_UNDEFINED; +} + +static int vulkan_map_from_drm_frame_desc(AVHWFramesContext *hwfc, AVVkFrame **frame, + AVDRMFrameDescriptor *desc) +{ + int err = 0; + VkResult ret; + AVVkFrame *f; + int bind_counts = 0; + AVHWDeviceContext *ctx = hwfc->device_ctx; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + VulkanDevicePriv *p = ctx->internal->priv; + VulkanFramesPriv *fp = hwfc->internal->priv; + AVVulkanFramesContext *frames_hwctx = hwfc->hwctx; + const AVPixFmtDescriptor *fmt_desc = av_pix_fmt_desc_get(hwfc->sw_format); + const int has_modifiers = p->extensions & EXT_DRM_MODIFIER_FLAGS; + VkSubresourceLayout plane_data[AV_NUM_DATA_POINTERS] = { 0 }; + VkBindImageMemoryInfo bind_info[AV_NUM_DATA_POINTERS] = { 0 }; + VkBindImagePlaneMemoryInfo plane_info[AV_NUM_DATA_POINTERS] = { 0 }; + VkExternalMemoryHandleTypeFlagBits htype = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; + + VK_LOAD_PFN(hwctx->inst, vkGetMemoryFdPropertiesKHR); + + for (int i = 0; i < desc->nb_layers; i++) { + if (drm_to_vulkan_fmt(desc->layers[i].format) == VK_FORMAT_UNDEFINED) { + av_log(ctx, AV_LOG_ERROR, "Unsupported DMABUF layer format %#08x!\n", + desc->layers[i].format); + return AVERROR(EINVAL); + } + } + + if (!(f = av_vk_frame_alloc())) { + av_log(ctx, AV_LOG_ERROR, "Unable to allocate memory for AVVkFrame!\n"); + err = AVERROR(ENOMEM); + goto fail; + } + + f->tiling = has_modifiers ? VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT : + desc->objects[0].format_modifier == DRM_FORMAT_MOD_LINEAR ? + VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL; + + for (int i = 0; i < desc->nb_layers; i++) { + const int planes = desc->layers[i].nb_planes; + VkImageDrmFormatModifierExplicitCreateInfoEXT drm_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT, + .drmFormatModifier = desc->objects[0].format_modifier, + .drmFormatModifierPlaneCount = planes, + .pPlaneLayouts = (const VkSubresourceLayout *)&plane_data, + }; + + VkExternalMemoryImageCreateInfo einfo = { + .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, + .pNext = has_modifiers ? &drm_info : NULL, + .handleTypes = htype, + }; + + VkSemaphoreCreateInfo sem_spawn = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }; + + const int p_w = i > 0 ? AV_CEIL_RSHIFT(hwfc->width, fmt_desc->log2_chroma_w) : hwfc->width; + const int p_h = i > 0 ? AV_CEIL_RSHIFT(hwfc->height, fmt_desc->log2_chroma_h) : hwfc->height; + + VkImageCreateInfo image_create_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = &einfo, + .imageType = VK_IMAGE_TYPE_2D, + .format = drm_to_vulkan_fmt(desc->layers[i].format), + .extent.width = p_w, + .extent.height = p_h, + .extent.depth = 1, + .mipLevels = 1, + .arrayLayers = 1, + .flags = VK_IMAGE_CREATE_ALIAS_BIT, + .tiling = f->tiling, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, /* specs say so */ + .usage = frames_hwctx->usage, + .samples = VK_SAMPLE_COUNT_1_BIT, + .pQueueFamilyIndices = p->qfs, + .queueFamilyIndexCount = p->num_qfs, + .sharingMode = p->num_qfs > 1 ? VK_SHARING_MODE_CONCURRENT : + VK_SHARING_MODE_EXCLUSIVE, + }; + + for (int j = 0; j < planes; j++) { + plane_data[j].offset = desc->layers[i].planes[j].offset; + plane_data[j].rowPitch = desc->layers[i].planes[j].pitch; + plane_data[j].size = 0; /* The specs say so for all 3 */ + plane_data[j].arrayPitch = 0; + plane_data[j].depthPitch = 0; + } + + /* Create image */ + ret = vkCreateImage(hwctx->act_dev, &image_create_info, + hwctx->alloc, &f->img[i]); + if (ret != VK_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Image creation failure: %s\n", + vk_ret2str(ret)); + err = AVERROR(EINVAL); + goto fail; + } + + ret = vkCreateSemaphore(hwctx->act_dev, &sem_spawn, + hwctx->alloc, &f->sem[i]); + if (ret != VK_SUCCESS) { + av_log(hwctx, AV_LOG_ERROR, "Failed to create semaphore: %s\n", + vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + /* We'd import a semaphore onto the one we created using + * vkImportSemaphoreFdKHR but unfortunately neither DRM nor VAAPI + * offer us anything we could import and sync with, so instead + * just signal the semaphore we created. */ + + f->layout[i] = image_create_info.initialLayout; + f->access[i] = 0x0; + } + + for (int i = 0; i < desc->nb_objects; i++) { + int use_ded_mem = 0; + VkMemoryFdPropertiesKHR fdmp = { + .sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR, + }; + VkMemoryRequirements req = { + .size = desc->objects[i].size, + }; + VkImportMemoryFdInfoKHR idesc = { + .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, + .handleType = htype, + .fd = dup(desc->objects[i].fd), + }; + VkMemoryDedicatedAllocateInfo ded_alloc = { + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, + .pNext = &idesc, + }; + + ret = pfn_vkGetMemoryFdPropertiesKHR(hwctx->act_dev, htype, + idesc.fd, &fdmp); + if (ret != VK_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Failed to get FD properties: %s\n", + vk_ret2str(ret)); + err = AVERROR_EXTERNAL; + close(idesc.fd); + goto fail; + } + + req.memoryTypeBits = fdmp.memoryTypeBits; + + /* Dedicated allocation only makes sense if there's a one to one mapping + * between images and the memory backing them, so only check in this + * case. */ + if (desc->nb_layers == desc->nb_objects) { + VkImageMemoryRequirementsInfo2 req_desc = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, + .image = f->img[i], + }; + VkMemoryDedicatedRequirements ded_req = { + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, + }; + VkMemoryRequirements2 req2 = { + .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, + .pNext = &ded_req, + }; + + vkGetImageMemoryRequirements2(hwctx->act_dev, &req_desc, &req2); + + use_ded_mem = ded_req.prefersDedicatedAllocation | + ded_req.requiresDedicatedAllocation; + if (use_ded_mem) + ded_alloc.image = f->img[i]; + } + + err = alloc_mem(ctx, &req, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + use_ded_mem ? &ded_alloc : ded_alloc.pNext, + &f->flags, &f->mem[i]); + if (err) { + close(idesc.fd); + return err; + } + + f->size[i] = desc->objects[i].size; + } + + for (int i = 0; i < desc->nb_layers; i++) { + const int planes = desc->layers[i].nb_planes; + const int signal_p = has_modifiers && (planes > 1); + for (int j = 0; j < planes; j++) { + VkImageAspectFlagBits aspect = j == 0 ? VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT : + j == 1 ? VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT : + VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT; + + plane_info[bind_counts].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO; + plane_info[bind_counts].planeAspect = aspect; + + bind_info[bind_counts].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO; + bind_info[bind_counts].pNext = signal_p ? &plane_info[bind_counts] : NULL; + bind_info[bind_counts].image = f->img[i]; + bind_info[bind_counts].memory = f->mem[desc->layers[i].planes[j].object_index]; + bind_info[bind_counts].memoryOffset = desc->layers[i].planes[j].offset; + bind_counts++; + } + } + + /* Bind the allocated memory to the images */ + ret = vkBindImageMemory2(hwctx->act_dev, bind_counts, bind_info); + if (ret != VK_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed to bind memory: %s\n", + vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + /* NOTE: This is completely uneccesary and unneeded once we can import + * semaphores from DRM. Otherwise we have to activate the semaphores. + * We're reusing the exec context that's also used for uploads/downloads. */ + err = prepare_frame(hwfc, &fp->conv_ctx, f, PREP_MODE_RO_SHADER); + if (err) + goto fail; + + *frame = f; + + return 0; + +fail: + for (int i = 0; i < desc->nb_layers; i++) { + vkDestroyImage(hwctx->act_dev, f->img[i], hwctx->alloc); + vkDestroySemaphore(hwctx->act_dev, f->sem[i], hwctx->alloc); + } + for (int i = 0; i < desc->nb_objects; i++) + vkFreeMemory(hwctx->act_dev, f->mem[i], hwctx->alloc); + + av_free(f); + + return err; +} + +static int vulkan_map_from_drm(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) +{ + int err = 0; + AVVkFrame *f; + VulkanMapping *map = NULL; + + err = vulkan_map_from_drm_frame_desc(hwfc, &f, + (AVDRMFrameDescriptor *)src->data[0]); + if (err) + return err; + + /* The unmapping function will free this */ + dst->data[0] = (uint8_t *)f; + dst->width = src->width; + dst->height = src->height; + + map = av_mallocz(sizeof(VulkanMapping)); + if (!map) + goto fail; + + map->frame = f; + map->flags = flags; + + err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src, + &vulkan_unmap_from, map); + if (err < 0) + goto fail; + + av_log(hwfc, AV_LOG_DEBUG, "Mapped DRM object to Vulkan!\n"); + + return 0; + +fail: + vulkan_frame_free(hwfc->device_ctx->hwctx, (uint8_t *)f); + av_free(map); + return err; +} + +#if CONFIG_VAAPI +static int vulkan_map_from_vaapi(AVHWFramesContext *dst_fc, + AVFrame *dst, const AVFrame *src, + int flags) +{ + int err; + AVFrame *tmp = av_frame_alloc(); + AVHWFramesContext *vaapi_fc = (AVHWFramesContext*)src->hw_frames_ctx->data; + AVVAAPIDeviceContext *vaapi_ctx = vaapi_fc->device_ctx->hwctx; + VASurfaceID surface_id = (VASurfaceID)(uintptr_t)src->data[3]; + + if (!tmp) + return AVERROR(ENOMEM); + + /* We have to sync since like the previous comment said, no semaphores */ + vaSyncSurface(vaapi_ctx->display, surface_id); + + tmp->format = AV_PIX_FMT_DRM_PRIME; + + err = av_hwframe_map(tmp, src, flags); + if (err < 0) + goto fail; + + err = vulkan_map_from_drm(dst_fc, dst, tmp, flags); + if (err < 0) + goto fail; + + err = ff_hwframe_map_replace(dst, src); + +fail: + av_frame_free(&tmp); + return err; +} +#endif +#endif + +#if CONFIG_CUDA +static int vulkan_export_to_cuda(AVHWFramesContext *hwfc, + AVBufferRef *cuda_hwfc, + const AVFrame *frame) +{ + int err; + VkResult ret; + AVVkFrame *dst_f; + AVVkFrameInternal *dst_int; + AVHWDeviceContext *ctx = hwfc->device_ctx; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + const int planes = av_pix_fmt_count_planes(hwfc->sw_format); + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(hwfc->sw_format); + VK_LOAD_PFN(hwctx->inst, vkGetMemoryFdKHR); + VK_LOAD_PFN(hwctx->inst, vkGetSemaphoreFdKHR); + + AVHWFramesContext *cuda_fc = (AVHWFramesContext*)cuda_hwfc->data; + AVHWDeviceContext *cuda_cu = cuda_fc->device_ctx; + AVCUDADeviceContext *cuda_dev = cuda_cu->hwctx; + AVCUDADeviceContextInternal *cu_internal = cuda_dev->internal; + CudaFunctions *cu = cu_internal->cuda_dl; + CUarray_format cufmt = desc->comp[0].depth > 8 ? CU_AD_FORMAT_UNSIGNED_INT16 : + CU_AD_FORMAT_UNSIGNED_INT8; + + dst_f = (AVVkFrame *)frame->data[0]; + + dst_int = dst_f->internal; + if (!dst_int || !dst_int->cuda_fc_ref) { + if (!dst_f->internal) + dst_f->internal = dst_int = av_mallocz(sizeof(*dst_f->internal)); + + if (!dst_int) { + err = AVERROR(ENOMEM); + goto fail; + } + + dst_int->cuda_fc_ref = av_buffer_ref(cuda_hwfc); + if (!dst_int->cuda_fc_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + + for (int i = 0; i < planes; i++) { + CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC tex_desc = { + .offset = 0, + .arrayDesc = { + .Width = i > 0 ? AV_CEIL_RSHIFT(hwfc->width, desc->log2_chroma_w) + : hwfc->width, + .Height = i > 0 ? AV_CEIL_RSHIFT(hwfc->height, desc->log2_chroma_h) + : hwfc->height, + .Depth = 0, + .Format = cufmt, + .NumChannels = 1 + ((planes == 2) && i), + .Flags = 0, + }, + .numLevels = 1, + }; + CUDA_EXTERNAL_MEMORY_HANDLE_DESC ext_desc = { + .type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD, + .size = dst_f->size[i], + }; + VkMemoryGetFdInfoKHR export_info = { + .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, + .memory = dst_f->mem[i], + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR, + }; + VkSemaphoreGetFdInfoKHR sem_export = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR, + .semaphore = dst_f->sem[i], + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, + }; + CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC ext_sem_desc = { + .type = CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD, + }; + + ret = pfn_vkGetMemoryFdKHR(hwctx->act_dev, &export_info, + &ext_desc.handle.fd); + if (ret != VK_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Unable to export the image as a FD!\n"); + err = AVERROR_EXTERNAL; + goto fail; + } + + ret = CHECK_CU(cu->cuImportExternalMemory(&dst_int->ext_mem[i], &ext_desc)); + if (ret < 0) { + err = AVERROR_EXTERNAL; + goto fail; + } + + ret = CHECK_CU(cu->cuExternalMemoryGetMappedMipmappedArray(&dst_int->cu_mma[i], + dst_int->ext_mem[i], + &tex_desc)); + if (ret < 0) { + err = AVERROR_EXTERNAL; + goto fail; + } + + ret = CHECK_CU(cu->cuMipmappedArrayGetLevel(&dst_int->cu_array[i], + dst_int->cu_mma[i], 0)); + if (ret < 0) { + err = AVERROR_EXTERNAL; + goto fail; + } + + ret = pfn_vkGetSemaphoreFdKHR(hwctx->act_dev, &sem_export, + &ext_sem_desc.handle.fd); + if (ret != VK_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed to export semaphore: %s\n", + vk_ret2str(ret)); + err = AVERROR_EXTERNAL; + goto fail; + } + + ret = CHECK_CU(cu->cuImportExternalSemaphore(&dst_int->cu_sem[i], + &ext_sem_desc)); + if (ret < 0) { + err = AVERROR_EXTERNAL; + goto fail; + } + } + } + + return 0; + +fail: + return err; +} + +static int vulkan_transfer_data_from_cuda(AVHWFramesContext *hwfc, + AVFrame *dst, const AVFrame *src) +{ + int err; + VkResult ret; + CUcontext dummy; + AVVkFrame *dst_f; + AVVkFrameInternal *dst_int; + const int planes = av_pix_fmt_count_planes(hwfc->sw_format); + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(hwfc->sw_format); + + AVHWFramesContext *cuda_fc = (AVHWFramesContext*)src->hw_frames_ctx->data; + AVHWDeviceContext *cuda_cu = cuda_fc->device_ctx; + AVCUDADeviceContext *cuda_dev = cuda_cu->hwctx; + AVCUDADeviceContextInternal *cu_internal = cuda_dev->internal; + CudaFunctions *cu = cu_internal->cuda_dl; + CUDA_EXTERNAL_SEMAPHORE_WAIT_PARAMS s_w_par[AV_NUM_DATA_POINTERS] = { 0 }; + CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS s_s_par[AV_NUM_DATA_POINTERS] = { 0 }; + + ret = CHECK_CU(cu->cuCtxPushCurrent(cuda_dev->cuda_ctx)); + if (ret < 0) { + err = AVERROR_EXTERNAL; + goto fail; + } + + dst_f = (AVVkFrame *)dst->data[0]; + + ret = vulkan_export_to_cuda(hwfc, src->hw_frames_ctx, dst); + if (ret < 0) { + goto fail; + } + dst_int = dst_f->internal; + + ret = CHECK_CU(cu->cuWaitExternalSemaphoresAsync(dst_int->cu_sem, s_w_par, + planes, cuda_dev->stream)); + if (ret < 0) { + err = AVERROR_EXTERNAL; + goto fail; + } + + for (int i = 0; i < planes; i++) { + CUDA_MEMCPY2D cpy = { + .srcMemoryType = CU_MEMORYTYPE_DEVICE, + .srcDevice = (CUdeviceptr)src->data[i], + .srcPitch = src->linesize[i], + .srcY = 0, + + .dstMemoryType = CU_MEMORYTYPE_ARRAY, + .dstArray = dst_int->cu_array[i], + .WidthInBytes = (i > 0 ? AV_CEIL_RSHIFT(hwfc->width, desc->log2_chroma_w) + : hwfc->width) * desc->comp[i].step, + .Height = i > 0 ? AV_CEIL_RSHIFT(hwfc->height, desc->log2_chroma_h) + : hwfc->height, + }; + + ret = CHECK_CU(cu->cuMemcpy2DAsync(&cpy, cuda_dev->stream)); + if (ret < 0) { + err = AVERROR_EXTERNAL; + goto fail; + } + } + + ret = CHECK_CU(cu->cuSignalExternalSemaphoresAsync(dst_int->cu_sem, s_s_par, + planes, cuda_dev->stream)); + if (ret < 0) { + err = AVERROR_EXTERNAL; + goto fail; + } + + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + + av_log(hwfc, AV_LOG_VERBOSE, "Transfered CUDA image to Vulkan!\n"); + + return 0; + +fail: + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + vulkan_free_internal(dst_int); + dst_f->internal = NULL; + av_buffer_unref(&dst->buf[0]); + return err; +} +#endif + +static int vulkan_map_to(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) +{ + av_unused VulkanDevicePriv *p = hwfc->device_ctx->internal->priv; + + switch (src->format) { +#if CONFIG_LIBDRM +#if CONFIG_VAAPI + case AV_PIX_FMT_VAAPI: + if (p->extensions & EXT_EXTERNAL_DMABUF_MEMORY) + return vulkan_map_from_vaapi(hwfc, dst, src, flags); +#endif + case AV_PIX_FMT_DRM_PRIME: + if (p->extensions & EXT_EXTERNAL_DMABUF_MEMORY) + return vulkan_map_from_drm(hwfc, dst, src, flags); +#endif + default: + return AVERROR(ENOSYS); + } +} + +#if CONFIG_LIBDRM +typedef struct VulkanDRMMapping { + AVDRMFrameDescriptor drm_desc; + AVVkFrame *source; +} VulkanDRMMapping; + +static void vulkan_unmap_to_drm(AVHWFramesContext *hwfc, HWMapDescriptor *hwmap) +{ + AVDRMFrameDescriptor *drm_desc = hwmap->priv; + + for (int i = 0; i < drm_desc->nb_objects; i++) + close(drm_desc->objects[i].fd); + + av_free(drm_desc); +} + +static inline uint32_t vulkan_fmt_to_drm(VkFormat vkfmt) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(vulkan_drm_format_map); i++) + if (vulkan_drm_format_map[i].vk_format == vkfmt) + return vulkan_drm_format_map[i].drm_fourcc; + return DRM_FORMAT_INVALID; +} + +static int vulkan_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) +{ + int err = 0; + VkResult ret; + AVVkFrame *f = (AVVkFrame *)src->data[0]; + VulkanDevicePriv *p = hwfc->device_ctx->internal->priv; + VulkanFramesPriv *fp = hwfc->internal->priv; + AVVulkanDeviceContext *hwctx = hwfc->device_ctx->hwctx; + const int planes = av_pix_fmt_count_planes(hwfc->sw_format); + VK_LOAD_PFN(hwctx->inst, vkGetMemoryFdKHR); + VkImageDrmFormatModifierPropertiesEXT drm_mod = { + .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT, + }; + + AVDRMFrameDescriptor *drm_desc = av_mallocz(sizeof(*drm_desc)); + if (!drm_desc) + return AVERROR(ENOMEM); + + err = prepare_frame(hwfc, &fp->conv_ctx, f, PREP_MODE_EXTERNAL_EXPORT); + if (err < 0) + goto end; + + err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, &vulkan_unmap_to_drm, drm_desc); + if (err < 0) + goto end; + + if (p->extensions & EXT_DRM_MODIFIER_FLAGS) { + VK_LOAD_PFN(hwctx->inst, vkGetImageDrmFormatModifierPropertiesEXT); + ret = pfn_vkGetImageDrmFormatModifierPropertiesEXT(hwctx->act_dev, f->img[0], + &drm_mod); + if (ret != VK_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Failed to retrieve DRM format modifier!\n"); + err = AVERROR_EXTERNAL; + goto end; + } + } + + for (int i = 0; (i < planes) && (f->mem[i]); i++) { + VkMemoryGetFdInfoKHR export_info = { + .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, + .memory = f->mem[i], + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + }; + + ret = pfn_vkGetMemoryFdKHR(hwctx->act_dev, &export_info, + &drm_desc->objects[i].fd); + if (ret != VK_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Unable to export the image as a FD!\n"); + err = AVERROR_EXTERNAL; + goto end; + } + + drm_desc->nb_objects++; + drm_desc->objects[i].size = f->size[i]; + drm_desc->objects[i].format_modifier = drm_mod.drmFormatModifier; + } + + drm_desc->nb_layers = planes; + for (int i = 0; i < drm_desc->nb_layers; i++) { + VkSubresourceLayout layout; + VkImageSubresource sub = { + .aspectMask = p->extensions & EXT_DRM_MODIFIER_FLAGS ? + VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT : + VK_IMAGE_ASPECT_COLOR_BIT, + }; + VkFormat plane_vkfmt = av_vkfmt_from_pixfmt(hwfc->sw_format)[i]; + + drm_desc->layers[i].format = vulkan_fmt_to_drm(plane_vkfmt); + drm_desc->layers[i].nb_planes = 1; + + if (drm_desc->layers[i].format == DRM_FORMAT_INVALID) { + av_log(hwfc, AV_LOG_ERROR, "Cannot map to DRM layer, unsupported!\n"); + err = AVERROR_PATCHWELCOME; + goto end; + } + + drm_desc->layers[i].planes[0].object_index = FFMIN(i, drm_desc->nb_objects - 1); + + if (f->tiling == VK_IMAGE_TILING_OPTIMAL) + continue; + + vkGetImageSubresourceLayout(hwctx->act_dev, f->img[i], &sub, &layout); + drm_desc->layers[i].planes[0].offset = layout.offset; + drm_desc->layers[i].planes[0].pitch = layout.rowPitch; + } + + dst->width = src->width; + dst->height = src->height; + dst->data[0] = (uint8_t *)drm_desc; + + av_log(hwfc, AV_LOG_VERBOSE, "Mapped AVVkFrame to a DRM object!\n"); + + return 0; + +end: + av_free(drm_desc); + return err; +} + +#if CONFIG_VAAPI +static int vulkan_map_to_vaapi(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) +{ + int err; + AVFrame *tmp = av_frame_alloc(); + if (!tmp) + return AVERROR(ENOMEM); + + tmp->format = AV_PIX_FMT_DRM_PRIME; + + err = vulkan_map_to_drm(hwfc, tmp, src, flags); + if (err < 0) + goto fail; + + err = av_hwframe_map(dst, tmp, flags); + if (err < 0) + goto fail; + + err = ff_hwframe_map_replace(dst, src); + +fail: + av_frame_free(&tmp); + return err; +} +#endif +#endif + +static int vulkan_map_from(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) +{ + av_unused VulkanDevicePriv *p = hwfc->device_ctx->internal->priv; + + switch (dst->format) { +#if CONFIG_LIBDRM + case AV_PIX_FMT_DRM_PRIME: + if (p->extensions & EXT_EXTERNAL_DMABUF_MEMORY) + return vulkan_map_to_drm(hwfc, dst, src, flags); +#if CONFIG_VAAPI + case AV_PIX_FMT_VAAPI: + if (p->extensions & EXT_EXTERNAL_DMABUF_MEMORY) + return vulkan_map_to_vaapi(hwfc, dst, src, flags); +#endif +#endif + default: + return vulkan_map_frame_to_mem(hwfc, dst, src, flags); + } +} + +typedef struct ImageBuffer { + VkBuffer buf; + VkDeviceMemory mem; + VkMemoryPropertyFlagBits flags; + int mapped_mem; +} ImageBuffer; + +static void free_buf(void *opaque, uint8_t *data) +{ + AVHWDeviceContext *ctx = opaque; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + ImageBuffer *vkbuf = (ImageBuffer *)data; + + if (vkbuf->buf) + vkDestroyBuffer(hwctx->act_dev, vkbuf->buf, hwctx->alloc); + if (vkbuf->mem) + vkFreeMemory(hwctx->act_dev, vkbuf->mem, hwctx->alloc); + + av_free(data); +} + +static int create_buf(AVHWDeviceContext *ctx, AVBufferRef **buf, size_t imp_size, + int height, int *stride, VkBufferUsageFlags usage, + VkMemoryPropertyFlagBits flags, void *create_pnext, + void *alloc_pnext) +{ + int err; + VkResult ret; + int use_ded_mem; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + VulkanDevicePriv *p = ctx->internal->priv; + + VkBufferCreateInfo buf_spawn = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = create_pnext, + .usage = usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + }; + + VkBufferMemoryRequirementsInfo2 req_desc = { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2, + }; + VkMemoryDedicatedAllocateInfo ded_alloc = { + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, + .pNext = alloc_pnext, + }; + VkMemoryDedicatedRequirements ded_req = { + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, + }; + VkMemoryRequirements2 req = { + .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, + .pNext = &ded_req, + }; + + ImageBuffer *vkbuf = av_mallocz(sizeof(*vkbuf)); + if (!vkbuf) + return AVERROR(ENOMEM); + + vkbuf->mapped_mem = !!imp_size; + + if (!vkbuf->mapped_mem) { + *stride = FFALIGN(*stride, p->props.properties.limits.optimalBufferCopyRowPitchAlignment); + buf_spawn.size = height*(*stride); + buf_spawn.size = FFALIGN(buf_spawn.size, p->props.properties.limits.minMemoryMapAlignment); + } else { + buf_spawn.size = imp_size; + } + + ret = vkCreateBuffer(hwctx->act_dev, &buf_spawn, NULL, &vkbuf->buf); + if (ret != VK_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed to create buffer: %s\n", + vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + req_desc.buffer = vkbuf->buf; + + vkGetBufferMemoryRequirements2(hwctx->act_dev, &req_desc, &req); + + /* In case the implementation prefers/requires dedicated allocation */ + use_ded_mem = ded_req.prefersDedicatedAllocation | + ded_req.requiresDedicatedAllocation; + if (use_ded_mem) + ded_alloc.buffer = vkbuf->buf; + + err = alloc_mem(ctx, &req.memoryRequirements, flags, + use_ded_mem ? &ded_alloc : (void *)ded_alloc.pNext, + &vkbuf->flags, &vkbuf->mem); + if (err) + return err; + + ret = vkBindBufferMemory(hwctx->act_dev, vkbuf->buf, vkbuf->mem, 0); + if (ret != VK_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed to bind memory to buffer: %s\n", + vk_ret2str(ret)); + free_buf(ctx, (uint8_t *)vkbuf); + return AVERROR_EXTERNAL; + } + + *buf = av_buffer_create((uint8_t *)vkbuf, sizeof(*vkbuf), free_buf, ctx, 0); + if (!(*buf)) { + free_buf(ctx, (uint8_t *)vkbuf); + return AVERROR(ENOMEM); + } + + return 0; +} + +/* Skips mapping of host mapped buffers but still invalidates them */ +static int map_buffers(AVHWDeviceContext *ctx, AVBufferRef **bufs, uint8_t *mem[], + int nb_buffers, int invalidate) +{ + VkResult ret; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + VkMappedMemoryRange invalidate_ctx[AV_NUM_DATA_POINTERS]; + int invalidate_count = 0; + + for (int i = 0; i < nb_buffers; i++) { + ImageBuffer *vkbuf = (ImageBuffer *)bufs[i]->data; + if (vkbuf->mapped_mem) + continue; + + ret = vkMapMemory(hwctx->act_dev, vkbuf->mem, 0, + VK_WHOLE_SIZE, 0, (void **)&mem[i]); + if (ret != VK_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed to map buffer memory: %s\n", + vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + } + + if (!invalidate) + return 0; + + for (int i = 0; i < nb_buffers; i++) { + ImageBuffer *vkbuf = (ImageBuffer *)bufs[i]->data; + const VkMappedMemoryRange ival_buf = { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = vkbuf->mem, + .size = VK_WHOLE_SIZE, + }; + if (vkbuf->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) + continue; + invalidate_ctx[invalidate_count++] = ival_buf; + } + + if (invalidate_count) { + ret = vkInvalidateMappedMemoryRanges(hwctx->act_dev, invalidate_count, + invalidate_ctx); + if (ret != VK_SUCCESS) + av_log(ctx, AV_LOG_WARNING, "Failed to invalidate memory: %s\n", + vk_ret2str(ret)); + } + + return 0; +} + +static int unmap_buffers(AVHWDeviceContext *ctx, AVBufferRef **bufs, + int nb_buffers, int flush) +{ + int err = 0; + VkResult ret; + AVVulkanDeviceContext *hwctx = ctx->hwctx; + VkMappedMemoryRange flush_ctx[AV_NUM_DATA_POINTERS]; + int flush_count = 0; + + if (flush) { + for (int i = 0; i < nb_buffers; i++) { + ImageBuffer *vkbuf = (ImageBuffer *)bufs[i]->data; + const VkMappedMemoryRange flush_buf = { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = vkbuf->mem, + .size = VK_WHOLE_SIZE, + }; + if (vkbuf->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) + continue; + flush_ctx[flush_count++] = flush_buf; + } + } + + if (flush_count) { + ret = vkFlushMappedMemoryRanges(hwctx->act_dev, flush_count, flush_ctx); + if (ret != VK_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed to flush memory: %s\n", + vk_ret2str(ret)); + err = AVERROR_EXTERNAL; /* We still want to try to unmap them */ + } + } + + for (int i = 0; i < nb_buffers; i++) { + ImageBuffer *vkbuf = (ImageBuffer *)bufs[i]->data; + if (vkbuf->mapped_mem) + continue; + + vkUnmapMemory(hwctx->act_dev, vkbuf->mem); + } + + return err; +} + +static int transfer_image_buf(AVHWFramesContext *hwfc, const AVFrame *f, + AVBufferRef **bufs, const int *buf_stride, int w, + int h, enum AVPixelFormat pix_fmt, int to_buf) +{ + int err; + AVVkFrame *frame = (AVVkFrame *)f->data[0]; + VulkanFramesPriv *fp = hwfc->internal->priv; + + int bar_num = 0; + VkPipelineStageFlagBits sem_wait_dst[AV_NUM_DATA_POINTERS]; + + const int planes = av_pix_fmt_count_planes(pix_fmt); + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); + + VkImageMemoryBarrier img_bar[AV_NUM_DATA_POINTERS] = { 0 }; + VulkanExecCtx *ectx = to_buf ? &fp->download_ctx : &fp->upload_ctx; + VkCommandBuffer cmd_buf = get_buf_exec_ctx(hwfc, ectx); + + VkSubmitInfo s_info = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pSignalSemaphores = frame->sem, + .pWaitSemaphores = frame->sem, + .pWaitDstStageMask = sem_wait_dst, + .signalSemaphoreCount = planes, + .waitSemaphoreCount = planes, + }; + + if ((err = wait_start_exec_ctx(hwfc, ectx))) + return err; + + /* Change the image layout to something more optimal for transfers */ + for (int i = 0; i < planes; i++) { + VkImageLayout new_layout = to_buf ? VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL : + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + VkAccessFlags new_access = to_buf ? VK_ACCESS_TRANSFER_READ_BIT : + VK_ACCESS_TRANSFER_WRITE_BIT; + + sem_wait_dst[i] = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + + /* If the layout matches and we have read access skip the barrier */ + if ((frame->layout[i] == new_layout) && (frame->access[i] & new_access)) + continue; + + img_bar[bar_num].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + img_bar[bar_num].srcAccessMask = 0x0; + img_bar[bar_num].dstAccessMask = new_access; + img_bar[bar_num].oldLayout = frame->layout[i]; + img_bar[bar_num].newLayout = new_layout; + img_bar[bar_num].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + img_bar[bar_num].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + img_bar[bar_num].image = frame->img[i]; + img_bar[bar_num].subresourceRange.levelCount = 1; + img_bar[bar_num].subresourceRange.layerCount = 1; + img_bar[bar_num].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + + frame->layout[i] = img_bar[bar_num].newLayout; + frame->access[i] = img_bar[bar_num].dstAccessMask; + + bar_num++; + } + + if (bar_num) + vkCmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + 0, NULL, 0, NULL, bar_num, img_bar); + + /* Schedule a copy for each plane */ + for (int i = 0; i < planes; i++) { + ImageBuffer *vkbuf = (ImageBuffer *)bufs[i]->data; + const int p_w = i > 0 ? AV_CEIL_RSHIFT(w, desc->log2_chroma_w) : w; + const int p_h = i > 0 ? AV_CEIL_RSHIFT(h, desc->log2_chroma_h) : h; + VkBufferImageCopy buf_reg = { + .bufferOffset = 0, + /* Buffer stride isn't in bytes, it's in samples, the implementation + * uses the image's VkFormat to know how many bytes per sample + * the buffer has. So we have to convert by dividing. Stupid. + * Won't work with YUVA or other planar formats with alpha. */ + .bufferRowLength = buf_stride[i] / desc->comp[i].step, + .bufferImageHeight = p_h, + .imageSubresource.layerCount = 1, + .imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .imageOffset = { 0, 0, 0, }, + .imageExtent = { p_w, p_h, 1, }, + }; + + if (to_buf) + vkCmdCopyImageToBuffer(cmd_buf, frame->img[i], frame->layout[i], + vkbuf->buf, 1, &buf_reg); + else + vkCmdCopyBufferToImage(cmd_buf, vkbuf->buf, frame->img[i], + frame->layout[i], 1, &buf_reg); + } + + /* When uploading, do this asynchronously if the source is refcounted by + * keeping the buffers as a submission dependency. + * The hwcontext is guaranteed to not be freed until all frames are freed + * in the frames_unint function. + * When downloading to buffer, do this synchronously and wait for the + * queue submission to finish executing */ + if (!to_buf) { + int ref; + for (ref = 0; ref < AV_NUM_DATA_POINTERS; ref++) { + if (!f->buf[ref]) + break; + if ((err = add_buf_dep_exec_ctx(hwfc, ectx, &f->buf[ref], 1))) + return err; + } + if (ref && (err = add_buf_dep_exec_ctx(hwfc, ectx, bufs, planes))) + return err; + return submit_exec_ctx(hwfc, ectx, &s_info, !ref); + } else { + return submit_exec_ctx(hwfc, ectx, &s_info, 1); + } +} + +static int vulkan_transfer_data_from_mem(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src) +{ + int err = 0; + AVFrame tmp; + AVVkFrame *f = (AVVkFrame *)dst->data[0]; + AVHWDeviceContext *dev_ctx = hwfc->device_ctx; + AVBufferRef *bufs[AV_NUM_DATA_POINTERS] = { 0 }; + const int planes = av_pix_fmt_count_planes(src->format); + int log2_chroma = av_pix_fmt_desc_get(src->format)->log2_chroma_h; + VulkanDevicePriv *p = hwfc->device_ctx->internal->priv; + int host_mapped[AV_NUM_DATA_POINTERS] = { 0 }; + int map_host = p->extensions & EXT_EXTERNAL_HOST_MEMORY; + + if ((src->format != AV_PIX_FMT_NONE && !av_vkfmt_from_pixfmt(src->format))) { + av_log(hwfc, AV_LOG_ERROR, "Unsupported source pixel format!\n"); + return AVERROR(EINVAL); + } + + if (src->width > hwfc->width || src->height > hwfc->height) + return AVERROR(EINVAL); + + /* For linear, host visiable images */ + if (f->tiling == VK_IMAGE_TILING_LINEAR && + f->flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + AVFrame *map = av_frame_alloc(); + if (!map) + return AVERROR(ENOMEM); + map->format = src->format; + + err = vulkan_map_frame_to_mem(hwfc, map, dst, AV_HWFRAME_MAP_WRITE); + if (err) + return err; + + err = av_frame_copy(map, src); + av_frame_free(&map); + return err; + } + + /* Create buffers */ + for (int i = 0; i < planes; i++) { + int h = src->height; + int p_height = i > 0 ? AV_CEIL_RSHIFT(h, log2_chroma) : h; + size_t p_size = FFABS(src->linesize[i]) * p_height; + + VkImportMemoryHostPointerInfoEXT import_desc = { + .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, + .pHostPointer = src->data[i], + }; + + /* We can only map images with positive stride and alignment appropriate + * for the device. */ + host_mapped[i] = map_host && src->linesize[i] > 0 && + !(p_size % p->hprops.minImportedHostPointerAlignment) && + !(((uintptr_t)import_desc.pHostPointer) % + p->hprops.minImportedHostPointerAlignment); + p_size = host_mapped[i] ? p_size : 0; + + tmp.linesize[i] = FFABS(src->linesize[i]); + err = create_buf(dev_ctx, &bufs[i], p_size, p_height, &tmp.linesize[i], + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, NULL, + host_mapped[i] ? &import_desc : NULL); + if (err) + goto end; + } + + /* Map, copy image to buffer, unmap */ + if ((err = map_buffers(dev_ctx, bufs, tmp.data, planes, 0))) + goto end; + + for (int i = 0; i < planes; i++) { + int h = src->height; + int p_height = i > 0 ? AV_CEIL_RSHIFT(h, log2_chroma) : h; + + if (host_mapped[i]) + continue; + + av_image_copy_plane(tmp.data[i], tmp.linesize[i], + (const uint8_t *)src->data[i], src->linesize[i], + FFMIN(tmp.linesize[i], FFABS(src->linesize[i])), + p_height); + } + + if ((err = unmap_buffers(dev_ctx, bufs, planes, 1))) + goto end; + + /* Copy buffers to image */ + err = transfer_image_buf(hwfc, dst, bufs, tmp.linesize, + src->width, src->height, src->format, 0); + +end: + for (int i = 0; i < planes; i++) + av_buffer_unref(&bufs[i]); + + return err; +} + +static int vulkan_transfer_data_to(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src) +{ + av_unused VulkanDevicePriv *p = hwfc->device_ctx->internal->priv; + + switch (src->format) { +#if CONFIG_CUDA + case AV_PIX_FMT_CUDA: + if ((p->extensions & EXT_EXTERNAL_FD_MEMORY) && + (p->extensions & EXT_EXTERNAL_FD_SEM)) + return vulkan_transfer_data_from_cuda(hwfc, dst, src); +#endif + default: + if (src->hw_frames_ctx) + return AVERROR(ENOSYS); + else + return vulkan_transfer_data_from_mem(hwfc, dst, src); + } +} + +#if CONFIG_CUDA +static int vulkan_transfer_data_to_cuda(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src) +{ + int err; + VkResult ret; + CUcontext dummy; + AVVkFrame *dst_f; + AVVkFrameInternal *dst_int; + const int planes = av_pix_fmt_count_planes(hwfc->sw_format); + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(hwfc->sw_format); + + AVHWFramesContext *cuda_fc = (AVHWFramesContext*)dst->hw_frames_ctx->data; + AVHWDeviceContext *cuda_cu = cuda_fc->device_ctx; + AVCUDADeviceContext *cuda_dev = cuda_cu->hwctx; + AVCUDADeviceContextInternal *cu_internal = cuda_dev->internal; + CudaFunctions *cu = cu_internal->cuda_dl; + + ret = CHECK_CU(cu->cuCtxPushCurrent(cuda_dev->cuda_ctx)); + if (ret < 0) { + err = AVERROR_EXTERNAL; + goto fail; + } + + dst_f = (AVVkFrame *)src->data[0]; + + err = vulkan_export_to_cuda(hwfc, dst->hw_frames_ctx, src); + if (err < 0) { + goto fail; + } + + dst_int = dst_f->internal; + + for (int i = 0; i < planes; i++) { + CUDA_MEMCPY2D cpy = { + .dstMemoryType = CU_MEMORYTYPE_DEVICE, + .dstDevice = (CUdeviceptr)dst->data[i], + .dstPitch = dst->linesize[i], + .dstY = 0, + + .srcMemoryType = CU_MEMORYTYPE_ARRAY, + .srcArray = dst_int->cu_array[i], + .WidthInBytes = (i > 0 ? AV_CEIL_RSHIFT(hwfc->width, desc->log2_chroma_w) + : hwfc->width) * desc->comp[i].step, + .Height = i > 0 ? AV_CEIL_RSHIFT(hwfc->height, desc->log2_chroma_h) + : hwfc->height, + }; + + ret = CHECK_CU(cu->cuMemcpy2DAsync(&cpy, cuda_dev->stream)); + if (ret < 0) { + err = AVERROR_EXTERNAL; + goto fail; + } + } + + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + + av_log(hwfc, AV_LOG_VERBOSE, "Transfered Vulkan image to CUDA!\n"); + + return 0; + +fail: + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + vulkan_free_internal(dst_int); + dst_f->internal = NULL; + av_buffer_unref(&dst->buf[0]); + return err; +} +#endif + +static int vulkan_transfer_data_to_mem(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src) +{ + int err = 0; + AVFrame tmp; + AVVkFrame *f = (AVVkFrame *)src->data[0]; + AVHWDeviceContext *dev_ctx = hwfc->device_ctx; + AVBufferRef *bufs[AV_NUM_DATA_POINTERS] = { 0 }; + const int planes = av_pix_fmt_count_planes(dst->format); + int log2_chroma = av_pix_fmt_desc_get(dst->format)->log2_chroma_h; + VulkanDevicePriv *p = hwfc->device_ctx->internal->priv; + int host_mapped[AV_NUM_DATA_POINTERS] = { 0 }; + int map_host = p->extensions & EXT_EXTERNAL_HOST_MEMORY; + + if (dst->width > hwfc->width || dst->height > hwfc->height) + return AVERROR(EINVAL); + + /* For linear, host visiable images */ + if (f->tiling == VK_IMAGE_TILING_LINEAR && + f->flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + AVFrame *map = av_frame_alloc(); + if (!map) + return AVERROR(ENOMEM); + map->format = dst->format; + + err = vulkan_map_frame_to_mem(hwfc, map, src, AV_HWFRAME_MAP_READ); + if (err) + return err; + + err = av_frame_copy(dst, map); + av_frame_free(&map); + return err; + } + + /* Create buffers */ + for (int i = 0; i < planes; i++) { + int h = dst->height; + int p_height = i > 0 ? AV_CEIL_RSHIFT(h, log2_chroma) : h; + size_t p_size = FFABS(dst->linesize[i]) * p_height; + + VkImportMemoryHostPointerInfoEXT import_desc = { + .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, + .pHostPointer = dst->data[i], + }; + + /* We can only map images with positive stride and alignment appropriate + * for the device. */ + host_mapped[i] = map_host && dst->linesize[i] > 0 && + !(p_size % p->hprops.minImportedHostPointerAlignment) && + !(((uintptr_t)import_desc.pHostPointer) % + p->hprops.minImportedHostPointerAlignment); + p_size = host_mapped[i] ? p_size : 0; + + tmp.linesize[i] = FFABS(dst->linesize[i]); + err = create_buf(dev_ctx, &bufs[i], p_size, p_height, + &tmp.linesize[i], VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, NULL, + host_mapped[i] ? &import_desc : NULL); + if (err) + goto end; + } + + /* Copy image to buffer */ + if ((err = transfer_image_buf(hwfc, src, bufs, tmp.linesize, + dst->width, dst->height, dst->format, 1))) + goto end; + + /* Map, copy buffer to frame, unmap */ + if ((err = map_buffers(dev_ctx, bufs, tmp.data, planes, 1))) + goto end; + + for (int i = 0; i < planes; i++) { + int h = dst->height; + int p_height = i > 0 ? AV_CEIL_RSHIFT(h, log2_chroma) : h; + + if (host_mapped[i]) + continue; + + av_image_copy_plane(dst->data[i], dst->linesize[i], + (const uint8_t *)tmp.data[i], tmp.linesize[i], + FFMIN(tmp.linesize[i], FFABS(dst->linesize[i])), + p_height); + } + + err = unmap_buffers(dev_ctx, bufs, planes, 0); + +end: + for (int i = 0; i < planes; i++) + av_buffer_unref(&bufs[i]); + + return err; +} + +static int vulkan_transfer_data_from(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src) +{ + av_unused VulkanDevicePriv *p = hwfc->device_ctx->internal->priv; + + switch (dst->format) { +#if CONFIG_CUDA + case AV_PIX_FMT_CUDA: + if ((p->extensions & EXT_EXTERNAL_FD_MEMORY) && + (p->extensions & EXT_EXTERNAL_FD_SEM)) + return vulkan_transfer_data_to_cuda(hwfc, dst, src); +#endif + default: + if (dst->hw_frames_ctx) + return AVERROR(ENOSYS); + else + return vulkan_transfer_data_to_mem(hwfc, dst, src); + } +} + +static int vulkan_frames_derive_to(AVHWFramesContext *dst_fc, + AVHWFramesContext *src_fc, int flags) +{ + return vulkan_frames_init(dst_fc); +} + +AVVkFrame *av_vk_frame_alloc(void) +{ + return av_mallocz(sizeof(AVVkFrame)); +} + +const HWContextType ff_hwcontext_type_vulkan = { + .type = AV_HWDEVICE_TYPE_VULKAN, + .name = "Vulkan", + + .device_hwctx_size = sizeof(AVVulkanDeviceContext), + .device_priv_size = sizeof(VulkanDevicePriv), + .frames_hwctx_size = sizeof(AVVulkanFramesContext), + .frames_priv_size = sizeof(VulkanFramesPriv), + + .device_init = &vulkan_device_init, + .device_create = &vulkan_device_create, + .device_derive = &vulkan_device_derive, + + .frames_get_constraints = &vulkan_frames_get_constraints, + .frames_init = vulkan_frames_init, + .frames_get_buffer = vulkan_get_buffer, + .frames_uninit = vulkan_frames_uninit, + + .transfer_get_formats = vulkan_transfer_get_formats, + .transfer_data_to = vulkan_transfer_data_to, + .transfer_data_from = vulkan_transfer_data_from, + + .map_to = vulkan_map_to, + .map_from = vulkan_map_from, + .frames_derive_to = &vulkan_frames_derive_to, + + .pix_fmts = (const enum AVPixelFormat []) { + AV_PIX_FMT_VULKAN, + AV_PIX_FMT_NONE + }, +}; diff --git a/libavutil/hwcontext_vulkan.h b/libavutil/hwcontext_vulkan.h new file mode 100644 index 00000000000..5cbeb8e7efa --- /dev/null +++ b/libavutil/hwcontext_vulkan.h @@ -0,0 +1,204 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 AVUTIL_HWCONTEXT_VULKAN_H +#define AVUTIL_HWCONTEXT_VULKAN_H + +#include + +#include "pixfmt.h" +#include "frame.h" + +/** + * @file + * API-specific header for AV_HWDEVICE_TYPE_VULKAN. + * + * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs + * with the data pointer set to an AVVkFrame. + */ + +/** + * Main Vulkan context, allocated as AVHWDeviceContext.hwctx. + * All of these can be set before init to change what the context uses + */ +typedef struct AVVulkanDeviceContext { + /** + * Custom memory allocator, else NULL + */ + const VkAllocationCallbacks *alloc; + /** + * Vulkan instance. Must be at least version 1.1. + */ + VkInstance inst; + /** + * Physical device + */ + VkPhysicalDevice phys_dev; + /** + * Active device + */ + VkDevice act_dev; + /** + * Queue family index for graphics + * @note av_hwdevice_create() will set all 3 queue indices if unset + * If there is no dedicated queue for compute or transfer operations, + * they will be set to the graphics queue index which can handle both. + * nb_graphics_queues indicates how many queues were enabled for the + * graphics queue (must be at least 1) + */ + int queue_family_index; + int nb_graphics_queues; + /** + * Queue family index to use for transfer operations, and the amount of queues + * enabled. In case there is no dedicated transfer queue, nb_tx_queues + * must be 0 and queue_family_tx_index must be the same as either the graphics + * queue or the compute queue, if available. + */ + int queue_family_tx_index; + int nb_tx_queues; + /** + * Queue family index for compute ops, and the amount of queues enabled. + * In case there are no dedicated compute queues, nb_comp_queues must be + * 0 and its queue family index must be set to the graphics queue. + */ + int queue_family_comp_index; + int nb_comp_queues; + /** + * Enabled instance extensions. + * If supplying your own device context, set this to an array of strings, with + * each entry containing the specified Vulkan extension string to enable. + * Duplicates are possible and accepted. + * If no extensions are enabled, set these fields to NULL, and 0 respectively. + */ + const char * const *enabled_inst_extensions; + int nb_enabled_inst_extensions; + /** + * Enabled device extensions. By default, VK_KHR_external_memory_fd, + * VK_EXT_external_memory_dma_buf, VK_EXT_image_drm_format_modifier, + * VK_KHR_external_semaphore_fd and VK_EXT_external_memory_host are enabled if found. + * If supplying your own device context, these fields takes the same format as + * the above fields, with the same conditions that duplicates are possible + * and accepted, and that NULL and 0 respectively means no extensions are enabled. + */ + const char * const *enabled_dev_extensions; + int nb_enabled_dev_extensions; + /** + * This structure should be set to the set of features that present and enabled + * during device creation. When a device is created by FFmpeg, it will default to + * enabling all that are present of the shaderImageGatherExtended, + * fragmentStoresAndAtomics, shaderInt64 and vertexPipelineStoresAndAtomics features. + */ + VkPhysicalDeviceFeatures2 device_features; +} AVVulkanDeviceContext; + +/** + * Allocated as AVHWFramesContext.hwctx, used to set pool-specific options + */ +typedef struct AVVulkanFramesContext { + /** + * Controls the tiling of allocated frames. + */ + VkImageTiling tiling; + /** + * Defines extra usage of output frames. If left as 0, the following bits + * are set: TRANSFER_SRC, TRANSFER_DST. SAMPLED and STORAGE. + */ + VkImageUsageFlagBits usage; + /** + * Extension data for image creation. + */ + void *create_pnext; + /** + * Extension data for memory allocation. Must have as many entries as + * the number of planes of the sw_format. + * This will be chained to VkExportMemoryAllocateInfo, which is used + * to make all pool images exportable to other APIs if the necessary + * extensions are present in enabled_dev_extensions. + */ + void *alloc_pnext[AV_NUM_DATA_POINTERS]; +} AVVulkanFramesContext; + +/* + * Frame structure, the VkFormat of the image will always match + * the pool's sw_format. + * All frames, imported or allocated, will be created with the + * VK_IMAGE_CREATE_ALIAS_BIT flag set, so the memory may be aliased if needed. + * + * If all three queue family indices in the device context are the same, + * images will be created with the EXCLUSIVE sharing mode. Otherwise, all images + * will be created using the CONCURRENT sharing mode. + * + * @note the size of this structure is not part of the ABI, to allocate + * you must use @av_vk_frame_alloc(). + */ +typedef struct AVVkFrame { + /** + * Vulkan images to which the memory is bound to. + */ + VkImage img[AV_NUM_DATA_POINTERS]; + + /** + * The same tiling must be used for all images in the frame. + */ + VkImageTiling tiling; + + /** + * Memory backing the images. Could be less than the amount of images + * if importing from a DRM or VAAPI frame. + */ + VkDeviceMemory mem[AV_NUM_DATA_POINTERS]; + size_t size[AV_NUM_DATA_POINTERS]; + + /** + * OR'd flags for all memory allocated + */ + VkMemoryPropertyFlagBits flags; + + /** + * Updated after every barrier + */ + VkAccessFlagBits access[AV_NUM_DATA_POINTERS]; + VkImageLayout layout[AV_NUM_DATA_POINTERS]; + + /** + * Synchronization semaphores. Must not be freed manually. Must be waited on + * and signalled at every queue submission. + * Could be less than the amount of images: either one per VkDeviceMemory + * or one for the entire frame. All others will be set to VK_NULL_HANDLE. + */ + VkSemaphore sem[AV_NUM_DATA_POINTERS]; + + /** + * Internal data. + */ + struct AVVkFrameInternal *internal; +} AVVkFrame; + +/** + * Allocates a single AVVkFrame and initializes everything as 0. + * @note Must be freed via av_free() + */ +AVVkFrame *av_vk_frame_alloc(void); + +/** + * Returns the format of each image up to the number of planes for a given sw_format. + * Returns NULL on unsupported formats. + */ +const VkFormat *av_vkfmt_from_pixfmt(enum AVPixelFormat p); + +#endif /* AVUTIL_HWCONTEXT_VULKAN_H */ diff --git a/libavutil/imgutils.c b/libavutil/imgutils.c index c733cb5cf54..7f9c1b632cf 100644 --- a/libavutil/imgutils.c +++ b/libavutil/imgutils.c @@ -519,7 +519,6 @@ static void memset_bytes(uint8_t *dst, size_t dst_size, uint8_t *clear, if (clear_size == 1) { memset(dst, clear[0], dst_size); - dst_size = 0; } else { if (clear_size > dst_size) clear_size = dst_size; diff --git a/libavutil/lfg.h b/libavutil/lfg.h index 03f779ad8a4..2b669205d18 100644 --- a/libavutil/lfg.h +++ b/libavutil/lfg.h @@ -24,6 +24,12 @@ #include +/** + * Context structure for the Lagged Fibonacci PRNG. + * The exact layout, types and content of this struct may change and should + * not be accessed directly. Only its sizeof() is guranteed to stay the same + * to allow easy instanciation. + */ typedef struct AVLFG { unsigned int state[64]; int index; @@ -45,8 +51,9 @@ int av_lfg_init_from_data(AVLFG *c, const uint8_t *data, unsigned int length); * it may be good enough and faster for your specific use case. */ static inline unsigned int av_lfg_get(AVLFG *c){ - c->state[c->index & 63] = c->state[(c->index-24) & 63] + c->state[(c->index-55) & 63]; - return c->state[c->index++ & 63]; + unsigned a = c->state[c->index & 63] = c->state[(c->index-24) & 63] + c->state[(c->index-55) & 63]; + c->index += 1U; + return a; } /** @@ -57,7 +64,9 @@ static inline unsigned int av_lfg_get(AVLFG *c){ static inline unsigned int av_mlfg_get(AVLFG *c){ unsigned int a= c->state[(c->index-55) & 63]; unsigned int b= c->state[(c->index-24) & 63]; - return c->state[c->index++ & 63] = 2*a*b+a+b; + a = c->state[c->index & 63] = 2*a*b+a+b; + c->index += 1U; + return a; } /** diff --git a/libavutil/log.c b/libavutil/log.c index 93a156b8e4c..66defa9c424 100644 --- a/libavutil/log.c +++ b/libavutil/log.c @@ -55,7 +55,7 @@ static int av_log_level = AV_LOG_INFO; static int flags; #define NB_LEVELS 8 -#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE +#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE #include static const uint8_t color[16 + AV_CLASS_CATEGORY_NB] = { [AV_LOG_PANIC /8] = 12, @@ -120,50 +120,68 @@ static const uint32_t color[16 + AV_CLASS_CATEGORY_NB] = { #endif static int use_color = -1; +#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE +static void win_console_puts(const char *str) +{ + const uint8_t *q = str; + uint16_t line[LINE_SZ]; + + while (*q) { + uint16_t *buf = line; + DWORD nb_chars = 0; + DWORD written; + + while (*q && nb_chars < LINE_SZ - 1) { + uint32_t ch; + uint16_t tmp; + + GET_UTF8(ch, *q ? *q++ : 0, ch = 0xfffd; goto continue_on_invalid;) +continue_on_invalid: + PUT_UTF16(ch, tmp, *buf++ = tmp; nb_chars++;) + } + + WriteConsoleW(con, line, nb_chars, &written, NULL); + } +} +#endif + static void check_color_terminal(void) { -#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE + char *term = getenv("TERM"); + +#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE CONSOLE_SCREEN_BUFFER_INFO con_info; + DWORD dummy; con = GetStdHandle(STD_ERROR_HANDLE); - use_color = (con != INVALID_HANDLE_VALUE) && !getenv("NO_COLOR") && - !getenv("AV_LOG_FORCE_NOCOLOR"); - if (use_color) { + if (con != INVALID_HANDLE_VALUE && !GetConsoleMode(con, &dummy)) + con = INVALID_HANDLE_VALUE; + if (con != INVALID_HANDLE_VALUE) { GetConsoleScreenBufferInfo(con, &con_info); attr_orig = con_info.wAttributes; background = attr_orig & 0xF0; } +#endif + + if (getenv("AV_LOG_FORCE_NOCOLOR")) { + use_color = 0; + } else if (getenv("AV_LOG_FORCE_COLOR")) { + use_color = 1; + } else { +#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE + use_color = (con != INVALID_HANDLE_VALUE); #elif HAVE_ISATTY - char *term = getenv("TERM"); - use_color = !getenv("NO_COLOR") && !getenv("AV_LOG_FORCE_NOCOLOR") && - (getenv("TERM") && isatty(2) || getenv("AV_LOG_FORCE_COLOR")); - if ( getenv("AV_LOG_FORCE_256COLOR") - || (term && strstr(term, "256color"))) - use_color *= 256; + use_color = (term && isatty(2)); #else - use_color = getenv("AV_LOG_FORCE_COLOR") && !getenv("NO_COLOR") && - !getenv("AV_LOG_FORCE_NOCOLOR"); + use_color = 0; #endif + } + + if (getenv("AV_LOG_FORCE_256COLOR") || term && strstr(term, "256color")) + use_color *= 256; } -static void colored_fputs(int level, int tint, const char *str) +static void ansi_fputs(int level, int tint, const char *str, int local_use_color) { - int local_use_color; - if (!*str) - return; - - if (use_color < 0) - check_color_terminal(); - - if (level == AV_LOG_INFO/8) local_use_color = 0; - else local_use_color = use_color; - -#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE - if (local_use_color) - SetConsoleTextAttribute(con, background | color[level]); - fputs(str, stderr); - if (local_use_color) - SetConsoleTextAttribute(con, attr_orig); -#else if (local_use_color == 1) { fprintf(stderr, "\033[%"PRIu32";3%"PRIu32"m%s\033[0m", @@ -184,6 +202,32 @@ static void colored_fputs(int level, int tint, const char *str) str); } else fputs(str, stderr); +} + +static void colored_fputs(int level, int tint, const char *str) +{ + int local_use_color; + if (!*str) + return; + + if (use_color < 0) + check_color_terminal(); + + if (level == AV_LOG_INFO/8) local_use_color = 0; + else local_use_color = use_color; + +#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE + if (con != INVALID_HANDLE_VALUE) { + if (local_use_color) + SetConsoleTextAttribute(con, background | color[level]); + win_console_puts(str); + if (local_use_color) + SetConsoleTextAttribute(con, attr_orig); + } else { + ansi_fputs(level, tint, str, local_use_color); + } +#else + ansi_fputs(level, tint, str, local_use_color); #endif } @@ -226,6 +270,8 @@ static const char *get_level_str(int level) return "quiet"; case AV_LOG_DEBUG: return "debug"; + case AV_LOG_TRACE: + return "trace"; case AV_LOG_VERBOSE: return "verbose"; case AV_LOG_INFO: @@ -360,19 +406,28 @@ static void (*av_log_callback)(void*, int, const char*, va_list) = void av_log(void* avcl, int level, const char *fmt, ...) { - AVClass* avc = avcl ? *(AVClass **) avcl : NULL; va_list vl; va_start(vl, fmt); - if (avc && avc->version >= (50 << 16 | 15 << 8 | 2) && - avc->log_level_offset_offset && level >= AV_LOG_FATAL) - level += *(int *) (((uint8_t *) avcl) + avc->log_level_offset_offset); av_vlog(avcl, level, fmt, vl); va_end(vl); } +void av_log_once(void* avcl, int initial_level, int subsequent_level, int *state, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + av_vlog(avcl, *state ? subsequent_level : initial_level, fmt, vl); + va_end(vl); + *state = 1; +} + void av_vlog(void* avcl, int level, const char *fmt, va_list vl) { + AVClass* avc = avcl ? *(AVClass **) avcl : NULL; void (*log_callback)(void*, int, const char*, va_list) = av_log_callback; + if (avc && avc->version >= (50 << 16 | 15 << 8 | 2) && + avc->log_level_offset_offset && level >= AV_LOG_FATAL) + level += *(int *) (((uint8_t *) avcl) + avc->log_level_offset_offset); if (log_callback) log_callback(avcl, level, fmt, vl); } @@ -412,7 +467,7 @@ static void missing_feature_sample(int sample, void *avc, const char *msg, "been implemented.\n"); if (sample) av_log(avc, AV_LOG_WARNING, "If you want to help, upload a sample " - "of this file to ftp://upload.ffmpeg.org/incoming/ " + "of this file to https://streams.videolan.org/upload/ " "and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)\n"); } diff --git a/libavutil/log.h b/libavutil/log.h index d9554e609d4..9c14188a9c9 100644 --- a/libavutil/log.h +++ b/libavutil/log.h @@ -233,6 +233,27 @@ typedef struct AVClass { */ void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4); +/** + * Send the specified message to the log once with the initial_level and then with + * the subsequent_level. By default, all logging messages are sent to + * stderr. This behavior can be altered by setting a different logging callback + * function. + * @see av_log + * + * @param avcl A pointer to an arbitrary struct of which the first field is a + * pointer to an AVClass struct or NULL if general log. + * @param initial_level importance level of the message expressed using a @ref + * lavu_log_constants "Logging Constant" for the first occurance. + * @param subsequent_level importance level of the message expressed using a @ref + * lavu_log_constants "Logging Constant" after the first occurance. + * @param fmt The format string (printf-compatible) that specifies how + * subsequent arguments are converted to output. + * @param state a variable to keep trak of if a message has already been printed + * this must be initialized to 0 before the first use. The same state + * must not be accessed by 2 Threads simultaneously. + */ +void av_log_once(void* avcl, int initial_level, int subsequent_level, int *state, const char *fmt, ...) av_printf_format(5, 6); + /** * Send the specified message to the log if the level is less than or equal diff --git a/libavutil/mathematics.c b/libavutil/mathematics.c index 1bf044cdf11..16c6e4db030 100644 --- a/libavutil/mathematics.c +++ b/libavutil/mathematics.c @@ -198,7 +198,7 @@ int64_t av_add_stable(AVRational ts_tb, int64_t ts, AVRational inc_tb, int64_t i m = inc_tb.num * (int64_t)ts_tb.den; d = inc_tb.den * (int64_t)ts_tb.num; - if (m % d == 0) + if (m % d == 0 && ts <= INT64_MAX - m / d) return ts + m / d; if (m < d) return ts; @@ -206,6 +206,10 @@ int64_t av_add_stable(AVRational ts_tb, int64_t ts, AVRational inc_tb, int64_t i { int64_t old = av_rescale_q(ts, ts_tb, inc_tb); int64_t old_ts = av_rescale_q(old, inc_tb, ts_tb); + + if (old == INT64_MAX || old == AV_NOPTS_VALUE || old_ts == AV_NOPTS_VALUE) + return ts; + return av_rescale_q(old + 1, inc_tb, ts_tb) + (ts - old_ts); } } diff --git a/libavutil/mem.c b/libavutil/mem.c index 88fe09b1794..cfb6d8ab8ff 100644 --- a/libavutil/mem.c +++ b/libavutil/mem.c @@ -78,8 +78,7 @@ void *av_malloc(size_t size) { void *ptr = NULL; - /* let's disallow possibly ambiguous cases */ - if (size > (max_alloc_size - 32)) + if (size > max_alloc_size) return NULL; #if HAVE_POSIX_MEMALIGN @@ -134,8 +133,7 @@ void *av_malloc(size_t size) void *av_realloc(void *ptr, size_t size) { - /* let's disallow possibly ambiguous cases */ - if (size > (max_alloc_size - 32)) + if (size > max_alloc_size) return NULL; #if HAVE_ALIGNED_MALLOC @@ -183,23 +181,26 @@ int av_reallocp(void *ptr, size_t size) void *av_malloc_array(size_t nmemb, size_t size) { - if (!size || nmemb >= INT_MAX / size) + size_t result; + if (av_size_mult(nmemb, size, &result) < 0) return NULL; - return av_malloc(nmemb * size); + return av_malloc(result); } void *av_mallocz_array(size_t nmemb, size_t size) { - if (!size || nmemb >= INT_MAX / size) + size_t result; + if (av_size_mult(nmemb, size, &result) < 0) return NULL; - return av_mallocz(nmemb * size); + return av_mallocz(result); } void *av_realloc_array(void *ptr, size_t nmemb, size_t size) { - if (!size || nmemb >= INT_MAX / size) + size_t result; + if (av_size_mult(nmemb, size, &result) < 0) return NULL; - return av_realloc(ptr, nmemb * size); + return av_realloc(ptr, result); } int av_reallocp_array(void *ptr, size_t nmemb, size_t size) @@ -243,9 +244,10 @@ void *av_mallocz(size_t size) void *av_calloc(size_t nmemb, size_t size) { - if (size <= 0 || nmemb >= INT_MAX / size) + size_t result; + if (av_size_mult(nmemb, size, &result) < 0) return NULL; - return av_mallocz(nmemb * size); + return av_mallocz(result); } char *av_strdup(const char *s) @@ -478,12 +480,12 @@ void *av_fast_realloc(void *ptr, unsigned int *size, size_t min_size) if (min_size <= *size) return ptr; - if (min_size > max_alloc_size - 32) { + if (min_size > max_alloc_size) { *size = 0; return NULL; } - min_size = FFMIN(max_alloc_size - 32, FFMAX(min_size + min_size / 16 + 32, min_size)); + min_size = FFMIN(max_alloc_size, FFMAX(min_size + min_size / 16 + 32, min_size)); ptr = av_realloc(ptr, min_size); /* we could set this to the unmodified min_size but this is safer diff --git a/libavutil/mips/generic_macros_msa.h b/libavutil/mips/generic_macros_msa.h index 9ac0583765f..267d4e6ca59 100644 --- a/libavutil/mips/generic_macros_msa.h +++ b/libavutil/mips/generic_macros_msa.h @@ -299,6 +299,7 @@ #define LD_SB4(...) LD_V4(v16i8, __VA_ARGS__) #define LD_UH4(...) LD_V4(v8u16, __VA_ARGS__) #define LD_SH4(...) LD_V4(v8i16, __VA_ARGS__) +#define LD_SW4(...) LD_V4(v4i32, __VA_ARGS__) #define LD_V5(RTYPE, psrc, stride, out0, out1, out2, out3, out4) \ { \ @@ -337,6 +338,7 @@ #define LD_SB8(...) LD_V8(v16i8, __VA_ARGS__) #define LD_UH8(...) LD_V8(v8u16, __VA_ARGS__) #define LD_SH8(...) LD_V8(v8i16, __VA_ARGS__) +#define LD_SW8(...) LD_V8(v4i32, __VA_ARGS__) #define LD_V16(RTYPE, psrc, stride, \ out0, out1, out2, out3, out4, out5, out6, out7, \ @@ -602,67 +604,48 @@ } #define AVER_UB4_UB(...) AVER_UB4(v16u8, __VA_ARGS__) -/* Description : Immediate number of columns to slide with zero - Arguments : Inputs - in0, in1, slide_val - Outputs - out0, out1 +/* Description : Immediate number of columns to slide + Arguments : Inputs - s, d, slide_val + Outputs - out Return Type - as per RTYPE - Details : Byte elements from 'zero_m' vector are slide into 'in0' by + Details : Byte elements from 'd' vector are slide into 's' by number of elements specified by 'slide_val' */ -#define SLDI_B2_0(RTYPE, in0, in1, out0, out1, slide_val) \ -{ \ - v16i8 zero_m = { 0 }; \ - out0 = (RTYPE) __msa_sldi_b((v16i8) zero_m, (v16i8) in0, slide_val); \ - out1 = (RTYPE) __msa_sldi_b((v16i8) zero_m, (v16i8) in1, slide_val); \ -} -#define SLDI_B2_0_UB(...) SLDI_B2_0(v16u8, __VA_ARGS__) -#define SLDI_B2_0_SB(...) SLDI_B2_0(v16i8, __VA_ARGS__) -#define SLDI_B2_0_SW(...) SLDI_B2_0(v4i32, __VA_ARGS__) - -#define SLDI_B3_0(RTYPE, in0, in1, in2, out0, out1, out2, slide_val) \ -{ \ - v16i8 zero_m = { 0 }; \ - SLDI_B2_0(RTYPE, in0, in1, out0, out1, slide_val); \ - out2 = (RTYPE) __msa_sldi_b((v16i8) zero_m, (v16i8) in2, slide_val); \ -} -#define SLDI_B3_0_UB(...) SLDI_B3_0(v16u8, __VA_ARGS__) -#define SLDI_B3_0_SB(...) SLDI_B3_0(v16i8, __VA_ARGS__) - -#define SLDI_B4_0(RTYPE, in0, in1, in2, in3, \ - out0, out1, out2, out3, slide_val) \ -{ \ - SLDI_B2_0(RTYPE, in0, in1, out0, out1, slide_val); \ - SLDI_B2_0(RTYPE, in2, in3, out2, out3, slide_val); \ +#define SLDI_B(RTYPE, d, s, slide_val, out) \ +{ \ + out = (RTYPE) __msa_sldi_b((v16i8) d, (v16i8) s, slide_val); \ } -#define SLDI_B4_0_UB(...) SLDI_B4_0(v16u8, __VA_ARGS__) -#define SLDI_B4_0_SB(...) SLDI_B4_0(v16i8, __VA_ARGS__) -#define SLDI_B4_0_SH(...) SLDI_B4_0(v8i16, __VA_ARGS__) -/* Description : Immediate number of columns to slide - Arguments : Inputs - in0_0, in0_1, in1_0, in1_1, slide_val - Outputs - out0, out1 - Return Type - as per RTYPE - Details : Byte elements from 'in0_0' vector are slide into 'in1_0' by - number of elements specified by 'slide_val' -*/ -#define SLDI_B2(RTYPE, in0_0, in0_1, in1_0, in1_1, out0, out1, slide_val) \ -{ \ - out0 = (RTYPE) __msa_sldi_b((v16i8) in0_0, (v16i8) in1_0, slide_val); \ - out1 = (RTYPE) __msa_sldi_b((v16i8) in0_1, (v16i8) in1_1, slide_val); \ +#define SLDI_B2(RTYPE, d0, s0, d1, s1, slide_val, out0, out1) \ +{ \ + SLDI_B(RTYPE, d0, s0, slide_val, out0) \ + SLDI_B(RTYPE, d1, s1, slide_val, out1) \ } #define SLDI_B2_UB(...) SLDI_B2(v16u8, __VA_ARGS__) #define SLDI_B2_SB(...) SLDI_B2(v16i8, __VA_ARGS__) #define SLDI_B2_SH(...) SLDI_B2(v8i16, __VA_ARGS__) +#define SLDI_B2_SW(...) SLDI_B2(v4i32, __VA_ARGS__) -#define SLDI_B3(RTYPE, in0_0, in0_1, in0_2, in1_0, in1_1, in1_2, \ - out0, out1, out2, slide_val) \ -{ \ - SLDI_B2(RTYPE, in0_0, in0_1, in1_0, in1_1, out0, out1, slide_val) \ - out2 = (RTYPE) __msa_sldi_b((v16i8) in0_2, (v16i8) in1_2, slide_val); \ +#define SLDI_B3(RTYPE, d0, s0, d1, s1, d2, s2, slide_val, \ + out0, out1, out2) \ +{ \ + SLDI_B2(RTYPE, d0, s0, d1, s1, slide_val, out0, out1) \ + SLDI_B(RTYPE, d2, s2, slide_val, out2) \ } +#define SLDI_B3_UB(...) SLDI_B3(v16u8, __VA_ARGS__) #define SLDI_B3_SB(...) SLDI_B3(v16i8, __VA_ARGS__) #define SLDI_B3_UH(...) SLDI_B3(v8u16, __VA_ARGS__) +#define SLDI_B4(RTYPE, d0, s0, d1, s1, d2, s2, d3, s3, \ + slide_val, out0, out1, out2, out3) \ +{ \ + SLDI_B2(RTYPE, d0, s0, d1, s1, slide_val, out0, out1) \ + SLDI_B2(RTYPE, d2, s2, d3, s3, slide_val, out2, out3) \ +} +#define SLDI_B4_UB(...) SLDI_B4(v16u8, __VA_ARGS__) +#define SLDI_B4_SB(...) SLDI_B4(v16i8, __VA_ARGS__) +#define SLDI_B4_SH(...) SLDI_B4(v8i16, __VA_ARGS__) + /* Description : Shuffle byte vector elements as per mask vector Arguments : Inputs - in0, in1, in2, in3, mask0, mask1 Outputs - out0, out1 @@ -933,99 +916,78 @@ /* Description : Clips all halfword elements of input vector between min & max out = ((in) < (min)) ? (min) : (((in) > (max)) ? (max) : (in)) - Arguments : Inputs - in (input vector) - - min (min threshold) - - max (max threshold) - Outputs - out_m (output vector with clipped elements) + Arguments : Inputs - in (input vector) + - min (min threshold) + - max (max threshold) + Outputs - in (output vector with clipped elements) Return Type - signed halfword */ -#define CLIP_SH(in, min, max) \ -( { \ - v8i16 out_m; \ - \ - out_m = __msa_max_s_h((v8i16) min, (v8i16) in); \ - out_m = __msa_min_s_h((v8i16) max, (v8i16) out_m); \ - out_m; \ -} ) +#define CLIP_SH(in, min, max) \ +{ \ + in = __msa_max_s_h((v8i16) min, (v8i16) in); \ + in = __msa_min_s_h((v8i16) max, (v8i16) in); \ +} /* Description : Clips all signed halfword elements of input vector between 0 & 255 - Arguments : Inputs - in (input vector) - Outputs - out_m (output vector with clipped elements) - Return Type - signed halfword + Arguments : Inputs - in (input vector) + Outputs - in (output vector with clipped elements) + Return Type - signed halfwords */ -#define CLIP_SH_0_255(in) \ -( { \ - v8i16 max_m = __msa_ldi_h(255); \ - v8i16 out_m; \ - \ - out_m = __msa_maxi_s_h((v8i16) in, 0); \ - out_m = __msa_min_s_h((v8i16) max_m, (v8i16) out_m); \ - out_m; \ -} ) +#define CLIP_SH_0_255(in) \ +{ \ + in = __msa_maxi_s_h((v8i16) in, 0); \ + in = (v8i16) __msa_sat_u_h((v8u16) in, 7); \ +} + #define CLIP_SH2_0_255(in0, in1) \ { \ - in0 = CLIP_SH_0_255(in0); \ - in1 = CLIP_SH_0_255(in1); \ + CLIP_SH_0_255(in0); \ + CLIP_SH_0_255(in1); \ } + #define CLIP_SH4_0_255(in0, in1, in2, in3) \ { \ CLIP_SH2_0_255(in0, in1); \ CLIP_SH2_0_255(in2, in3); \ } -#define CLIP_SH_0_255_MAX_SATU(in) \ -( { \ - v8i16 out_m; \ - \ - out_m = __msa_maxi_s_h((v8i16) in, 0); \ - out_m = (v8i16) __msa_sat_u_h((v8u16) out_m, 7); \ - out_m; \ -} ) -#define CLIP_SH2_0_255_MAX_SATU(in0, in1) \ -{ \ - in0 = CLIP_SH_0_255_MAX_SATU(in0); \ - in1 = CLIP_SH_0_255_MAX_SATU(in1); \ -} -#define CLIP_SH4_0_255_MAX_SATU(in0, in1, in2, in3) \ -{ \ - CLIP_SH2_0_255_MAX_SATU(in0, in1); \ - CLIP_SH2_0_255_MAX_SATU(in2, in3); \ +#define CLIP_SH8_0_255(in0, in1, in2, in3, \ + in4, in5, in6, in7) \ +{ \ + CLIP_SH4_0_255(in0, in1, in2, in3); \ + CLIP_SH4_0_255(in4, in5, in6, in7); \ } /* Description : Clips all signed word elements of input vector between 0 & 255 - Arguments : Inputs - in (input vector) - Outputs - out_m (output vector with clipped elements) + Arguments : Inputs - in (input vector) + Outputs - in (output vector with clipped elements) Return Type - signed word */ -#define CLIP_SW_0_255(in) \ -( { \ - v4i32 max_m = __msa_ldi_w(255); \ - v4i32 out_m; \ - \ - out_m = __msa_maxi_s_w((v4i32) in, 0); \ - out_m = __msa_min_s_w((v4i32) max_m, (v4i32) out_m); \ - out_m; \ -} ) +#define CLIP_SW_0_255(in) \ +{ \ + in = __msa_maxi_s_w((v4i32) in, 0); \ + in = (v4i32) __msa_sat_u_w((v4u32) in, 7); \ +} -#define CLIP_SW_0_255_MAX_SATU(in) \ -( { \ - v4i32 out_m; \ - \ - out_m = __msa_maxi_s_w((v4i32) in, 0); \ - out_m = (v4i32) __msa_sat_u_w((v4u32) out_m, 7); \ - out_m; \ -} ) -#define CLIP_SW2_0_255_MAX_SATU(in0, in1) \ -{ \ - in0 = CLIP_SW_0_255_MAX_SATU(in0); \ - in1 = CLIP_SW_0_255_MAX_SATU(in1); \ +#define CLIP_SW2_0_255(in0, in1) \ +{ \ + CLIP_SW_0_255(in0); \ + CLIP_SW_0_255(in1); \ } -#define CLIP_SW4_0_255_MAX_SATU(in0, in1, in2, in3) \ -{ \ - CLIP_SW2_0_255_MAX_SATU(in0, in1); \ - CLIP_SW2_0_255_MAX_SATU(in2, in3); \ + +#define CLIP_SW4_0_255(in0, in1, in2, in3) \ +{ \ + CLIP_SW2_0_255(in0, in1); \ + CLIP_SW2_0_255(in2, in3); \ +} + +#define CLIP_SW8_0_255(in0, in1, in2, in3, \ + in4, in5, in6, in7) \ +{ \ + CLIP_SW4_0_255(in0, in1, in2, in3); \ + CLIP_SW4_0_255(in4, in5, in6, in7); \ } /* Description : Addition of 4 signed word elements @@ -1422,6 +1384,7 @@ out4, out5, out6, out7); \ } #define ILVR_B8_UH(...) ILVR_B8(v8u16, __VA_ARGS__) +#define ILVR_B8_SW(...) ILVR_B8(v4i32, __VA_ARGS__) /* Description : Interleave right half of halfword elements from vectors Arguments : Inputs - in0, in1, in2, in3, in4, in5, in6, in7 @@ -2433,6 +2396,7 @@ { \ v16i8 tmp0_m, tmp1_m, tmp2_m, tmp3_m; \ v16i8 tmp4_m, tmp5_m, tmp6_m, tmp7_m; \ + v16i8 zeros = { 0 }; \ \ ILVR_B4_SB(in2, in0, in3, in1, in6, in4, in7, in5, \ tmp0_m, tmp1_m, tmp2_m, tmp3_m); \ @@ -2440,8 +2404,8 @@ ILVRL_B2_SB(tmp3_m, tmp2_m, tmp6_m, tmp7_m); \ ILVRL_W2(RTYPE, tmp6_m, tmp4_m, out0, out2); \ ILVRL_W2(RTYPE, tmp7_m, tmp5_m, out4, out6); \ - SLDI_B2_0(RTYPE, out0, out2, out1, out3, 8); \ - SLDI_B2_0(RTYPE, out4, out6, out5, out7, 8); \ + SLDI_B4(RTYPE, zeros, out0, zeros, out2, zeros, out4, zeros, out6, \ + 8, out1, out3, out5, out7); \ } #define TRANSPOSE8x8_UB_UB(...) TRANSPOSE8x8_UB(v16u8, __VA_ARGS__) #define TRANSPOSE8x8_UB_UH(...) TRANSPOSE8x8_UB(v8u16, __VA_ARGS__) @@ -2523,8 +2487,6 @@ out5 = (v16u8) __msa_ilvod_w((v4i32) tmp3_m, (v4i32) tmp2_m); \ \ tmp2_m = (v16u8) __msa_ilvod_h((v8i16) tmp5_m, (v8i16) tmp4_m); \ - tmp2_m = (v16u8) __msa_ilvod_h((v8i16) tmp5_m, (v8i16) tmp4_m); \ - tmp3_m = (v16u8) __msa_ilvod_h((v8i16) tmp7_m, (v8i16) tmp6_m); \ tmp3_m = (v16u8) __msa_ilvod_h((v8i16) tmp7_m, (v8i16) tmp6_m); \ out3 = (v16u8) __msa_ilvev_w((v4i32) tmp3_m, (v4i32) tmp2_m); \ out7 = (v16u8) __msa_ilvod_w((v4i32) tmp3_m, (v4i32) tmp2_m); \ diff --git a/libavutil/mips/mmiutils.h b/libavutil/mips/mmiutils.h index 05f6b31155b..8f692e86c5a 100644 --- a/libavutil/mips/mmiutils.h +++ b/libavutil/mips/mmiutils.h @@ -205,7 +205,7 @@ * backup register */ #define BACKUP_REG \ - double temp_backup_reg[8]; \ + LOCAL_ALIGNED_16(double, temp_backup_reg, [8]); \ if (_MIPS_SIM == _ABI64) \ __asm__ volatile ( \ "gssqc1 $f25, $f24, 0x00(%[temp]) \n\t" \ diff --git a/libavutil/opt.c b/libavutil/opt.c index 93d6c26c11a..423313bce2b 100644 --- a/libavutil/opt.c +++ b/libavutil/opt.c @@ -229,13 +229,15 @@ static int set_string(void *obj, const AVOption *o, const char *val, uint8_t **d static int set_string_number(void *obj, void *target_obj, const AVOption *o, const char *val, void *dst) { int ret = 0; - int num, den; - char c; - if (sscanf(val, "%d%*1[:/]%d%c", &num, &den, &c) == 2) { - if ((ret = write_number(obj, o, dst, 1, den, num)) >= 0) - return ret; - ret = 0; + if (o->type == AV_OPT_TYPE_RATIONAL || o->type == AV_OPT_TYPE_VIDEO_RATE) { + int num, den; + char c; + if (sscanf(val, "%d%*1[:/]%d%c", &num, &den, &c) == 2) { + if ((ret = write_number(obj, o, dst, 1, den, num)) >= 0) + return ret; + ret = 0; + } } for (;;) { @@ -254,11 +256,12 @@ static int set_string_number(void *obj, void *target_obj, const AVOption *o, con } { - const AVOption *o_named = av_opt_find(target_obj, i ? buf : val, o->unit, 0, 0); int res; int ci = 0; double const_values[64]; const char * const_names[64]; + int search_flags = (o->flags & AV_OPT_FLAG_CHILD_CONSTS) ? AV_OPT_SEARCH_CHILDREN : 0; + const AVOption *o_named = av_opt_find(target_obj, i ? buf : val, o->unit, 0, search_flags); if (o_named && o_named->type == AV_OPT_TYPE_CONST) d = DEFAULT_NUMVAL(o_named); else { @@ -330,12 +333,7 @@ static int set_string_image_size(void *obj, const AVOption *o, const char *val, static int set_string_video_rate(void *obj, const AVOption *o, const char *val, AVRational *dst) { - int ret; - if (!val) { - ret = AVERROR(EINVAL); - } else { - ret = av_parse_video_rate(dst, val); - } + int ret = av_parse_video_rate(dst, val); if (ret < 0) av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\" as video rate\n", val); return ret; @@ -446,6 +444,24 @@ static int set_string_sample_fmt(void *obj, const AVOption *o, const char *val, AV_SAMPLE_FMT_NB, av_get_sample_fmt, "sample format"); } +static int set_string_dict(void *obj, const AVOption *o, const char *val, uint8_t **dst) +{ + AVDictionary *options = NULL; + + if (val) { + int ret = av_dict_parse_string(&options, val, "=", ":", 0); + if (ret < 0) { + av_dict_free(&options); + return ret; + } + } + + av_dict_free((AVDictionary **)dst); + *dst = (uint8_t *)options; + + return 0; +} + int av_opt_set(void *obj, const char *name, const char *val, int search_flags) { int ret = 0; @@ -455,7 +471,7 @@ int av_opt_set(void *obj, const char *name, const char *val, int search_flags) return AVERROR_OPTION_NOT_FOUND; if (!val && (o->type != AV_OPT_TYPE_STRING && o->type != AV_OPT_TYPE_PIXEL_FMT && o->type != AV_OPT_TYPE_SAMPLE_FMT && - o->type != AV_OPT_TYPE_IMAGE_SIZE && o->type != AV_OPT_TYPE_VIDEO_RATE && + o->type != AV_OPT_TYPE_IMAGE_SIZE && o->type != AV_OPT_TYPE_DURATION && o->type != AV_OPT_TYPE_COLOR && o->type != AV_OPT_TYPE_CHANNEL_LAYOUT && o->type != AV_OPT_TYPE_BOOL)) return AVERROR(EINVAL); @@ -527,6 +543,8 @@ int av_opt_set(void *obj, const char *name, const char *val, int search_flags) return ret; } break; + case AV_OPT_TYPE_DICT: + return set_string_dict(obj, o, val, dst); } av_log(obj, AV_LOG_ERROR, "Invalid option type.\n"); @@ -855,6 +873,12 @@ int av_opt_get(void *obj, const char *name, int search_flags, uint8_t **out_val) i64 = *(int64_t *)dst; ret = snprintf(buf, sizeof(buf), "0x%"PRIx64, i64); break; + case AV_OPT_TYPE_DICT: + if (!*(AVDictionary **)dst && (search_flags & AV_OPT_ALLOW_NULL)) { + *out_val = NULL; + return 0; + } + return av_dict_get_string(*(AVDictionary **)dst, (char **)out_val, '=', ':'); default: return AVERROR(EINVAL); } @@ -1034,6 +1058,23 @@ int av_opt_flag_is_set(void *obj, const char *field_name, const char *flag_name) return res & flag->default_val.i64; } +static void log_int_value(void *av_log_obj, int level, int64_t i) +{ + if (i == INT_MAX) { + av_log(av_log_obj, level, "INT_MAX"); + } else if (i == INT_MIN) { + av_log(av_log_obj, level, "INT_MIN"); + } else if (i == UINT32_MAX) { + av_log(av_log_obj, level, "UINT32_MAX"); + } else if (i == INT64_MAX) { + av_log(av_log_obj, level, "I64_MAX"); + } else if (i == INT64_MIN) { + av_log(av_log_obj, level, "I64_MIN"); + } else { + av_log(av_log_obj, level, "%"PRId64, i); + } +} + static void log_value(void *av_log_obj, int level, double d) { if (d == INT_MAX) { @@ -1102,7 +1143,7 @@ static char *get_opt_flags_string(void *obj, const char *unit, int64_t value) } static void opt_list(void *obj, void *av_log_obj, const char *unit, - int req_flags, int rej_flags) + int req_flags, int rej_flags, enum AVOptionType parent_type) { const AVOption *opt = NULL; AVOptionRanges *r; @@ -1157,6 +1198,9 @@ static void opt_list(void *obj, void *av_log_obj, const char *unit, case AV_OPT_TYPE_BINARY: av_log(av_log_obj, AV_LOG_INFO, "%-12s ", ""); break; + case AV_OPT_TYPE_DICT: + av_log(av_log_obj, AV_LOG_INFO, "%-12s ", ""); + break; case AV_OPT_TYPE_IMAGE_SIZE: av_log(av_log_obj, AV_LOG_INFO, "%-12s ", ""); break; @@ -1182,6 +1226,11 @@ static void opt_list(void *obj, void *av_log_obj, const char *unit, av_log(av_log_obj, AV_LOG_INFO, "%-12s ", ""); break; case AV_OPT_TYPE_CONST: + if (parent_type == AV_OPT_TYPE_INT) + av_log(av_log_obj, AV_LOG_INFO, "%-12"PRId64" ", opt->default_val.i64); + else + av_log(av_log_obj, AV_LOG_INFO, "%-12s ", ""); + break; default: av_log(av_log_obj, AV_LOG_INFO, "%-12s ", ""); break; @@ -1195,6 +1244,7 @@ static void opt_list(void *obj, void *av_log_obj, const char *unit, av_log(av_log_obj, AV_LOG_INFO, "%c", (opt->flags & AV_OPT_FLAG_EXPORT) ? 'X' : '.'); av_log(av_log_obj, AV_LOG_INFO, "%c", (opt->flags & AV_OPT_FLAG_READONLY) ? 'R' : '.'); av_log(av_log_obj, AV_LOG_INFO, "%c", (opt->flags & AV_OPT_FLAG_BSF_PARAM) ? 'B' : '.'); + av_log(av_log_obj, AV_LOG_INFO, "%c", (opt->flags & AV_OPT_FLAG_RUNTIME_PARAM) ? 'T' : '.'); if (opt->help) av_log(av_log_obj, AV_LOG_INFO, " %s", opt->help); @@ -1224,6 +1274,7 @@ static void opt_list(void *obj, void *av_log_obj, const char *unit, !((opt->type == AV_OPT_TYPE_COLOR || opt->type == AV_OPT_TYPE_IMAGE_SIZE || opt->type == AV_OPT_TYPE_STRING || + opt->type == AV_OPT_TYPE_DICT || opt->type == AV_OPT_TYPE_VIDEO_RATE) && !opt->default_val.str)) { av_log(av_log_obj, AV_LOG_INFO, " (default "); @@ -1254,7 +1305,7 @@ static void opt_list(void *obj, void *av_log_obj, const char *unit, if (def_const) av_log(av_log_obj, AV_LOG_INFO, "%s", def_const); else - log_value(av_log_obj, AV_LOG_INFO, opt->default_val.i64); + log_int_value(av_log_obj, AV_LOG_INFO, opt->default_val.i64); break; } case AV_OPT_TYPE_DOUBLE: @@ -1274,6 +1325,7 @@ static void opt_list(void *obj, void *av_log_obj, const char *unit, case AV_OPT_TYPE_COLOR: case AV_OPT_TYPE_IMAGE_SIZE: case AV_OPT_TYPE_STRING: + case AV_OPT_TYPE_DICT: case AV_OPT_TYPE_VIDEO_RATE: av_log(av_log_obj, AV_LOG_INFO, "\"%s\"", opt->default_val.str); break; @@ -1286,7 +1338,7 @@ static void opt_list(void *obj, void *av_log_obj, const char *unit, av_log(av_log_obj, AV_LOG_INFO, "\n"); if (opt->unit && opt->type != AV_OPT_TYPE_CONST) - opt_list(obj, av_log_obj, opt->unit, req_flags, rej_flags); + opt_list(obj, av_log_obj, opt->unit, req_flags, rej_flags, opt->type); } } @@ -1297,7 +1349,7 @@ int av_opt_show2(void *obj, void *av_log_obj, int req_flags, int rej_flags) av_log(av_log_obj, AV_LOG_INFO, "%s AVOptions:\n", (*(AVClass **)obj)->class_name); - opt_list(obj, av_log_obj, NULL, req_flags, rej_flags); + opt_list(obj, av_log_obj, NULL, req_flags, rej_flags, -1); return 0; } @@ -1363,8 +1415,8 @@ void av_opt_set_defaults2(void *s, int mask, int flags) set_string_binary(s, opt, opt->default_val.str, dst); break; case AV_OPT_TYPE_DICT: - /* Cannot set defaults for these types */ - break; + set_string_dict(s, opt, opt->default_val.str, dst); + break; default: av_log(s, AV_LOG_DEBUG, "AVOption type %d of option %s not implemented yet\n", opt->type, opt->name); @@ -1948,9 +2000,23 @@ int av_opt_is_set_to_default(void *obj, const AVOption *o) av_free(tmp.data); return ret; } - case AV_OPT_TYPE_DICT: - /* Binary and dict have not default support yet. Any pointer is not default. */ - return !!(*(void **)dst); + case AV_OPT_TYPE_DICT: { + AVDictionary *dict1 = NULL; + AVDictionary *dict2 = *(AVDictionary **)dst; + AVDictionaryEntry *en1 = NULL; + AVDictionaryEntry *en2 = NULL; + ret = av_dict_parse_string(&dict1, o->default_val.str, "=", ":", 0); + if (ret < 0) { + av_dict_free(&dict1); + return ret; + } + do { + en1 = av_dict_get(dict1, "", en1, AV_DICT_IGNORE_SUFFIX); + en2 = av_dict_get(dict2, "", en2, AV_DICT_IGNORE_SUFFIX); + } while (en1 && en2 && !strcmp(en1->key, en2->key) && !strcmp(en1->value, en2->value)); + av_dict_free(&dict1); + return (!en1 && !en2); + } case AV_OPT_TYPE_IMAGE_SIZE: if (!o->default_val.str || !strcmp(o->default_val.str, "none")) w = h = 0; diff --git a/libavutil/opt.h b/libavutil/opt.h index 39f4a8dda0e..e46119572aa 100644 --- a/libavutil/opt.h +++ b/libavutil/opt.h @@ -288,8 +288,10 @@ typedef struct AVOption { */ #define AV_OPT_FLAG_READONLY 128 #define AV_OPT_FLAG_BSF_PARAM (1<<8) ///< a generic parameter which can be set by the user for bit stream filtering +#define AV_OPT_FLAG_RUNTIME_PARAM (1<<15) ///< a generic parameter which can be set by the user at runtime #define AV_OPT_FLAG_FILTERING_PARAM (1<<16) ///< a generic parameter which can be set by the user for filtering #define AV_OPT_FLAG_DEPRECATED (1<<17) ///< set if option is deprecated, users should refer to AVOption.help text for more information +#define AV_OPT_FLAG_CHILD_CONSTS (1<<18) ///< set if option constants can also reside in child objects //FIXME think about enc-audio, ... style flags /** @@ -669,6 +671,9 @@ const AVClass *av_opt_child_class_next(const AVClass *parent, const AVClass *pre * scalars or named flags separated by '+' or '-'. Prefixing a flag * with '+' causes it to be set without affecting the other flags; * similarly, '-' unsets a flag. + * If the field is of a dictionary type, it has to be a ':' separated list of + * key=value parameters. Values containing ':' special characters must be + * escaped. * @param search_flags flags passed to av_opt_find2. I.e. if AV_OPT_SEARCH_CHILDREN * is passed here, then the option may be set on a child of obj. * @@ -729,9 +734,10 @@ int av_opt_set_dict_val(void *obj, const char *name, const AVDictionary *val, in /** * @note the returned string will be av_malloc()ed and must be av_free()ed by the caller * - * @note if AV_OPT_ALLOW_NULL is set in search_flags in av_opt_get, and the option has - * AV_OPT_TYPE_STRING or AV_OPT_TYPE_BINARY and is set to NULL, *out_val will be set - * to NULL instead of an allocated empty string. + * @note if AV_OPT_ALLOW_NULL is set in search_flags in av_opt_get, and the + * option is of type AV_OPT_TYPE_STRING, AV_OPT_TYPE_BINARY or AV_OPT_TYPE_DICT + * and is set to NULL, *out_val will be set to NULL instead of an allocated + * empty string. */ int av_opt_get (void *obj, const char *name, int search_flags, uint8_t **out_val); int av_opt_get_int (void *obj, const char *name, int search_flags, int64_t *out_val); diff --git a/libavutil/pixdesc.c b/libavutil/pixdesc.c index b97b0665b0a..9d61c525676 100644 --- a/libavutil/pixdesc.c +++ b/libavutil/pixdesc.c @@ -205,6 +205,29 @@ static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = { { 0, 4, 1, 0, 8, 3, 7, 2 }, /* V */ }, }, + [AV_PIX_FMT_Y210LE] = { + .name = "y210le", + .nb_components = 3, + .log2_chroma_w = 1, + .log2_chroma_h = 0, + .comp = { + { 0, 4, 0, 6, 10, 3, 9, 1 }, /* Y */ + { 0, 8, 2, 6, 10, 7, 9, 3 }, /* U */ + { 0, 8, 6, 6, 10, 7, 9, 7 }, /* V */ + }, + }, + [AV_PIX_FMT_Y210BE] = { + .name = "y210be", + .nb_components = 3, + .log2_chroma_w = 1, + .log2_chroma_h = 0, + .comp = { + { 0, 4, 0, 6, 10, 3, 9, 1 }, /* Y */ + { 0, 8, 2, 6, 10, 7, 9, 3 }, /* U */ + { 0, 8, 6, 6, 10, 7, 9, 7 }, /* V */ + }, + .flags = AV_PIX_FMT_FLAG_BE, + }, [AV_PIX_FMT_RGB24] = { .name = "rgb24", .nb_components = 3, @@ -2344,6 +2367,10 @@ static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = { }, .flags = AV_PIX_FMT_FLAG_PLANAR, }, + [AV_PIX_FMT_VULKAN] = { + .name = "vulkan", + .flags = AV_PIX_FMT_FLAG_HWACCEL, + }, }; #if FF_API_PLUS1_MINUS1 FF_ENABLE_DEPRECATION_WARNINGS @@ -2369,7 +2396,7 @@ static const char * const color_primaries_names[AVCOL_PRI_NB] = { [AVCOL_PRI_SMPTE428] = "smpte428", [AVCOL_PRI_SMPTE431] = "smpte431", [AVCOL_PRI_SMPTE432] = "smpte432", - [AVCOL_PRI_JEDEC_P22] = "jedec-p22", + [AVCOL_PRI_EBU3213] = "ebu3213", }; static const char * const color_transfer_names[] = { diff --git a/libavutil/pixfmt.h b/libavutil/pixfmt.h index 8b54c9415bb..1c625cfc8a2 100644 --- a/libavutil/pixfmt.h +++ b/libavutil/pixfmt.h @@ -257,18 +257,18 @@ enum AVPixelFormat { AV_PIX_FMT_GBRP14LE, ///< planar GBR 4:4:4 42bpp, little-endian AV_PIX_FMT_YUVJ411P, ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV411P and setting color_range - AV_PIX_FMT_BAYER_BGGR8, ///< bayer, BGBG..(odd line), GRGR..(even line), 8-bit samples */ - AV_PIX_FMT_BAYER_RGGB8, ///< bayer, RGRG..(odd line), GBGB..(even line), 8-bit samples */ - AV_PIX_FMT_BAYER_GBRG8, ///< bayer, GBGB..(odd line), RGRG..(even line), 8-bit samples */ - AV_PIX_FMT_BAYER_GRBG8, ///< bayer, GRGR..(odd line), BGBG..(even line), 8-bit samples */ - AV_PIX_FMT_BAYER_BGGR16LE, ///< bayer, BGBG..(odd line), GRGR..(even line), 16-bit samples, little-endian */ - AV_PIX_FMT_BAYER_BGGR16BE, ///< bayer, BGBG..(odd line), GRGR..(even line), 16-bit samples, big-endian */ - AV_PIX_FMT_BAYER_RGGB16LE, ///< bayer, RGRG..(odd line), GBGB..(even line), 16-bit samples, little-endian */ - AV_PIX_FMT_BAYER_RGGB16BE, ///< bayer, RGRG..(odd line), GBGB..(even line), 16-bit samples, big-endian */ - AV_PIX_FMT_BAYER_GBRG16LE, ///< bayer, GBGB..(odd line), RGRG..(even line), 16-bit samples, little-endian */ - AV_PIX_FMT_BAYER_GBRG16BE, ///< bayer, GBGB..(odd line), RGRG..(even line), 16-bit samples, big-endian */ - AV_PIX_FMT_BAYER_GRBG16LE, ///< bayer, GRGR..(odd line), BGBG..(even line), 16-bit samples, little-endian */ - AV_PIX_FMT_BAYER_GRBG16BE, ///< bayer, GRGR..(odd line), BGBG..(even line), 16-bit samples, big-endian */ + AV_PIX_FMT_BAYER_BGGR8, ///< bayer, BGBG..(odd line), GRGR..(even line), 8-bit samples + AV_PIX_FMT_BAYER_RGGB8, ///< bayer, RGRG..(odd line), GBGB..(even line), 8-bit samples + AV_PIX_FMT_BAYER_GBRG8, ///< bayer, GBGB..(odd line), RGRG..(even line), 8-bit samples + AV_PIX_FMT_BAYER_GRBG8, ///< bayer, GRGR..(odd line), BGBG..(even line), 8-bit samples + AV_PIX_FMT_BAYER_BGGR16LE, ///< bayer, BGBG..(odd line), GRGR..(even line), 16-bit samples, little-endian + AV_PIX_FMT_BAYER_BGGR16BE, ///< bayer, BGBG..(odd line), GRGR..(even line), 16-bit samples, big-endian + AV_PIX_FMT_BAYER_RGGB16LE, ///< bayer, RGRG..(odd line), GBGB..(even line), 16-bit samples, little-endian + AV_PIX_FMT_BAYER_RGGB16BE, ///< bayer, RGRG..(odd line), GBGB..(even line), 16-bit samples, big-endian + AV_PIX_FMT_BAYER_GBRG16LE, ///< bayer, GBGB..(odd line), RGRG..(even line), 16-bit samples, little-endian + AV_PIX_FMT_BAYER_GBRG16BE, ///< bayer, GBGB..(odd line), RGRG..(even line), 16-bit samples, big-endian + AV_PIX_FMT_BAYER_GRBG16LE, ///< bayer, GRGR..(odd line), BGBG..(even line), 16-bit samples, little-endian + AV_PIX_FMT_BAYER_GRBG16BE, ///< bayer, GRGR..(odd line), BGBG..(even line), 16-bit samples, big-endian AV_PIX_FMT_XVMC,///< XVideo Motion Acceleration via common packet passing @@ -348,6 +348,16 @@ enum AVPixelFormat { AV_PIX_FMT_NV24, ///< planar YUV 4:4:4, 24bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V) AV_PIX_FMT_NV42, ///< as above, but U and V bytes are swapped + /** + * Vulkan hardware images. + * + * data[0] points to an AVVkFrame + */ + AV_PIX_FMT_VULKAN, + + AV_PIX_FMT_Y210BE, ///< packed YUV 4:2:2 like YUYV422, 20bpp, data in the high bits, big-endian + AV_PIX_FMT_Y210LE, ///< packed YUV 4:2:2 like YUYV422, 20bpp, data in the high bits, little-endian + AV_PIX_FMT_NB ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions }; @@ -436,6 +446,8 @@ enum AVPixelFormat { #define AV_PIX_FMT_P010 AV_PIX_FMT_NE(P010BE, P010LE) #define AV_PIX_FMT_P016 AV_PIX_FMT_NE(P016BE, P016LE) +#define AV_PIX_FMT_Y210 AV_PIX_FMT_NE(Y210BE, Y210LE) + /** * Chromaticity coordinates of the source primaries. * These values match the ones defined by ISO/IEC 23001-8_2013 § 7.1. @@ -456,7 +468,8 @@ enum AVColorPrimaries { AVCOL_PRI_SMPTEST428_1 = AVCOL_PRI_SMPTE428, AVCOL_PRI_SMPTE431 = 11, ///< SMPTE ST 431-2 (2011) / DCI P3 AVCOL_PRI_SMPTE432 = 12, ///< SMPTE ST 432-1 (2010) / P3 D65 / Display P3 - AVCOL_PRI_JEDEC_P22 = 22, ///< JEDEC P22 phosphors + AVCOL_PRI_EBU3213 = 22, ///< EBU Tech. 3213-E / JEDEC P22 phosphors + AVCOL_PRI_JEDEC_P22 = AVCOL_PRI_EBU3213, AVCOL_PRI_NB ///< Not part of ABI }; diff --git a/libavutil/rational.c b/libavutil/rational.c index 35ee08877f3..eb148ddb12c 100644 --- a/libavutil/rational.c +++ b/libavutil/rational.c @@ -182,3 +182,12 @@ uint32_t av_q2intfloat(AVRational q) { return sign<<31 | (150-shift)<<23 | (n - (1<<23)); } + +AVRational av_gcd_q(AVRational a, AVRational b, int max_den, AVRational def) +{ + int64_t gcd, lcm; + + gcd = av_gcd(a.den, b.den); + lcm = (a.den / gcd) * b.den; + return lcm < max_den ? av_make_q(av_gcd(a.num, b.num), lcm) : def; +} diff --git a/libavutil/rational.h b/libavutil/rational.h index 5c6b67b4e9f..cbb08a0baf1 100644 --- a/libavutil/rational.h +++ b/libavutil/rational.h @@ -207,6 +207,12 @@ int av_find_nearest_q_idx(AVRational q, const AVRational* q_list); */ uint32_t av_q2intfloat(AVRational q); +/** + * Return the best rational so that a and b are multiple of it. + * If the resulting denominator is larger than max_den, return def. + */ +AVRational av_gcd_q(AVRational a, AVRational b, int max_den, AVRational def); + /** * @} */ diff --git a/libavutil/tests/.gitignore b/libavutil/tests/.gitignore new file mode 100644 index 00000000000..9d908279540 --- /dev/null +++ b/libavutil/tests/.gitignore @@ -0,0 +1,51 @@ +/adler32 +/aes +/aes_ctr +/atomic +/audio_fifo +/avstring +/base64 +/blowfish +/bprint +/camellia +/cast5 +/color_utils +/cpu +/cpu_init +/crc +/des +/dict +/display +/error +/encryption_info +/eval +/fifo +/file +/hash +/hmac +/hwdevice +/imgutils +/integer +/lfg +/lls +/log +/lzo +/md5 +/murmur3 +/opt +/parseutils +/pca +/pixdesc +/pixelutils +/pixfmt_best +/random_seed +/rational +/ripemd +/sha +/sha512 +/softfloat +/tea +/tree +/twofish +/utf8 +/xtea diff --git a/libavutil/tests/opt.c b/libavutil/tests/opt.c index f4cfa590aa9..3134ffd354d 100644 --- a/libavutil/tests/opt.c +++ b/libavutil/tests/opt.c @@ -55,6 +55,8 @@ typedef struct TestContext { int bool1; int bool2; int bool3; + AVDictionary *dict1; + AVDictionary *dict2; } TestContext; #define OFFSET(x) offsetof(TestContext, x) @@ -89,6 +91,8 @@ static const AVOption test_options[]= { {"bool1", "set boolean value", OFFSET(bool1), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, 1 }, {"bool2", "set boolean value", OFFSET(bool2), AV_OPT_TYPE_BOOL, { .i64 = 1 }, -1, 1, 1 }, {"bool3", "set boolean value", OFFSET(bool3), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, 1 }, + {"dict1", "set dictionary value", OFFSET(dict1), AV_OPT_TYPE_DICT, { .str = NULL}, 0, 0, 1 }, + {"dict2", "set dictionary value", OFFSET(dict2), AV_OPT_TYPE_DICT, { .str = "happy=':-)'"}, 0, 0, 1 }, { NULL }, }; @@ -167,6 +171,47 @@ int main(void) av_opt_free(&test_ctx); } + printf("\nTesting av_opt_get/av_opt_set()\n"); + { + TestContext test_ctx = { 0 }; + TestContext test2_ctx = { 0 }; + const AVOption *o = NULL; + test_ctx.class = &test_class; + test2_ctx.class = &test_class; + + av_log_set_level(AV_LOG_QUIET); + + av_opt_set_defaults(&test_ctx); + + while (o = av_opt_next(&test_ctx, o)) { + char *value1 = NULL; + char *value2 = NULL; + int ret1 = AVERROR_BUG; + int ret2 = AVERROR_BUG; + int ret3 = AVERROR_BUG; + + if (o->type == AV_OPT_TYPE_CONST) + continue; + + ret1 = av_opt_get(&test_ctx, o->name, 0, (uint8_t **)&value1); + if (ret1 >= 0) { + ret2 = av_opt_set(&test2_ctx, o->name, value1, 0); + if (ret2 >= 0) + ret3 = av_opt_get(&test2_ctx, o->name, 0, (uint8_t **)&value2); + } + + printf("name: %-11s get: %-16s set: %-16s get: %-16s %s\n", o->name, + ret1 >= 0 ? value1 : av_err2str(ret1), + ret2 >= 0 ? "OK" : av_err2str(ret2), + ret3 >= 0 ? value2 : av_err2str(ret3), + ret1 >= 0 && ret2 >= 0 && ret3 >= 0 && !strcmp(value1, value2) ? "OK" : "Mismatch"); + av_free(value1); + av_free(value2); + } + av_opt_free(&test_ctx); + av_opt_free(&test2_ctx); + } + printf("\nTest av_opt_serialize()\n"); { TestContext test_ctx = { 0 }; @@ -256,6 +301,7 @@ int main(void) "dbl=101", "bool1=true", "bool2=auto", + "dict1='happy=\\:-):sad=\\:-('", }; test_ctx.class = &test_class; diff --git a/libavutil/thread.h b/libavutil/thread.h index cc5272d3793..be5c4b1340e 100644 --- a/libavutil/thread.h +++ b/libavutil/thread.h @@ -33,16 +33,19 @@ #include "log.h" +#define ASSERT_PTHREAD_ABORT(func, ret) do { \ + char errbuf[AV_ERROR_MAX_STRING_SIZE] = ""; \ + av_log(NULL, AV_LOG_FATAL, AV_STRINGIFY(func) \ + " failed with error: %s\n", \ + av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, \ + AVERROR(ret))); \ + abort(); \ +} while (0) + #define ASSERT_PTHREAD_NORET(func, ...) do { \ int ret = func(__VA_ARGS__); \ - if (ret) { \ - char errbuf[AV_ERROR_MAX_STRING_SIZE] = ""; \ - av_log(NULL, AV_LOG_FATAL, AV_STRINGIFY(func) \ - " failed with error: %s\n", \ - av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, \ - AVERROR(ret))); \ - abort(); \ - } \ + if (ret) \ + ASSERT_PTHREAD_ABORT(func, ret); \ } while (0) #define ASSERT_PTHREAD(func, ...) do { \ @@ -109,6 +112,15 @@ static inline int strict_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t ASSERT_PTHREAD(pthread_cond_wait, cond, mutex); } +static inline int strict_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + int ret = pthread_cond_timedwait(cond, mutex, abstime); + if (ret && ret != ETIMEDOUT) + ASSERT_PTHREAD_ABORT(pthread_cond_timedwait, ret); + return ret; +} + static inline int strict_pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) { ASSERT_PTHREAD(pthread_once, once_control, init_routine); @@ -124,6 +136,7 @@ static inline int strict_pthread_once(pthread_once_t *once_control, void (*init_ #define pthread_cond_signal strict_pthread_cond_signal #define pthread_cond_broadcast strict_pthread_cond_broadcast #define pthread_cond_wait strict_pthread_cond_wait +#define pthread_cond_timedwait strict_pthread_cond_timedwait #define pthread_once strict_pthread_once #endif diff --git a/libavutil/tx.c b/libavutil/tx.c index 93f6e489d36..3b0568a5e17 100644 --- a/libavutil/tx.c +++ b/libavutil/tx.c @@ -1,10 +1,4 @@ /* - * Copyright (c) 2019 Lynne - * Power of two FFT: - * 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 @@ -22,576 +16,22 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include "tx.h" -#include "thread.h" -#include "mem.h" -#include "avassert.h" - -typedef float FFTSample; -typedef AVComplexFloat FFTComplex; - -struct AVTXContext { - int n; /* Nptwo part */ - int m; /* Ptwo part */ - - FFTComplex *exptab; /* MDCT exptab */ - FFTComplex *tmp; /* Temporary buffer needed for all compound transforms */ - int *pfatab; /* Input/Output mapping for compound transforms */ - int *revtab; /* Input mapping for power of two transforms */ -}; - -#define FFT_NAME(x) x - -#define COSTABLE(size) \ - static DECLARE_ALIGNED(32, FFTSample, FFT_NAME(ff_cos_##size))[size/2] - -static FFTSample * const FFT_NAME(ff_cos_tabs)[18]; - -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 m = 1 << index; - double freq = 2*M_PI/m; - FFTSample *tab = FFT_NAME(ff_cos_tabs)[index]; - for(int i = 0; i <= m/4; i++) - tab[i] = cos(i*freq); - for(int i = 1; i < m/4; i++) - tab[m/2 - i] = tab[i]; -} - -typedef struct CosTabsInitOnce { - void (*func)(void); - AVOnce control; -} CosTabsInitOnce; - -#define INIT_FF_COS_TABS_FUNC(index, size) \ -static av_cold void init_ff_cos_tabs_ ## size (void) \ -{ \ - init_ff_cos_tabs(index); \ -} - -INIT_FF_COS_TABS_FUNC(4, 16) -INIT_FF_COS_TABS_FUNC(5, 32) -INIT_FF_COS_TABS_FUNC(6, 64) -INIT_FF_COS_TABS_FUNC(7, 128) -INIT_FF_COS_TABS_FUNC(8, 256) -INIT_FF_COS_TABS_FUNC(9, 512) -INIT_FF_COS_TABS_FUNC(10, 1024) -INIT_FF_COS_TABS_FUNC(11, 2048) -INIT_FF_COS_TABS_FUNC(12, 4096) -INIT_FF_COS_TABS_FUNC(13, 8192) -INIT_FF_COS_TABS_FUNC(14, 16384) -INIT_FF_COS_TABS_FUNC(15, 32768) -INIT_FF_COS_TABS_FUNC(16, 65536) -INIT_FF_COS_TABS_FUNC(17, 131072) - -static CosTabsInitOnce cos_tabs_init_once[] = { - { NULL }, - { NULL }, - { NULL }, - { NULL }, - { init_ff_cos_tabs_16, AV_ONCE_INIT }, - { init_ff_cos_tabs_32, AV_ONCE_INIT }, - { init_ff_cos_tabs_64, AV_ONCE_INIT }, - { init_ff_cos_tabs_128, AV_ONCE_INIT }, - { init_ff_cos_tabs_256, AV_ONCE_INIT }, - { init_ff_cos_tabs_512, AV_ONCE_INIT }, - { init_ff_cos_tabs_1024, AV_ONCE_INIT }, - { init_ff_cos_tabs_2048, AV_ONCE_INIT }, - { init_ff_cos_tabs_4096, AV_ONCE_INIT }, - { init_ff_cos_tabs_8192, AV_ONCE_INIT }, - { init_ff_cos_tabs_16384, AV_ONCE_INIT }, - { init_ff_cos_tabs_32768, AV_ONCE_INIT }, - { init_ff_cos_tabs_65536, AV_ONCE_INIT }, - { init_ff_cos_tabs_131072, AV_ONCE_INIT }, -}; - -static FFTSample * const FFT_NAME(ff_cos_tabs)[] = { - NULL, NULL, NULL, NULL, - FFT_NAME(ff_cos_16), - FFT_NAME(ff_cos_32), - FFT_NAME(ff_cos_64), - FFT_NAME(ff_cos_128), - FFT_NAME(ff_cos_256), - FFT_NAME(ff_cos_512), - FFT_NAME(ff_cos_1024), - FFT_NAME(ff_cos_2048), - FFT_NAME(ff_cos_4096), - FFT_NAME(ff_cos_8192), - FFT_NAME(ff_cos_16384), - FFT_NAME(ff_cos_32768), - FFT_NAME(ff_cos_65536), - FFT_NAME(ff_cos_131072), -}; - -static av_cold void ff_init_ff_cos_tabs(int index) -{ - ff_thread_once(&cos_tabs_init_once[index].control, - cos_tabs_init_once[index].func); -} - -static AVOnce tabs_53_once = AV_ONCE_INIT; -static DECLARE_ALIGNED(32, FFTComplex, FFT_NAME(ff_53_tabs))[4]; - -static av_cold void ff_init_53_tabs(void) -{ - ff_53_tabs[0] = (FFTComplex){ cos(2 * M_PI / 12), cos(2 * M_PI / 12) }; - ff_53_tabs[1] = (FFTComplex){ 0.5, 0.5 }; - ff_53_tabs[2] = (FFTComplex){ cos(2 * M_PI / 5), sin(2 * M_PI / 5) }; - ff_53_tabs[3] = (FFTComplex){ cos(2 * M_PI / 10), sin(2 * M_PI / 10) }; -} - -#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) - -#define CMUL3(c, a, b) CMUL((c).re, (c).im, (a).re, (a).im, (b).re, (b).im) - -static av_always_inline void fft3(FFTComplex *out, FFTComplex *in, - ptrdiff_t stride) -{ - FFTComplex tmp[2]; - - tmp[0].re = in[1].im - in[2].im; - tmp[0].im = in[1].re - in[2].re; - tmp[1].re = in[1].re + in[2].re; - tmp[1].im = in[1].im + in[2].im; - - out[0*stride].re = in[0].re + tmp[1].re; - out[0*stride].im = in[0].im + tmp[1].im; - - tmp[0].re *= ff_53_tabs[0].re; - tmp[0].im *= ff_53_tabs[0].im; - tmp[1].re *= ff_53_tabs[1].re; - tmp[1].im *= ff_53_tabs[1].re; - - out[1*stride].re = in[0].re - tmp[1].re + tmp[0].re; - out[1*stride].im = in[0].im - tmp[1].im - tmp[0].im; - out[2*stride].re = in[0].re - tmp[1].re - tmp[0].re; - out[2*stride].im = in[0].im - tmp[1].im + tmp[0].im; -} - -#define DECL_FFT5(NAME, D0, D1, D2, D3, D4) \ -static av_always_inline void NAME(FFTComplex *out, FFTComplex *in, \ - ptrdiff_t stride) \ -{ \ - FFTComplex z0[4], t[6]; \ - \ - t[0].re = in[1].re + in[4].re; \ - t[0].im = in[1].im + in[4].im; \ - t[1].im = in[1].re - in[4].re; \ - t[1].re = in[1].im - in[4].im; \ - t[2].re = in[2].re + in[3].re; \ - t[2].im = in[2].im + in[3].im; \ - t[3].im = in[2].re - in[3].re; \ - t[3].re = in[2].im - in[3].im; \ - \ - out[D0*stride].re = in[0].re + in[1].re + in[2].re + \ - in[3].re + in[4].re; \ - out[D0*stride].im = in[0].im + in[1].im + in[2].im + \ - in[3].im + in[4].im; \ - \ - t[4].re = ff_53_tabs[2].re * t[2].re - ff_53_tabs[3].re * t[0].re; \ - t[4].im = ff_53_tabs[2].re * t[2].im - ff_53_tabs[3].re * t[0].im; \ - t[0].re = ff_53_tabs[2].re * t[0].re - ff_53_tabs[3].re * t[2].re; \ - t[0].im = ff_53_tabs[2].re * t[0].im - ff_53_tabs[3].re * t[2].im; \ - t[5].re = ff_53_tabs[2].im * t[3].re - ff_53_tabs[3].im * t[1].re; \ - t[5].im = ff_53_tabs[2].im * t[3].im - ff_53_tabs[3].im * t[1].im; \ - t[1].re = ff_53_tabs[2].im * t[1].re + ff_53_tabs[3].im * t[3].re; \ - t[1].im = ff_53_tabs[2].im * t[1].im + ff_53_tabs[3].im * t[3].im; \ - \ - z0[0].re = t[0].re - t[1].re; \ - z0[0].im = t[0].im - t[1].im; \ - z0[1].re = t[4].re + t[5].re; \ - z0[1].im = t[4].im + t[5].im; \ - \ - z0[2].re = t[4].re - t[5].re; \ - z0[2].im = t[4].im - t[5].im; \ - z0[3].re = t[0].re + t[1].re; \ - z0[3].im = t[0].im + t[1].im; \ - \ - out[D1*stride].re = in[0].re + z0[3].re; \ - out[D1*stride].im = in[0].im + z0[0].im; \ - out[D2*stride].re = in[0].re + z0[2].re; \ - out[D2*stride].im = in[0].im + z0[1].im; \ - out[D3*stride].re = in[0].re + z0[1].re; \ - out[D3*stride].im = in[0].im + z0[2].im; \ - out[D4*stride].re = in[0].re + z0[0].re; \ - out[D4*stride].im = in[0].im + z0[3].im; \ -} - -DECL_FFT5(fft5, 0, 1, 2, 3, 4) -DECL_FFT5(fft5_m1, 0, 6, 12, 3, 9) -DECL_FFT5(fft5_m2, 10, 1, 7, 13, 4) -DECL_FFT5(fft5_m3, 5, 11, 2, 8, 14) - -static av_always_inline void fft15(FFTComplex *out, FFTComplex *in, - ptrdiff_t stride) -{ - FFTComplex tmp[15]; - - for (int i = 0; i < 5; i++) - fft3(tmp + i, in + i*3, 5); - - fft5_m1(out, tmp + 0, stride); - fft5_m2(out, tmp + 5, stride); - fft5_m3(out, tmp + 10, stride); -} - -#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)\ -{\ - FFTSample 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) -#undef BUTTERFLIES -#define BUTTERFLIES BUTTERFLIES_BIG -PASS(pass_big) - -#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) -{ - FFTSample 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) -{ - FFTSample 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],M_SQRT1_2,M_SQRT1_2); -} - -static void fft16(FFTComplex *z) -{ - FFTSample 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); +#include "tx_priv.h" - TRANSFORM_ZERO(z[0],z[4],z[8],z[12]); - TRANSFORM(z[2],z[6],z[10],z[14],M_SQRT1_2,M_SQRT1_2); - 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); -} - -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) -#define pass pass_big -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 -}; - -#define DECL_COMP_FFT(N) \ -static void compound_fft_##N##xM(AVTXContext *s, void *_out, \ - void *_in, ptrdiff_t stride) \ -{ \ - const int m = s->m, *in_map = s->pfatab, *out_map = in_map + N*m; \ - FFTComplex *in = _in; \ - FFTComplex *out = _out; \ - FFTComplex fft##N##in[N]; \ - void (*fftp)(FFTComplex *z) = fft_dispatch[av_log2(m) - 2]; \ - \ - for (int i = 0; i < m; i++) { \ - for (int j = 0; j < N; j++) \ - fft##N##in[j] = in[in_map[i*N + j]]; \ - fft##N(s->tmp + s->revtab[i], fft##N##in, m); \ - } \ - \ - for (int i = 0; i < N; i++) \ - fftp(s->tmp + m*i); \ - \ - for (int i = 0; i < N*m; i++) \ - out[i] = s->tmp[out_map[i]]; \ -} - -DECL_COMP_FFT(3) -DECL_COMP_FFT(5) -DECL_COMP_FFT(15) - -static void monolithic_fft(AVTXContext *s, void *_out, void *_in, - ptrdiff_t stride) -{ - FFTComplex *in = _in; - FFTComplex *out = _out; - int m = s->m, mb = av_log2(m) - 2; - for (int i = 0; i < m; i++) - out[s->revtab[i]] = in[i]; - fft_dispatch[mb](out); -} - -#define DECL_COMP_IMDCT(N) \ -static void compound_imdct_##N##xM(AVTXContext *s, void *_dst, void *_src, \ - ptrdiff_t stride) \ -{ \ - FFTComplex fft##N##in[N]; \ - FFTComplex *z = _dst, *exp = s->exptab; \ - const int m = s->m, len8 = N*m >> 1; \ - const int *in_map = s->pfatab, *out_map = in_map + N*m; \ - const float *src = _src, *in1, *in2; \ - void (*fftp)(FFTComplex *) = fft_dispatch[av_log2(m) - 2]; \ - \ - stride /= sizeof(*src); /* To convert it from bytes */ \ - in1 = src; \ - in2 = src + ((N*m*2) - 1) * stride; \ - \ - for (int i = 0; i < m; i++) { \ - for (int j = 0; j < N; j++) { \ - const int k = in_map[i*N + j]; \ - FFTComplex tmp = { in2[-k*stride], in1[k*stride] }; \ - CMUL3(fft##N##in[j], tmp, exp[k >> 1]); \ - } \ - fft##N(s->tmp + s->revtab[i], fft##N##in, m); \ - } \ - \ - for (int i = 0; i < N; i++) \ - fftp(s->tmp + m*i); \ - \ - for (int i = 0; i < len8; i++) { \ - const int i0 = len8 + i, i1 = len8 - i - 1; \ - const int s0 = out_map[i0], s1 = out_map[i1]; \ - FFTComplex src1 = { s->tmp[s1].im, s->tmp[s1].re }; \ - FFTComplex src0 = { s->tmp[s0].im, s->tmp[s0].re }; \ - \ - CMUL(z[i1].re, z[i0].im, src1.re, src1.im, exp[i1].im, exp[i1].re); \ - CMUL(z[i0].re, z[i1].im, src0.re, src0.im, exp[i0].im, exp[i0].re); \ - } \ -} - -DECL_COMP_IMDCT(3) -DECL_COMP_IMDCT(5) -DECL_COMP_IMDCT(15) - -#define DECL_COMP_MDCT(N) \ -static void compound_mdct_##N##xM(AVTXContext *s, void *_dst, void *_src, \ - ptrdiff_t stride) \ -{ \ - float *src = _src, *dst = _dst; \ - FFTComplex *exp = s->exptab, tmp, fft##N##in[N]; \ - const int m = s->m, len4 = N*m, len3 = len4 * 3, len8 = len4 >> 1; \ - const int *in_map = s->pfatab, *out_map = in_map + N*m; \ - void (*fftp)(FFTComplex *) = fft_dispatch[av_log2(m) - 2]; \ - \ - stride /= sizeof(*dst); \ - \ - for (int i = 0; i < m; i++) { /* Folding and pre-reindexing */ \ - for (int j = 0; j < N; j++) { \ - const int k = in_map[i*N + j]; \ - if (k < len4) { \ - tmp.re = -src[ len4 + k] + src[1*len4 - 1 - k]; \ - tmp.im = -src[ len3 + k] - src[1*len3 - 1 - k]; \ - } else { \ - tmp.re = -src[ len4 + k] - src[5*len4 - 1 - k]; \ - tmp.im = src[-len4 + k] - src[1*len3 - 1 - k]; \ - } \ - CMUL(fft##N##in[j].im, fft##N##in[j].re, tmp.re, tmp.im, \ - exp[k >> 1].re, exp[k >> 1].im); \ - } \ - fft##N(s->tmp + s->revtab[i], fft##N##in, m); \ - } \ - \ - for (int i = 0; i < N; i++) \ - fftp(s->tmp + m*i); \ - \ - for (int i = 0; i < len8; i++) { \ - const int i0 = len8 + i, i1 = len8 - i - 1; \ - const int s0 = out_map[i0], s1 = out_map[i1]; \ - FFTComplex src1 = { s->tmp[s1].re, s->tmp[s1].im }; \ - FFTComplex src0 = { s->tmp[s0].re, s->tmp[s0].im }; \ - \ - CMUL(dst[2*i1*stride + stride], dst[2*i0*stride], src0.re, src0.im, \ - exp[i0].im, exp[i0].re); \ - CMUL(dst[2*i0*stride + stride], dst[2*i1*stride], src1.re, src1.im, \ - exp[i1].im, exp[i1].re); \ - } \ -} - -DECL_COMP_MDCT(3) -DECL_COMP_MDCT(5) -DECL_COMP_MDCT(15) - -static void monolithic_imdct(AVTXContext *s, void *_dst, void *_src, - ptrdiff_t stride) -{ - FFTComplex *z = _dst, *exp = s->exptab; - const int m = s->m, len8 = m >> 1; - const float *src = _src, *in1, *in2; - void (*fftp)(FFTComplex *) = fft_dispatch[av_log2(m) - 2]; - - stride /= sizeof(*src); - in1 = src; - in2 = src + ((m*2) - 1) * stride; - - for (int i = 0; i < m; i++) { - FFTComplex tmp = { in2[-2*i*stride], in1[2*i*stride] }; - CMUL3(z[s->revtab[i]], tmp, exp[i]); - } - - fftp(z); - - for (int i = 0; i < len8; i++) { - const int i0 = len8 + i, i1 = len8 - i - 1; - FFTComplex src1 = { z[i1].im, z[i1].re }; - FFTComplex src0 = { z[i0].im, z[i0].re }; - - CMUL(z[i1].re, z[i0].im, src1.re, src1.im, exp[i1].im, exp[i1].re); - CMUL(z[i0].re, z[i1].im, src0.re, src0.im, exp[i0].im, exp[i0].re); - } -} - -static void monolithic_mdct(AVTXContext *s, void *_dst, void *_src, - ptrdiff_t stride) +int ff_tx_type_is_mdct(enum AVTXType type) { - float *src = _src, *dst = _dst; - FFTComplex *exp = s->exptab, tmp, *z = _dst; - const int m = s->m, len4 = m, len3 = len4 * 3, len8 = len4 >> 1; - void (*fftp)(FFTComplex *) = fft_dispatch[av_log2(m) - 2]; - - stride /= sizeof(*dst); - - for (int i = 0; i < m; i++) { /* Folding and pre-reindexing */ - const int k = 2*i; - if (k < len4) { - tmp.re = -src[ len4 + k] + src[1*len4 - 1 - k]; - tmp.im = -src[ len3 + k] - src[1*len3 - 1 - k]; - } else { - tmp.re = -src[ len4 + k] - src[5*len4 - 1 - k]; - tmp.im = src[-len4 + k] - src[1*len3 - 1 - k]; - } - CMUL(z[s->revtab[i]].im, z[s->revtab[i]].re, tmp.re, tmp.im, - exp[i].re, exp[i].im); - } - - fftp(z); - - for (int i = 0; i < len8; i++) { - const int i0 = len8 + i, i1 = len8 - i - 1; - FFTComplex src1 = { z[i1].re, z[i1].im }; - FFTComplex src0 = { z[i0].re, z[i0].im }; - - CMUL(dst[2*i1*stride + stride], dst[2*i0*stride], src0.re, src0.im, - exp[i0].im, exp[i0].re); - CMUL(dst[2*i0*stride + stride], dst[2*i1*stride], src1.re, src1.im, - exp[i1].im, exp[i1].re); + switch (type) { + case AV_TX_FLOAT_MDCT: + case AV_TX_DOUBLE_MDCT: + case AV_TX_INT32_MDCT: + return 1; + default: + return 0; } } /* Calculates the modular multiplicative inverse, not fast, replace */ -static int mulinv(int n, int m) +static av_always_inline int mulinv(int n, int m) { n = n % m; for (int x = 1; x < m; x++) @@ -601,14 +41,16 @@ static int mulinv(int n, int m) } /* Guaranteed to work for any n, m where gcd(n, m) == 1 */ -static int gen_compound_mapping(AVTXContext *s, int n, int m, int inv, - enum AVTXType type) +int ff_tx_gen_compound_mapping(AVTXContext *s) { int *in_map, *out_map; + const int n = s->n; + const int m = s->m; + const int inv = s->inv; const int len = n*m; const int m_inv = mulinv(m, n); const int n_inv = mulinv(n, m); - const int mdct = type == AV_TX_FLOAT_MDCT; + const int mdct = ff_tx_type_is_mdct(s->type); if (!(s->pfatab = av_malloc(2*len*sizeof(*s->pfatab)))) return AVERROR(ENOMEM); @@ -619,7 +61,7 @@ static int gen_compound_mapping(AVTXContext *s, int n, int m, int inv, /* Ruritanian map for input, CRT map for output, can be swapped */ for (int j = 0; j < m; j++) { for (int i = 0; i < n; i++) { - /* Shifted by 1 to simplify forward MDCTs */ + /* Shifted by 1 to simplify MDCTs */ in_map[j*n + i] = ((i*m + j*n) % len) << mdct; out_map[(i*m*m_inv + j*n*n_inv) % len] = i*m + j; } @@ -649,23 +91,10 @@ static int gen_compound_mapping(AVTXContext *s, int n, int m, int inv, return 0; } -static int split_radix_permutation(int i, int n, int inverse) +int ff_tx_gen_ptwo_revtab(AVTXContext *s) { - int m; - if (n <= 2) - return i & 1; - m = n >> 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; -} + const int m = s->m, inv = s->inv; -static int get_ptwo_revtab(AVTXContext *s, int m, int inv) -{ if (!(s->revtab = av_malloc(m*sizeof(*s->revtab)))) return AVERROR(ENOMEM); @@ -678,23 +107,6 @@ static int get_ptwo_revtab(AVTXContext *s, int m, int inv) return 0; } -static int gen_mdct_exptab(AVTXContext *s, int len4, double scale) -{ - const double theta = (scale < 0 ? len4 : 0) + 1.0/8.0; - - if (!(s->exptab = av_malloc_array(len4, sizeof(*s->exptab)))) - return AVERROR(ENOMEM); - - scale = sqrt(fabs(scale)); - for (int i = 0; i < len4; i++) { - const double alpha = M_PI_2 * (i + theta) / len4; - s->exptab[i].re = cos(alpha) * scale; - s->exptab[i].im = sin(alpha) * scale; - } - - return 0; -} - av_cold void av_tx_uninit(AVTXContext **ctx) { if (!(*ctx)) @@ -708,71 +120,6 @@ av_cold void av_tx_uninit(AVTXContext **ctx) av_freep(ctx); } -static int init_mdct_fft(AVTXContext *s, av_tx_fn *tx, enum AVTXType type, - int inv, int len, const void *scale, uint64_t flags) -{ - int err, n = 1, m = 1, max_ptwo = 1 << (FF_ARRAY_ELEMS(fft_dispatch) + 1); - - if (type == AV_TX_FLOAT_MDCT) - len >>= 1; - -#define CHECK_FACTOR(DST, FACTOR, SRC) \ - if (DST == 1 && !(SRC % FACTOR)) { \ - DST = FACTOR; \ - SRC /= FACTOR; \ - } - CHECK_FACTOR(n, 15, len) - CHECK_FACTOR(n, 5, len) - CHECK_FACTOR(n, 3, len) -#undef CHECK_NPTWO_FACTOR - - /* len must be a power of two now */ - if (!(len & (len - 1)) && len >= 4 && len <= max_ptwo) { - m = len; - len = 1; - } - - /* Filter out direct 3, 5 and 15 transforms, too niche */ - if (len > 1 || m == 1) { - av_log(NULL, AV_LOG_ERROR, "Unsupported transform size: n = %i, " - "m = %i, residual = %i!\n", n, m, len); - return AVERROR(EINVAL); - } else if (n > 1 && m > 1) { /* 2D transform case */ - if ((err = gen_compound_mapping(s, n, m, inv, type))) - return err; - if (!(s->tmp = av_malloc(n*m*sizeof(*s->tmp)))) - return AVERROR(ENOMEM); - *tx = n == 3 ? compound_fft_3xM : - n == 5 ? compound_fft_5xM : - compound_fft_15xM; - if (type == AV_TX_FLOAT_MDCT) - *tx = n == 3 ? inv ? compound_imdct_3xM : compound_mdct_3xM : - n == 5 ? inv ? compound_imdct_5xM : compound_mdct_5xM : - inv ? compound_imdct_15xM : compound_mdct_15xM; - } else { /* Direct transform case */ - *tx = monolithic_fft; - if (type == AV_TX_FLOAT_MDCT) - *tx = inv ? monolithic_imdct : monolithic_mdct; - } - - if (n != 1) - ff_thread_once(&tabs_53_once, ff_init_53_tabs); - if (m != 1) { - get_ptwo_revtab(s, m, inv); - for (int i = 4; i <= av_log2(m); i++) - ff_init_ff_cos_tabs(i); - } - - if (type == AV_TX_FLOAT_MDCT) - if ((err = gen_mdct_exptab(s, n*m, *((float *)scale)))) - return err; - - s->n = n; - s->m = m; - - return 0; -} - av_cold int av_tx_init(AVTXContext **ctx, av_tx_fn *tx, enum AVTXType type, int inv, int len, const void *scale, uint64_t flags) { @@ -784,7 +131,17 @@ av_cold int av_tx_init(AVTXContext **ctx, av_tx_fn *tx, enum AVTXType type, switch (type) { case AV_TX_FLOAT_FFT: case AV_TX_FLOAT_MDCT: - if ((err = init_mdct_fft(s, tx, type, inv, len, scale, flags))) + if ((err = ff_tx_init_mdct_fft_float(s, tx, type, inv, len, scale, flags))) + goto fail; + break; + case AV_TX_DOUBLE_FFT: + case AV_TX_DOUBLE_MDCT: + if ((err = ff_tx_init_mdct_fft_double(s, tx, type, inv, len, scale, flags))) + goto fail; + break; + case AV_TX_INT32_FFT: + case AV_TX_INT32_MDCT: + if ((err = ff_tx_init_mdct_fft_int32(s, tx, type, inv, len, scale, flags))) goto fail; break; default: diff --git a/libavutil/tx.h b/libavutil/tx.h index b1f2d963533..418e8ec1ed1 100644 --- a/libavutil/tx.h +++ b/libavutil/tx.h @@ -28,17 +28,50 @@ typedef struct AVComplexFloat { float re, im; } AVComplexFloat; +typedef struct AVComplexDouble { + double re, im; +} AVComplexDouble; + +typedef struct AVComplexInt32 { + int32_t re, im; +} AVComplexInt32; + enum AVTXType { /** * Standard complex to complex FFT with sample data type AVComplexFloat. - * Scaling currently unsupported + * Output is not 1/len normalized. Scaling currently unsupported. + * The stride parameter is ignored. */ AV_TX_FLOAT_FFT = 0, /** * Standard MDCT with sample data type of float and a scale type of * float. Length is the frame size, not the window size (which is 2x frame) + * For forward transforms, the stride specifies the spacing between each + * sample in the output array in bytes. The input must be a flat array. + * For inverse transforms, the stride specifies the spacing between each + * sample in the input array in bytes. The output will be a flat array. + * Stride must be a non-zero multiple of sizeof(float). */ AV_TX_FLOAT_MDCT = 1, + /** + * Same as AV_TX_FLOAT_FFT with a data type of AVComplexDouble. + */ + AV_TX_DOUBLE_FFT = 2, + /** + * Same as AV_TX_FLOAT_MDCT with data and scale type of double. + * Stride must be a non-zero multiple of sizeof(double). + */ + AV_TX_DOUBLE_MDCT = 3, + /** + * Same as AV_TX_FLOAT_FFT with a data type of AVComplexInt32. + */ + AV_TX_INT32_FFT = 4, + /** + * Same as AV_TX_FLOAT_MDCT with data type of int32_t and scale type of float. + * Only scale values less than or equal to 1.0 are supported. + * Stride must be a non-zero multiple of sizeof(int32_t). + */ + AV_TX_INT32_MDCT = 5, }; /** @@ -50,14 +83,17 @@ enum AVTXType { * @param s the transform context * @param out the output array * @param in the input array - * @param stride the input or output stride (depending on transform direction) - * in bytes, currently implemented for all MDCT transforms + * @param stride the input or output stride in bytes + * + * The out and in arrays must be aligned to the maximum required by the CPU + * architecture. + * The stride must follow the constraints the transform type has specified. */ typedef void (*av_tx_fn)(AVTXContext *s, void *out, void *in, ptrdiff_t stride); /** * Initialize a transform context with the given configuration - * Currently power of two lengths from 4 to 131072 are supported, along with + * Currently power of two lengths from 2 to 131072 are supported, along with * any length decomposable to a power of two and either 3, 5 or 15. * * @param ctx the context to allocate, will be NULL on error diff --git a/libavdevice/libndi_newtek_common.h b/libavutil/tx_double.c similarity index 73% rename from libavdevice/libndi_newtek_common.h rename to libavutil/tx_double.c index 899031707d1..7ea4283c947 100644 --- a/libavdevice/libndi_newtek_common.h +++ b/libavutil/tx_double.c @@ -1,7 +1,4 @@ /* - * NewTek NDI common code - * Copyright (c) 2017 Maksym Veremeyenko - * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -19,12 +16,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVDEVICE_LIBNDI_NEWTEK_COMMON_H -#define AVDEVICE_LIBNDI_NEWTEK_COMMON_H - -#include - -#define NDI_TIME_BASE 10000000 -#define NDI_TIME_BASE_Q (AVRational){1, NDI_TIME_BASE} - -#endif +#define TX_DOUBLE +#include "tx_priv.h" +#include "tx_template.c" diff --git a/libavutil/tx_float.c b/libavutil/tx_float.c new file mode 100644 index 00000000000..018f2a21acc --- /dev/null +++ b/libavutil/tx_float.c @@ -0,0 +1,21 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 TX_FLOAT +#include "tx_priv.h" +#include "tx_template.c" diff --git a/libavutil/tx_int32.c b/libavutil/tx_int32.c new file mode 100644 index 00000000000..9261013bf61 --- /dev/null +++ b/libavutil/tx_int32.c @@ -0,0 +1,21 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 TX_INT32 +#include "tx_priv.h" +#include "tx_template.c" diff --git a/libavutil/tx_priv.h b/libavutil/tx_priv.h new file mode 100644 index 00000000000..e0d980abfba --- /dev/null +++ b/libavutil/tx_priv.h @@ -0,0 +1,154 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 AVUTIL_TX_PRIV_H +#define AVUTIL_TX_PRIV_H + +#include "tx.h" +#include +#include "thread.h" +#include "mem.h" +#include "avassert.h" +#include "attributes.h" + +#ifdef TX_FLOAT +#define TX_NAME(x) x ## _float +#define SCALE_TYPE float +typedef float FFTSample; +typedef AVComplexFloat FFTComplex; +#elif defined(TX_DOUBLE) +#define TX_NAME(x) x ## _double +#define SCALE_TYPE double +typedef double FFTSample; +typedef AVComplexDouble FFTComplex; +#elif defined(TX_INT32) +#define TX_NAME(x) x ## _int32 +#define SCALE_TYPE float +typedef int32_t FFTSample; +typedef AVComplexInt32 FFTComplex; +#else +typedef void FFTComplex; +#endif + +#if defined(TX_FLOAT) || defined(TX_DOUBLE) + +#define CMUL(dre, dim, are, aim, bre, bim) do { \ + (dre) = (are) * (bre) - (aim) * (bim); \ + (dim) = (are) * (bim) + (aim) * (bre); \ + } while (0) + +#define SMUL(dre, dim, are, aim, bre, bim) do { \ + (dre) = (are) * (bre) - (aim) * (bim); \ + (dim) = (are) * (bim) - (aim) * (bre); \ + } while (0) + +#define RESCALE(x) (x) + +#define FOLD(a, b) ((a) + (b)) + +#elif defined(TX_INT32) + +/* Properly rounds the result */ +#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)(bim) * (are); \ + (accu) += (int64_t)(bre) * (aim); \ + (dim) = (int)(((accu) + 0x40000000) >> 31); \ + } while (0) + +#define SMUL(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)(bim) * (are); \ + (accu) -= (int64_t)(bre) * (aim); \ + (dim) = (int)(((accu) + 0x40000000) >> 31); \ + } while (0) + +#define RESCALE(x) (lrintf((x) * 2147483648.0)) + +#define FOLD(x, y) ((int)((x) + (unsigned)(y) + 32) >> 6) + +#endif + +#define BF(x, y, a, b) do { \ + x = (a) - (b); \ + y = (a) + (b); \ + } while (0) + +#define CMUL3(c, a, b) \ + CMUL((c).re, (c).im, (a).re, (a).im, (b).re, (b).im) + +#define COSTABLE(size) \ + DECLARE_ALIGNED(32, FFTSample, TX_NAME(ff_cos_##size))[size/2] + +/* Used by asm, reorder with care */ +struct AVTXContext { + int n; /* Nptwo part */ + int m; /* Ptwo part */ + int inv; /* Is inverted */ + int type; /* Type */ + + FFTComplex *exptab; /* MDCT exptab */ + FFTComplex *tmp; /* Temporary buffer needed for all compound transforms */ + int *pfatab; /* Input/Output mapping for compound transforms */ + int *revtab; /* Input mapping for power of two transforms */ +}; + +/* Shared functions */ +int ff_tx_type_is_mdct(enum AVTXType type); +int ff_tx_gen_compound_mapping(AVTXContext *s); +int ff_tx_gen_ptwo_revtab(AVTXContext *s); + +/* Also used by SIMD init */ +static inline int split_radix_permutation(int i, int n, int inverse) +{ + int m; + if (n <= 2) + return i & 1; + m = n >> 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; +} + +/* Templated functions */ +int ff_tx_init_mdct_fft_float(AVTXContext *s, av_tx_fn *tx, + enum AVTXType type, int inv, int len, + const void *scale, uint64_t flags); +int ff_tx_init_mdct_fft_double(AVTXContext *s, av_tx_fn *tx, + enum AVTXType type, int inv, int len, + const void *scale, uint64_t flags); +int ff_tx_init_mdct_fft_int32(AVTXContext *s, av_tx_fn *tx, + enum AVTXType type, int inv, int len, + const void *scale, uint64_t flags); + +typedef struct CosTabsInitOnce { + void (*func)(void); + AVOnce control; +} CosTabsInitOnce; + +#endif /* AVUTIL_TX_PRIV_H */ diff --git a/libavutil/tx_template.c b/libavutil/tx_template.c new file mode 100644 index 00000000000..7f4ca2f31ec --- /dev/null +++ b/libavutil/tx_template.c @@ -0,0 +1,639 @@ +/* + * Copyright (c) 2019 Lynne + * Power of two FFT: + * 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 + */ + +/* All costabs for a type are defined here */ +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); +DECLARE_ALIGNED(32, FFTComplex, TX_NAME(ff_cos_53))[4]; + +static FFTSample * const cos_tabs[18] = { + NULL, + NULL, + NULL, + NULL, + TX_NAME(ff_cos_16), + TX_NAME(ff_cos_32), + TX_NAME(ff_cos_64), + TX_NAME(ff_cos_128), + TX_NAME(ff_cos_256), + TX_NAME(ff_cos_512), + TX_NAME(ff_cos_1024), + TX_NAME(ff_cos_2048), + TX_NAME(ff_cos_4096), + TX_NAME(ff_cos_8192), + TX_NAME(ff_cos_16384), + TX_NAME(ff_cos_32768), + TX_NAME(ff_cos_65536), + TX_NAME(ff_cos_131072), +}; + +static av_always_inline void init_cos_tabs_idx(int index) +{ + int m = 1 << index; + double freq = 2*M_PI/m; + FFTSample *tab = cos_tabs[index]; + for(int i = 0; i <= m/4; i++) + tab[i] = RESCALE(cos(i*freq)); + for(int i = 1; i < m/4; i++) + tab[m/2 - i] = tab[i]; +} + +#define INIT_FF_COS_TABS_FUNC(index, size) \ +static av_cold void init_cos_tabs_ ## size (void) \ +{ \ + init_cos_tabs_idx(index); \ +} + +INIT_FF_COS_TABS_FUNC(4, 16) +INIT_FF_COS_TABS_FUNC(5, 32) +INIT_FF_COS_TABS_FUNC(6, 64) +INIT_FF_COS_TABS_FUNC(7, 128) +INIT_FF_COS_TABS_FUNC(8, 256) +INIT_FF_COS_TABS_FUNC(9, 512) +INIT_FF_COS_TABS_FUNC(10, 1024) +INIT_FF_COS_TABS_FUNC(11, 2048) +INIT_FF_COS_TABS_FUNC(12, 4096) +INIT_FF_COS_TABS_FUNC(13, 8192) +INIT_FF_COS_TABS_FUNC(14, 16384) +INIT_FF_COS_TABS_FUNC(15, 32768) +INIT_FF_COS_TABS_FUNC(16, 65536) +INIT_FF_COS_TABS_FUNC(17, 131072) + +static av_cold void ff_init_53_tabs(void) +{ + TX_NAME(ff_cos_53)[0] = (FFTComplex){ RESCALE(cos(2 * M_PI / 12)), RESCALE(cos(2 * M_PI / 12)) }; + TX_NAME(ff_cos_53)[1] = (FFTComplex){ RESCALE(cos(2 * M_PI / 6)), RESCALE(cos(2 * M_PI / 6)) }; + TX_NAME(ff_cos_53)[2] = (FFTComplex){ RESCALE(cos(2 * M_PI / 5)), RESCALE(sin(2 * M_PI / 5)) }; + TX_NAME(ff_cos_53)[3] = (FFTComplex){ RESCALE(cos(2 * M_PI / 10)), RESCALE(sin(2 * M_PI / 10)) }; +} + +static CosTabsInitOnce cos_tabs_init_once[] = { + { ff_init_53_tabs, AV_ONCE_INIT }, + { NULL }, + { NULL }, + { NULL }, + { init_cos_tabs_16, AV_ONCE_INIT }, + { init_cos_tabs_32, AV_ONCE_INIT }, + { init_cos_tabs_64, AV_ONCE_INIT }, + { init_cos_tabs_128, AV_ONCE_INIT }, + { init_cos_tabs_256, AV_ONCE_INIT }, + { init_cos_tabs_512, AV_ONCE_INIT }, + { init_cos_tabs_1024, AV_ONCE_INIT }, + { init_cos_tabs_2048, AV_ONCE_INIT }, + { init_cos_tabs_4096, AV_ONCE_INIT }, + { init_cos_tabs_8192, AV_ONCE_INIT }, + { init_cos_tabs_16384, AV_ONCE_INIT }, + { init_cos_tabs_32768, AV_ONCE_INIT }, + { init_cos_tabs_65536, AV_ONCE_INIT }, + { init_cos_tabs_131072, AV_ONCE_INIT }, +}; + +static av_cold void init_cos_tabs(int index) +{ + ff_thread_once(&cos_tabs_init_once[index].control, + cos_tabs_init_once[index].func); +} + +static av_always_inline void fft3(FFTComplex *out, FFTComplex *in, + ptrdiff_t stride) +{ + FFTComplex tmp[2]; +#ifdef TX_INT32 + int64_t mtmp[4]; +#endif + + BF(tmp[0].re, tmp[1].im, in[1].im, in[2].im); + BF(tmp[0].im, tmp[1].re, in[1].re, in[2].re); + + out[0*stride].re = in[0].re + tmp[1].re; + out[0*stride].im = in[0].im + tmp[1].im; + +#ifdef TX_INT32 + mtmp[0] = (int64_t)TX_NAME(ff_cos_53)[0].re * tmp[0].re; + mtmp[1] = (int64_t)TX_NAME(ff_cos_53)[0].im * tmp[0].im; + mtmp[2] = (int64_t)TX_NAME(ff_cos_53)[1].re * tmp[1].re; + mtmp[3] = (int64_t)TX_NAME(ff_cos_53)[1].re * tmp[1].im; + out[1*stride].re = in[0].re - (mtmp[2] + mtmp[0] + 0x40000000 >> 31); + out[1*stride].im = in[0].im - (mtmp[3] - mtmp[1] + 0x40000000 >> 31); + out[2*stride].re = in[0].re - (mtmp[2] - mtmp[0] + 0x40000000 >> 31); + out[2*stride].im = in[0].im - (mtmp[3] + mtmp[1] + 0x40000000 >> 31); +#else + tmp[0].re = TX_NAME(ff_cos_53)[0].re * tmp[0].re; + tmp[0].im = TX_NAME(ff_cos_53)[0].im * tmp[0].im; + tmp[1].re = TX_NAME(ff_cos_53)[1].re * tmp[1].re; + tmp[1].im = TX_NAME(ff_cos_53)[1].re * tmp[1].im; + out[1*stride].re = in[0].re - tmp[1].re + tmp[0].re; + out[1*stride].im = in[0].im - tmp[1].im - tmp[0].im; + out[2*stride].re = in[0].re - tmp[1].re - tmp[0].re; + out[2*stride].im = in[0].im - tmp[1].im + tmp[0].im; +#endif +} + +#define DECL_FFT5(NAME, D0, D1, D2, D3, D4) \ +static av_always_inline void NAME(FFTComplex *out, FFTComplex *in, \ + ptrdiff_t stride) \ +{ \ + FFTComplex z0[4], t[6]; \ + \ + BF(t[1].im, t[0].re, in[1].re, in[4].re); \ + BF(t[1].re, t[0].im, in[1].im, in[4].im); \ + BF(t[3].im, t[2].re, in[2].re, in[3].re); \ + BF(t[3].re, t[2].im, in[2].im, in[3].im); \ + \ + out[D0*stride].re = in[0].re + t[0].re + t[2].re; \ + out[D0*stride].im = in[0].im + t[0].im + t[2].im; \ + \ + SMUL(t[4].re, t[0].re, TX_NAME(ff_cos_53)[2].re, TX_NAME(ff_cos_53)[3].re, t[2].re, t[0].re); \ + SMUL(t[4].im, t[0].im, TX_NAME(ff_cos_53)[2].re, TX_NAME(ff_cos_53)[3].re, t[2].im, t[0].im); \ + CMUL(t[5].re, t[1].re, TX_NAME(ff_cos_53)[2].im, TX_NAME(ff_cos_53)[3].im, t[3].re, t[1].re); \ + CMUL(t[5].im, t[1].im, TX_NAME(ff_cos_53)[2].im, TX_NAME(ff_cos_53)[3].im, t[3].im, t[1].im); \ + \ + BF(z0[0].re, z0[3].re, t[0].re, t[1].re); \ + BF(z0[0].im, z0[3].im, t[0].im, t[1].im); \ + BF(z0[2].re, z0[1].re, t[4].re, t[5].re); \ + BF(z0[2].im, z0[1].im, t[4].im, t[5].im); \ + \ + out[D1*stride].re = in[0].re + z0[3].re; \ + out[D1*stride].im = in[0].im + z0[0].im; \ + out[D2*stride].re = in[0].re + z0[2].re; \ + out[D2*stride].im = in[0].im + z0[1].im; \ + out[D3*stride].re = in[0].re + z0[1].re; \ + out[D3*stride].im = in[0].im + z0[2].im; \ + out[D4*stride].re = in[0].re + z0[0].re; \ + out[D4*stride].im = in[0].im + z0[3].im; \ +} + +DECL_FFT5(fft5, 0, 1, 2, 3, 4) +DECL_FFT5(fft5_m1, 0, 6, 12, 3, 9) +DECL_FFT5(fft5_m2, 10, 1, 7, 13, 4) +DECL_FFT5(fft5_m3, 5, 11, 2, 8, 14) + +static av_always_inline void fft15(FFTComplex *out, FFTComplex *in, + ptrdiff_t stride) +{ + FFTComplex tmp[15]; + + for (int i = 0; i < 5; i++) + fft3(tmp + i, in + i*3, 5); + + fft5_m1(out, tmp + 0, stride); + fft5_m2(out, tmp + 5, stride); + fft5_m3(out, tmp + 10, stride); +} + +#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)\ +{\ + FFTSample 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) +#undef BUTTERFLIES +#define BUTTERFLIES BUTTERFLIES_BIG +PASS(pass_big) + +#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,TX_NAME(ff_cos_##n),n4/2);\ +} + +static void fft2(FFTComplex *z) +{ + FFTComplex tmp; + BF(tmp.re, z[0].re, z[0].re, z[1].re); + BF(tmp.im, z[0].im, z[0].im, z[1].im); + z[1] = tmp; +} + +static void fft4(FFTComplex *z) +{ + FFTSample 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) +{ + FFTSample 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],RESCALE(M_SQRT1_2),RESCALE(M_SQRT1_2)); +} + +static void fft16(FFTComplex *z) +{ + FFTSample t1, t2, t3, t4, t5, t6; + FFTSample cos_16_1 = TX_NAME(ff_cos_16)[1]; + FFTSample cos_16_3 = TX_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],RESCALE(M_SQRT1_2),RESCALE(M_SQRT1_2)); + 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); +} + +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) +#define pass pass_big +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*) = { + NULL, fft2, fft4, fft8, fft16, fft32, fft64, fft128, fft256, fft512, + fft1024, fft2048, fft4096, fft8192, fft16384, fft32768, fft65536, fft131072 +}; + +#define DECL_COMP_FFT(N) \ +static void compound_fft_##N##xM(AVTXContext *s, void *_out, \ + void *_in, ptrdiff_t stride) \ +{ \ + const int m = s->m, *in_map = s->pfatab, *out_map = in_map + N*m; \ + FFTComplex *in = _in; \ + FFTComplex *out = _out; \ + FFTComplex fft##N##in[N]; \ + void (*fftp)(FFTComplex *z) = fft_dispatch[av_log2(m)]; \ + \ + for (int i = 0; i < m; i++) { \ + for (int j = 0; j < N; j++) \ + fft##N##in[j] = in[in_map[i*N + j]]; \ + fft##N(s->tmp + s->revtab[i], fft##N##in, m); \ + } \ + \ + for (int i = 0; i < N; i++) \ + fftp(s->tmp + m*i); \ + \ + for (int i = 0; i < N*m; i++) \ + out[i] = s->tmp[out_map[i]]; \ +} + +DECL_COMP_FFT(3) +DECL_COMP_FFT(5) +DECL_COMP_FFT(15) + +static void monolithic_fft(AVTXContext *s, void *_out, void *_in, + ptrdiff_t stride) +{ + FFTComplex *in = _in; + FFTComplex *out = _out; + int m = s->m, mb = av_log2(m); + for (int i = 0; i < m; i++) + out[s->revtab[i]] = in[i]; + fft_dispatch[mb](out); +} + +#define DECL_COMP_IMDCT(N) \ +static void compound_imdct_##N##xM(AVTXContext *s, void *_dst, void *_src, \ + ptrdiff_t stride) \ +{ \ + FFTComplex fft##N##in[N]; \ + FFTComplex *z = _dst, *exp = s->exptab; \ + const int m = s->m, len8 = N*m >> 1; \ + const int *in_map = s->pfatab, *out_map = in_map + N*m; \ + const FFTSample *src = _src, *in1, *in2; \ + void (*fftp)(FFTComplex *) = fft_dispatch[av_log2(m)]; \ + \ + stride /= sizeof(*src); /* To convert it from bytes */ \ + in1 = src; \ + in2 = src + ((N*m*2) - 1) * stride; \ + \ + for (int i = 0; i < m; i++) { \ + for (int j = 0; j < N; j++) { \ + const int k = in_map[i*N + j]; \ + FFTComplex tmp = { in2[-k*stride], in1[k*stride] }; \ + CMUL3(fft##N##in[j], tmp, exp[k >> 1]); \ + } \ + fft##N(s->tmp + s->revtab[i], fft##N##in, m); \ + } \ + \ + for (int i = 0; i < N; i++) \ + fftp(s->tmp + m*i); \ + \ + for (int i = 0; i < len8; i++) { \ + const int i0 = len8 + i, i1 = len8 - i - 1; \ + const int s0 = out_map[i0], s1 = out_map[i1]; \ + FFTComplex src1 = { s->tmp[s1].im, s->tmp[s1].re }; \ + FFTComplex src0 = { s->tmp[s0].im, s->tmp[s0].re }; \ + \ + CMUL(z[i1].re, z[i0].im, src1.re, src1.im, exp[i1].im, exp[i1].re); \ + CMUL(z[i0].re, z[i1].im, src0.re, src0.im, exp[i0].im, exp[i0].re); \ + } \ +} + +DECL_COMP_IMDCT(3) +DECL_COMP_IMDCT(5) +DECL_COMP_IMDCT(15) + +#define DECL_COMP_MDCT(N) \ +static void compound_mdct_##N##xM(AVTXContext *s, void *_dst, void *_src, \ + ptrdiff_t stride) \ +{ \ + FFTSample *src = _src, *dst = _dst; \ + FFTComplex *exp = s->exptab, tmp, fft##N##in[N]; \ + const int m = s->m, len4 = N*m, len3 = len4 * 3, len8 = len4 >> 1; \ + const int *in_map = s->pfatab, *out_map = in_map + N*m; \ + void (*fftp)(FFTComplex *) = fft_dispatch[av_log2(m)]; \ + \ + stride /= sizeof(*dst); \ + \ + for (int i = 0; i < m; i++) { /* Folding and pre-reindexing */ \ + for (int j = 0; j < N; j++) { \ + const int k = in_map[i*N + j]; \ + if (k < len4) { \ + tmp.re = FOLD(-src[ len4 + k], src[1*len4 - 1 - k]); \ + tmp.im = FOLD(-src[ len3 + k], -src[1*len3 - 1 - k]); \ + } else { \ + tmp.re = FOLD(-src[ len4 + k], -src[5*len4 - 1 - k]); \ + tmp.im = FOLD( src[-len4 + k], -src[1*len3 - 1 - k]); \ + } \ + CMUL(fft##N##in[j].im, fft##N##in[j].re, tmp.re, tmp.im, \ + exp[k >> 1].re, exp[k >> 1].im); \ + } \ + fft##N(s->tmp + s->revtab[i], fft##N##in, m); \ + } \ + \ + for (int i = 0; i < N; i++) \ + fftp(s->tmp + m*i); \ + \ + for (int i = 0; i < len8; i++) { \ + const int i0 = len8 + i, i1 = len8 - i - 1; \ + const int s0 = out_map[i0], s1 = out_map[i1]; \ + FFTComplex src1 = { s->tmp[s1].re, s->tmp[s1].im }; \ + FFTComplex src0 = { s->tmp[s0].re, s->tmp[s0].im }; \ + \ + CMUL(dst[2*i1*stride + stride], dst[2*i0*stride], src0.re, src0.im, \ + exp[i0].im, exp[i0].re); \ + CMUL(dst[2*i0*stride + stride], dst[2*i1*stride], src1.re, src1.im, \ + exp[i1].im, exp[i1].re); \ + } \ +} + +DECL_COMP_MDCT(3) +DECL_COMP_MDCT(5) +DECL_COMP_MDCT(15) + +static void monolithic_imdct(AVTXContext *s, void *_dst, void *_src, + ptrdiff_t stride) +{ + FFTComplex *z = _dst, *exp = s->exptab; + const int m = s->m, len8 = m >> 1; + const FFTSample *src = _src, *in1, *in2; + void (*fftp)(FFTComplex *) = fft_dispatch[av_log2(m)]; + + stride /= sizeof(*src); + in1 = src; + in2 = src + ((m*2) - 1) * stride; + + for (int i = 0; i < m; i++) { + FFTComplex tmp = { in2[-2*i*stride], in1[2*i*stride] }; + CMUL3(z[s->revtab[i]], tmp, exp[i]); + } + + fftp(z); + + for (int i = 0; i < len8; i++) { + const int i0 = len8 + i, i1 = len8 - i - 1; + FFTComplex src1 = { z[i1].im, z[i1].re }; + FFTComplex src0 = { z[i0].im, z[i0].re }; + + CMUL(z[i1].re, z[i0].im, src1.re, src1.im, exp[i1].im, exp[i1].re); + CMUL(z[i0].re, z[i1].im, src0.re, src0.im, exp[i0].im, exp[i0].re); + } +} + +static void monolithic_mdct(AVTXContext *s, void *_dst, void *_src, + ptrdiff_t stride) +{ + FFTSample *src = _src, *dst = _dst; + FFTComplex *exp = s->exptab, tmp, *z = _dst; + const int m = s->m, len4 = m, len3 = len4 * 3, len8 = len4 >> 1; + void (*fftp)(FFTComplex *) = fft_dispatch[av_log2(m)]; + + stride /= sizeof(*dst); + + for (int i = 0; i < m; i++) { /* Folding and pre-reindexing */ + const int k = 2*i; + if (k < len4) { + tmp.re = FOLD(-src[ len4 + k], src[1*len4 - 1 - k]); + tmp.im = FOLD(-src[ len3 + k], -src[1*len3 - 1 - k]); + } else { + tmp.re = FOLD(-src[ len4 + k], -src[5*len4 - 1 - k]); + tmp.im = FOLD( src[-len4 + k], -src[1*len3 - 1 - k]); + } + CMUL(z[s->revtab[i]].im, z[s->revtab[i]].re, tmp.re, tmp.im, + exp[i].re, exp[i].im); + } + + fftp(z); + + for (int i = 0; i < len8; i++) { + const int i0 = len8 + i, i1 = len8 - i - 1; + FFTComplex src1 = { z[i1].re, z[i1].im }; + FFTComplex src0 = { z[i0].re, z[i0].im }; + + CMUL(dst[2*i1*stride + stride], dst[2*i0*stride], src0.re, src0.im, + exp[i0].im, exp[i0].re); + CMUL(dst[2*i0*stride + stride], dst[2*i1*stride], src1.re, src1.im, + exp[i1].im, exp[i1].re); + } +} + +static int gen_mdct_exptab(AVTXContext *s, int len4, double scale) +{ + const double theta = (scale < 0 ? len4 : 0) + 1.0/8.0; + + if (!(s->exptab = av_malloc_array(len4, sizeof(*s->exptab)))) + return AVERROR(ENOMEM); + + scale = sqrt(fabs(scale)); + for (int i = 0; i < len4; i++) { + const double alpha = M_PI_2 * (i + theta) / len4; + s->exptab[i].re = RESCALE(cos(alpha) * scale); + s->exptab[i].im = RESCALE(sin(alpha) * scale); + } + + return 0; +} + +int TX_NAME(ff_tx_init_mdct_fft)(AVTXContext *s, av_tx_fn *tx, + enum AVTXType type, int inv, int len, + const void *scale, uint64_t flags) +{ + const int is_mdct = ff_tx_type_is_mdct(type); + int err, n = 1, m = 1, max_ptwo = 1 << (FF_ARRAY_ELEMS(fft_dispatch) - 1); + + if (is_mdct) + len >>= 1; + +#define CHECK_FACTOR(DST, FACTOR, SRC) \ + if (DST == 1 && !(SRC % FACTOR)) { \ + DST = FACTOR; \ + SRC /= FACTOR; \ + } + CHECK_FACTOR(n, 15, len) + CHECK_FACTOR(n, 5, len) + CHECK_FACTOR(n, 3, len) +#undef CHECK_FACTOR + + /* len must be a power of two now */ + if (!(len & (len - 1)) && len >= 2 && len <= max_ptwo) { + m = len; + len = 1; + } + + s->n = n; + s->m = m; + s->inv = inv; + s->type = type; + + /* Filter out direct 3, 5 and 15 transforms, too niche */ + if (len > 1 || m == 1) { + av_log(NULL, AV_LOG_ERROR, "Unsupported transform size: n = %i, " + "m = %i, residual = %i!\n", n, m, len); + return AVERROR(EINVAL); + } else if (n > 1 && m > 1) { /* 2D transform case */ + if ((err = ff_tx_gen_compound_mapping(s))) + return err; + if (!(s->tmp = av_malloc(n*m*sizeof(*s->tmp)))) + return AVERROR(ENOMEM); + *tx = n == 3 ? compound_fft_3xM : + n == 5 ? compound_fft_5xM : + compound_fft_15xM; + if (is_mdct) + *tx = n == 3 ? inv ? compound_imdct_3xM : compound_mdct_3xM : + n == 5 ? inv ? compound_imdct_5xM : compound_mdct_5xM : + inv ? compound_imdct_15xM : compound_mdct_15xM; + } else { /* Direct transform case */ + *tx = monolithic_fft; + if (is_mdct) + *tx = inv ? monolithic_imdct : monolithic_mdct; + } + + if (n != 1) + init_cos_tabs(0); + if (m != 1) { + ff_tx_gen_ptwo_revtab(s); + for (int i = 4; i <= av_log2(m); i++) + init_cos_tabs(i); + } + + if (is_mdct) + return gen_mdct_exptab(s, n*m, *((SCALE_TYPE *)scale)); + + return 0; +} diff --git a/libavutil/utils.c b/libavutil/utils.c index 230081ea475..c1cd452eee5 100644 --- a/libavutil/utils.c +++ b/libavutil/utils.c @@ -70,7 +70,7 @@ const char *avutil_configuration(void) const char *avutil_license(void) { #define LICENSE_PREFIX "libavutil license: " - return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1; + return &LICENSE_PREFIX FFMPEG_LICENSE[sizeof(LICENSE_PREFIX) - 1]; } const char *av_get_media_type_string(enum AVMediaType media_type) diff --git a/libavutil/version.h b/libavutil/version.h index 24ca8ab7db7..0ff722fbf86 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -79,7 +79,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 56 -#define LIBAVUTIL_VERSION_MINOR 31 +#define LIBAVUTIL_VERSION_MINOR 51 #define LIBAVUTIL_VERSION_MICRO 100 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ diff --git a/libavutil/video_enc_params.c b/libavutil/video_enc_params.c new file mode 100644 index 00000000000..c46c0f1dc69 --- /dev/null +++ b/libavutil/video_enc_params.c @@ -0,0 +1,79 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 "buffer.h" +#include "common.h" +#include "frame.h" +#include "mem.h" +#include "video_enc_params.h" + +AVVideoEncParams *av_video_enc_params_alloc(enum AVVideoEncParamsType type, + unsigned int nb_blocks, size_t *out_size) +{ + AVVideoEncParams *par; + size_t size; + + size = sizeof(*par); + if (nb_blocks > SIZE_MAX / sizeof(AVVideoBlockParams) || + nb_blocks * sizeof(AVVideoBlockParams) > SIZE_MAX - size) + return NULL; + size += sizeof(AVVideoBlockParams) * nb_blocks; + + par = av_mallocz(size); + if (!par) + return NULL; + + par->type = type; + par->nb_blocks = nb_blocks; + par->block_size = sizeof(AVVideoBlockParams); + par->blocks_offset = sizeof(*par); + + if (out_size) + *out_size = size; + + return par; +} + +AVVideoEncParams* +av_video_enc_params_create_side_data(AVFrame *frame, enum AVVideoEncParamsType type, + unsigned int nb_blocks) +{ + AVBufferRef *buf; + AVVideoEncParams *par; + size_t size; + + par = av_video_enc_params_alloc(type, nb_blocks, &size); + if (!par) + return NULL; + buf = av_buffer_create((uint8_t *)par, size, NULL, NULL, 0); + if (!buf) { + av_freep(&par); + return NULL; + } + + if (!av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_VIDEO_ENC_PARAMS, buf)) { + av_buffer_unref(&buf); + return NULL; + } + + return par; +} diff --git a/libavutil/video_enc_params.h b/libavutil/video_enc_params.h new file mode 100644 index 00000000000..43fa4431542 --- /dev/null +++ b/libavutil/video_enc_params.h @@ -0,0 +1,163 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 AVUTIL_VIDEO_ENC_PARAMS_H +#define AVUTIL_VIDEO_ENC_PARAMS_H + +#include +#include + +#include "libavutil/avassert.h" +#include "libavutil/frame.h" + +enum AVVideoEncParamsType { + AV_VIDEO_ENC_PARAMS_NONE = -1, + /** + * VP9 stores: + * - per-frame base (luma AC) quantizer index, exported as AVVideoEncParams.qp + * - deltas for luma DC, chroma AC and chroma DC, exported in the + * corresponding entries in AVVideoEncParams.delta_qp + * - per-segment delta, exported as for each block as AVVideoBlockParams.delta_qp + * + * To compute the resulting quantizer index for a block: + * - for luma AC, add the base qp and the per-block delta_qp, saturating to + * unsigned 8-bit. + * - for luma DC and chroma AC/DC, add the corresponding + * AVVideoBlockParams.delta_qp to the luma AC index, again saturating to + * unsigned 8-bit. + */ + AV_VIDEO_ENC_PARAMS_VP9, + + /** + * H.264 stores: + * - in PPS (per-picture): + * * initial QP_Y (luma) value, exported as AVVideoEncParams.qp + * * delta(s) for chroma QP values (same for both, or each separately), + * exported as in the corresponding entries in AVVideoEncParams.delta_qp + * - per-slice QP delta, not exported directly, added to the per-MB value + * - per-MB delta; not exported directly; the final per-MB quantizer + * parameter - QP_Y - minus the value in AVVideoEncParams.qp is exported + * as AVVideoBlockParams.qp_delta. + */ + AV_VIDEO_ENC_PARAMS_H264, +}; + +/** + * Video encoding parameters for a given frame. This struct is allocated along + * with an optional array of per-block AVVideoBlockParams descriptors. + * Must be allocated with av_video_enc_params_alloc(). + */ +typedef struct AVVideoEncParams { + /** + * Number of blocks in the array. + * + * May be 0, in which case no per-block information is present. In this case + * the values of blocks_offset / block_size are unspecified and should not + * be accessed. + */ + unsigned int nb_blocks; + /** + * Offset in bytes from the beginning of this structure at which the array + * of blocks starts. + */ + size_t blocks_offset; + /* + * Size of each block in bytes. May not match sizeof(AVVideoBlockParams). + */ + size_t block_size; + + /** + * Type of the parameters (the codec they are used with). + */ + enum AVVideoEncParamsType type; + + /** + * Base quantisation parameter for the frame. The final quantiser for a + * given block in a given plane is obtained from this value, possibly + * combined with {@code delta_qp} and the per-block delta in a manner + * documented for each type. + */ + int32_t qp; + + /** + * Quantisation parameter offset from the base (per-frame) qp for a given + * plane (first index) and AC/DC coefficients (second index). + */ + int32_t delta_qp[4][2]; +} AVVideoEncParams; + +/** + * Data structure for storing block-level encoding information. + * It is allocated as a part of AVVideoEncParams and should be retrieved with + * av_video_enc_params_block(). + * + * sizeof(AVVideoBlockParams) is not a part of the ABI and new fields may be + * added to it. + */ +typedef struct AVVideoBlockParams { + /** + * Distance in luma pixels from the top-left corner of the visible frame + * to the top-left corner of the block. + * Can be negative if top/right padding is present on the coded frame. + */ + int src_x, src_y; + /** + * Width and height of the block in luma pixels. + */ + int w, h; + + /** + * Difference between this block's final quantization parameter and the + * corresponding per-frame value. + */ + int32_t delta_qp; +} AVVideoBlockParams; + +/* + * Get the block at the specified {@code idx}. Must be between 0 and nb_blocks. + */ +static av_always_inline AVVideoBlockParams* +av_video_enc_params_block(AVVideoEncParams *par, unsigned int idx) +{ + av_assert0(idx < par->nb_blocks); + return (AVVideoBlockParams *)((uint8_t *)par + par->blocks_offset + + idx * par->block_size); +} + +/** + * Allocates memory for AVVideoEncParams of the given type, plus an array of + * {@code nb_blocks} AVVideoBlockParams and initializes the variables. Can be + * freed with a normal av_free() call. + * + * @param out_size if non-NULL, the size in bytes of the resulting data array is + * written here. + */ +AVVideoEncParams *av_video_enc_params_alloc(enum AVVideoEncParamsType type, + unsigned int nb_blocks, size_t *out_size); + +/** + * Allocates memory for AVEncodeInfoFrame plus an array of + * {@code nb_blocks} AVEncodeInfoBlock in the given AVFrame {@code frame} + * as AVFrameSideData of type AV_FRAME_DATA_ENCODE_INFO + * and initializes the variables. + */ +AVVideoEncParams* +av_video_enc_params_create_side_data(AVFrame *frame, enum AVVideoEncParamsType type, + unsigned int nb_blocks); + +#endif /* AVUTIL_VIDEO_ENC_PARAMS_H */ diff --git a/libavutil/x86/bswap.h b/libavutil/x86/bswap.h index ffa59e4c82d..b2f18b6c939 100644 --- a/libavutil/x86/bswap.h +++ b/libavutil/x86/bswap.h @@ -26,6 +26,7 @@ #include #if defined(_MSC_VER) +#include #include #endif #include "config.h" diff --git a/libpostproc/postprocess.c b/libpostproc/postprocess.c index 1fef8747c00..8d44165dee0 100644 --- a/libpostproc/postprocess.c +++ b/libpostproc/postprocess.c @@ -108,7 +108,7 @@ const char *postproc_configuration(void) const char *postproc_license(void) { #define LICENSE_PREFIX "libpostproc license: " - return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1; + return &LICENSE_PREFIX FFMPEG_LICENSE[sizeof(LICENSE_PREFIX) - 1]; } #define GET_MODE_BUFFER_SIZE 500 @@ -407,7 +407,7 @@ static av_always_inline void do_a_deblock_C(uint8_t *src, int step, const int QP= c->QP; const int dcOffset= ((c->nonBQP*c->ppMode.baseDcDiff)>>8) + 1; const int dcThreshold= dcOffset*2 + 1; -//START_TIMER + src+= step*4; // src points to begin of the 8x8 Block for(y=0; y<8; y++){ int numEq= 0; @@ -511,11 +511,6 @@ static av_always_inline void do_a_deblock_C(uint8_t *src, int step, src += stride; } -/*if(step==16){ - STOP_TIMER("step16") -}else{ - STOP_TIMER("stepX") -}*/ } //Note: we have C, MMX, MMX2, 3DNOW version there is no 3DNOW+MMX2 one diff --git a/libpostproc/postprocess_template.c b/libpostproc/postprocess_template.c index b0adfd168cc..df1684d1e22 100644 --- a/libpostproc/postprocess_template.c +++ b/libpostproc/postprocess_template.c @@ -2548,7 +2548,7 @@ static av_always_inline void RENAME(do_a_deblock)(uint8_t *src, int step, int st int64_t dc_mask, eq_mask, both_masks; int64_t sums[10*8*2]; src+= step*3; // src points to begin of the 8x8 Block - //{ START_TIMER + __asm__ volatile( "movq %0, %%mm7 \n\t" "movq %1, %%mm6 \n\t" @@ -3071,12 +3071,6 @@ static av_always_inline void RENAME(do_a_deblock)(uint8_t *src, int step, int st : "%"FF_REG_a ); } -/*if(step==16){ - STOP_TIMER("step16") -}else{ - STOP_TIMER("stepX") -} - } */ } #endif //TEMPLATE_PP_MMX diff --git a/libpostproc/version.h b/libpostproc/version.h index fa6d12c83db..e5de9aefbf5 100644 --- a/libpostproc/version.h +++ b/libpostproc/version.h @@ -29,7 +29,7 @@ #include "libavutil/avutil.h" #define LIBPOSTPROC_VERSION_MAJOR 55 -#define LIBPOSTPROC_VERSION_MINOR 5 +#define LIBPOSTPROC_VERSION_MINOR 7 #define LIBPOSTPROC_VERSION_MICRO 100 #define LIBPOSTPROC_VERSION_INT AV_VERSION_INT(LIBPOSTPROC_VERSION_MAJOR, \ diff --git a/libswresample/audioconvert.c b/libswresample/audioconvert.c index 96ce84ac44a..d21fc8ef42e 100644 --- a/libswresample/audioconvert.c +++ b/libswresample/audioconvert.c @@ -73,18 +73,18 @@ CONV_FUNC(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_S64, (*(const int64_t*)pi>>5 CONV_FUNC(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_S64, *(const int64_t*)pi>>48) CONV_FUNC(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_S64, *(const int64_t*)pi>>32) CONV_FUNC(AV_SAMPLE_FMT_S64, int64_t, AV_SAMPLE_FMT_S64, *(const int64_t*)pi) -CONV_FUNC(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_S64, *(const int64_t*)pi*(1.0f/ (INT64_C(1)<<63))) -CONV_FUNC(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_S64, *(const int64_t*)pi*(1.0 / (INT64_C(1)<<63))) +CONV_FUNC(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_S64, *(const int64_t*)pi*(1.0f/ (UINT64_C(1)<<63))) +CONV_FUNC(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_S64, *(const int64_t*)pi*(1.0 / (UINT64_C(1)<<63))) CONV_FUNC(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_FLT, av_clip_uint8( lrintf(*(const float*)pi * (1<<7)) + 0x80)) CONV_FUNC(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, av_clip_int16( lrintf(*(const float*)pi * (1<<15)))) CONV_FUNC(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, av_clipl_int32(llrintf(*(const float*)pi * (1U<<31)))) -CONV_FUNC(AV_SAMPLE_FMT_S64, int64_t, AV_SAMPLE_FMT_FLT, llrintf(*(const float*)pi * (INT64_C(1)<<63))) +CONV_FUNC(AV_SAMPLE_FMT_S64, int64_t, AV_SAMPLE_FMT_FLT, llrintf(*(const float*)pi * (UINT64_C(1)<<63))) CONV_FUNC(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_FLT, *(const float*)pi) CONV_FUNC(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_FLT, *(const float*)pi) CONV_FUNC(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_DBL, av_clip_uint8( lrint(*(const double*)pi * (1<<7)) + 0x80)) CONV_FUNC(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, av_clip_int16( lrint(*(const double*)pi * (1<<15)))) CONV_FUNC(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, av_clipl_int32(llrint(*(const double*)pi * (1U<<31)))) -CONV_FUNC(AV_SAMPLE_FMT_S64, int64_t, AV_SAMPLE_FMT_DBL, llrint(*(const double*)pi * (INT64_C(1)<<63))) +CONV_FUNC(AV_SAMPLE_FMT_S64, int64_t, AV_SAMPLE_FMT_DBL, llrint(*(const double*)pi * (UINT64_C(1)<<63))) CONV_FUNC(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_DBL, *(const double*)pi) CONV_FUNC(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_DBL, *(const double*)pi) diff --git a/libswresample/swresample.c b/libswresample/swresample.c index 1ac5ef9a309..a7bb69dd4fd 100644 --- a/libswresample/swresample.c +++ b/libswresample/swresample.c @@ -46,7 +46,7 @@ const char *swresample_configuration(void) const char *swresample_license(void) { #define LICENSE_PREFIX "libswresample license: " - return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1; + return &LICENSE_PREFIX FFMPEG_LICENSE[sizeof(LICENSE_PREFIX) - 1]; } int swr_set_channel_mapping(struct SwrContext *s, const int *channel_map){ diff --git a/libswresample/tests/.gitignore b/libswresample/tests/.gitignore new file mode 100644 index 00000000000..2dc986bd0ee --- /dev/null +++ b/libswresample/tests/.gitignore @@ -0,0 +1 @@ +/swresample diff --git a/libswresample/version.h b/libswresample/version.h index a0b361bc1f1..257739195af 100644 --- a/libswresample/version.h +++ b/libswresample/version.h @@ -29,7 +29,7 @@ #include "libavutil/avutil.h" #define LIBSWRESAMPLE_VERSION_MAJOR 3 -#define LIBSWRESAMPLE_VERSION_MINOR 5 +#define LIBSWRESAMPLE_VERSION_MINOR 7 #define LIBSWRESAMPLE_VERSION_MICRO 100 #define LIBSWRESAMPLE_VERSION_INT AV_VERSION_INT(LIBSWRESAMPLE_VERSION_MAJOR, \ diff --git a/libswscale/aarch64/Makefile b/libswscale/aarch64/Makefile index 64a3fe208d5..da1d9095614 100644 --- a/libswscale/aarch64/Makefile +++ b/libswscale/aarch64/Makefile @@ -1,6 +1,8 @@ -OBJS += aarch64/swscale.o \ +OBJS += aarch64/rgb2rgb.o \ + aarch64/swscale.o \ aarch64/swscale_unscaled.o \ NEON-OBJS += aarch64/hscale.o \ aarch64/output.o \ + aarch64/rgb2rgb_neon.o \ aarch64/yuv2rgb_neon.o \ diff --git a/libswscale/aarch64/hscale.S b/libswscale/aarch64/hscale.S index cc78c1901da..af55ffe2b75 100644 --- a/libswscale/aarch64/hscale.S +++ b/libswscale/aarch64/hscale.S @@ -21,39 +21,60 @@ #include "libavutil/aarch64/asm.S" function ff_hscale_8_to_15_neon, export=1 - add x10, x4, w6, UXTW #1 // filter2 = filter + filterSize*2 (x2 because int16) -1: ldr w8, [x5], #4 // filterPos[0] - ldr w9, [x5], #4 // filterPos[1] - movi v4.4S, #0 // val sum part 1 (for dst[0]) - movi v5.4S, #0 // val sum part 2 (for dst[1]) - mov w7, w6 // filterSize counter - mov x13, x3 // srcp = src -2: add x11, x13, w8, UXTW // srcp + filterPos[0] - add x12, x13, w9, UXTW // srcp + filterPos[1] - ld1 {v0.8B}, [x11] // srcp[filterPos[0] + {0..7}] - ld1 {v1.8B}, [x12] // srcp[filterPos[1] + {0..7}] - ld1 {v2.8H}, [x4], #16 // load 8x16-bit filter values, part 1 - ld1 {v3.8H}, [x10], #16 // ditto at filter+filterSize for part 2 - uxtl v0.8H, v0.8B // unpack part 1 to 16-bit - uxtl v1.8H, v1.8B // unpack part 2 to 16-bit - smull v16.4S, v0.4H, v2.4H // v16.i32{0..3} = part 1 of: srcp[filterPos[0] + {0..7}] * filter[{0..7}] - smull v18.4S, v1.4H, v3.4H // v18.i32{0..3} = part 1 of: srcp[filterPos[1] + {0..7}] * filter[{0..7}] - smull2 v17.4S, v0.8H, v2.8H // v17.i32{0..3} = part 2 of: srcp[filterPos[0] + {0..7}] * filter[{0..7}] - smull2 v19.4S, v1.8H, v3.8H // v19.i32{0..3} = part 2 of: srcp[filterPos[1] + {0..7}] * filter[{0..7}] - addp v16.4S, v16.4S, v17.4S // horizontal pair adding of the 8x32-bit multiplied values for part 1 into 4x32-bit - addp v18.4S, v18.4S, v19.4S // horizontal pair adding of the 8x32-bit multiplied values for part 2 into 4x32-bit - add v4.4S, v4.4S, v16.4S // update val accumulator for part 1 - add v5.4S, v5.4S, v18.4S // update val accumulator for part 2 - add x13, x13, #8 // srcp += 8 - subs w7, w7, #8 // processed 8/filterSize + sbfiz x7, x6, #1, #32 // filterSize*2 (*2 because int16) +1: ldr w8, [x5], #4 // filterPos[idx] + ldr w0, [x5], #4 // filterPos[idx + 1] + ldr w11, [x5], #4 // filterPos[idx + 2] + ldr w9, [x5], #4 // filterPos[idx + 3] + mov x16, x4 // filter0 = filter + add x12, x16, x7 // filter1 = filter0 + filterSize*2 + add x13, x12, x7 // filter2 = filter1 + filterSize*2 + add x4, x13, x7 // filter3 = filter2 + filterSize*2 + movi v0.2D, #0 // val sum part 1 (for dst[0]) + movi v1.2D, #0 // val sum part 2 (for dst[1]) + movi v2.2D, #0 // val sum part 3 (for dst[2]) + movi v3.2D, #0 // val sum part 4 (for dst[3]) + add x17, x3, w8, UXTW // srcp + filterPos[0] + add x8, x3, w0, UXTW // srcp + filterPos[1] + add x0, x3, w11, UXTW // srcp + filterPos[2] + add x11, x3, w9, UXTW // srcp + filterPos[3] + mov w15, w6 // filterSize counter +2: ld1 {v4.8B}, [x17], #8 // srcp[filterPos[0] + {0..7}] + ld1 {v5.8H}, [x16], #16 // load 8x16-bit filter values, part 1 + ld1 {v6.8B}, [x8], #8 // srcp[filterPos[1] + {0..7}] + ld1 {v7.8H}, [x12], #16 // load 8x16-bit at filter+filterSize + uxtl v4.8H, v4.8B // unpack part 1 to 16-bit + smlal v0.4S, v4.4H, v5.4H // v0 accumulates srcp[filterPos[0] + {0..3}] * filter[{0..3}] + smlal2 v0.4S, v4.8H, v5.8H // v0 accumulates srcp[filterPos[0] + {4..7}] * filter[{4..7}] + ld1 {v16.8B}, [x0], #8 // srcp[filterPos[2] + {0..7}] + ld1 {v17.8H}, [x13], #16 // load 8x16-bit at filter+2*filterSize + uxtl v6.8H, v6.8B // unpack part 2 to 16-bit + smlal v1.4S, v6.4H, v7.4H // v1 accumulates srcp[filterPos[1] + {0..3}] * filter[{0..3}] + uxtl v16.8H, v16.8B // unpack part 3 to 16-bit + smlal v2.4S, v16.4H, v17.4H // v2 accumulates srcp[filterPos[2] + {0..3}] * filter[{0..3}] + smlal2 v2.4S, v16.8H, v17.8H // v2 accumulates srcp[filterPos[2] + {4..7}] * filter[{4..7}] + ld1 {v18.8B}, [x11], #8 // srcp[filterPos[3] + {0..7}] + smlal2 v1.4S, v6.8H, v7.8H // v1 accumulates srcp[filterPos[1] + {4..7}] * filter[{4..7}] + ld1 {v19.8H}, [x4], #16 // load 8x16-bit at filter+3*filterSize + subs w15, w15, #8 // j -= 8: processed 8/filterSize + uxtl v18.8H, v18.8B // unpack part 4 to 16-bit + smlal v3.4S, v18.4H, v19.4H // v3 accumulates srcp[filterPos[3] + {0..3}] * filter[{0..3}] + smlal2 v3.4S, v18.8H, v19.8H // v3 accumulates srcp[filterPos[3] + {4..7}] * filter[{4..7}] b.gt 2b // inner loop if filterSize not consumed completely - mov x4, x10 // filter = filter2 - add x10, x10, w6, UXTW #1 // filter2 += filterSize*2 - addp v4.4S, v4.4S, v5.4S // horizontal pair adding of the 8x32-bit sums into 4x32-bit - addp v4.4S, v4.4S, v4.4S // horizontal pair adding of the 4x32-bit sums into 2x32-bit - sqshrn v4.4H, v4.4S, #7 // shift and clip the 2x16-bit final values - st1 {v4.S}[0], [x1], #4 // write to destination - subs w2, w2, #2 // dstW -= 2 + addp v0.4S, v0.4S, v0.4S // part0 horizontal pair adding + addp v1.4S, v1.4S, v1.4S // part1 horizontal pair adding + addp v2.4S, v2.4S, v2.4S // part2 horizontal pair adding + addp v3.4S, v3.4S, v3.4S // part3 horizontal pair adding + addp v0.4S, v0.4S, v0.4S // part0 horizontal pair adding + addp v1.4S, v1.4S, v1.4S // part1 horizontal pair adding + addp v2.4S, v2.4S, v2.4S // part2 horizontal pair adding + addp v3.4S, v3.4S, v3.4S // part3 horizontal pair adding + zip1 v0.4S, v0.4S, v1.4S // part01 = zip values from part0 and part1 + zip1 v2.4S, v2.4S, v3.4S // part23 = zip values from part2 and part3 + mov v0.d[1], v2.d[0] // part0123 = zip values from part01 and part23 + subs w2, w2, #4 // dstW -= 4 + sqshrn v0.4H, v0.4S, #7 // shift and clip the 2x16-bit final values + st1 {v0.4H}, [x1], #8 // write to destination part0123 b.gt 1b // loop until end of line ret endfunc diff --git a/libswscale/aarch64/output.S b/libswscale/aarch64/output.S index 90d3b57b103..af71de60504 100644 --- a/libswscale/aarch64/output.S +++ b/libswscale/aarch64/output.S @@ -38,29 +38,21 @@ function ff_yuv2planeX_8_neon, export=1 add x12, x12, x7, lsl #1 // &src[j+1][i] ld1 {v5.8H}, [x11] // read 8x16-bit @ src[j ][i + {0..7}]: A,B,C,D,E,F,G,H ld1 {v6.8H}, [x12] // read 8x16-bit @ src[j+1][i + {0..7}]: I,J,K,L,M,N,O,P - ldr w11, [x10], #4 // read 2x16-bit coeffs (X, Y) at (filter[j], filter[j+1]) - zip1 v16.8H, v5.8H, v6.8H // A,I,B,J,C,K,D,L - zip2 v17.8H, v5.8H, v6.8H // E,M,F,N,F,O,H,P - dup v7.4S, w11 // X,Y,X,Y,X,Y,X,Y - smull v18.4S, v16.4H, v7.4H // A.X I.Y B.X J.Y - smull v20.4S, v17.4H, v7.4H // E.X M.Y F.X N.Y - smull2 v19.4S, v16.8H, v7.8H // C.X K.Y D.X L.Y - smull2 v21.4S, v17.8H, v7.8H // G.X O.Y H.X P.Y - addp v16.4S, v18.4S, v19.4S // A.X+I.Y B.X+J.Y C.X+K.Y D.X+L.Y - addp v17.4S, v20.4S, v21.4S // E.X+M.Y F.X+N.Y F.X+O.Y H.X+P.Y - add v3.4S, v3.4S, v16.4S // update val accumulator for part 1 - add v4.4S, v4.4S, v17.4S // update val accumulator for part 2 + ld1r {v7.8H}, [x10], #2 // read 1x16-bit coeff X at filter[j ] and duplicate across lanes + ld1r {v16.8H}, [x10], #2 // read 1x16-bit coeff Y at filter[j+1] and duplicate across lanes + smlal v3.4S, v5.4H, v7.4H // val0 += {A,B,C,D} * X + smlal2 v4.4S, v5.8H, v7.8H // val1 += {E,F,G,H} * X + smlal v3.4S, v6.4H, v16.4H // val0 += {I,J,K,L} * Y + smlal2 v4.4S, v6.8H, v16.8H // val1 += {M,N,O,P} * Y subs w8, w8, #2 // tmpfilterSize -= 2 b.gt 3b // loop until filterSize consumed - sshr v3.4S, v3.4S, #19 // val>>19 (part 1) - sshr v4.4S, v4.4S, #19 // val>>19 (part 2) - sqxtun v3.4H, v3.4S // clip16(val>>19) (part 1) - sqxtun v4.4H, v4.4S // clip16(val>>19) (part 2) - mov v3.D[1], v4.D[0] // merge part 1 and part 2 - uqxtn v3.8B, v3.8H // clip8(val>>19) - st1 {v3.1D}, [x3], #8 // write to destination - add x7, x7, #8 // i += 8 + + sqshrun v3.4h, v3.4s, #16 // clip16(val0>>16) + sqshrun2 v3.8h, v4.4s, #16 // clip16(val1>>16) + uqshrn v3.8b, v3.8h, #3 // clip8(val>>19) + st1 {v3.8b}, [x3], #8 // write to destination subs w4, w4, #8 // dstW -= 8 + add x7, x7, #8 // i += 8 b.gt 2b // loop until width consumed ret endfunc diff --git a/libswscale/aarch64/rgb2rgb.c b/libswscale/aarch64/rgb2rgb.c new file mode 100644 index 00000000000..a9bf6ff9e0a --- /dev/null +++ b/libswscale/aarch64/rgb2rgb.c @@ -0,0 +1,41 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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 "config.h" +#include "libavutil/attributes.h" +#include "libavutil/aarch64/cpu.h" +#include "libavutil/cpu.h" +#include "libavutil/bswap.h" +#include "libswscale/rgb2rgb.h" +#include "libswscale/swscale.h" +#include "libswscale/swscale_internal.h" + +void ff_interleave_bytes_neon(const uint8_t *src1, const uint8_t *src2, + uint8_t *dest, int width, int height, + int src1Stride, int src2Stride, int dstStride); + +av_cold void rgb2rgb_init_aarch64(void) +{ + int cpu_flags = av_get_cpu_flags(); + + if (have_neon(cpu_flags)) { + interleaveBytes = ff_interleave_bytes_neon; + } +} diff --git a/libswscale/aarch64/rgb2rgb_neon.S b/libswscale/aarch64/rgb2rgb_neon.S new file mode 100644 index 00000000000..d81110ec571 --- /dev/null +++ b/libswscale/aarch64/rgb2rgb_neon.S @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020 Martin Storsjo + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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" + +// void ff_interleave_bytes_neon(const uint8_t *src1, const uint8_t *src2, +// uint8_t *dest, int width, int height, +// int src1Stride, int src2Stride, int dstStride); +function ff_interleave_bytes_neon, export=1 + sub w5, w5, w3 + sub w6, w6, w3 + sub w7, w7, w3, lsl #1 +1: + ands w8, w3, #0xfffffff0 // & ~15 + b.eq 3f +2: + ld1 {v0.16b}, [x0], #16 + ld1 {v1.16b}, [x1], #16 + subs w8, w8, #16 + st2 {v0.16b, v1.16b}, [x2], #32 + b.gt 2b + + tst w3, #15 + b.eq 9f + +3: + tst w3, #8 + b.eq 4f + ld1 {v0.8b}, [x0], #8 + ld1 {v1.8b}, [x1], #8 + st2 {v0.8b, v1.8b}, [x2], #16 +4: + tst w3, #4 + b.eq 5f + + ld1 {v0.s}[0], [x0], #4 + ld1 {v1.s}[0], [x1], #4 + zip1 v0.8b, v0.8b, v1.8b + st1 {v0.8b}, [x2], #8 + +5: + ands w8, w3, #3 + b.eq 9f +6: + ldrb w9, [x0], #1 + ldrb w10, [x1], #1 + subs w8, w8, #1 + bfi w9, w10, #8, #8 + strh w9, [x2], #2 + b.gt 6b + +9: + subs w4, w4, #1 + b.eq 0f + add x0, x0, w5, sxtw + add x1, x1, w6, sxtw + add x2, x2, w7, sxtw + b 1b + +0: + ret +endfunc diff --git a/libswscale/aarch64/swscale.c b/libswscale/aarch64/swscale.c index 54a3beabe85..eecbea88ca8 100644 --- a/libswscale/aarch64/swscale.c +++ b/libswscale/aarch64/swscale.c @@ -34,7 +34,10 @@ av_cold void ff_sws_init_swscale_aarch64(SwsContext *c) int cpu_flags = av_get_cpu_flags(); if (have_neon(cpu_flags)) { - if (c->srcBpc == 8 && c->dstBpc <= 14) { + if (c->srcBpc == 8 && c->dstBpc <= 14 && + (c->hLumFilterSize % 8) == 0 && + (c->hChrFilterSize % 8) == 0) + { c->hyScale = c->hcScale = ff_hscale_8_to_15_neon; } if (c->dstBpc == 8) { diff --git a/libswscale/arm/swscale.c b/libswscale/arm/swscale.c index 1ec360fe24a..7b8fbcbc79f 100644 --- a/libswscale/arm/swscale.c +++ b/libswscale/arm/swscale.c @@ -34,7 +34,10 @@ av_cold void ff_sws_init_swscale_arm(SwsContext *c) int cpu_flags = av_get_cpu_flags(); if (have_neon(cpu_flags)) { - if (c->srcBpc == 8 && c->dstBpc <= 14) { + if (c->srcBpc == 8 && c->dstBpc <= 14 && + (c->hLumFilterSize % 8) == 0 && + (c->hChrFilterSize % 8) == 0) + { c->hyScale = c->hcScale = ff_hscale_8_to_15_neon; } if (c->dstBpc == 8) { diff --git a/libswscale/input.c b/libswscale/input.c index 064f8da314e..e74cf04133e 100644 --- a/libswscale/input.c +++ b/libswscale/input.c @@ -286,8 +286,8 @@ static av_always_inline void rgb16_32ToUV_c_template(int16_t *dstU, int gsh, int bsh, int S, int32_t *rgb2yuv) { - const int ru = rgb2yuv[RU_IDX] << rsh, gu = rgb2yuv[GU_IDX] << gsh, bu = rgb2yuv[BU_IDX] << bsh, - rv = rgb2yuv[RV_IDX] << rsh, gv = rgb2yuv[GV_IDX] << gsh, bv = rgb2yuv[BV_IDX] << bsh; + const int ru = rgb2yuv[RU_IDX] * (1 << rsh), gu = rgb2yuv[GU_IDX] * (1 << gsh), bu = rgb2yuv[BU_IDX] * (1 << bsh), + rv = rgb2yuv[RV_IDX] * (1 << rsh), gv = rgb2yuv[GV_IDX] * (1 << gsh), bv = rgb2yuv[BV_IDX] * (1 << bsh); const unsigned rnd = (256u<<((S)-1)) + (1<<(S-7)); int i; @@ -314,8 +314,8 @@ static av_always_inline void rgb16_32ToUV_half_c_template(int16_t *dstU, int gsh, int bsh, int S, int32_t *rgb2yuv) { - const int ru = rgb2yuv[RU_IDX] << rsh, gu = rgb2yuv[GU_IDX] << gsh, bu = rgb2yuv[BU_IDX] << bsh, - rv = rgb2yuv[RV_IDX] << rsh, gv = rgb2yuv[GV_IDX] << gsh, bv = rgb2yuv[BV_IDX] << bsh, + const int ru = rgb2yuv[RU_IDX] * (1 << rsh), gu = rgb2yuv[GU_IDX] * (1 << gsh), bu = rgb2yuv[BU_IDX] * (1 << bsh), + rv = rgb2yuv[RV_IDX] * (1 << rsh), gv = rgb2yuv[GV_IDX] * (1 << gsh), bv = rgb2yuv[BV_IDX] * (1 << bsh), maskgx = ~(maskr | maskb); const unsigned rnd = (256U<<(S)) + (1<<(S-6)); int i; @@ -437,7 +437,7 @@ static void abgrToA_c(uint8_t *_dst, const uint8_t *src, const uint8_t *unused1, int16_t *dst = (int16_t *)_dst; int i; for (i=0; i>2; } } @@ -446,7 +446,7 @@ static void rgbaToA_c(uint8_t *_dst, const uint8_t *src, const uint8_t *unused1, int16_t *dst = (int16_t *)_dst; int i; for (i=0; i>2; } } @@ -457,7 +457,7 @@ static void palToA_c(uint8_t *_dst, const uint8_t *src, const uint8_t *unused1, for (i=0; i> 24)<<6; + dst[i]= (pal[d] >> 24)<<6 | pal[d]>>26; } } @@ -552,6 +552,24 @@ static void yvy2ToUV_c(uint8_t *dstU, uint8_t *dstV, const uint8_t *unused0, con av_assert1(src1 == src2); } +static void y210le_UV_c(uint8_t *dstU, uint8_t *dstV, const uint8_t *unused0, const uint8_t *src, + const uint8_t *unused1, int width, uint32_t *unused2) +{ + int i; + for (i = 0; i < width; i++) { + AV_WN16(dstU + i * 2, AV_RL16(src + i * 8 + 2) >> 6); + AV_WN16(dstV + i * 2, AV_RL16(src + i * 8 + 6) >> 6); + } +} + +static void y210le_Y_c(uint8_t *dst, const uint8_t *src, const uint8_t *unused0, + const uint8_t *unused1, int width, uint32_t *unused2) +{ + int i; + for (i = 0; i < width; i++) + AV_WN16(dst + i * 2, AV_RL16(src + i * 4) >> 6); +} + static void bswap16Y_c(uint8_t *_dst, const uint8_t *_src, const uint8_t *unused1, const uint8_t *unused2, int width, uint32_t *unused) { @@ -942,6 +960,59 @@ static av_always_inline void planar_rgb16_to_uv(uint8_t *_dstU, uint8_t *_dstV, } #undef rdpx +#define rdpx(src) (is_be ? av_int2float(AV_RB32(src)): av_int2float(AV_RL32(src))) + +static av_always_inline void planar_rgbf32_to_a(uint8_t *_dst, const uint8_t *_src[4], int width, int is_be, int32_t *rgb2yuv) +{ + int i; + const float **src = (const float **)_src; + uint16_t *dst = (uint16_t *)_dst; + + for (i = 0; i < width; i++) { + dst[i] = av_clip_uint16(lrintf(65535.0f * rdpx(src[3] + i))); + } +} + +static av_always_inline void planar_rgbf32_to_uv(uint8_t *_dstU, uint8_t *_dstV, const uint8_t *_src[4], int width, int is_be, int32_t *rgb2yuv) +{ + int i; + const float **src = (const float **)_src; + uint16_t *dstU = (uint16_t *)_dstU; + uint16_t *dstV = (uint16_t *)_dstV; + int32_t ru = rgb2yuv[RU_IDX], gu = rgb2yuv[GU_IDX], bu = rgb2yuv[BU_IDX]; + int32_t rv = rgb2yuv[RV_IDX], gv = rgb2yuv[GV_IDX], bv = rgb2yuv[BV_IDX]; + int bpc = 16; + int shift = 14; + for (i = 0; i < width; i++) { + int g = av_clip_uint16(lrintf(65535.0f * rdpx(src[0] + i))); + int b = av_clip_uint16(lrintf(65535.0f * rdpx(src[1] + i))); + int r = av_clip_uint16(lrintf(65535.0f * rdpx(src[2] + i))); + + dstU[i] = (ru*r + gu*g + bu*b + (257 << (RGB2YUV_SHIFT + bpc - 9))) >> (RGB2YUV_SHIFT + shift - 14); + dstV[i] = (rv*r + gv*g + bv*b + (257 << (RGB2YUV_SHIFT + bpc - 9))) >> (RGB2YUV_SHIFT + shift - 14); + } +} + +static av_always_inline void planar_rgbf32_to_y(uint8_t *_dst, const uint8_t *_src[4], int width, int is_be, int32_t *rgb2yuv) +{ + int i; + const float **src = (const float **)_src; + uint16_t *dst = (uint16_t *)_dst; + + int32_t ry = rgb2yuv[RY_IDX], gy = rgb2yuv[GY_IDX], by = rgb2yuv[BY_IDX]; + int bpc = 16; + int shift = 14; + for (i = 0; i < width; i++) { + int g = av_clip_uint16(lrintf(65535.0f * rdpx(src[0] + i))); + int b = av_clip_uint16(lrintf(65535.0f * rdpx(src[1] + i))); + int r = av_clip_uint16(lrintf(65535.0f * rdpx(src[2] + i))); + + dst[i] = ((ry*r + gy*g + by*b + (33 << (RGB2YUV_SHIFT + bpc - 9))) >> (RGB2YUV_SHIFT + shift - 14)); + } +} + +#undef rdpx + static av_always_inline void grayf32ToY16_c(uint8_t *_dst, const uint8_t *_src, const uint8_t *unused1, const uint8_t *unused2, int width, uint32_t *unused) { @@ -1004,6 +1075,26 @@ rgb9plus_planar_transparency_funcs(10) rgb9plus_planar_transparency_funcs(12) rgb9plus_planar_transparency_funcs(16) +#define rgbf32_planar_funcs_endian(endian_name, endian) \ +static void planar_rgbf32##endian_name##_to_y(uint8_t *dst, const uint8_t *src[4], \ + int w, int32_t *rgb2yuv) \ +{ \ + planar_rgbf32_to_y(dst, src, w, endian, rgb2yuv); \ +} \ +static void planar_rgbf32##endian_name##_to_uv(uint8_t *dstU, uint8_t *dstV, \ + const uint8_t *src[4], int w, int32_t *rgb2yuv) \ +{ \ + planar_rgbf32_to_uv(dstU, dstV, src, w, endian, rgb2yuv); \ +} \ +static void planar_rgbf32##endian_name##_to_a(uint8_t *dst, const uint8_t *src[4], \ + int w, int32_t *rgb2yuv) \ +{ \ + planar_rgbf32_to_a(dst, src, w, endian, rgb2yuv); \ +} + +rgbf32_planar_funcs_endian(le, 0) +rgbf32_planar_funcs_endian(be, 1) + av_cold void ff_sws_init_input_funcs(SwsContext *c) { enum AVPixelFormat srcFormat = c->srcFormat; @@ -1052,6 +1143,10 @@ av_cold void ff_sws_init_input_funcs(SwsContext *c) case AV_PIX_FMT_GBRP16LE: c->readChrPlanar = planar_rgb16le_to_uv; break; + case AV_PIX_FMT_GBRAPF32LE: + case AV_PIX_FMT_GBRPF32LE: + c->readChrPlanar = planar_rgbf32le_to_uv; + break; case AV_PIX_FMT_GBRP9BE: c->readChrPlanar = planar_rgb9be_to_uv; break; @@ -1070,6 +1165,10 @@ av_cold void ff_sws_init_input_funcs(SwsContext *c) case AV_PIX_FMT_GBRP16BE: c->readChrPlanar = planar_rgb16be_to_uv; break; + case AV_PIX_FMT_GBRAPF32BE: + case AV_PIX_FMT_GBRPF32BE: + c->readChrPlanar = planar_rgbf32be_to_uv; + break; case AV_PIX_FMT_GBRAP: case AV_PIX_FMT_GBRP: c->readChrPlanar = planar_rgb_to_uv; @@ -1154,6 +1253,9 @@ av_cold void ff_sws_init_input_funcs(SwsContext *c) case AV_PIX_FMT_P016BE: c->chrToYV12 = p016BEToUV_c; break; + case AV_PIX_FMT_Y210LE: + c->chrToYV12 = y210le_UV_c; + break; } if (c->chrSrcHSubSample) { switch (srcFormat) { @@ -1347,6 +1449,11 @@ av_cold void ff_sws_init_input_funcs(SwsContext *c) case AV_PIX_FMT_GBRP16LE: c->readLumPlanar = planar_rgb16le_to_y; break; + case AV_PIX_FMT_GBRAPF32LE: + c->readAlpPlanar = planar_rgbf32le_to_a; + case AV_PIX_FMT_GBRPF32LE: + c->readLumPlanar = planar_rgbf32le_to_y; + break; case AV_PIX_FMT_GBRP9BE: c->readLumPlanar = planar_rgb9be_to_y; break; @@ -1368,6 +1475,11 @@ av_cold void ff_sws_init_input_funcs(SwsContext *c) case AV_PIX_FMT_GBRP16BE: c->readLumPlanar = planar_rgb16be_to_y; break; + case AV_PIX_FMT_GBRAPF32BE: + c->readAlpPlanar = planar_rgbf32be_to_a; + case AV_PIX_FMT_GBRPF32BE: + c->readLumPlanar = planar_rgbf32be_to_y; + break; case AV_PIX_FMT_GBRAP: c->readAlpPlanar = planar_rgb_to_a; case AV_PIX_FMT_GBRP: @@ -1586,6 +1698,9 @@ av_cold void ff_sws_init_input_funcs(SwsContext *c) c->lumToYV12 = grayf32ToY16_bswap_c; #endif break; + case AV_PIX_FMT_Y210LE: + c->lumToYV12 = y210le_Y_c; + break; } if (c->needAlpha) { if (is16BPS(srcFormat) || isNBPS(srcFormat)) { diff --git a/libswscale/output.c b/libswscale/output.c index 26b0ff3d480..e864e515d0b 100644 --- a/libswscale/output.c +++ b/libswscale/output.c @@ -630,28 +630,28 @@ yuv2mono_2_c_template(SwsContext *c, const int16_t *buf[2], } c->dither_error[0][i] = err; } else { - for (i = 0; i < dstW; i += 8) { - int Y, acc = 0; - - Y = (buf0[i + 0] * yalpha1 + buf1[i + 0] * yalpha) >> 19; - accumulate_bit(acc, Y + d128[0]); - Y = (buf0[i + 1] * yalpha1 + buf1[i + 1] * yalpha) >> 19; - accumulate_bit(acc, Y + d128[1]); - Y = (buf0[i + 2] * yalpha1 + buf1[i + 2] * yalpha) >> 19; - accumulate_bit(acc, Y + d128[2]); - Y = (buf0[i + 3] * yalpha1 + buf1[i + 3] * yalpha) >> 19; - accumulate_bit(acc, Y + d128[3]); - Y = (buf0[i + 4] * yalpha1 + buf1[i + 4] * yalpha) >> 19; - accumulate_bit(acc, Y + d128[4]); - Y = (buf0[i + 5] * yalpha1 + buf1[i + 5] * yalpha) >> 19; - accumulate_bit(acc, Y + d128[5]); - Y = (buf0[i + 6] * yalpha1 + buf1[i + 6] * yalpha) >> 19; - accumulate_bit(acc, Y + d128[6]); - Y = (buf0[i + 7] * yalpha1 + buf1[i + 7] * yalpha) >> 19; - accumulate_bit(acc, Y + d128[7]); - - output_pixel(*dest++, acc); - } + for (i = 0; i < dstW; i += 8) { + int Y, acc = 0; + + Y = (buf0[i + 0] * yalpha1 + buf1[i + 0] * yalpha) >> 19; + accumulate_bit(acc, Y + d128[0]); + Y = (buf0[i + 1] * yalpha1 + buf1[i + 1] * yalpha) >> 19; + accumulate_bit(acc, Y + d128[1]); + Y = (buf0[i + 2] * yalpha1 + buf1[i + 2] * yalpha) >> 19; + accumulate_bit(acc, Y + d128[2]); + Y = (buf0[i + 3] * yalpha1 + buf1[i + 3] * yalpha) >> 19; + accumulate_bit(acc, Y + d128[3]); + Y = (buf0[i + 4] * yalpha1 + buf1[i + 4] * yalpha) >> 19; + accumulate_bit(acc, Y + d128[4]); + Y = (buf0[i + 5] * yalpha1 + buf1[i + 5] * yalpha) >> 19; + accumulate_bit(acc, Y + d128[5]); + Y = (buf0[i + 6] * yalpha1 + buf1[i + 6] * yalpha) >> 19; + accumulate_bit(acc, Y + d128[6]); + Y = (buf0[i + 7] * yalpha1 + buf1[i + 7] * yalpha) >> 19; + accumulate_bit(acc, Y + d128[7]); + + output_pixel(*dest++, acc); + } } } @@ -687,19 +687,19 @@ yuv2mono_1_c_template(SwsContext *c, const int16_t *buf0, } c->dither_error[0][i] = err; } else { - for (i = 0; i < dstW; i += 8) { - int acc = 0; - accumulate_bit(acc, ((buf0[i + 0] + 64) >> 7) + d128[0]); - accumulate_bit(acc, ((buf0[i + 1] + 64) >> 7) + d128[1]); - accumulate_bit(acc, ((buf0[i + 2] + 64) >> 7) + d128[2]); - accumulate_bit(acc, ((buf0[i + 3] + 64) >> 7) + d128[3]); - accumulate_bit(acc, ((buf0[i + 4] + 64) >> 7) + d128[4]); - accumulate_bit(acc, ((buf0[i + 5] + 64) >> 7) + d128[5]); - accumulate_bit(acc, ((buf0[i + 6] + 64) >> 7) + d128[6]); - accumulate_bit(acc, ((buf0[i + 7] + 64) >> 7) + d128[7]); - - output_pixel(*dest++, acc); - } + for (i = 0; i < dstW; i += 8) { + int acc = 0; + accumulate_bit(acc, ((buf0[i + 0] + 64) >> 7) + d128[0]); + accumulate_bit(acc, ((buf0[i + 1] + 64) >> 7) + d128[1]); + accumulate_bit(acc, ((buf0[i + 2] + 64) >> 7) + d128[2]); + accumulate_bit(acc, ((buf0[i + 3] + 64) >> 7) + d128[3]); + accumulate_bit(acc, ((buf0[i + 4] + 64) >> 7) + d128[4]); + accumulate_bit(acc, ((buf0[i + 5] + 64) >> 7) + d128[5]); + accumulate_bit(acc, ((buf0[i + 6] + 64) >> 7) + d128[6]); + accumulate_bit(acc, ((buf0[i + 7] + 64) >> 7) + d128[7]); + + output_pixel(*dest++, acc); + } } } @@ -904,25 +904,28 @@ yuv2ya16_X_c_template(SwsContext *c, const int16_t *lumFilter, for (i = 0; i < dstW; i++) { int j; - int Y = 1 << 18; - int64_t A = 0xffff<<14; + int Y = -0x40000000; + int A = 0xffff; for (j = 0; j < lumFilterSize; j++) Y += lumSrc[j][i] * lumFilter[j]; Y >>= 15; + Y += (1<<3) + 0x8000; Y = av_clip_uint16(Y); if (hasAlpha) { + A = -0x40000000 + (1<<14); for (j = 0; j < lumFilterSize; j++) A += alpSrc[j][i] * lumFilter[j]; A >>= 15; + A += 0x8000; A = av_clip_uint16(A); } output_pixel(&dest[2 * i ], Y); - output_pixel(&dest[2 * i + 1], hasAlpha ? A : 65535); + output_pixel(&dest[2 * i + 1], A); } } @@ -1847,9 +1850,9 @@ static av_always_inline void yuv2rgb_write_full(SwsContext *c, Y -= c->yuv2rgb_y_offset; Y *= c->yuv2rgb_y_coeff; Y += 1 << 21; - R = Y + V*c->yuv2rgb_v2r_coeff; - G = Y + V*c->yuv2rgb_v2g_coeff + U*c->yuv2rgb_u2g_coeff; - B = Y + U*c->yuv2rgb_u2b_coeff; + R = (unsigned)Y + V*c->yuv2rgb_v2r_coeff; + G = (unsigned)Y + V*c->yuv2rgb_v2g_coeff + U*c->yuv2rgb_u2g_coeff; + B = (unsigned)Y + U*c->yuv2rgb_u2b_coeff; if ((R | G | B) & 0xC0000000) { R = av_clip_uintp2(R, 30); G = av_clip_uintp2(G, 30); @@ -2090,7 +2093,7 @@ yuv2rgb_full_1_c_template(SwsContext *c, const int16_t *buf0, if (uvalpha < 2048) { int A = 0; //init to silence warning for (i = 0; i < dstW; i++) { - int Y = buf0[i] << 2; + int Y = buf0[i] * 4; int U = (ubuf0[i] - (128<<7)) * 4; int V = (vbuf0[i] - (128<<7)) * 4; @@ -2107,9 +2110,9 @@ yuv2rgb_full_1_c_template(SwsContext *c, const int16_t *buf0, const int16_t *ubuf1 = ubuf[1], *vbuf1 = vbuf[1]; int A = 0; //init to silence warning for (i = 0; i < dstW; i++) { - int Y = buf0[i] << 2; - int U = (ubuf0[i] + ubuf1[i] - (128<<8)) << 1; - int V = (vbuf0[i] + vbuf1[i] - (128<<8)) << 1; + int Y = buf0[i] * 4; + int U = (ubuf0[i] + ubuf1[i] - (128<<8)) * 2; + int V = (vbuf0[i] + vbuf1[i] - (128<<8)) * 2; if (hasAlpha) { A = (abuf0[i] + 64) >> 7; @@ -2275,7 +2278,7 @@ yuv2gbrp16_full_X_c(SwsContext *c, const int16_t *lumFilter, A = -0x40000000; for (j = 0; j < lumFilterSize; j++) - A += alpSrc[j][i] * lumFilter[j]; + A += alpSrc[j][i] * (unsigned)lumFilter[j]; A >>= 1; A += 0x20002000; @@ -2309,6 +2312,82 @@ yuv2gbrp16_full_X_c(SwsContext *c, const int16_t *lumFilter, } } +static void +yuv2gbrpf32_full_X_c(SwsContext *c, const int16_t *lumFilter, + const int16_t **lumSrcx, int lumFilterSize, + const int16_t *chrFilter, const int16_t **chrUSrcx, + const int16_t **chrVSrcx, int chrFilterSize, + const int16_t **alpSrcx, uint8_t **dest, + int dstW, int y) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(c->dstFormat); + int i; + int hasAlpha = (desc->flags & AV_PIX_FMT_FLAG_ALPHA) && alpSrcx; + uint32_t **dest32 = (uint32_t**)dest; + const int32_t **lumSrc = (const int32_t**)lumSrcx; + const int32_t **chrUSrc = (const int32_t**)chrUSrcx; + const int32_t **chrVSrc = (const int32_t**)chrVSrcx; + const int32_t **alpSrc = (const int32_t**)alpSrcx; + static const float float_mult = 1.0f / 65535.0f; + + for (i = 0; i < dstW; i++) { + int j; + int Y = -0x40000000; + int U = -(128 << 23); + int V = -(128 << 23); + int R, G, B, A; + + for (j = 0; j < lumFilterSize; j++) + Y += lumSrc[j][i] * (unsigned)lumFilter[j]; + + for (j = 0; j < chrFilterSize; j++) { + U += chrUSrc[j][i] * (unsigned)chrFilter[j]; + V += chrVSrc[j][i] * (unsigned)chrFilter[j]; + } + + Y >>= 14; + Y += 0x10000; + U >>= 14; + V >>= 14; + + if (hasAlpha) { + A = -0x40000000; + + for (j = 0; j < lumFilterSize; j++) + A += alpSrc[j][i] * (unsigned)lumFilter[j]; + + A >>= 1; + A += 0x20002000; + } + + Y -= c->yuv2rgb_y_offset; + Y *= c->yuv2rgb_y_coeff; + Y += 1 << 13; + R = V * c->yuv2rgb_v2r_coeff; + G = V * c->yuv2rgb_v2g_coeff + U * c->yuv2rgb_u2g_coeff; + B = U * c->yuv2rgb_u2b_coeff; + + R = av_clip_uintp2(Y + R, 30); + G = av_clip_uintp2(Y + G, 30); + B = av_clip_uintp2(Y + B, 30); + + dest32[0][i] = av_float2int(float_mult * (float)(G >> 14)); + dest32[1][i] = av_float2int(float_mult * (float)(B >> 14)); + dest32[2][i] = av_float2int(float_mult * (float)(R >> 14)); + if (hasAlpha) + dest32[3][i] = av_float2int(float_mult * (float)(av_clip_uintp2(A, 30) >> 14)); + } + if ((!isBE(c->dstFormat)) != (!HAVE_BIGENDIAN)) { + for (i = 0; i < dstW; i++) { + dest32[0][i] = av_bswap32(dest32[0][i]); + dest32[1][i] = av_bswap32(dest32[1][i]); + dest32[2][i] = av_bswap32(dest32[2][i]); + if (hasAlpha) + dest32[3][i] = av_bswap32(dest32[3][i]); + } + } +} + static void yuv2ya8_1_c(SwsContext *c, const int16_t *buf0, const int16_t *ubuf[2], const int16_t *vbuf[2], @@ -2713,6 +2792,12 @@ av_cold void ff_sws_init_output_funcs(SwsContext *c, case AV_PIX_FMT_GBRAP16LE: *yuv2anyX = yuv2gbrp16_full_X_c; break; + case AV_PIX_FMT_GBRPF32BE: + case AV_PIX_FMT_GBRPF32LE: + case AV_PIX_FMT_GBRAPF32BE: + case AV_PIX_FMT_GBRAPF32LE: + *yuv2anyX = yuv2gbrpf32_full_X_c; + break; } if (!*yuv2packedX && !*yuv2anyX) goto YUV_PACKED; diff --git a/libswscale/ppc/swscale_altivec.c b/libswscale/ppc/swscale_altivec.c index 6b8cc2c194a..1630355f51c 100644 --- a/libswscale/ppc/swscale_altivec.c +++ b/libswscale/ppc/swscale_altivec.c @@ -153,13 +153,13 @@ static void yuv2plane1_float_altivec(const int32_t *src, float *dest, int dstW) const int add = (1 << (shift - 1)); const int clip = (1 << 16) - 1; const float fmult = 1.0f / 65535.0f; - const vector uint32_t vadd = (vector uint32_t) {add, add, add, add}; - const vector uint32_t vshift = (vector uint32_t) vec_splat_u32(shift); - const vector uint32_t vlargest = (vector uint32_t) {clip, clip, clip, clip}; - const vector float vmul = (vector float) {fmult, fmult, fmult, fmult}; - const vector float vzero = (vector float) {0, 0, 0, 0}; - vector uint32_t v; - vector float vd; + const vec_u32 vadd = (vec_u32) {add, add, add, add}; + const vec_u32 vshift = (vec_u32) vec_splat_u32(shift); + const vec_u32 vlargest = (vec_u32) {clip, clip, clip, clip}; + const vec_f vmul = (vec_f) {fmult, fmult, fmult, fmult}; + const vec_f vzero = (vec_f) {0, 0, 0, 0}; + vec_u32 v; + vec_f vd; int i; yuv2plane1_float_u(src, dest, dst_u, 0); @@ -186,15 +186,15 @@ static void yuv2plane1_float_bswap_altivec(const int32_t *src, uint32_t *dest, i const int add = (1 << (shift - 1)); const int clip = (1 << 16) - 1; const float fmult = 1.0f / 65535.0f; - const vector uint32_t vadd = (vector uint32_t) {add, add, add, add}; - const vector uint32_t vshift = (vector uint32_t) vec_splat_u32(shift); - const vector uint32_t vlargest = (vector uint32_t) {clip, clip, clip, clip}; - const vector float vmul = (vector float) {fmult, fmult, fmult, fmult}; - const vector float vzero = (vector float) {0, 0, 0, 0}; - const vector uint32_t vswapbig = (vector uint32_t) {16, 16, 16, 16}; - const vector uint16_t vswapsmall = vec_splat_u16(8); - vector uint32_t v; - vector float vd; + const vec_u32 vadd = (vec_u32) {add, add, add, add}; + const vec_u32 vshift = (vec_u32) vec_splat_u32(shift); + const vec_u32 vlargest = (vec_u32) {clip, clip, clip, clip}; + const vec_f vmul = (vec_f) {fmult, fmult, fmult, fmult}; + const vec_f vzero = (vec_f) {0, 0, 0, 0}; + const vec_u32 vswapbig = (vec_u32) {16, 16, 16, 16}; + const vec_u16 vswapsmall = vec_splat_u16(8); + vec_u32 v; + vec_f vd; int i; yuv2plane1_float_bswap_u(src, dest, dst_u, 0); @@ -208,8 +208,8 @@ static void yuv2plane1_float_bswap_altivec(const int32_t *src, uint32_t *dest, i vd = vec_ctf(v, 0); vd = vec_madd(vd, vmul, vzero); - vd = (vector float) vec_rl((vector uint32_t) vd, vswapbig); - vd = (vector float) vec_rl((vector uint16_t) vd, vswapsmall); + vd = (vec_f) vec_rl((vec_u32) vd, vswapbig); + vd = (vec_f) vec_rl((vec_u16) vd, vswapsmall); vec_st(vd, 0, (float *) &dest[i]); } diff --git a/libswscale/ppc/swscale_vsx.c b/libswscale/ppc/swscale_vsx.c index 75dee5ea588..af8b0e1fa30 100644 --- a/libswscale/ppc/swscale_vsx.c +++ b/libswscale/ppc/swscale_vsx.c @@ -103,9 +103,9 @@ static void yuv2plane1_8_vsx(const int16_t *src, uint8_t *dest, int dstW, const int dst_u = -(uintptr_t)dest & 15; int i, j; LOCAL_ALIGNED(16, int16_t, val, [16]); - const vector uint16_t shifts = (vector uint16_t) {7, 7, 7, 7, 7, 7, 7, 7}; - vector int16_t vi, vileft, ditherleft, ditherright; - vector uint8_t vd; + const vec_u16 shifts = (vec_u16) {7, 7, 7, 7, 7, 7, 7, 7}; + vec_s16 vi, vileft, ditherleft, ditherright; + vec_u8 vd; for (j = 0; j < 16; j++) { val[j] = dither[(dst_u + offset + j) & 7]; @@ -154,18 +154,20 @@ static void yuv2plane1_nbps_u(const int16_t *src, uint16_t *dest, int dstW, } } -static void yuv2plane1_nbps_vsx(const int16_t *src, uint16_t *dest, int dstW, - int big_endian, int output_bits) +static av_always_inline void yuv2plane1_nbps_vsx(const int16_t *src, + uint16_t *dest, int dstW, + const int big_endian, + const int output_bits) { const int dst_u = -(uintptr_t)dest & 7; const int shift = 15 - output_bits; const int add = (1 << (shift - 1)); const int clip = (1 << output_bits) - 1; - const vector uint16_t vadd = (vector uint16_t) {add, add, add, add, add, add, add, add}; - const vector uint16_t vswap = (vector uint16_t) vec_splat_u16(big_endian ? 8 : 0); - const vector uint16_t vshift = (vector uint16_t) vec_splat_u16(shift); - const vector uint16_t vlargest = (vector uint16_t) {clip, clip, clip, clip, clip, clip, clip, clip}; - vector uint16_t v; + const vec_u16 vadd = (vec_u16) {add, add, add, add, add, add, add, add}; + const vec_u16 vswap = (vec_u16) vec_splat_u16(big_endian ? 8 : 0); + const vec_u16 vshift = (vec_u16) vec_splat_u16(shift); + const vec_u16 vlargest = (vec_u16) {clip, clip, clip, clip, clip, clip, clip, clip}; + vec_u16 v; int i; yuv2plane1_nbps_u(src, dest, dst_u, big_endian, output_bits, 0); @@ -209,20 +211,20 @@ static void yuv2planeX_nbps_vsx(const int16_t *filter, int filterSize, const int add = (1 << (shift - 1)); const int clip = (1 << output_bits) - 1; const uint16_t swap = big_endian ? 8 : 0; - const vector uint32_t vadd = (vector uint32_t) {add, add, add, add}; - const vector uint32_t vshift = (vector uint32_t) {shift, shift, shift, shift}; - const vector uint16_t vswap = (vector uint16_t) {swap, swap, swap, swap, swap, swap, swap, swap}; - const vector uint16_t vlargest = (vector uint16_t) {clip, clip, clip, clip, clip, clip, clip, clip}; - const vector int16_t vzero = vec_splat_s16(0); - const vector uint8_t vperm = (vector uint8_t) {0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15}; - vector int16_t vfilter[MAX_FILTER_SIZE], vin; - vector uint16_t v; - vector uint32_t vleft, vright, vtmp; + const vec_u32 vadd = (vec_u32) {add, add, add, add}; + const vec_u32 vshift = (vec_u32) {shift, shift, shift, shift}; + const vec_u16 vswap = (vec_u16) {swap, swap, swap, swap, swap, swap, swap, swap}; + const vec_u16 vlargest = (vec_u16) {clip, clip, clip, clip, clip, clip, clip, clip}; + const vec_s16 vzero = vec_splat_s16(0); + const vec_u8 vperm = (vec_u8) {0, 1, 8, 9, 2, 3, 10, 11, 4, 5, 12, 13, 6, 7, 14, 15}; + vec_s16 vfilter[MAX_FILTER_SIZE], vin; + vec_u16 v; + vec_u32 vleft, vright, vtmp; int i, j; for (i = 0; i < filterSize; i++) { - vfilter[i] = (vector int16_t) {filter[i], filter[i], filter[i], filter[i], - filter[i], filter[i], filter[i], filter[i]}; + vfilter[i] = (vec_s16) {filter[i], filter[i], filter[i], filter[i], + filter[i], filter[i], filter[i], filter[i]}; } yuv2planeX_nbps_u(filter, filterSize, src, dest, dst_u, big_endian, output_bits, 0); @@ -232,16 +234,16 @@ static void yuv2planeX_nbps_vsx(const int16_t *filter, int filterSize, for (j = 0; j < filterSize; j++) { vin = vec_vsx_ld(0, &src[j][i]); - vtmp = (vector uint32_t) vec_mule(vin, vfilter[j]); + vtmp = (vec_u32) vec_mule(vin, vfilter[j]); vleft = vec_add(vleft, vtmp); - vtmp = (vector uint32_t) vec_mulo(vin, vfilter[j]); + vtmp = (vec_u32) vec_mulo(vin, vfilter[j]); vright = vec_add(vright, vtmp); } vleft = vec_sra(vleft, vshift); vright = vec_sra(vright, vshift); v = vec_packsu(vleft, vright); - v = (vector uint16_t) vec_max((vector int16_t) v, vzero); + v = (vec_u16) vec_max((vec_s16) v, vzero); v = vec_min(v, vlargest); v = vec_rl(v, vswap); v = vec_perm(v, v, vperm); @@ -273,17 +275,19 @@ static void yuv2plane1_16_u(const int32_t *src, uint16_t *dest, int dstW, } } -static void yuv2plane1_16_vsx(const int32_t *src, uint16_t *dest, int dstW, - int big_endian, int output_bits) +static av_always_inline void yuv2plane1_16_vsx(const int32_t *src, + uint16_t *dest, int dstW, + const int big_endian, + int output_bits) { const int dst_u = -(uintptr_t)dest & 7; const int shift = 3; const int add = (1 << (shift - 1)); - const vector uint32_t vadd = (vector uint32_t) {add, add, add, add}; - const vector uint16_t vswap = (vector uint16_t) vec_splat_u16(big_endian ? 8 : 0); - const vector uint32_t vshift = (vector uint32_t) vec_splat_u32(shift); - vector uint32_t v, v2; - vector uint16_t vd; + const vec_u32 vadd = (vec_u32) {add, add, add, add}; + const vec_u16 vswap = (vec_u16) vec_splat_u16(big_endian ? 8 : 0); + const vec_u32 vshift = (vec_u32) vec_splat_u32(shift); + vec_u32 v, v2; + vec_u16 vd; int i; yuv2plane1_16_u(src, dest, dst_u, big_endian, output_bits, 0); @@ -341,18 +345,18 @@ static void yuv2planeX_16_vsx(const int16_t *filter, int filterSize, const int bias = 0x8000; const int add = (1 << (shift - 1)) - 0x40000000; const uint16_t swap = big_endian ? 8 : 0; - const vector uint32_t vadd = (vector uint32_t) {add, add, add, add}; - const vector uint32_t vshift = (vector uint32_t) {shift, shift, shift, shift}; - const vector uint16_t vswap = (vector uint16_t) {swap, swap, swap, swap, swap, swap, swap, swap}; - const vector uint16_t vbias = (vector uint16_t) {bias, bias, bias, bias, bias, bias, bias, bias}; - vector int32_t vfilter[MAX_FILTER_SIZE]; - vector uint16_t v; - vector uint32_t vleft, vright, vtmp; - vector int32_t vin32l, vin32r; + const vec_u32 vadd = (vec_u32) {add, add, add, add}; + const vec_u32 vshift = (vec_u32) {shift, shift, shift, shift}; + const vec_u16 vswap = (vec_u16) {swap, swap, swap, swap, swap, swap, swap, swap}; + const vec_u16 vbias = (vec_u16) {bias, bias, bias, bias, bias, bias, bias, bias}; + vec_s32 vfilter[MAX_FILTER_SIZE]; + vec_u16 v; + vec_u32 vleft, vright, vtmp; + vec_s32 vin32l, vin32r; int i, j; for (i = 0; i < filterSize; i++) { - vfilter[i] = (vector int32_t) {filter[i], filter[i], filter[i], filter[i]}; + vfilter[i] = (vec_s32) {filter[i], filter[i], filter[i], filter[i]}; } yuv2planeX_16_u(filter, filterSize, src, dest, dst_u, big_endian, output_bits, 0); @@ -364,15 +368,15 @@ static void yuv2planeX_16_vsx(const int16_t *filter, int filterSize, vin32l = vec_vsx_ld(0, &src[j][i]); vin32r = vec_vsx_ld(0, &src[j][i + 4]); - vtmp = (vector uint32_t) vec_mul(vin32l, vfilter[j]); + vtmp = (vec_u32) vec_mul(vin32l, vfilter[j]); vleft = vec_add(vleft, vtmp); - vtmp = (vector uint32_t) vec_mul(vin32r, vfilter[j]); + vtmp = (vec_u32) vec_mul(vin32r, vfilter[j]); vright = vec_add(vright, vtmp); } vleft = vec_sra(vleft, vshift); vright = vec_sra(vright, vshift); - v = (vector uint16_t) vec_packs((vector int32_t) vleft, (vector int32_t) vright); + v = (vec_u16) vec_packs((vec_s32) vleft, (vec_s32) vright); v = vec_add(v, vbias); v = vec_rl(v, vswap); vec_st(v, 0, &dest[i]); @@ -478,9 +482,9 @@ yuv2NBPSX(16, LE, 0, 16, int32_t) out0 = vec_mergeh(bd, gd); \ out1 = vec_mergeh(rd, ad); \ \ - tmp8 = (vector uint8_t) vec_mergeh((vector uint16_t) out0, (vector uint16_t) out1); \ + tmp8 = (vec_u8) vec_mergeh((vec_u16) out0, (vec_u16) out1); \ vec_vsx_st(tmp8, 0, dest); \ - tmp8 = (vector uint8_t) vec_mergel((vector uint16_t) out0, (vector uint16_t) out1); \ + tmp8 = (vec_u8) vec_mergel((vec_u16) out0, (vec_u16) out1); \ vec_vsx_st(tmp8, 16, dest); \ \ dest += 32; \ @@ -489,9 +493,9 @@ yuv2NBPSX(16, LE, 0, 16, int32_t) out0 = vec_mergeh(rd, gd); \ out1 = vec_mergeh(bd, ad); \ \ - tmp8 = (vector uint8_t) vec_mergeh((vector uint16_t) out0, (vector uint16_t) out1); \ + tmp8 = (vec_u8) vec_mergeh((vec_u16) out0, (vec_u16) out1); \ vec_vsx_st(tmp8, 0, dest); \ - tmp8 = (vector uint8_t) vec_mergel((vector uint16_t) out0, (vector uint16_t) out1); \ + tmp8 = (vec_u8) vec_mergel((vec_u16) out0, (vec_u16) out1); \ vec_vsx_st(tmp8, 16, dest); \ \ dest += 32; \ @@ -500,9 +504,9 @@ yuv2NBPSX(16, LE, 0, 16, int32_t) out0 = vec_mergeh(ad, rd); \ out1 = vec_mergeh(gd, bd); \ \ - tmp8 = (vector uint8_t) vec_mergeh((vector uint16_t) out0, (vector uint16_t) out1); \ + tmp8 = (vec_u8) vec_mergeh((vec_u16) out0, (vec_u16) out1); \ vec_vsx_st(tmp8, 0, dest); \ - tmp8 = (vector uint8_t) vec_mergel((vector uint16_t) out0, (vector uint16_t) out1); \ + tmp8 = (vec_u8) vec_mergel((vec_u16) out0, (vec_u16) out1); \ vec_vsx_st(tmp8, 16, dest); \ \ dest += 32; \ @@ -511,9 +515,9 @@ yuv2NBPSX(16, LE, 0, 16, int32_t) out0 = vec_mergeh(ad, bd); \ out1 = vec_mergeh(gd, rd); \ \ - tmp8 = (vector uint8_t) vec_mergeh((vector uint16_t) out0, (vector uint16_t) out1); \ + tmp8 = (vec_u8) vec_mergeh((vec_u16) out0, (vec_u16) out1); \ vec_vsx_st(tmp8, 0, dest); \ - tmp8 = (vector uint8_t) vec_mergel((vector uint16_t) out0, (vector uint16_t) out1); \ + tmp8 = (vec_u8) vec_mergel((vec_u16) out0, (vec_u16) out1); \ vec_vsx_st(tmp8, 16, dest); \ \ dest += 32; \ @@ -528,48 +532,48 @@ yuv2rgb_full_X_vsx_template(SwsContext *c, const int16_t *lumFilter, const int16_t **alpSrc, uint8_t *dest, int dstW, int y, enum AVPixelFormat target, int hasAlpha) { - vector int16_t vv; - vector int32_t vy32_l, vy32_r, vu32_l, vu32_r, vv32_l, vv32_r, tmp32; - vector int32_t R_l, R_r, G_l, G_r, B_l, B_r; - vector int32_t tmp, tmp2, tmp3, tmp4; - vector uint16_t rd16, gd16, bd16; - vector uint8_t rd, bd, gd, ad, out0, out1, tmp8; - vector int16_t vlumFilter[MAX_FILTER_SIZE], vchrFilter[MAX_FILTER_SIZE]; - const vector int32_t ystart = vec_splats(1 << 9); - const vector int32_t uvstart = vec_splats((1 << 9) - (128 << 19)); - const vector uint16_t zero16 = vec_splat_u16(0); - const vector int32_t y_offset = vec_splats(c->yuv2rgb_y_offset); - const vector int32_t y_coeff = vec_splats(c->yuv2rgb_y_coeff); - const vector int32_t y_add = vec_splats(1 << 21); - const vector int32_t v2r_coeff = vec_splats(c->yuv2rgb_v2r_coeff); - const vector int32_t v2g_coeff = vec_splats(c->yuv2rgb_v2g_coeff); - const vector int32_t u2g_coeff = vec_splats(c->yuv2rgb_u2g_coeff); - const vector int32_t u2b_coeff = vec_splats(c->yuv2rgb_u2b_coeff); - const vector int32_t rgbclip = vec_splats(1 << 30); - const vector int32_t zero32 = vec_splat_s32(0); - const vector uint32_t shift22 = vec_splats(22U); - const vector uint32_t shift10 = vec_splat_u32(10); + vec_s16 vv; + vec_s32 vy32_l, vy32_r, vu32_l, vu32_r, vv32_l, vv32_r, tmp32; + vec_s32 R_l, R_r, G_l, G_r, B_l, B_r; + vec_s32 tmp, tmp2, tmp3, tmp4; + vec_u16 rd16, gd16, bd16; + vec_u8 rd, bd, gd, ad, out0, out1, tmp8; + vec_s16 vlumFilter[MAX_FILTER_SIZE], vchrFilter[MAX_FILTER_SIZE]; + const vec_s32 ystart = vec_splats(1 << 9); + const vec_s32 uvstart = vec_splats((1 << 9) - (128 << 19)); + const vec_u16 zero16 = vec_splat_u16(0); + const vec_s32 y_offset = vec_splats(c->yuv2rgb_y_offset); + const vec_s32 y_coeff = vec_splats(c->yuv2rgb_y_coeff); + const vec_s32 y_add = vec_splats(1 << 21); + const vec_s32 v2r_coeff = vec_splats(c->yuv2rgb_v2r_coeff); + const vec_s32 v2g_coeff = vec_splats(c->yuv2rgb_v2g_coeff); + const vec_s32 u2g_coeff = vec_splats(c->yuv2rgb_u2g_coeff); + const vec_s32 u2b_coeff = vec_splats(c->yuv2rgb_u2b_coeff); + const vec_s32 rgbclip = vec_splats(1 << 30); + const vec_s32 zero32 = vec_splat_s32(0); + const vec_u32 shift22 = vec_splats(22U); + const vec_u32 shift10 = vec_splat_u32(10); int i, j; // Various permutations - const vector uint8_t perm3rg0 = (vector uint8_t) {0x0, 0x10, 0, - 0x1, 0x11, 0, - 0x2, 0x12, 0, - 0x3, 0x13, 0, - 0x4, 0x14, 0, - 0x5 }; - const vector uint8_t perm3rg1 = (vector uint8_t) { 0x15, 0, - 0x6, 0x16, 0, - 0x7, 0x17, 0 }; - const vector uint8_t perm3tb0 = (vector uint8_t) {0x0, 0x1, 0x10, - 0x3, 0x4, 0x11, - 0x6, 0x7, 0x12, - 0x9, 0xa, 0x13, - 0xc, 0xd, 0x14, - 0xf }; - const vector uint8_t perm3tb1 = (vector uint8_t) { 0x0, 0x15, - 0x2, 0x3, 0x16, - 0x5, 0x6, 0x17 }; + const vec_u8 perm3rg0 = (vec_u8) {0x0, 0x10, 0, + 0x1, 0x11, 0, + 0x2, 0x12, 0, + 0x3, 0x13, 0, + 0x4, 0x14, 0, + 0x5 }; + const vec_u8 perm3rg1 = (vec_u8) { 0x15, 0, + 0x6, 0x16, 0, + 0x7, 0x17, 0 }; + const vec_u8 perm3tb0 = (vec_u8) {0x0, 0x1, 0x10, + 0x3, 0x4, 0x11, + 0x6, 0x7, 0x12, + 0x9, 0xa, 0x13, + 0xc, 0xd, 0x14, + 0xf }; + const vec_u8 perm3tb1 = (vec_u8) { 0x0, 0x15, + 0x2, 0x3, 0x16, + 0x5, 0x6, 0x17 }; ad = vec_splats((uint8_t) 255); @@ -685,52 +689,52 @@ yuv2rgb_full_2_vsx_template(SwsContext *c, const int16_t *buf[2], *abuf1 = hasAlpha ? abuf[1] : NULL; const int16_t yalpha1 = 4096 - yalpha; const int16_t uvalpha1 = 4096 - uvalpha; - vector int16_t vy, vu, vv, A = vec_splat_s16(0); - vector int32_t vy32_l, vy32_r, vu32_l, vu32_r, vv32_l, vv32_r, tmp32; - vector int32_t R_l, R_r, G_l, G_r, B_l, B_r; - vector int32_t tmp, tmp2, tmp3, tmp4, tmp5, tmp6; - vector uint16_t rd16, gd16, bd16; - vector uint8_t rd, bd, gd, ad, out0, out1, tmp8; - const vector int16_t vyalpha1 = vec_splats(yalpha1); - const vector int16_t vuvalpha1 = vec_splats(uvalpha1); - const vector int16_t vyalpha = vec_splats((int16_t) yalpha); - const vector int16_t vuvalpha = vec_splats((int16_t) uvalpha); - const vector uint16_t zero16 = vec_splat_u16(0); - const vector int32_t y_offset = vec_splats(c->yuv2rgb_y_offset); - const vector int32_t y_coeff = vec_splats(c->yuv2rgb_y_coeff); - const vector int32_t y_add = vec_splats(1 << 21); - const vector int32_t v2r_coeff = vec_splats(c->yuv2rgb_v2r_coeff); - const vector int32_t v2g_coeff = vec_splats(c->yuv2rgb_v2g_coeff); - const vector int32_t u2g_coeff = vec_splats(c->yuv2rgb_u2g_coeff); - const vector int32_t u2b_coeff = vec_splats(c->yuv2rgb_u2b_coeff); - const vector int32_t rgbclip = vec_splats(1 << 30); - const vector int32_t zero32 = vec_splat_s32(0); - const vector uint32_t shift19 = vec_splats(19U); - const vector uint32_t shift22 = vec_splats(22U); - const vector uint32_t shift10 = vec_splat_u32(10); - const vector int32_t dec128 = vec_splats(128 << 19); - const vector int32_t add18 = vec_splats(1 << 18); + vec_s16 vy, vu, vv, A = vec_splat_s16(0); + vec_s32 vy32_l, vy32_r, vu32_l, vu32_r, vv32_l, vv32_r, tmp32; + vec_s32 R_l, R_r, G_l, G_r, B_l, B_r; + vec_s32 tmp, tmp2, tmp3, tmp4, tmp5, tmp6; + vec_u16 rd16, gd16, bd16; + vec_u8 rd, bd, gd, ad, out0, out1, tmp8; + const vec_s16 vyalpha1 = vec_splats(yalpha1); + const vec_s16 vuvalpha1 = vec_splats(uvalpha1); + const vec_s16 vyalpha = vec_splats((int16_t) yalpha); + const vec_s16 vuvalpha = vec_splats((int16_t) uvalpha); + const vec_u16 zero16 = vec_splat_u16(0); + const vec_s32 y_offset = vec_splats(c->yuv2rgb_y_offset); + const vec_s32 y_coeff = vec_splats(c->yuv2rgb_y_coeff); + const vec_s32 y_add = vec_splats(1 << 21); + const vec_s32 v2r_coeff = vec_splats(c->yuv2rgb_v2r_coeff); + const vec_s32 v2g_coeff = vec_splats(c->yuv2rgb_v2g_coeff); + const vec_s32 u2g_coeff = vec_splats(c->yuv2rgb_u2g_coeff); + const vec_s32 u2b_coeff = vec_splats(c->yuv2rgb_u2b_coeff); + const vec_s32 rgbclip = vec_splats(1 << 30); + const vec_s32 zero32 = vec_splat_s32(0); + const vec_u32 shift19 = vec_splats(19U); + const vec_u32 shift22 = vec_splats(22U); + const vec_u32 shift10 = vec_splat_u32(10); + const vec_s32 dec128 = vec_splats(128 << 19); + const vec_s32 add18 = vec_splats(1 << 18); int i; // Various permutations - const vector uint8_t perm3rg0 = (vector uint8_t) {0x0, 0x10, 0, - 0x1, 0x11, 0, - 0x2, 0x12, 0, - 0x3, 0x13, 0, - 0x4, 0x14, 0, - 0x5 }; - const vector uint8_t perm3rg1 = (vector uint8_t) { 0x15, 0, - 0x6, 0x16, 0, - 0x7, 0x17, 0 }; - const vector uint8_t perm3tb0 = (vector uint8_t) {0x0, 0x1, 0x10, - 0x3, 0x4, 0x11, - 0x6, 0x7, 0x12, - 0x9, 0xa, 0x13, - 0xc, 0xd, 0x14, - 0xf }; - const vector uint8_t perm3tb1 = (vector uint8_t) { 0x0, 0x15, - 0x2, 0x3, 0x16, - 0x5, 0x6, 0x17 }; + const vec_u8 perm3rg0 = (vec_u8) {0x0, 0x10, 0, + 0x1, 0x11, 0, + 0x2, 0x12, 0, + 0x3, 0x13, 0, + 0x4, 0x14, 0, + 0x5 }; + const vec_u8 perm3rg1 = (vec_u8) { 0x15, 0, + 0x6, 0x16, 0, + 0x7, 0x17, 0 }; + const vec_u8 perm3tb0 = (vec_u8) {0x0, 0x1, 0x10, + 0x3, 0x4, 0x11, + 0x6, 0x7, 0x12, + 0x9, 0xa, 0x13, + 0xc, 0xd, 0x14, + 0xf }; + const vec_u8 perm3tb1 = (vec_u8) { 0x0, 0x15, + 0x2, 0x3, 0x16, + 0x5, 0x6, 0x17 }; av_assert2(yalpha <= 4096U); av_assert2(uvalpha <= 4096U); @@ -759,7 +763,7 @@ yuv2rgb_full_2_vsx_template(SwsContext *c, const int16_t *buf[2], tmp3 = vec_sra(tmp3, shift19); tmp4 = vec_sra(tmp4, shift19); A = vec_packs(tmp3, tmp4); - ad = vec_packsu(A, (vector int16_t) zero16); + ad = vec_packsu(A, (vec_s16) zero16); } else { ad = vec_splats((uint8_t) 255); } @@ -807,60 +811,60 @@ yuv2rgb_2_vsx_template(SwsContext *c, const int16_t *buf[2], *abuf1 = hasAlpha ? abuf[1] : NULL; const int16_t yalpha1 = 4096 - yalpha; const int16_t uvalpha1 = 4096 - uvalpha; - vector int16_t vy, vu, vv, A = vec_splat_s16(0); - vector int32_t vy32_l, vy32_r, vu32_l, vu32_r, vv32_l, vv32_r, tmp32; - vector int32_t R_l, R_r, G_l, G_r, B_l, B_r, vud32_l, vud32_r, vvd32_l, vvd32_r; - vector int32_t tmp, tmp2, tmp3, tmp4, tmp5, tmp6; - vector uint16_t rd16, gd16, bd16; - vector uint8_t rd, bd, gd, ad, out0, out1, tmp8; - const vector int16_t vyalpha1 = vec_splats(yalpha1); - const vector int16_t vuvalpha1 = vec_splats(uvalpha1); - const vector int16_t vyalpha = vec_splats((int16_t) yalpha); - const vector int16_t vuvalpha = vec_splats((int16_t) uvalpha); - const vector uint16_t zero16 = vec_splat_u16(0); - const vector int32_t y_offset = vec_splats(c->yuv2rgb_y_offset); - const vector int32_t y_coeff = vec_splats(c->yuv2rgb_y_coeff); - const vector int32_t y_add = vec_splats(1 << 21); - const vector int32_t v2r_coeff = vec_splats(c->yuv2rgb_v2r_coeff); - const vector int32_t v2g_coeff = vec_splats(c->yuv2rgb_v2g_coeff); - const vector int32_t u2g_coeff = vec_splats(c->yuv2rgb_u2g_coeff); - const vector int32_t u2b_coeff = vec_splats(c->yuv2rgb_u2b_coeff); - const vector int32_t rgbclip = vec_splats(1 << 30); - const vector int32_t zero32 = vec_splat_s32(0); - const vector uint32_t shift19 = vec_splats(19U); - const vector uint32_t shift22 = vec_splats(22U); - const vector uint32_t shift10 = vec_splat_u32(10); - const vector int32_t dec128 = vec_splats(128 << 19); - const vector int32_t add18 = vec_splats(1 << 18); + vec_s16 vy, vu, vv, A = vec_splat_s16(0); + vec_s32 vy32_l, vy32_r, vu32_l, vu32_r, vv32_l, vv32_r, tmp32; + vec_s32 R_l, R_r, G_l, G_r, B_l, B_r, vud32_l, vud32_r, vvd32_l, vvd32_r; + vec_s32 tmp, tmp2, tmp3, tmp4, tmp5, tmp6; + vec_u16 rd16, gd16, bd16; + vec_u8 rd, bd, gd, ad, out0, out1, tmp8; + const vec_s16 vyalpha1 = vec_splats(yalpha1); + const vec_s16 vuvalpha1 = vec_splats(uvalpha1); + const vec_s16 vyalpha = vec_splats((int16_t) yalpha); + const vec_s16 vuvalpha = vec_splats((int16_t) uvalpha); + const vec_u16 zero16 = vec_splat_u16(0); + const vec_s32 y_offset = vec_splats(c->yuv2rgb_y_offset); + const vec_s32 y_coeff = vec_splats(c->yuv2rgb_y_coeff); + const vec_s32 y_add = vec_splats(1 << 21); + const vec_s32 v2r_coeff = vec_splats(c->yuv2rgb_v2r_coeff); + const vec_s32 v2g_coeff = vec_splats(c->yuv2rgb_v2g_coeff); + const vec_s32 u2g_coeff = vec_splats(c->yuv2rgb_u2g_coeff); + const vec_s32 u2b_coeff = vec_splats(c->yuv2rgb_u2b_coeff); + const vec_s32 rgbclip = vec_splats(1 << 30); + const vec_s32 zero32 = vec_splat_s32(0); + const vec_u32 shift19 = vec_splats(19U); + const vec_u32 shift22 = vec_splats(22U); + const vec_u32 shift10 = vec_splat_u32(10); + const vec_s32 dec128 = vec_splats(128 << 19); + const vec_s32 add18 = vec_splats(1 << 18); int i; // Various permutations - const vector uint8_t doubleleft = (vector uint8_t) {0, 1, 2, 3, - 0, 1, 2, 3, - 4, 5, 6, 7, - 4, 5, 6, 7 }; - const vector uint8_t doubleright = (vector uint8_t) {8, 9, 10, 11, - 8, 9, 10, 11, - 12, 13, 14, 15, - 12, 13, 14, 15 }; - const vector uint8_t perm3rg0 = (vector uint8_t) {0x0, 0x10, 0, - 0x1, 0x11, 0, - 0x2, 0x12, 0, - 0x3, 0x13, 0, - 0x4, 0x14, 0, - 0x5 }; - const vector uint8_t perm3rg1 = (vector uint8_t) { 0x15, 0, - 0x6, 0x16, 0, - 0x7, 0x17, 0 }; - const vector uint8_t perm3tb0 = (vector uint8_t) {0x0, 0x1, 0x10, - 0x3, 0x4, 0x11, - 0x6, 0x7, 0x12, - 0x9, 0xa, 0x13, - 0xc, 0xd, 0x14, - 0xf }; - const vector uint8_t perm3tb1 = (vector uint8_t) { 0x0, 0x15, - 0x2, 0x3, 0x16, - 0x5, 0x6, 0x17 }; + const vec_u8 doubleleft = (vec_u8) {0, 1, 2, 3, + 0, 1, 2, 3, + 4, 5, 6, 7, + 4, 5, 6, 7 }; + const vec_u8 doubleright = (vec_u8) {8, 9, 10, 11, + 8, 9, 10, 11, + 12, 13, 14, 15, + 12, 13, 14, 15 }; + const vec_u8 perm3rg0 = (vec_u8) {0x0, 0x10, 0, + 0x1, 0x11, 0, + 0x2, 0x12, 0, + 0x3, 0x13, 0, + 0x4, 0x14, 0, + 0x5 }; + const vec_u8 perm3rg1 = (vec_u8) { 0x15, 0, + 0x6, 0x16, 0, + 0x7, 0x17, 0 }; + const vec_u8 perm3tb0 = (vec_u8) {0x0, 0x1, 0x10, + 0x3, 0x4, 0x11, + 0x6, 0x7, 0x12, + 0x9, 0xa, 0x13, + 0xc, 0xd, 0x14, + 0xf }; + const vec_u8 perm3tb1 = (vec_u8) { 0x0, 0x15, + 0x2, 0x3, 0x16, + 0x5, 0x6, 0x17 }; av_assert2(yalpha <= 4096U); av_assert2(uvalpha <= 4096U); @@ -889,7 +893,7 @@ yuv2rgb_2_vsx_template(SwsContext *c, const int16_t *buf[2], tmp3 = vec_sra(tmp3, shift19); tmp4 = vec_sra(tmp4, shift19); A = vec_packs(tmp3, tmp4); - ad = vec_packsu(A, (vector int16_t) zero16); + ad = vec_packsu(A, (vec_s16) zero16); } else { ad = vec_splats((uint8_t) 255); } @@ -978,51 +982,51 @@ yuv2rgb_full_1_vsx_template(SwsContext *c, const int16_t *buf0, { const int16_t *ubuf0 = ubuf[0], *vbuf0 = vbuf[0]; const int16_t *ubuf1 = ubuf[1], *vbuf1 = vbuf[1]; - vector int16_t vy, vu, vv, A = vec_splat_s16(0), tmp16; - vector int32_t vy32_l, vy32_r, vu32_l, vu32_r, vv32_l, vv32_r, tmp32, tmp32_2; - vector int32_t R_l, R_r, G_l, G_r, B_l, B_r; - vector uint16_t rd16, gd16, bd16; - vector uint8_t rd, bd, gd, ad, out0, out1, tmp8; - const vector uint16_t zero16 = vec_splat_u16(0); - const vector int32_t y_offset = vec_splats(c->yuv2rgb_y_offset); - const vector int32_t y_coeff = vec_splats(c->yuv2rgb_y_coeff); - const vector int32_t y_add = vec_splats(1 << 21); - const vector int32_t v2r_coeff = vec_splats(c->yuv2rgb_v2r_coeff); - const vector int32_t v2g_coeff = vec_splats(c->yuv2rgb_v2g_coeff); - const vector int32_t u2g_coeff = vec_splats(c->yuv2rgb_u2g_coeff); - const vector int32_t u2b_coeff = vec_splats(c->yuv2rgb_u2b_coeff); - const vector int32_t rgbclip = vec_splats(1 << 30); - const vector int32_t zero32 = vec_splat_s32(0); - const vector uint32_t shift2 = vec_splat_u32(2); - const vector uint32_t shift22 = vec_splats(22U); - const vector uint16_t sub7 = vec_splats((uint16_t) (128 << 7)); - const vector uint16_t sub8 = vec_splats((uint16_t) (128 << 8)); - const vector int16_t mul4 = vec_splat_s16(4); - const vector int16_t mul8 = vec_splat_s16(8); - const vector int16_t add64 = vec_splat_s16(64); - const vector uint16_t shift7 = vec_splat_u16(7); - const vector int16_t max255 = vec_splat_s16(255); + vec_s16 vy, vu, vv, A = vec_splat_s16(0), tmp16; + vec_s32 vy32_l, vy32_r, vu32_l, vu32_r, vv32_l, vv32_r, tmp32, tmp32_2; + vec_s32 R_l, R_r, G_l, G_r, B_l, B_r; + vec_u16 rd16, gd16, bd16; + vec_u8 rd, bd, gd, ad, out0, out1, tmp8; + const vec_u16 zero16 = vec_splat_u16(0); + const vec_s32 y_offset = vec_splats(c->yuv2rgb_y_offset); + const vec_s32 y_coeff = vec_splats(c->yuv2rgb_y_coeff); + const vec_s32 y_add = vec_splats(1 << 21); + const vec_s32 v2r_coeff = vec_splats(c->yuv2rgb_v2r_coeff); + const vec_s32 v2g_coeff = vec_splats(c->yuv2rgb_v2g_coeff); + const vec_s32 u2g_coeff = vec_splats(c->yuv2rgb_u2g_coeff); + const vec_s32 u2b_coeff = vec_splats(c->yuv2rgb_u2b_coeff); + const vec_s32 rgbclip = vec_splats(1 << 30); + const vec_s32 zero32 = vec_splat_s32(0); + const vec_u32 shift2 = vec_splat_u32(2); + const vec_u32 shift22 = vec_splats(22U); + const vec_u16 sub7 = vec_splats((uint16_t) (128 << 7)); + const vec_u16 sub8 = vec_splats((uint16_t) (128 << 8)); + const vec_s16 mul4 = vec_splat_s16(4); + const vec_s16 mul8 = vec_splat_s16(8); + const vec_s16 add64 = vec_splat_s16(64); + const vec_u16 shift7 = vec_splat_u16(7); + const vec_s16 max255 = vec_splat_s16(255); int i; // Various permutations - const vector uint8_t perm3rg0 = (vector uint8_t) {0x0, 0x10, 0, - 0x1, 0x11, 0, - 0x2, 0x12, 0, - 0x3, 0x13, 0, - 0x4, 0x14, 0, - 0x5 }; - const vector uint8_t perm3rg1 = (vector uint8_t) { 0x15, 0, - 0x6, 0x16, 0, - 0x7, 0x17, 0 }; - const vector uint8_t perm3tb0 = (vector uint8_t) {0x0, 0x1, 0x10, - 0x3, 0x4, 0x11, - 0x6, 0x7, 0x12, - 0x9, 0xa, 0x13, - 0xc, 0xd, 0x14, - 0xf }; - const vector uint8_t perm3tb1 = (vector uint8_t) { 0x0, 0x15, - 0x2, 0x3, 0x16, - 0x5, 0x6, 0x17 }; + const vec_u8 perm3rg0 = (vec_u8) {0x0, 0x10, 0, + 0x1, 0x11, 0, + 0x2, 0x12, 0, + 0x3, 0x13, 0, + 0x4, 0x14, 0, + 0x5 }; + const vec_u8 perm3rg1 = (vec_u8) { 0x15, 0, + 0x6, 0x16, 0, + 0x7, 0x17, 0 }; + const vec_u8 perm3tb0 = (vec_u8) {0x0, 0x1, 0x10, + 0x3, 0x4, 0x11, + 0x6, 0x7, 0x12, + 0x9, 0xa, 0x13, + 0xc, 0xd, 0x14, + 0xf }; + const vec_u8 perm3tb1 = (vec_u8) { 0x0, 0x15, + 0x2, 0x3, 0x16, + 0x5, 0x6, 0x17 }; for (i = 0; i < dstW; i += 8) { // The x86 asm also overwrites padding bytes. vy = vec_ld(0, &buf0[i]); @@ -1034,8 +1038,8 @@ yuv2rgb_full_1_vsx_template(SwsContext *c, const int16_t *buf0, vu = vec_ld(0, &ubuf0[i]); vv = vec_ld(0, &vbuf0[i]); if (uvalpha < 2048) { - vu = (vector int16_t) vec_sub((vector uint16_t) vu, sub7); - vv = (vector int16_t) vec_sub((vector uint16_t) vv, sub7); + vu = (vec_s16) vec_sub((vec_u16) vu, sub7); + vv = (vec_s16) vec_sub((vec_u16) vv, sub7); tmp32 = vec_mule(vu, mul4); tmp32_2 = vec_mulo(vu, mul4); @@ -1048,10 +1052,10 @@ yuv2rgb_full_1_vsx_template(SwsContext *c, const int16_t *buf0, } else { tmp16 = vec_ld(0, &ubuf1[i]); vu = vec_add(vu, tmp16); - vu = (vector int16_t) vec_sub((vector uint16_t) vu, sub8); + vu = (vec_s16) vec_sub((vec_u16) vu, sub8); tmp16 = vec_ld(0, &vbuf1[i]); vv = vec_add(vv, tmp16); - vv = (vector int16_t) vec_sub((vector uint16_t) vv, sub8); + vv = (vec_s16) vec_sub((vec_u16) vv, sub8); vu32_l = vec_mule(vu, mul8); vu32_r = vec_mulo(vu, mul8); @@ -1064,7 +1068,7 @@ yuv2rgb_full_1_vsx_template(SwsContext *c, const int16_t *buf0, A = vec_add(A, add64); A = vec_sr(A, shift7); A = vec_max(A, max255); - ad = vec_packsu(A, (vector int16_t) zero16); + ad = vec_packsu(A, (vec_s16) zero16); } else { ad = vec_splats((uint8_t) 255); } @@ -1107,60 +1111,60 @@ yuv2rgb_1_vsx_template(SwsContext *c, const int16_t *buf0, { const int16_t *ubuf0 = ubuf[0], *vbuf0 = vbuf[0]; const int16_t *ubuf1 = ubuf[1], *vbuf1 = vbuf[1]; - vector int16_t vy, vu, vv, A = vec_splat_s16(0), tmp16; - vector int32_t vy32_l, vy32_r, vu32_l, vu32_r, vv32_l, vv32_r, tmp32, tmp32_2; - vector int32_t vud32_l, vud32_r, vvd32_l, vvd32_r; - vector int32_t R_l, R_r, G_l, G_r, B_l, B_r; - vector uint16_t rd16, gd16, bd16; - vector uint8_t rd, bd, gd, ad, out0, out1, tmp8; - const vector uint16_t zero16 = vec_splat_u16(0); - const vector int32_t y_offset = vec_splats(c->yuv2rgb_y_offset); - const vector int32_t y_coeff = vec_splats(c->yuv2rgb_y_coeff); - const vector int32_t y_add = vec_splats(1 << 21); - const vector int32_t v2r_coeff = vec_splats(c->yuv2rgb_v2r_coeff); - const vector int32_t v2g_coeff = vec_splats(c->yuv2rgb_v2g_coeff); - const vector int32_t u2g_coeff = vec_splats(c->yuv2rgb_u2g_coeff); - const vector int32_t u2b_coeff = vec_splats(c->yuv2rgb_u2b_coeff); - const vector int32_t rgbclip = vec_splats(1 << 30); - const vector int32_t zero32 = vec_splat_s32(0); - const vector uint32_t shift2 = vec_splat_u32(2); - const vector uint32_t shift22 = vec_splats(22U); - const vector uint16_t sub7 = vec_splats((uint16_t) (128 << 7)); - const vector uint16_t sub8 = vec_splats((uint16_t) (128 << 8)); - const vector int16_t mul4 = vec_splat_s16(4); - const vector int16_t mul8 = vec_splat_s16(8); - const vector int16_t add64 = vec_splat_s16(64); - const vector uint16_t shift7 = vec_splat_u16(7); - const vector int16_t max255 = vec_splat_s16(255); + vec_s16 vy, vu, vv, A = vec_splat_s16(0), tmp16; + vec_s32 vy32_l, vy32_r, vu32_l, vu32_r, vv32_l, vv32_r, tmp32, tmp32_2; + vec_s32 vud32_l, vud32_r, vvd32_l, vvd32_r; + vec_s32 R_l, R_r, G_l, G_r, B_l, B_r; + vec_u16 rd16, gd16, bd16; + vec_u8 rd, bd, gd, ad, out0, out1, tmp8; + const vec_u16 zero16 = vec_splat_u16(0); + const vec_s32 y_offset = vec_splats(c->yuv2rgb_y_offset); + const vec_s32 y_coeff = vec_splats(c->yuv2rgb_y_coeff); + const vec_s32 y_add = vec_splats(1 << 21); + const vec_s32 v2r_coeff = vec_splats(c->yuv2rgb_v2r_coeff); + const vec_s32 v2g_coeff = vec_splats(c->yuv2rgb_v2g_coeff); + const vec_s32 u2g_coeff = vec_splats(c->yuv2rgb_u2g_coeff); + const vec_s32 u2b_coeff = vec_splats(c->yuv2rgb_u2b_coeff); + const vec_s32 rgbclip = vec_splats(1 << 30); + const vec_s32 zero32 = vec_splat_s32(0); + const vec_u32 shift2 = vec_splat_u32(2); + const vec_u32 shift22 = vec_splats(22U); + const vec_u16 sub7 = vec_splats((uint16_t) (128 << 7)); + const vec_u16 sub8 = vec_splats((uint16_t) (128 << 8)); + const vec_s16 mul4 = vec_splat_s16(4); + const vec_s16 mul8 = vec_splat_s16(8); + const vec_s16 add64 = vec_splat_s16(64); + const vec_u16 shift7 = vec_splat_u16(7); + const vec_s16 max255 = vec_splat_s16(255); int i; // Various permutations - const vector uint8_t doubleleft = (vector uint8_t) {0, 1, 2, 3, - 0, 1, 2, 3, - 4, 5, 6, 7, - 4, 5, 6, 7 }; - const vector uint8_t doubleright = (vector uint8_t) {8, 9, 10, 11, - 8, 9, 10, 11, - 12, 13, 14, 15, - 12, 13, 14, 15 }; - const vector uint8_t perm3rg0 = (vector uint8_t) {0x0, 0x10, 0, - 0x1, 0x11, 0, - 0x2, 0x12, 0, - 0x3, 0x13, 0, - 0x4, 0x14, 0, - 0x5 }; - const vector uint8_t perm3rg1 = (vector uint8_t) { 0x15, 0, - 0x6, 0x16, 0, - 0x7, 0x17, 0 }; - const vector uint8_t perm3tb0 = (vector uint8_t) {0x0, 0x1, 0x10, - 0x3, 0x4, 0x11, - 0x6, 0x7, 0x12, - 0x9, 0xa, 0x13, - 0xc, 0xd, 0x14, - 0xf }; - const vector uint8_t perm3tb1 = (vector uint8_t) { 0x0, 0x15, - 0x2, 0x3, 0x16, - 0x5, 0x6, 0x17 }; + const vec_u8 doubleleft = (vec_u8) {0, 1, 2, 3, + 0, 1, 2, 3, + 4, 5, 6, 7, + 4, 5, 6, 7 }; + const vec_u8 doubleright = (vec_u8) {8, 9, 10, 11, + 8, 9, 10, 11, + 12, 13, 14, 15, + 12, 13, 14, 15 }; + const vec_u8 perm3rg0 = (vec_u8) {0x0, 0x10, 0, + 0x1, 0x11, 0, + 0x2, 0x12, 0, + 0x3, 0x13, 0, + 0x4, 0x14, 0, + 0x5 }; + const vec_u8 perm3rg1 = (vec_u8) { 0x15, 0, + 0x6, 0x16, 0, + 0x7, 0x17, 0 }; + const vec_u8 perm3tb0 = (vec_u8) {0x0, 0x1, 0x10, + 0x3, 0x4, 0x11, + 0x6, 0x7, 0x12, + 0x9, 0xa, 0x13, + 0xc, 0xd, 0x14, + 0xf }; + const vec_u8 perm3tb1 = (vec_u8) { 0x0, 0x15, + 0x2, 0x3, 0x16, + 0x5, 0x6, 0x17 }; for (i = 0; i < (dstW + 1) >> 1; i += 8) { // The x86 asm also overwrites padding bytes. vy = vec_ld(0, &buf0[i * 2]); @@ -1172,8 +1176,8 @@ yuv2rgb_1_vsx_template(SwsContext *c, const int16_t *buf0, vu = vec_ld(0, &ubuf0[i]); vv = vec_ld(0, &vbuf0[i]); if (uvalpha < 2048) { - vu = (vector int16_t) vec_sub((vector uint16_t) vu, sub7); - vv = (vector int16_t) vec_sub((vector uint16_t) vv, sub7); + vu = (vec_s16) vec_sub((vec_u16) vu, sub7); + vv = (vec_s16) vec_sub((vec_u16) vv, sub7); tmp32 = vec_mule(vu, mul4); tmp32_2 = vec_mulo(vu, mul4); @@ -1186,10 +1190,10 @@ yuv2rgb_1_vsx_template(SwsContext *c, const int16_t *buf0, } else { tmp16 = vec_ld(0, &ubuf1[i]); vu = vec_add(vu, tmp16); - vu = (vector int16_t) vec_sub((vector uint16_t) vu, sub8); + vu = (vec_s16) vec_sub((vec_u16) vu, sub8); tmp16 = vec_ld(0, &vbuf1[i]); vv = vec_add(vv, tmp16); - vv = (vector int16_t) vec_sub((vector uint16_t) vv, sub8); + vv = (vec_s16) vec_sub((vec_u16) vv, sub8); vu32_l = vec_mule(vu, mul8); vu32_r = vec_mulo(vu, mul8); @@ -1202,7 +1206,7 @@ yuv2rgb_1_vsx_template(SwsContext *c, const int16_t *buf0, A = vec_add(A, add64); A = vec_sr(A, shift7); A = vec_max(A, max255); - ad = vec_packsu(A, (vector int16_t) zero16); + ad = vec_packsu(A, (vec_s16) zero16); } else { ad = vec_splats((uint8_t) 255); } @@ -1358,41 +1362,41 @@ YUV2RGBWRAPPERX(yuv2, rgb_full, rgb24_full, AV_PIX_FMT_RGB24, 0) YUV2RGBWRAPPERX(yuv2, rgb_full, bgr24_full, AV_PIX_FMT_BGR24, 0) static av_always_inline void -write422(const vector int16_t vy1, const vector int16_t vy2, - const vector int16_t vu, const vector int16_t vv, +write422(const vec_s16 vy1, const vec_s16 vy2, + const vec_s16 vu, const vec_s16 vv, uint8_t *dest, const enum AVPixelFormat target) { - vector uint8_t vd1, vd2, tmp; - const vector uint8_t yuyv1 = (vector uint8_t) { - 0x0, 0x10, 0x1, 0x18, - 0x2, 0x11, 0x3, 0x19, - 0x4, 0x12, 0x5, 0x1a, - 0x6, 0x13, 0x7, 0x1b }; - const vector uint8_t yuyv2 = (vector uint8_t) { - 0x8, 0x14, 0x9, 0x1c, - 0xa, 0x15, 0xb, 0x1d, - 0xc, 0x16, 0xd, 0x1e, - 0xe, 0x17, 0xf, 0x1f }; - const vector uint8_t yvyu1 = (vector uint8_t) { - 0x0, 0x18, 0x1, 0x10, - 0x2, 0x19, 0x3, 0x11, - 0x4, 0x1a, 0x5, 0x12, - 0x6, 0x1b, 0x7, 0x13 }; - const vector uint8_t yvyu2 = (vector uint8_t) { - 0x8, 0x1c, 0x9, 0x14, - 0xa, 0x1d, 0xb, 0x15, - 0xc, 0x1e, 0xd, 0x16, - 0xe, 0x1f, 0xf, 0x17 }; - const vector uint8_t uyvy1 = (vector uint8_t) { - 0x10, 0x0, 0x18, 0x1, - 0x11, 0x2, 0x19, 0x3, - 0x12, 0x4, 0x1a, 0x5, - 0x13, 0x6, 0x1b, 0x7 }; - const vector uint8_t uyvy2 = (vector uint8_t) { - 0x14, 0x8, 0x1c, 0x9, - 0x15, 0xa, 0x1d, 0xb, - 0x16, 0xc, 0x1e, 0xd, - 0x17, 0xe, 0x1f, 0xf }; + vec_u8 vd1, vd2, tmp; + const vec_u8 yuyv1 = (vec_u8) { + 0x0, 0x10, 0x1, 0x18, + 0x2, 0x11, 0x3, 0x19, + 0x4, 0x12, 0x5, 0x1a, + 0x6, 0x13, 0x7, 0x1b }; + const vec_u8 yuyv2 = (vec_u8) { + 0x8, 0x14, 0x9, 0x1c, + 0xa, 0x15, 0xb, 0x1d, + 0xc, 0x16, 0xd, 0x1e, + 0xe, 0x17, 0xf, 0x1f }; + const vec_u8 yvyu1 = (vec_u8) { + 0x0, 0x18, 0x1, 0x10, + 0x2, 0x19, 0x3, 0x11, + 0x4, 0x1a, 0x5, 0x12, + 0x6, 0x1b, 0x7, 0x13 }; + const vec_u8 yvyu2 = (vec_u8) { + 0x8, 0x1c, 0x9, 0x14, + 0xa, 0x1d, 0xb, 0x15, + 0xc, 0x1e, 0xd, 0x16, + 0xe, 0x1f, 0xf, 0x17 }; + const vec_u8 uyvy1 = (vec_u8) { + 0x10, 0x0, 0x18, 0x1, + 0x11, 0x2, 0x19, 0x3, + 0x12, 0x4, 0x1a, 0x5, + 0x13, 0x6, 0x1b, 0x7 }; + const vec_u8 uyvy2 = (vec_u8) { + 0x14, 0x8, 0x1c, 0x9, + 0x15, 0xa, 0x1d, 0xb, + 0x16, 0xc, 0x1e, 0xd, + 0x17, 0xe, 0x1f, 0xf }; vd1 = vec_packsu(vy1, vy2); vd2 = vec_packsu(vu, vv); @@ -1428,11 +1432,11 @@ yuv2422_X_vsx_template(SwsContext *c, const int16_t *lumFilter, int y, enum AVPixelFormat target) { int i, j; - vector int16_t vy1, vy2, vu, vv; - vector int32_t vy32[4], vu32[2], vv32[2], tmp, tmp2, tmp3, tmp4; - vector int16_t vlumFilter[MAX_FILTER_SIZE], vchrFilter[MAX_FILTER_SIZE]; - const vector int32_t start = vec_splats(1 << 18); - const vector uint32_t shift19 = vec_splats(19U); + vec_s16 vy1, vy2, vu, vv; + vec_s32 vy32[4], vu32[2], vv32[2], tmp, tmp2, tmp3, tmp4; + vec_s16 vlumFilter[MAX_FILTER_SIZE], vchrFilter[MAX_FILTER_SIZE]; + const vec_s32 start = vec_splats(1 << 18); + const vec_u32 shift19 = vec_splats(19U); for (i = 0; i < lumFilterSize; i++) vlumFilter[i] = vec_splats(lumFilter[i]); @@ -1539,11 +1543,11 @@ yuv2422_2_vsx_template(SwsContext *c, const int16_t *buf[2], *vbuf0 = vbuf[0], *vbuf1 = vbuf[1]; const int16_t yalpha1 = 4096 - yalpha; const int16_t uvalpha1 = 4096 - uvalpha; - vector int16_t vy1, vy2, vu, vv; - vector int32_t tmp, tmp2, tmp3, tmp4, tmp5, tmp6; - const vector int16_t vyalpha1 = vec_splats(yalpha1); - const vector int16_t vuvalpha1 = vec_splats(uvalpha1); - const vector uint32_t shift19 = vec_splats(19U); + vec_s16 vy1, vy2, vu, vv; + vec_s32 tmp, tmp2, tmp3, tmp4, tmp5, tmp6; + const vec_s16 vyalpha1 = vec_splats(yalpha1); + const vec_s16 vuvalpha1 = vec_splats(uvalpha1); + const vec_u32 shift19 = vec_splats(19U); int i; av_assert2(yalpha <= 4096U); av_assert2(uvalpha <= 4096U); @@ -1568,11 +1572,11 @@ yuv2422_1_vsx_template(SwsContext *c, const int16_t *buf0, int uvalpha, int y, enum AVPixelFormat target) { const int16_t *ubuf0 = ubuf[0], *vbuf0 = vbuf[0]; - vector int16_t vy1, vy2, vu, vv, tmp; - const vector int16_t add64 = vec_splats((int16_t) 64); - const vector int16_t add128 = vec_splats((int16_t) 128); - const vector uint16_t shift7 = vec_splat_u16(7); - const vector uint16_t shift8 = vec_splat_u16(8); + vec_s16 vy1, vy2, vu, vv, tmp; + const vec_s16 add64 = vec_splats((int16_t) 64); + const vec_s16 add128 = vec_splats((int16_t) 128); + const vec_u16 shift7 = vec_splat_u16(7); + const vec_u16 shift8 = vec_splat_u16(8); int i; if (uvalpha < 2048) { @@ -1666,18 +1670,18 @@ static void hyscale_fast_vsx(SwsContext *c, int16_t *dst, int dstWidth, { int i; unsigned int xpos = 0, xx; - vector uint8_t vin, vin2, vperm; - vector int8_t vmul, valpha; - vector int16_t vtmp, vtmp2, vtmp3, vtmp4; - vector uint16_t vd_l, vd_r, vcoord16[2]; - vector uint32_t vcoord[4]; - const vector uint32_t vadd = (vector uint32_t) { + vec_u8 vin, vin2, vperm; + vec_s8 vmul, valpha; + vec_s16 vtmp, vtmp2, vtmp3, vtmp4; + vec_u16 vd_l, vd_r, vcoord16[2]; + vec_u32 vcoord[4]; + const vec_u32 vadd = (vec_u32) { 0, xInc * 1, xInc * 2, xInc * 3, }; - const vector uint16_t vadd16 = (vector uint16_t) { // Modulo math + const vec_u16 vadd16 = (vec_u16) { // Modulo math 0, xInc * 1, xInc * 2, @@ -1687,10 +1691,10 @@ static void hyscale_fast_vsx(SwsContext *c, int16_t *dst, int dstWidth, xInc * 6, xInc * 7, }; - const vector uint32_t vshift16 = vec_splats((uint32_t) 16); - const vector uint16_t vshift9 = vec_splat_u16(9); - const vector uint8_t vzero = vec_splat_u8(0); - const vector uint16_t vshift = vec_splat_u16(7); + const vec_u32 vshift16 = vec_splats((uint32_t) 16); + const vec_u16 vshift9 = vec_splat_u16(9); + const vec_u8 vzero = vec_splat_u8(0); + const vec_u16 vshift = vec_splat_u16(7); for (i = 0; i < dstWidth; i += 16) { vcoord16[0] = vec_splats((uint16_t) xpos); @@ -1701,7 +1705,7 @@ static void hyscale_fast_vsx(SwsContext *c, int16_t *dst, int dstWidth, vcoord16[0] = vec_sr(vcoord16[0], vshift9); vcoord16[1] = vec_sr(vcoord16[1], vshift9); - valpha = (vector int8_t) vec_pack(vcoord16[0], vcoord16[1]); + valpha = (vec_s8) vec_pack(vcoord16[0], vcoord16[1]); xx = xpos >> 16; vin = vec_vsx_ld(0, &src[xx]); @@ -1730,22 +1734,22 @@ static void hyscale_fast_vsx(SwsContext *c, int16_t *dst, int dstWidth, vin2 = vec_vsx_ld(1, &src[xx]); vin2 = vec_perm(vin2, vin2, vperm); - vmul = (vector int8_t) vec_sub(vin2, vin); + vmul = (vec_s8) vec_sub(vin2, vin); vtmp = vec_mule(vmul, valpha); vtmp2 = vec_mulo(vmul, valpha); vtmp3 = vec_mergeh(vtmp, vtmp2); vtmp4 = vec_mergel(vtmp, vtmp2); - vd_l = (vector uint16_t) vec_mergeh(vin, vzero); - vd_r = (vector uint16_t) vec_mergel(vin, vzero); + vd_l = (vec_u16) vec_mergeh(vin, vzero); + vd_r = (vec_u16) vec_mergel(vin, vzero); vd_l = vec_sl(vd_l, vshift); vd_r = vec_sl(vd_r, vshift); - vd_l = vec_add(vd_l, (vector uint16_t) vtmp3); - vd_r = vec_add(vd_r, (vector uint16_t) vtmp4); + vd_l = vec_add(vd_l, (vec_u16) vtmp3); + vd_r = vec_add(vd_r, (vec_u16) vtmp4); - vec_st((vector int16_t) vd_l, 0, &dst[i]); - vec_st((vector int16_t) vd_r, 0, &dst[i + 8]); + vec_st((vec_s16) vd_l, 0, &dst[i]); + vec_st((vec_s16) vd_r, 0, &dst[i + 8]); xpos += xInc * 16; } @@ -1773,8 +1777,8 @@ static void hyscale_fast_vsx(SwsContext *c, int16_t *dst, int dstWidth, vd_l = vec_add(vd_l, vtmp3); \ vd_r = vec_add(vd_r, vtmp4); \ \ - vec_st((vector int16_t) vd_l, 0, &out[i]); \ - vec_st((vector int16_t) vd_r, 0, &out[i + 8]) + vec_st((vec_s16) vd_l, 0, &out[i]); \ + vec_st((vec_s16) vd_r, 0, &out[i + 8]) static void hcscale_fast_vsx(SwsContext *c, int16_t *dst1, int16_t *dst2, int dstWidth, const uint8_t *src1, @@ -1782,19 +1786,19 @@ static void hcscale_fast_vsx(SwsContext *c, int16_t *dst1, int16_t *dst2, { int i; unsigned int xpos = 0, xx; - vector uint8_t vin, vin2, vperm; - vector uint8_t valpha, valphaxor; - vector uint16_t vtmp, vtmp2, vtmp3, vtmp4; - vector uint16_t vd_l, vd_r, vcoord16[2]; - vector uint32_t vcoord[4]; - const vector uint8_t vxor = vec_splats((uint8_t) 127); - const vector uint32_t vadd = (vector uint32_t) { + vec_u8 vin, vin2, vperm; + vec_u8 valpha, valphaxor; + vec_u16 vtmp, vtmp2, vtmp3, vtmp4; + vec_u16 vd_l, vd_r, vcoord16[2]; + vec_u32 vcoord[4]; + const vec_u8 vxor = vec_splats((uint8_t) 127); + const vec_u32 vadd = (vec_u32) { 0, xInc * 1, xInc * 2, xInc * 3, }; - const vector uint16_t vadd16 = (vector uint16_t) { // Modulo math + const vec_u16 vadd16 = (vec_u16) { // Modulo math 0, xInc * 1, xInc * 2, @@ -1804,8 +1808,8 @@ static void hcscale_fast_vsx(SwsContext *c, int16_t *dst1, int16_t *dst2, xInc * 6, xInc * 7, }; - const vector uint32_t vshift16 = vec_splats((uint32_t) 16); - const vector uint16_t vshift9 = vec_splat_u16(9); + const vec_u32 vshift16 = vec_splats((uint32_t) 16); + const vec_u16 vshift9 = vec_splat_u16(9); for (i = 0; i < dstWidth; i += 16) { vcoord16[0] = vec_splats((uint16_t) xpos); @@ -1859,29 +1863,29 @@ static void hScale8To19_vsx(SwsContext *c, int16_t *_dst, int dstW, { int i, j; int32_t *dst = (int32_t *) _dst; - vector int16_t vfilter, vin; - vector uint8_t vin8; - vector int32_t vout; - const vector uint8_t vzero = vec_splat_u8(0); - const vector uint8_t vunusedtab[8] = { - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}, - (vector uint8_t) {0x0, 0x1, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0xa, 0xb, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0x10, 0x10}, + vec_s16 vfilter, vin; + vec_u8 vin8; + vec_s32 vout; + const vec_u8 vzero = vec_splat_u8(0); + const vec_u8 vunusedtab[8] = { + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}, + (vec_u8) {0x0, 0x1, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0x10, 0x10}, }; - const vector uint8_t vunused = vunusedtab[filterSize % 8]; + const vec_u8 vunused = vunusedtab[filterSize % 8]; if (filterSize == 1) { for (i = 0; i < dstW; i++) { @@ -1898,14 +1902,14 @@ static void hScale8To19_vsx(SwsContext *c, int16_t *_dst, int dstW, vout = vec_splat_s32(0); for (j = 0; j < filterSize; j += 8) { vin8 = vec_vsx_ld(0, &src[srcPos + j]); - vin = (vector int16_t) vec_mergeh(vin8, vzero); + vin = (vec_s16) vec_mergeh(vin8, vzero); if (j + 8 > filterSize) // Remove the unused elements on the last round - vin = vec_perm(vin, (vector int16_t) vzero, vunused); + vin = vec_perm(vin, (vec_s16) vzero, vunused); vfilter = vec_vsx_ld(0, &filter[filterSize * i + j]); vout = vec_msums(vin, vfilter, vout); } - vout = vec_sums(vout, (vector int32_t) vzero); + vout = vec_sums(vout, (vec_s32) vzero); dst[i] = FFMIN(vout[3] >> 3, (1 << 19) - 1); } } @@ -1921,28 +1925,28 @@ static void hScale16To19_vsx(SwsContext *c, int16_t *_dst, int dstW, const uint16_t *src = (const uint16_t *) _src; int bits = desc->comp[0].depth - 1; int sh = bits - 4; - vector int16_t vfilter, vin; - vector int32_t vout, vtmp, vtmp2, vfilter32_l, vfilter32_r; - const vector uint8_t vzero = vec_splat_u8(0); - const vector uint8_t vunusedtab[8] = { - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}, - (vector uint8_t) {0x0, 0x1, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0xa, 0xb, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0x10, 0x10}, + vec_s16 vfilter, vin; + vec_s32 vout, vtmp, vtmp2, vfilter32_l, vfilter32_r; + const vec_u8 vzero = vec_splat_u8(0); + const vec_u8 vunusedtab[8] = { + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}, + (vec_u8) {0x0, 0x1, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0x10, 0x10}, }; - const vector uint8_t vunused = vunusedtab[filterSize % 8]; + const vec_u8 vunused = vunusedtab[filterSize % 8]; if ((isAnyRGB(c->srcFormat) || c->srcFormat==AV_PIX_FMT_PAL8) && desc->comp[0].depth<16) { sh = 9; @@ -1966,16 +1970,16 @@ static void hScale16To19_vsx(SwsContext *c, int16_t *_dst, int dstW, const int srcPos = filterPos[i]; vout = vec_splat_s32(0); for (j = 0; j < filterSize; j += 8) { - vin = (vector int16_t) vec_vsx_ld(0, &src[srcPos + j]); + vin = (vec_s16) vec_vsx_ld(0, &src[srcPos + j]); if (j + 8 > filterSize) // Remove the unused elements on the last round - vin = vec_perm(vin, (vector int16_t) vzero, vunused); + vin = vec_perm(vin, (vec_s16) vzero, vunused); vfilter = vec_vsx_ld(0, &filter[filterSize * i + j]); vfilter32_l = vec_unpackh(vfilter); vfilter32_r = vec_unpackl(vfilter); - vtmp = (vector int32_t) vec_mergeh(vin, (vector int16_t) vzero); - vtmp2 = (vector int32_t) vec_mergel(vin, (vector int16_t) vzero); + vtmp = (vec_s32) vec_mergeh(vin, (vec_s16) vzero); + vtmp2 = (vec_s32) vec_mergel(vin, (vec_s16) vzero); vtmp = vec_mul(vtmp, vfilter32_l); vtmp2 = vec_mul(vtmp2, vfilter32_r); @@ -1983,7 +1987,7 @@ static void hScale16To19_vsx(SwsContext *c, int16_t *_dst, int dstW, vout = vec_adds(vout, vtmp); vout = vec_adds(vout, vtmp2); } - vout = vec_sums(vout, (vector int32_t) vzero); + vout = vec_sums(vout, (vec_s32) vzero); dst[i] = FFMIN(vout[3] >> sh, (1 << 19) - 1); } } @@ -1997,28 +2001,28 @@ static void hScale16To15_vsx(SwsContext *c, int16_t *dst, int dstW, int i, j; const uint16_t *src = (const uint16_t *) _src; int sh = desc->comp[0].depth - 1; - vector int16_t vfilter, vin; - vector int32_t vout, vtmp, vtmp2, vfilter32_l, vfilter32_r; - const vector uint8_t vzero = vec_splat_u8(0); - const vector uint8_t vunusedtab[8] = { - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}, - (vector uint8_t) {0x0, 0x1, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0xa, 0xb, 0x10, 0x10, 0x10, 0x10}, - (vector uint8_t) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0x10, 0x10}, + vec_s16 vfilter, vin; + vec_s32 vout, vtmp, vtmp2, vfilter32_l, vfilter32_r; + const vec_u8 vzero = vec_splat_u8(0); + const vec_u8 vunusedtab[8] = { + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}, + (vec_u8) {0x0, 0x1, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0x10, 0x10, 0x10, 0x10}, + (vec_u8) {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0x10, 0x10}, }; - const vector uint8_t vunused = vunusedtab[filterSize % 8]; + const vec_u8 vunused = vunusedtab[filterSize % 8]; if (sh<15) { sh = isAnyRGB(c->srcFormat) || c->srcFormat==AV_PIX_FMT_PAL8 ? 13 : (desc->comp[0].depth - 1); @@ -2042,16 +2046,16 @@ static void hScale16To15_vsx(SwsContext *c, int16_t *dst, int dstW, const int srcPos = filterPos[i]; vout = vec_splat_s32(0); for (j = 0; j < filterSize; j += 8) { - vin = (vector int16_t) vec_vsx_ld(0, &src[srcPos + j]); + vin = (vec_s16) vec_vsx_ld(0, &src[srcPos + j]); if (j + 8 > filterSize) // Remove the unused elements on the last round - vin = vec_perm(vin, (vector int16_t) vzero, vunused); + vin = vec_perm(vin, (vec_s16) vzero, vunused); vfilter = vec_vsx_ld(0, &filter[filterSize * i + j]); vfilter32_l = vec_unpackh(vfilter); vfilter32_r = vec_unpackl(vfilter); - vtmp = (vector int32_t) vec_mergeh(vin, (vector int16_t) vzero); - vtmp2 = (vector int32_t) vec_mergel(vin, (vector int16_t) vzero); + vtmp = (vec_s32) vec_mergeh(vin, (vec_s16) vzero); + vtmp2 = (vec_s32) vec_mergel(vin, (vec_s16) vzero); vtmp = vec_mul(vtmp, vfilter32_l); vtmp2 = vec_mul(vtmp2, vfilter32_r); @@ -2059,7 +2063,7 @@ static void hScale16To15_vsx(SwsContext *c, int16_t *dst, int dstW, vout = vec_adds(vout, vtmp); vout = vec_adds(vout, vtmp2); } - vout = vec_sums(vout, (vector int32_t) vzero); + vout = vec_sums(vout, (vec_s32) vzero); dst[i] = FFMIN(vout[3] >> sh, (1 << 15) - 1); } } diff --git a/libswscale/ppc/yuv2rgb_altivec.c b/libswscale/ppc/yuv2rgb_altivec.c index c1e2852adb7..536545293db 100644 --- a/libswscale/ppc/yuv2rgb_altivec.c +++ b/libswscale/ppc/yuv2rgb_altivec.c @@ -305,9 +305,6 @@ static int altivec_ ## name(SwsContext *c, const unsigned char **in, \ vector signed short R1, G1, B1; \ vector unsigned char R, G, B; \ \ - const vector unsigned char *y1ivP, *y2ivP, *uivP, *vivP; \ - vector unsigned char align_perm; \ - \ vector signed short lCY = c->CY; \ vector signed short lOY = c->OY; \ vector signed short lCRV = c->CRV; \ @@ -338,26 +335,13 @@ static int altivec_ ## name(SwsContext *c, const unsigned char **in, \ vec_dstst(oute, (0x02000002 | (((w * 3 + 32) / 32) << 16)), 1); \ \ for (j = 0; j < w / 16; j++) { \ - y1ivP = (const vector unsigned char *) y1i; \ - y2ivP = (const vector unsigned char *) y2i; \ - uivP = (const vector unsigned char *) ui; \ - vivP = (const vector unsigned char *) vi; \ - \ - align_perm = vec_lvsl(0, y1i); \ - y0 = (vector unsigned char) \ - vec_perm(y1ivP[0], y1ivP[1], align_perm); \ + y0 = vec_xl(0, y1i); \ \ - align_perm = vec_lvsl(0, y2i); \ - y1 = (vector unsigned char) \ - vec_perm(y2ivP[0], y2ivP[1], align_perm); \ + y1 = vec_xl(0, y2i); \ \ - align_perm = vec_lvsl(0, ui); \ - u = (vector signed char) \ - vec_perm(uivP[0], uivP[1], align_perm); \ + u = (vector signed char) vec_xl(0, ui); \ \ - align_perm = vec_lvsl(0, vi); \ - v = (vector signed char) \ - vec_perm(vivP[0], vivP[1], align_perm); \ + v = (vector signed char) vec_xl(0, vi); \ \ u = (vector signed char) \ vec_sub(u, \ diff --git a/libswscale/rgb2rgb.c b/libswscale/rgb2rgb.c index eab8e6aebb3..a7300f3ba4e 100644 --- a/libswscale/rgb2rgb.c +++ b/libswscale/rgb2rgb.c @@ -137,6 +137,8 @@ void (*yuyvtoyuv422)(uint8_t *ydst, uint8_t *udst, uint8_t *vdst, av_cold void ff_sws_rgb2rgb_init(void) { rgb2rgb_init_c(); + if (ARCH_AARCH64) + rgb2rgb_init_aarch64(); if (ARCH_X86) rgb2rgb_init_x86(); } diff --git a/libswscale/rgb2rgb.h b/libswscale/rgb2rgb.h index 3569254df93..48bba1586ad 100644 --- a/libswscale/rgb2rgb.h +++ b/libswscale/rgb2rgb.h @@ -169,6 +169,7 @@ extern void (*yuyvtoyuv422)(uint8_t *ydst, uint8_t *udst, uint8_t *vdst, const u void ff_sws_rgb2rgb_init(void); +void rgb2rgb_init_aarch64(void); void rgb2rgb_init_x86(void); #endif /* SWSCALE_RGB2RGB_H */ diff --git a/libswscale/slice.c b/libswscale/slice.c index db4fa874ffc..7849b70f4d4 100644 --- a/libswscale/slice.c +++ b/libswscale/slice.c @@ -189,23 +189,26 @@ int ff_init_slice_from_src(SwsSlice * s, uint8_t *src[4], int stride[4], int src return 0; } -static void fill_ones(SwsSlice *s, int n, int is16bit) +static void fill_ones(SwsSlice *s, int n, int bpc) { - int i; + int i, j, k, size, end; + for (i = 0; i < 4; ++i) { - int j; - int size = s->plane[i].available_lines; + size = s->plane[i].available_lines; for (j = 0; j < size; ++j) { - int k; - int end = is16bit ? n>>1: n; - // fill also one extra element - end += 1; - if (is16bit) + if (bpc == 16) { + end = (n>>1) + 1; for (k = 0; k < end; ++k) ((int32_t*)(s->plane[i].line[j]))[k] = 1<<18; - else + } else if (bpc == 32) { + end = (n>>2) + 1; + for (k = 0; k < end; ++k) + ((int64_t*)(s->plane[i].line[j]))[k] = 1LL<<34; + } else { + end = n + 1; for (k = 0; k < end; ++k) ((int16_t*)(s->plane[i].line[j]))[k] = 1<<14; + } } } } @@ -272,6 +275,9 @@ int ff_init_filters(SwsContext * c) if (c->dstBpc == 16) dst_stride <<= 1; + if (c->dstBpc == 32) + dst_stride <<= 2; + num_ydesc = need_lum_conv ? 2 : 1; num_cdesc = need_chr_conv ? 2 : 1; @@ -302,7 +308,7 @@ int ff_init_filters(SwsContext * c) res = alloc_lines(&c->slice[i], dst_stride, c->dstW); if (res < 0) goto cleanup; - fill_ones(&c->slice[i], dst_stride>>1, c->dstBpc == 16); + fill_ones(&c->slice[i], dst_stride>>1, c->dstBpc); // vertical scaler output ++i; diff --git a/libswscale/swscale.c b/libswscale/swscale.c index 40695503ad2..9cb7e8f6acd 100644 --- a/libswscale/swscale.c +++ b/libswscale/swscale.c @@ -266,12 +266,9 @@ static int swscale(SwsContext *c, const uint8_t *src[], /* vars which will change and which we need to store back in the context */ int dstY = c->dstY; - int lumBufIndex = c->lumBufIndex; - int chrBufIndex = c->chrBufIndex; int lastInLumBuf = c->lastInLumBuf; int lastInChrBuf = c->lastInChrBuf; - int lumStart = 0; int lumEnd = c->descIndex[0]; int chrStart = lumEnd; @@ -283,25 +280,21 @@ static int swscale(SwsContext *c, const uint8_t *src[], SwsSlice *vout_slice = &c->slice[c->numSlice-1]; SwsFilterDescriptor *desc = c->desc; - int needAlpha = c->needAlpha; int hasLumHoles = 1; int hasChrHoles = 1; - if (isPacked(c->srcFormat)) { - src[0] = src[1] = src[2] = src[3] = src[0]; - srcStride[0] = srcStride[1] = srcStride[2] = srcStride[3] = srcStride[0]; } - srcStride[1] <<= c->vChrDrop; - srcStride[2] <<= c->vChrDrop; + srcStride[1] *= 1 << c->vChrDrop; + srcStride[2] *= 1 << c->vChrDrop; DEBUG_BUFFERS("swscale() %p[%d] %p[%d] %p[%d] %p[%d] -> %p[%d] %p[%d] %p[%d] %p[%d]\n", src[0], srcStride[0], src[1], srcStride[1], @@ -341,8 +334,6 @@ static int swscale(SwsContext *c, const uint8_t *src[], * will not get executed. This is not really intended but works * currently, so people might do it. */ if (srcSliceY == 0) { - lumBufIndex = -1; - chrBufIndex = -1; dstY = 0; lastInLumBuf = -1; lastInChrBuf = -1; @@ -466,7 +457,6 @@ static int swscale(SwsContext *c, const uint8_t *src[], desc[i].process(c, &desc[i], firstPosY, lastPosY - firstPosY + 1); } - lumBufIndex += lastLumSrcY - lastInLumBuf; lastInLumBuf = lastLumSrcY; if (cPosY < lastChrSrcY + 1) { @@ -474,20 +464,13 @@ static int swscale(SwsContext *c, const uint8_t *src[], desc[i].process(c, &desc[i], firstCPosY, lastCPosY - firstCPosY + 1); } - chrBufIndex += lastChrSrcY - lastInChrBuf; lastInChrBuf = lastChrSrcY; - // wrap buf index around to stay inside the ring buffer - if (lumBufIndex >= vLumFilterSize) - lumBufIndex -= vLumFilterSize; - if (chrBufIndex >= vChrFilterSize) - chrBufIndex -= vChrFilterSize; if (!enough_lines) break; // we can't output a dstY line so let's try with the next slice #if HAVE_MMX_INLINE - ff_updateMMXDitherTables(c, dstY, lumBufIndex, chrBufIndex, - lastInLumBuf, lastInChrBuf); + ff_updateMMXDitherTables(c, dstY); #endif if (should_dither) { c->chrDither8 = ff_dither_8x8_128[chrDstY & 7]; @@ -517,6 +500,11 @@ static int swscale(SwsContext *c, const uint8_t *src[], fillPlane16(dst[3], dstStride[3], length, height, lastDstY, 1, desc->comp[3].depth, isBE(dstFormat)); + } else if (is32BPS(dstFormat)) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(dstFormat); + fillPlane32(dst[3], dstStride[3], length, height, lastDstY, + 1, desc->comp[3].depth, + isBE(dstFormat), desc->flags & AV_PIX_FMT_FLAG_FLOAT); } else fillPlane(dst[3], dstStride[3], length, height, lastDstY, 255); } @@ -529,8 +517,6 @@ static int swscale(SwsContext *c, const uint8_t *src[], /* store changed local vars back in the context */ c->dstY = dstY; - c->lumBufIndex = lumBufIndex; - c->chrBufIndex = chrBufIndex; c->lastInLumBuf = lastInLumBuf; c->lastInChrBuf = lastInChrBuf; @@ -572,7 +558,6 @@ static av_cold void sws_init_swscale(SwsContext *c) ff_sws_init_input_funcs(c); - if (c->srcBpc == 8) { if (c->dstBpc <= 14) { c->hyScale = c->hcScale = hScale8To15_c; @@ -790,8 +775,6 @@ int attribute_align_arg sws_scale(struct SwsContext *c, } if (c->gamma_flag && c->cascaded_context[0]) { - - ret = sws_scale(c->cascaded_context[0], srcSlice, srcStride, srcSliceY, srcSliceH, c->cascaded_tmp, c->cascaded_tmpStride); @@ -985,7 +968,6 @@ int attribute_align_arg sws_scale(struct SwsContext *c, c->sliceDir = 0; ret = c->swscale(c, src2, srcStride2, srcSliceY_internal, srcSliceH, dst2, dstStride2); - if (c->dstXYZ && !(c->srcXYZ && c->srcW==c->dstW && c->srcH==c->dstH)) { int dstY = c->dstY ? c->dstY : srcSliceY + srcSliceH; uint16_t *dst16 = (uint16_t*)(dst2[0] + (dstY - ret) * dstStride2[0]); diff --git a/libswscale/swscale_internal.h b/libswscale/swscale_internal.h index a59d12745ae..ee46092ff6e 100644 --- a/libswscale/swscale_internal.h +++ b/libswscale/swscale_internal.h @@ -350,8 +350,6 @@ typedef struct SwsContext { //@{ int lastInLumBuf; ///< Last scaled horizontal luma/alpha line from source in the ring buffer. int lastInChrBuf; ///< Last scaled horizontal chroma line from source in the ring buffer. - int lumBufIndex; ///< Index in ring buffer of the last scaled horizontal luma/alpha line from source. - int chrBufIndex; ///< Index in ring buffer of the last scaled horizontal chroma line from source. //@} uint8_t *formatConvBuffer; @@ -635,8 +633,7 @@ int ff_yuv2rgb_c_init_tables(SwsContext *c, const int inv_table[4], void ff_yuv2rgb_init_tables_ppc(SwsContext *c, const int inv_table[4], int brightness, int contrast, int saturation); -void ff_updateMMXDitherTables(SwsContext *c, int dstY, int lumBufIndex, int chrBufIndex, - int lastInLumBuf, int lastInChrBuf); +void ff_updateMMXDitherTables(SwsContext *c, int dstY); av_cold void ff_sws_init_range_convert(SwsContext *c); @@ -650,6 +647,13 @@ static av_always_inline int is16BPS(enum AVPixelFormat pix_fmt) return desc->comp[0].depth == 16; } +static av_always_inline int is32BPS(enum AVPixelFormat pix_fmt) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); + av_assert0(desc); + return desc->comp[0].depth == 32; +} + static av_always_inline int isNBPS(enum AVPixelFormat pix_fmt) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); @@ -921,8 +925,37 @@ static inline void fillPlane16(uint8_t *plane, int stride, int width, int height } ptr += stride; } +#undef FILL } +static inline void fillPlane32(uint8_t *plane, int stride, int width, int height, int y, + int alpha, int bits, const int big_endian, int is_float) +{ + int i, j; + uint8_t *ptr = plane + stride * y; + uint32_t v; + uint32_t onef32 = 0x3f800000; + if (is_float) + v = alpha ? onef32 : 0; + else + v = alpha ? 0xFFFFFFFF>>(32-bits) : (1<<(bits-1)); + + for (i = 0; i < height; i++) { +#define FILL(wfunc) \ + for (j = 0; j < width; j++) {\ + wfunc(ptr+4*j, v);\ + } + if (big_endian) { + FILL(AV_WB32); + } else { + FILL(AV_WL32); + } + ptr += stride; + } +#undef FILL +} + + #define MAX_SLICE_PLANES 4 /// Slice plane diff --git a/libswscale/swscale_unscaled.c b/libswscale/swscale_unscaled.c index e0b9e99373c..5fb572b51a4 100644 --- a/libswscale/swscale_unscaled.c +++ b/libswscale/swscale_unscaled.c @@ -491,6 +491,34 @@ static int bswap_16bpc(SwsContext *c, const uint8_t *src[], return srcSliceH; } +static int bswap_32bpc(SwsContext *c, const uint8_t *src[], + int srcStride[], int srcSliceY, int srcSliceH, + uint8_t *dst[], int dstStride[]) +{ + int i, j, p; + + for (p = 0; p < 4; p++) { + int srcstr = srcStride[p] / 4; + int dststr = dstStride[p] / 4; + uint32_t *dstPtr = (uint32_t *) dst[p]; + const uint32_t *srcPtr = (const uint32_t *) src[p]; + int min_stride = FFMIN(FFABS(srcstr), FFABS(dststr)); + if(!dstPtr || !srcPtr) + continue; + dstPtr += (srcSliceY >> c->chrDstVSubSample) * dststr; + for (i = 0; i < (srcSliceH >> c->chrDstVSubSample); i++) { + for (j = 0; j < min_stride; j++) { + dstPtr[j] = av_bswap32(srcPtr[j]); + } + srcPtr += srcstr; + dstPtr += dststr; + } + } + + return srcSliceH; +} + + static int palToRgbWrapper(SwsContext *c, const uint8_t *src[], int srcStride[], int srcSliceY, int srcSliceH, uint8_t *dst[], int dstStride[]) @@ -1993,6 +2021,7 @@ void ff_get_unscaled_swscale(SwsContext *c) dstFormat == AV_PIX_FMT_GBRP12LE || dstFormat == AV_PIX_FMT_GBRP12BE || dstFormat == AV_PIX_FMT_GBRP14LE || dstFormat == AV_PIX_FMT_GBRP14BE || dstFormat == AV_PIX_FMT_GBRP16LE || dstFormat == AV_PIX_FMT_GBRP16BE || + dstFormat == AV_PIX_FMT_GBRAP10LE || dstFormat == AV_PIX_FMT_GBRAP10BE || dstFormat == AV_PIX_FMT_GBRAP12LE || dstFormat == AV_PIX_FMT_GBRAP12BE || dstFormat == AV_PIX_FMT_GBRAP16LE || dstFormat == AV_PIX_FMT_GBRAP16BE )) c->swscale = Rgb16ToPlanarRgb16Wrapper; @@ -2002,6 +2031,7 @@ void ff_get_unscaled_swscale(SwsContext *c) srcFormat == AV_PIX_FMT_GBRP10LE || srcFormat == AV_PIX_FMT_GBRP10BE || srcFormat == AV_PIX_FMT_GBRP12LE || srcFormat == AV_PIX_FMT_GBRP12BE || srcFormat == AV_PIX_FMT_GBRP14LE || srcFormat == AV_PIX_FMT_GBRP14BE || + srcFormat == AV_PIX_FMT_GBRAP10LE || srcFormat == AV_PIX_FMT_GBRAP10BE || srcFormat == AV_PIX_FMT_GBRAP12LE || srcFormat == AV_PIX_FMT_GBRAP12BE || srcFormat == AV_PIX_FMT_GBRAP16LE || srcFormat == AV_PIX_FMT_GBRAP16BE) && (dstFormat == AV_PIX_FMT_RGB48LE || dstFormat == AV_PIX_FMT_RGB48BE || @@ -2032,7 +2062,6 @@ void ff_get_unscaled_swscale(SwsContext *c) IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_BAYER_GRBG16) || IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_BGR444) || IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_BGR48) || - IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_BGRA64) || IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_BGR555) || IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_BGR565) || IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_BGRA64) || @@ -2048,11 +2077,11 @@ void ff_get_unscaled_swscale(SwsContext *c) IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_GBRP12) || IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_GBRP14) || IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_GBRP16) || + IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_GBRAP10) || IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_GBRAP12) || IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_GBRAP16) || IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_RGB444) || IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_RGB48) || - IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_RGBA64) || IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_RGB555) || IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_RGB565) || IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_RGBA64) || @@ -2076,6 +2105,11 @@ void ff_get_unscaled_swscale(SwsContext *c) IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_YUV444P16)) c->swscale = bswap_16bpc; + /* bswap 32 bits per pixel/component formats */ + if (IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_GBRPF32) || + IS_DIFFERENT_ENDIANESS(srcFormat, dstFormat, AV_PIX_FMT_GBRAPF32)) + c->swscale = bswap_32bpc; + if (usePal(srcFormat) && isByteRGB(dstFormat)) c->swscale = palToRgbWrapper; diff --git a/libswscale/tests/.gitignore b/libswscale/tests/.gitignore new file mode 100644 index 00000000000..1a26f038c45 --- /dev/null +++ b/libswscale/tests/.gitignore @@ -0,0 +1,3 @@ +/colorspace +/pixdesc_query +/swscale diff --git a/libswscale/utils.c b/libswscale/utils.c index 1b1f7795325..111062e915a 100644 --- a/libswscale/utils.c +++ b/libswscale/utils.c @@ -86,7 +86,7 @@ const char *swscale_configuration(void) const char *swscale_license(void) { #define LICENSE_PREFIX "libswscale license: " - return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1; + return &LICENSE_PREFIX FFMPEG_LICENSE[sizeof(LICENSE_PREFIX) - 1]; } typedef struct FormatEntry { @@ -95,7 +95,7 @@ typedef struct FormatEntry { uint8_t is_supported_endianness :1; } FormatEntry; -static const FormatEntry format_entries[AV_PIX_FMT_NB] = { +static const FormatEntry format_entries[] = { [AV_PIX_FMT_YUV420P] = { 1, 1 }, [AV_PIX_FMT_YUYV422] = { 1, 1 }, [AV_PIX_FMT_RGB24] = { 1, 1 }, @@ -236,6 +236,10 @@ static const FormatEntry format_entries[AV_PIX_FMT_NB] = { [AV_PIX_FMT_GBRP14BE] = { 1, 1 }, [AV_PIX_FMT_GBRP16LE] = { 1, 1 }, [AV_PIX_FMT_GBRP16BE] = { 1, 1 }, + [AV_PIX_FMT_GBRPF32LE] = { 1, 1 }, + [AV_PIX_FMT_GBRPF32BE] = { 1, 1 }, + [AV_PIX_FMT_GBRAPF32LE] = { 1, 1 }, + [AV_PIX_FMT_GBRAPF32BE] = { 1, 1 }, [AV_PIX_FMT_GBRAP] = { 1, 1 }, [AV_PIX_FMT_GBRAP16LE] = { 1, 1 }, [AV_PIX_FMT_GBRAP16BE] = { 1, 1 }, @@ -266,23 +270,24 @@ static const FormatEntry format_entries[AV_PIX_FMT_NB] = { [AV_PIX_FMT_YUVA444P12LE] = { 1, 1 }, [AV_PIX_FMT_NV24] = { 1, 1 }, [AV_PIX_FMT_NV42] = { 1, 1 }, + [AV_PIX_FMT_Y210LE] = { 1, 0 }, }; int sws_isSupportedInput(enum AVPixelFormat pix_fmt) { - return (unsigned)pix_fmt < AV_PIX_FMT_NB ? + return (unsigned)pix_fmt < FF_ARRAY_ELEMS(format_entries) ? format_entries[pix_fmt].is_supported_in : 0; } int sws_isSupportedOutput(enum AVPixelFormat pix_fmt) { - return (unsigned)pix_fmt < AV_PIX_FMT_NB ? + return (unsigned)pix_fmt < FF_ARRAY_ELEMS(format_entries) ? format_entries[pix_fmt].is_supported_out : 0; } int sws_isSupportedEndiannessConversion(enum AVPixelFormat pix_fmt) { - return (unsigned)pix_fmt < AV_PIX_FMT_NB ? + return (unsigned)pix_fmt < FF_ARRAY_ELEMS(format_entries) ? format_entries[pix_fmt].is_supported_endianness : 0; } @@ -390,7 +395,7 @@ static av_cold int initFilter(int16_t **outFilter, int32_t **filterPos, (*filterPos)[i] = xx; // bilinear upscale / linear interpolate / area averaging for (j = 0; j < filterSize; j++) { - int64_t coeff= fone - FFABS(((int64_t)xx<<16) - xDstInSrc)*(fone>>16); + int64_t coeff = fone - FFABS((int64_t)xx * (1 << 16) - xDstInSrc) * (fone >> 16); if (coeff < 0) coeff = 0; filter[i * filterSize + j] = coeff; @@ -1400,6 +1405,8 @@ av_cold int sws_init_context(SwsContext *c, SwsFilter *srcFilter, srcFormat != AV_PIX_FMT_GBRP14BE && srcFormat != AV_PIX_FMT_GBRP14LE && srcFormat != AV_PIX_FMT_GBRP16BE && srcFormat != AV_PIX_FMT_GBRP16LE && srcFormat != AV_PIX_FMT_GBRAP16BE && srcFormat != AV_PIX_FMT_GBRAP16LE && + srcFormat != AV_PIX_FMT_GBRPF32BE && srcFormat != AV_PIX_FMT_GBRPF32LE && + srcFormat != AV_PIX_FMT_GBRAPF32BE && srcFormat != AV_PIX_FMT_GBRAPF32LE && ((dstW >> c->chrDstHSubSample) <= (srcW >> 1) || (flags & SWS_FAST_BILINEAR))) c->chrSrcHSubSample = 1; @@ -1502,6 +1509,7 @@ av_cold int sws_init_context(SwsContext *c, SwsFilter *srcFilter, ff_free_filters(c2); if (ff_init_filters(c2) < 0) { sws_freeContext(c2); + c->cascaded_context[1] = NULL; return -1; } diff --git a/libswscale/version.h b/libswscale/version.h index acb289d7cf0..3aec51f1b9b 100644 --- a/libswscale/version.h +++ b/libswscale/version.h @@ -27,7 +27,7 @@ #include "libavutil/version.h" #define LIBSWSCALE_VERSION_MAJOR 5 -#define LIBSWSCALE_VERSION_MINOR 5 +#define LIBSWSCALE_VERSION_MINOR 7 #define LIBSWSCALE_VERSION_MICRO 100 #define LIBSWSCALE_VERSION_INT AV_VERSION_INT(LIBSWSCALE_VERSION_MAJOR, \ diff --git a/libswscale/vscale.c b/libswscale/vscale.c index 72352dedb3b..9ed227e908a 100644 --- a/libswscale/vscale.c +++ b/libswscale/vscale.c @@ -25,7 +25,14 @@ typedef struct VScalerContext int32_t *filter_pos; int filter_size; int isMMX; - void *pfn; + union { + yuv2planar1_fn yuv2planar1; + yuv2planarX_fn yuv2planarX; + yuv2interleavedX_fn yuv2interleavedX; + yuv2packed1_fn yuv2packed1; + yuv2packed2_fn yuv2packed2; + yuv2anyX_fn yuv2anyX; + } pfn; yuv2packedX_fn yuv2packedX; } VScalerContext; @@ -43,9 +50,9 @@ static int lum_planar_vscale(SwsContext *c, SwsFilterDescriptor *desc, int slice uint16_t *filter = inst->filter[0] + (inst->isMMX ? 0 : sliceY * inst->filter_size); if (inst->filter_size == 1) - ((yuv2planar1_fn)inst->pfn)((const int16_t*)src[0], dst[0], dstW, c->lumDither8, 0); + inst->pfn.yuv2planar1((const int16_t*)src[0], dst[0], dstW, c->lumDither8, 0); else - ((yuv2planarX_fn)inst->pfn)(filter, inst->filter_size, (const int16_t**)src, dst[0], dstW, c->lumDither8, 0); + inst->pfn.yuv2planarX(filter, inst->filter_size, (const int16_t**)src, dst[0], dstW, c->lumDither8, 0); if (desc->alpha) { int sp = first - desc->src->plane[3].sliceY; @@ -55,9 +62,9 @@ static int lum_planar_vscale(SwsContext *c, SwsFilterDescriptor *desc, int slice uint16_t *filter = inst->filter[1] + (inst->isMMX ? 0 : sliceY * inst->filter_size); if (inst->filter_size == 1) - ((yuv2planar1_fn)inst->pfn)((const int16_t*)src[0], dst[0], dstW, c->lumDither8, 0); + inst->pfn.yuv2planar1((const int16_t*)src[0], dst[0], dstW, c->lumDither8, 0); else - ((yuv2planarX_fn)inst->pfn)(filter, inst->filter_size, (const int16_t**)src, dst[0], dstW, c->lumDither8, 0); + inst->pfn.yuv2planarX(filter, inst->filter_size, (const int16_t**)src, dst[0], dstW, c->lumDither8, 0); } return 1; @@ -85,13 +92,13 @@ static int chr_planar_vscale(SwsContext *c, SwsFilterDescriptor *desc, int slice uint16_t *filter = inst->filter[0] + (inst->isMMX ? 0 : chrSliceY * inst->filter_size); if (c->yuv2nv12cX) { - ((yuv2interleavedX_fn)inst->pfn)(c, filter, inst->filter_size, (const int16_t**)src1, (const int16_t**)src2, dst1[0], dstW); + inst->pfn.yuv2interleavedX(c, filter, inst->filter_size, (const int16_t**)src1, (const int16_t**)src2, dst1[0], dstW); } else if (inst->filter_size == 1) { - ((yuv2planar1_fn)inst->pfn)((const int16_t*)src1[0], dst1[0], dstW, c->chrDither8, 0); - ((yuv2planar1_fn)inst->pfn)((const int16_t*)src2[0], dst2[0], dstW, c->chrDither8, 3); + inst->pfn.yuv2planar1((const int16_t*)src1[0], dst1[0], dstW, c->chrDither8, 0); + inst->pfn.yuv2planar1((const int16_t*)src2[0], dst2[0], dstW, c->chrDither8, 3); } else { - ((yuv2planarX_fn)inst->pfn)(filter, inst->filter_size, (const int16_t**)src1, dst1[0], dstW, c->chrDither8, 0); - ((yuv2planarX_fn)inst->pfn)(filter, inst->filter_size, (const int16_t**)src2, dst2[0], dstW, c->chrDither8, inst->isMMX ? (c->uv_offx2 >> 1) : 3); + inst->pfn.yuv2planarX(filter, inst->filter_size, (const int16_t**)src1, dst1[0], dstW, c->chrDither8, 0); + inst->pfn.yuv2planarX(filter, inst->filter_size, (const int16_t**)src2, dst2[0], dstW, c->chrDither8, inst->isMMX ? (c->uv_offx2 >> 1) : 3); } } @@ -125,13 +132,13 @@ static int packed_vscale(SwsContext *c, SwsFilterDescriptor *desc, int sliceY, i if (c->yuv2packed1 && lum_fsize == 1 && chr_fsize == 1) { // unscaled RGB - ((yuv2packed1_fn)inst->pfn)(c, (const int16_t*)*src0, (const int16_t**)src1, (const int16_t**)src2, + inst->pfn.yuv2packed1(c, (const int16_t*)*src0, (const int16_t**)src1, (const int16_t**)src2, (const int16_t*)(desc->alpha ? *src3 : NULL), *dst, dstW, 0, sliceY); } else if (c->yuv2packed1 && lum_fsize == 1 && chr_fsize == 2 && chr_filter[2 * chrSliceY + 1] + chr_filter[2 * chrSliceY] == 4096 && chr_filter[2 * chrSliceY + 1] <= 4096U) { // unscaled RGB int chrAlpha = chr_filter[2 * chrSliceY + 1]; - ((yuv2packed1_fn)inst->pfn)(c, (const int16_t*)*src0, (const int16_t**)src1, (const int16_t**)src2, + inst->pfn.yuv2packed1(c, (const int16_t*)*src0, (const int16_t**)src1, (const int16_t**)src2, (const int16_t*)(desc->alpha ? *src3 : NULL), *dst, dstW, chrAlpha, sliceY); } else if (c->yuv2packed2 && lum_fsize == 2 && chr_fsize == 2 && lum_filter[2 * sliceY + 1] + lum_filter[2 * sliceY] == 4096 && @@ -145,7 +152,7 @@ static int packed_vscale(SwsContext *c, SwsFilterDescriptor *desc, int sliceY, i c->lumMmxFilter[3] = lum_filter[2 * sliceY] * 0x10001; c->chrMmxFilter[2] = c->chrMmxFilter[3] = chr_filter[2 * chrSliceY] * 0x10001; - ((yuv2packed2_fn)inst->pfn)(c, (const int16_t**)src0, (const int16_t**)src1, (const int16_t**)src2, (const int16_t**)src3, + inst->pfn.yuv2packed2(c, (const int16_t**)src0, (const int16_t**)src1, (const int16_t**)src2, (const int16_t**)src3, *dst, dstW, lumAlpha, chrAlpha, sliceY); } else { // general RGB if ((c->yuv2packed1 && lum_fsize == 1 && chr_fsize == 2) || @@ -195,7 +202,7 @@ static int any_vscale(SwsContext *c, SwsFilterDescriptor *desc, int sliceY, int desc->alpha ? desc->dst->plane[3].line[dp3] : NULL }; av_assert1(!c->yuv2packed1 && !c->yuv2packed2); - ((yuv2anyX_fn)inst->pfn)(c, lum_filter + sliceY * lum_fsize, + inst->pfn.yuv2anyX(c, lum_filter + sliceY * lum_fsize, (const int16_t**)src0, lum_fsize, chr_filter + sliceY * chr_fsize, (const int16_t**)src1, (const int16_t**)src2, chr_fsize, (const int16_t**)src3, dst, dstW, sliceY); @@ -270,9 +277,9 @@ void ff_init_vscale_pfn(SwsContext *c, chrCtx->isMMX = use_mmx; --idx; - if (yuv2nv12cX) chrCtx->pfn = yuv2nv12cX; - else if (c->vChrFilterSize == 1) chrCtx->pfn = yuv2plane1; - else chrCtx->pfn = yuv2planeX; + if (yuv2nv12cX) chrCtx->pfn.yuv2interleavedX = yuv2nv12cX; + else if (c->vChrFilterSize == 1) chrCtx->pfn.yuv2planar1 = yuv2plane1; + else chrCtx->pfn.yuv2planarX = yuv2planeX; } lumCtx = c->desc[idx].instance; @@ -283,8 +290,8 @@ void ff_init_vscale_pfn(SwsContext *c, lumCtx->filter_pos = c->vLumFilterPos; lumCtx->isMMX = use_mmx; - if (c->vLumFilterSize == 1) lumCtx->pfn = yuv2plane1; - else lumCtx->pfn = yuv2planeX; + if (c->vLumFilterSize == 1) lumCtx->pfn.yuv2planar1 = yuv2plane1; + else lumCtx->pfn.yuv2planarX = yuv2planeX; } else { lumCtx = c->desc[idx].instance; @@ -303,12 +310,12 @@ void ff_init_vscale_pfn(SwsContext *c, if (yuv2packedX) { if (c->yuv2packed1 && c->vLumFilterSize == 1 && c->vChrFilterSize <= 2) - lumCtx->pfn = yuv2packed1; + lumCtx->pfn.yuv2packed1 = yuv2packed1; else if (c->yuv2packed2 && c->vLumFilterSize == 2 && c->vChrFilterSize == 2) - lumCtx->pfn = yuv2packed2; + lumCtx->pfn.yuv2packed2 = yuv2packed2; lumCtx->yuv2packedX = yuv2packedX; } else - lumCtx->pfn = yuv2anyX; + lumCtx->pfn.yuv2anyX = yuv2anyX; } } diff --git a/libswscale/x86/Makefile b/libswscale/x86/Makefile index f317d5dd9bf..831d5359aa0 100644 --- a/libswscale/x86/Makefile +++ b/libswscale/x86/Makefile @@ -12,3 +12,4 @@ X86ASM-OBJS += x86/input.o \ x86/output.o \ x86/scale.o \ x86/rgb_2_rgb.o \ + x86/yuv_2_rgb.o \ diff --git a/libswscale/x86/swscale.c b/libswscale/x86/swscale.c index 7dc2d705744..61110839ee2 100644 --- a/libswscale/x86/swscale.c +++ b/libswscale/x86/swscale.c @@ -29,6 +29,14 @@ #include "libavutil/cpu.h" #include "libavutil/pixdesc.h" +const DECLARE_ALIGNED(8, uint64_t, ff_dither4)[2] = { + 0x0103010301030103LL, + 0x0200020002000200LL,}; + +const DECLARE_ALIGNED(8, uint64_t, ff_dither8)[2] = { + 0x0602060206020602LL, + 0x0004000400040004LL,}; + #if HAVE_INLINE_ASM #define DITHER1XBPP @@ -38,14 +46,6 @@ DECLARE_ASM_CONST(8, uint64_t, bFC)= 0xFCFCFCFCFCFCFCFCLL; DECLARE_ASM_CONST(8, uint64_t, w10)= 0x0010001000100010LL; DECLARE_ASM_CONST(8, uint64_t, w02)= 0x0002000200020002LL; -const DECLARE_ALIGNED(8, uint64_t, ff_dither4)[2] = { - 0x0103010301030103LL, - 0x0200020002000200LL,}; - -const DECLARE_ALIGNED(8, uint64_t, ff_dither8)[2] = { - 0x0602060206020602LL, - 0x0004000400040004LL,}; - DECLARE_ASM_CONST(8, uint64_t, b16Mask)= 0x001F001F001F001FLL; DECLARE_ASM_CONST(8, uint64_t, g16Mask)= 0x07E007E007E007E0LL; DECLARE_ASM_CONST(8, uint64_t, r16Mask)= 0xF800F800F800F800LL; @@ -79,8 +79,7 @@ DECLARE_ASM_ALIGNED(8, const uint64_t, ff_w1111) = 0x0001000100010001ULL; #include "swscale_template.c" #endif -void ff_updateMMXDitherTables(SwsContext *c, int dstY, int lumBufIndex, int chrBufIndex, - int lastInLumBuf, int lastInChrBuf) +void ff_updateMMXDitherTables(SwsContext *c, int dstY) { const int dstH= c->dstH; const int flags= c->flags; @@ -160,7 +159,7 @@ void ff_updateMMXDitherTables(SwsContext *c, int dstY, int lumBufIndex, int chrB *(const void**)&lumMmxFilter[s*i+APCK_PTR2/4 ]= lumSrcPtr[i+(vLumFilterSize>1)]; lumMmxFilter[s*i+APCK_COEF/4 ]= lumMmxFilter[s*i+APCK_COEF/4+1]= vLumFilter[dstY*vLumFilterSize + i ] - + (vLumFilterSize>1 ? vLumFilter[dstY*vLumFilterSize + i + 1]<<16 : 0); + + (vLumFilterSize>1 ? vLumFilter[dstY*vLumFilterSize + i + 1] * (1 << 16) : 0); if (CONFIG_SWSCALE_ALPHA && hasAlpha) { *(const void**)&alpMmxFilter[s*i ]= alpSrcPtr[i ]; *(const void**)&alpMmxFilter[s*i+APCK_PTR2/4 ]= alpSrcPtr[i+(vLumFilterSize>1)]; @@ -173,7 +172,7 @@ void ff_updateMMXDitherTables(SwsContext *c, int dstY, int lumBufIndex, int chrB *(const void**)&chrMmxFilter[s*i+APCK_PTR2/4 ]= chrUSrcPtr[i+(vChrFilterSize>1)]; chrMmxFilter[s*i+APCK_COEF/4 ]= chrMmxFilter[s*i+APCK_COEF/4+1]= vChrFilter[chrDstY*vChrFilterSize + i ] - + (vChrFilterSize>1 ? vChrFilter[chrDstY*vChrFilterSize + i + 1]<<16 : 0); + + (vChrFilterSize>1 ? vChrFilter[chrDstY*vChrFilterSize + i + 1] * (1 << 16) : 0); } } else { for (i=0; idstFormat) { + case AV_PIX_FMT_RGB32: + if (c->srcFormat == AV_PIX_FMT_YUVA420P) { +#if CONFIG_SWSCALE_ALPHA + return yuva420_rgb32_ssse3; +#endif + break; + } else + return yuv420_rgb32_ssse3; + case AV_PIX_FMT_BGR32: + if (c->srcFormat == AV_PIX_FMT_YUVA420P) { +#if CONFIG_SWSCALE_ALPHA + return yuva420_bgr32_ssse3; +#endif + break; + } else + return yuv420_bgr32_ssse3; + case AV_PIX_FMT_RGB24: + return yuv420_rgb24_ssse3; + case AV_PIX_FMT_BGR24: + return yuv420_bgr24_ssse3; + case AV_PIX_FMT_RGB565: + return yuv420_rgb16_ssse3; + case AV_PIX_FMT_RGB555: + return yuv420_rgb15_ssse3; + } + } + + if (EXTERNAL_MMXEXT(cpu_flags)) { switch (c->dstFormat) { case AV_PIX_FMT_RGB24: return yuv420_rgb24_mmxext; @@ -83,13 +120,12 @@ av_cold SwsFunc ff_yuv2rgb_init_x86(SwsContext *c) return yuv420_bgr24_mmxext; } } -#endif - if (INLINE_MMX(cpu_flags)) { + if (EXTERNAL_MMX(cpu_flags)) { switch (c->dstFormat) { case AV_PIX_FMT_RGB32: if (c->srcFormat == AV_PIX_FMT_YUVA420P) { -#if HAVE_7REGS && CONFIG_SWSCALE_ALPHA +#if CONFIG_SWSCALE_ALPHA return yuva420_rgb32_mmx; #endif break; @@ -97,7 +133,7 @@ av_cold SwsFunc ff_yuv2rgb_init_x86(SwsContext *c) return yuv420_rgb32_mmx; case AV_PIX_FMT_BGR32: if (c->srcFormat == AV_PIX_FMT_YUVA420P) { -#if HAVE_7REGS && CONFIG_SWSCALE_ALPHA +#if CONFIG_SWSCALE_ALPHA return yuva420_bgr32_mmx; #endif break; @@ -113,7 +149,7 @@ av_cold SwsFunc ff_yuv2rgb_init_x86(SwsContext *c) return yuv420_rgb15_mmx; } } -#endif /* HAVE_MMX_INLINE && HAVE_6REGS */ +#endif /* HAVE_X86ASM */ return NULL; } diff --git a/libswscale/x86/yuv2rgb_template.c b/libswscale/x86/yuv2rgb_template.c index acb78f520e4..d506f75e15d 100644 --- a/libswscale/x86/yuv2rgb_template.c +++ b/libswscale/x86/yuv2rgb_template.c @@ -26,23 +26,6 @@ #include "libavutil/x86/asm.h" #include "libswscale/swscale_internal.h" -#undef MOVNTQ -#undef EMMS -#undef SFENCE - -#if COMPILE_TEMPLATE_MMXEXT -#define MOVNTQ "movntq" -#define SFENCE "sfence" -#else -#define MOVNTQ "movq" -#define SFENCE " # nop" -#endif - -#define REG_BLUE "0" -#define REG_RED "1" -#define REG_GREEN "2" -#define REG_ALPHA "3" - #define YUV2RGB_LOOP(depth) \ h_size = (c->dstW + 7) & ~7; \ if (h_size * depth > FFABS(dstStride[0])) \ @@ -50,7 +33,6 @@ \ vshift = c->srcFormat != AV_PIX_FMT_YUV422P; \ \ - __asm__ volatile ("pxor %mm4, %mm4\n\t"); \ for (y = 0; y < srcSliceH; y++) { \ uint8_t *image = dst[0] + (y + srcSliceY) * dstStride[0]; \ const uint8_t *py = src[0] + y * srcStride[0]; \ @@ -58,146 +40,33 @@ const uint8_t *pv = src[2] + (y >> vshift) * srcStride[2]; \ x86_reg index = -h_size / 2; \ -#define YUV2RGB_INITIAL_LOAD \ - __asm__ volatile ( \ - "movq (%5, %0, 2), %%mm6\n\t" \ - "movd (%2, %0), %%mm0\n\t" \ - "movd (%3, %0), %%mm1\n\t" \ - "1: \n\t" \ - -/* YUV2RGB core - * Conversion is performed in usual way: - * R = Y' * Ycoef + Vred * V' - * G = Y' * Ycoef + Vgreen * V' + Ugreen * U' - * B = Y' * Ycoef + Ublue * U' - * - * where X' = X * 8 - Xoffset (multiplication is performed to increase - * precision a bit). - * Since it operates in YUV420 colorspace, Y component is additionally - * split into Y1 and Y2 for even and odd pixels. - * - * Input: - * mm0 - U (4 elems), mm1 - V (4 elems), mm6 - Y (8 elems), mm4 - zero register - * Output: - * mm1 - R, mm2 - G, mm0 - B - */ -#define YUV2RGB \ - /* convert Y, U, V into Y1', Y2', U', V' */ \ - "movq %%mm6, %%mm7\n\t" \ - "punpcklbw %%mm4, %%mm0\n\t" \ - "punpcklbw %%mm4, %%mm1\n\t" \ - "pand "MANGLE(mmx_00ffw)", %%mm6\n\t" \ - "psrlw $8, %%mm7\n\t" \ - "psllw $3, %%mm0\n\t" \ - "psllw $3, %%mm1\n\t" \ - "psllw $3, %%mm6\n\t" \ - "psllw $3, %%mm7\n\t" \ - "psubsw "U_OFFSET"(%4), %%mm0\n\t" \ - "psubsw "V_OFFSET"(%4), %%mm1\n\t" \ - "psubw "Y_OFFSET"(%4), %%mm6\n\t" \ - "psubw "Y_OFFSET"(%4), %%mm7\n\t" \ -\ - /* multiply by coefficients */ \ - "movq %%mm0, %%mm2\n\t" \ - "movq %%mm1, %%mm3\n\t" \ - "pmulhw "UG_COEFF"(%4), %%mm2\n\t" \ - "pmulhw "VG_COEFF"(%4), %%mm3\n\t" \ - "pmulhw "Y_COEFF" (%4), %%mm6\n\t" \ - "pmulhw "Y_COEFF" (%4), %%mm7\n\t" \ - "pmulhw "UB_COEFF"(%4), %%mm0\n\t" \ - "pmulhw "VR_COEFF"(%4), %%mm1\n\t" \ - "paddsw %%mm3, %%mm2\n\t" \ - /* now: mm0 = UB, mm1 = VR, mm2 = CG */ \ - /* mm6 = Y1, mm7 = Y2 */ \ -\ - /* produce RGB */ \ - "movq %%mm7, %%mm3\n\t" \ - "movq %%mm7, %%mm5\n\t" \ - "paddsw %%mm0, %%mm3\n\t" \ - "paddsw %%mm1, %%mm5\n\t" \ - "paddsw %%mm2, %%mm7\n\t" \ - "paddsw %%mm6, %%mm0\n\t" \ - "paddsw %%mm6, %%mm1\n\t" \ - "paddsw %%mm6, %%mm2\n\t" \ - -#define RGB_PACK_INTERLEAVE \ - /* pack and interleave even/odd pixels */ \ - "packuswb %%mm1, %%mm0\n\t" \ - "packuswb %%mm5, %%mm3\n\t" \ - "packuswb %%mm2, %%mm2\n\t" \ - "movq %%mm0, %%mm1\n\n" \ - "packuswb %%mm7, %%mm7\n\t" \ - "punpcklbw %%mm3, %%mm0\n\t" \ - "punpckhbw %%mm3, %%mm1\n\t" \ - "punpcklbw %%mm7, %%mm2\n\t" \ - -#define YUV2RGB_ENDLOOP(depth) \ - "movq 8 (%5, %0, 2), %%mm6\n\t" \ - "movd 4 (%3, %0), %%mm1\n\t" \ - "movd 4 (%2, %0), %%mm0\n\t" \ - "add $"AV_STRINGIFY(depth * 8)", %1\n\t" \ - "add $4, %0\n\t" \ - "js 1b\n\t" \ - -#if COMPILE_TEMPLATE_MMXEXT -#undef RGB_PACK24_B_OPERANDS -#define RGB_PACK24_B_OPERANDS NAMED_CONSTRAINTS_ARRAY_ADD(mask1101,mask0110,mask0100,mask0010,mask1001) -#else -#undef RGB_PACK24_B_OPERANDS -#define RGB_PACK24_B_OPERANDS -#endif - -#define YUV2RGB_OPERANDS \ - : "+r" (index), "+r" (image) \ - : "r" (pu - index), "r" (pv - index), "r"(&c->redDither), \ - "r" (py - 2*index) \ - NAMED_CONSTRAINTS_ADD(mmx_00ffw,pb_03,pb_07,mmx_redmask,pb_e0) \ - RGB_PACK24_B_OPERANDS \ - : "memory" \ - ); \ - } \ - -#define YUV2RGB_OPERANDS_ALPHA \ - : "+r" (index), "+r" (image) \ - : "r" (pu - index), "r" (pv - index), "r"(&c->redDither), \ - "r" (py - 2*index), "r" (pa - 2*index) \ - NAMED_CONSTRAINTS_ADD(mmx_00ffw) \ - : "memory" \ - ); \ - } \ - -#define YUV2RGB_ENDFUNC \ - __asm__ volatile (SFENCE"\n\t" \ - "emms \n\t"); \ - return srcSliceH; \ - -#define IF0(x) -#define IF1(x) x - -#define RGB_PACK16(gmask, is15) \ - "pand "MANGLE(mmx_redmask)", %%mm0\n\t" \ - "pand "MANGLE(mmx_redmask)", %%mm1\n\t" \ - "movq %%mm2, %%mm3\n\t" \ - "psllw $"AV_STRINGIFY(3-is15)", %%mm2\n\t" \ - "psrlw $"AV_STRINGIFY(5+is15)", %%mm3\n\t" \ - "psrlw $3, %%mm0\n\t" \ - IF##is15("psrlw $1, %%mm1\n\t") \ - "pand "MANGLE(pb_e0)", %%mm2\n\t" \ - "pand "MANGLE(gmask)", %%mm3\n\t" \ - "por %%mm2, %%mm0\n\t" \ - "por %%mm3, %%mm1\n\t" \ - "movq %%mm0, %%mm2\n\t" \ - "punpcklbw %%mm1, %%mm0\n\t" \ - "punpckhbw %%mm1, %%mm2\n\t" \ - MOVNTQ " %%mm0, (%1)\n\t" \ - MOVNTQ " %%mm2, 8(%1)\n\t" \ - -#define DITHER_RGB \ - "paddusb "BLUE_DITHER"(%4), %%mm0\n\t" \ - "paddusb "GREEN_DITHER"(%4), %%mm2\n\t" \ - "paddusb "RED_DITHER"(%4), %%mm1\n\t" \ +extern void RENAME(ff_yuv_420_rgb24)(x86_reg index, uint8_t *image, const uint8_t *pu_index, + const uint8_t *pv_index, const uint64_t *pointer_c_dither, + const uint8_t *py_2index); +extern void RENAME(ff_yuv_420_bgr24)(x86_reg index, uint8_t *image, const uint8_t *pu_index, + const uint8_t *pv_index, const uint64_t *pointer_c_dither, + const uint8_t *py_2index); #if !COMPILE_TEMPLATE_MMXEXT +extern void RENAME(ff_yuv_420_rgb15)(x86_reg index, uint8_t *image, const uint8_t *pu_index, + const uint8_t *pv_index, const uint64_t *pointer_c_dither, + const uint8_t *py_2index); +extern void RENAME(ff_yuv_420_rgb16)(x86_reg index, uint8_t *image, const uint8_t *pu_index, + const uint8_t *pv_index, const uint64_t *pointer_c_dither, + const uint8_t *py_2index); +extern void RENAME(ff_yuv_420_rgb32)(x86_reg index, uint8_t *image, const uint8_t *pu_index, + const uint8_t *pv_index, const uint64_t *pointer_c_dither, + const uint8_t *py_2index); +extern void RENAME(ff_yuv_420_bgr32)(x86_reg index, uint8_t *image, const uint8_t *pu_index, + const uint8_t *pv_index, const uint64_t *pointer_c_dither, + const uint8_t *py_2index); +extern void RENAME(ff_yuva_420_rgb32)(x86_reg index, uint8_t *image, const uint8_t *pu_index, + const uint8_t *pv_index, const uint64_t *pointer_c_dither, + const uint8_t *py_2index, const uint8_t *pa_2index); +extern void RENAME(ff_yuva_420_bgr32)(x86_reg index, uint8_t *image, const uint8_t *pu_index, + const uint8_t *pv_index, const uint64_t *pointer_c_dither, + const uint8_t *py_2index, const uint8_t *pa_2index); + static inline int RENAME(yuv420_rgb15)(SwsContext *c, const uint8_t *src[], int srcStride[], int srcSliceY, int srcSliceH, @@ -213,17 +82,9 @@ static inline int RENAME(yuv420_rgb15)(SwsContext *c, const uint8_t *src[], c->redDither = ff_dither8[(y + 1) & 1]; #endif - YUV2RGB_INITIAL_LOAD - YUV2RGB - RGB_PACK_INTERLEAVE -#ifdef DITHER1XBPP - DITHER_RGB -#endif - RGB_PACK16(pb_03, 1) - - YUV2RGB_ENDLOOP(2) - YUV2RGB_OPERANDS - YUV2RGB_ENDFUNC + RENAME(ff_yuv_420_rgb15)(index, image, pu - index, pv - index, &(c->redDither), py - 2 * index); + } + return srcSliceH; } static inline int RENAME(yuv420_rgb16)(SwsContext *c, const uint8_t *src[], @@ -241,165 +102,54 @@ static inline int RENAME(yuv420_rgb16)(SwsContext *c, const uint8_t *src[], c->redDither = ff_dither8[(y + 1) & 1]; #endif - YUV2RGB_INITIAL_LOAD - YUV2RGB - RGB_PACK_INTERLEAVE -#ifdef DITHER1XBPP - DITHER_RGB -#endif - RGB_PACK16(pb_07, 0) - - YUV2RGB_ENDLOOP(2) - YUV2RGB_OPERANDS - YUV2RGB_ENDFUNC + RENAME(ff_yuv_420_rgb16)(index, image, pu - index, pv - index, &(c->redDither), py - 2 * index); + } + return srcSliceH; } -#endif /* !COMPILE_TEMPLATE_MMXEXT */ -#define RGB_PACK24(blue, red)\ - "packuswb %%mm3, %%mm0 \n" /* R0 R2 R4 R6 R1 R3 R5 R7 */\ - "packuswb %%mm5, %%mm1 \n" /* B0 B2 B4 B6 B1 B3 B5 B7 */\ - "packuswb %%mm7, %%mm2 \n" /* G0 G2 G4 G6 G1 G3 G5 G7 */\ - "movq %%mm"red", %%mm3 \n"\ - "movq %%mm"blue", %%mm6 \n"\ - "psrlq $32, %%mm"red" \n" /* R1 R3 R5 R7 */\ - "punpcklbw %%mm2, %%mm3 \n" /* R0 G0 R2 G2 R4 G4 R6 G6 */\ - "punpcklbw %%mm"red", %%mm6 \n" /* B0 R1 B2 R3 B4 R5 B6 R7 */\ - "movq %%mm3, %%mm5 \n"\ - "punpckhbw %%mm"blue", %%mm2 \n" /* G1 B1 G3 B3 G5 B5 G7 B7 */\ - "punpcklwd %%mm6, %%mm3 \n" /* R0 G0 B0 R1 R2 G2 B2 R3 */\ - "punpckhwd %%mm6, %%mm5 \n" /* R4 G4 B4 R5 R6 G6 B6 R7 */\ - RGB_PACK24_B - -#if COMPILE_TEMPLATE_MMXEXT -DECLARE_ASM_CONST(8, int16_t, mask1101[4]) = {-1,-1, 0,-1}; -DECLARE_ASM_CONST(8, int16_t, mask0010[4]) = { 0, 0,-1, 0}; -DECLARE_ASM_CONST(8, int16_t, mask0110[4]) = { 0,-1,-1, 0}; -DECLARE_ASM_CONST(8, int16_t, mask1001[4]) = {-1, 0, 0,-1}; -DECLARE_ASM_CONST(8, int16_t, mask0100[4]) = { 0,-1, 0, 0}; -#undef RGB_PACK24_B -#define RGB_PACK24_B\ - "pshufw $0xc6, %%mm2, %%mm1 \n"\ - "pshufw $0x84, %%mm3, %%mm6 \n"\ - "pshufw $0x38, %%mm5, %%mm7 \n"\ - "pand "MANGLE(mask1101)", %%mm6 \n" /* R0 G0 B0 R1 -- -- R2 G2 */\ - "movq %%mm1, %%mm0 \n"\ - "pand "MANGLE(mask0110)", %%mm7 \n" /* -- -- R6 G6 B6 R7 -- -- */\ - "movq %%mm1, %%mm2 \n"\ - "pand "MANGLE(mask0100)", %%mm1 \n" /* -- -- G3 B3 -- -- -- -- */\ - "psrlq $48, %%mm3 \n" /* B2 R3 -- -- -- -- -- -- */\ - "pand "MANGLE(mask0010)", %%mm0 \n" /* -- -- -- -- G1 B1 -- -- */\ - "psllq $32, %%mm5 \n" /* -- -- -- -- R4 G4 B4 R5 */\ - "pand "MANGLE(mask1001)", %%mm2 \n" /* G5 B5 -- -- -- -- G7 B7 */\ - "por %%mm3, %%mm1 \n"\ - "por %%mm6, %%mm0 \n"\ - "por %%mm5, %%mm1 \n"\ - "por %%mm7, %%mm2 \n"\ - MOVNTQ" %%mm0, (%1) \n"\ - MOVNTQ" %%mm1, 8(%1) \n"\ - MOVNTQ" %%mm2, 16(%1) \n"\ - -#else -#undef RGB_PACK24_B -#define RGB_PACK24_B\ - "movd %%mm3, (%1) \n" /* R0 G0 B0 R1 */\ - "movd %%mm2, 4(%1) \n" /* G1 B1 */\ - "psrlq $32, %%mm3 \n"\ - "psrlq $16, %%mm2 \n"\ - "movd %%mm3, 6(%1) \n" /* R2 G2 B2 R3 */\ - "movd %%mm2, 10(%1) \n" /* G3 B3 */\ - "psrlq $16, %%mm2 \n"\ - "movd %%mm5, 12(%1) \n" /* R4 G4 B4 R5 */\ - "movd %%mm2, 16(%1) \n" /* G5 B5 */\ - "psrlq $32, %%mm5 \n"\ - "movd %%mm2, 20(%1) \n" /* -- -- G7 B7 */\ - "movd %%mm5, 18(%1) \n" /* R6 G6 B6 R7 */\ - -#endif - -static inline int RENAME(yuv420_rgb24)(SwsContext *c, const uint8_t *src[], +static inline int RENAME(yuv420_rgb32)(SwsContext *c, const uint8_t *src[], int srcStride[], int srcSliceY, int srcSliceH, uint8_t *dst[], int dstStride[]) { int y, h_size, vshift; - YUV2RGB_LOOP(3) - - YUV2RGB_INITIAL_LOAD - YUV2RGB - RGB_PACK24(REG_BLUE, REG_RED) + YUV2RGB_LOOP(4) - YUV2RGB_ENDLOOP(3) - YUV2RGB_OPERANDS - YUV2RGB_ENDFUNC + RENAME(ff_yuv_420_rgb32)(index, image, pu - index, pv - index, &(c->redDither), py - 2 * index); + } + return srcSliceH; } -static inline int RENAME(yuv420_bgr24)(SwsContext *c, const uint8_t *src[], +static inline int RENAME(yuv420_bgr32)(SwsContext *c, const uint8_t *src[], int srcStride[], int srcSliceY, int srcSliceH, uint8_t *dst[], int dstStride[]) { int y, h_size, vshift; - YUV2RGB_LOOP(3) - - YUV2RGB_INITIAL_LOAD - YUV2RGB - RGB_PACK24(REG_RED, REG_BLUE) + YUV2RGB_LOOP(4) - YUV2RGB_ENDLOOP(3) - YUV2RGB_OPERANDS - YUV2RGB_ENDFUNC + RENAME(ff_yuv_420_bgr32)(index, image, pu - index, pv - index, &(c->redDither), py - 2 * index); + } + return srcSliceH; } - -#define SET_EMPTY_ALPHA \ - "pcmpeqd %%mm"REG_ALPHA", %%mm"REG_ALPHA"\n\t" /* set alpha to 0xFF */ \ - -#define LOAD_ALPHA \ - "movq (%6, %0, 2), %%mm"REG_ALPHA"\n\t" \ - -#define RGB_PACK32(red, green, blue, alpha) \ - "movq %%mm"blue", %%mm5\n\t" \ - "movq %%mm"red", %%mm6\n\t" \ - "punpckhbw %%mm"green", %%mm5\n\t" \ - "punpcklbw %%mm"green", %%mm"blue"\n\t" \ - "punpckhbw %%mm"alpha", %%mm6\n\t" \ - "punpcklbw %%mm"alpha", %%mm"red"\n\t" \ - "movq %%mm"blue", %%mm"green"\n\t" \ - "movq %%mm5, %%mm"alpha"\n\t" \ - "punpcklwd %%mm"red", %%mm"blue"\n\t" \ - "punpckhwd %%mm"red", %%mm"green"\n\t" \ - "punpcklwd %%mm6, %%mm5\n\t" \ - "punpckhwd %%mm6, %%mm"alpha"\n\t" \ - MOVNTQ " %%mm"blue", 0(%1)\n\t" \ - MOVNTQ " %%mm"green", 8(%1)\n\t" \ - MOVNTQ " %%mm5, 16(%1)\n\t" \ - MOVNTQ " %%mm"alpha", 24(%1)\n\t" \ - -#if !COMPILE_TEMPLATE_MMXEXT -static inline int RENAME(yuv420_rgb32)(SwsContext *c, const uint8_t *src[], +static inline int RENAME(yuva420_rgb32)(SwsContext *c, const uint8_t *src[], int srcStride[], int srcSliceY, int srcSliceH, uint8_t *dst[], int dstStride[]) { int y, h_size, vshift; - YUV2RGB_LOOP(4) - YUV2RGB_INITIAL_LOAD - YUV2RGB - RGB_PACK_INTERLEAVE - SET_EMPTY_ALPHA - RGB_PACK32(REG_RED, REG_GREEN, REG_BLUE, REG_ALPHA) - - YUV2RGB_ENDLOOP(4) - YUV2RGB_OPERANDS - YUV2RGB_ENDFUNC + const uint8_t *pa = src[3] + y * srcStride[3]; + RENAME(ff_yuva_420_rgb32)(index, image, pu - index, pv - index, &(c->redDither), py - 2 * index, pa - 2 * index); + } + return srcSliceH; } -#if HAVE_7REGS && CONFIG_SWSCALE_ALPHA -static inline int RENAME(yuva420_rgb32)(SwsContext *c, const uint8_t *src[], +static inline int RENAME(yuva420_bgr32)(SwsContext *c, const uint8_t *src[], int srcStride[], int srcSliceY, int srcSliceH, uint8_t *dst[], int dstStride[]) @@ -409,59 +159,37 @@ static inline int RENAME(yuva420_rgb32)(SwsContext *c, const uint8_t *src[], YUV2RGB_LOOP(4) const uint8_t *pa = src[3] + y * srcStride[3]; - YUV2RGB_INITIAL_LOAD - YUV2RGB - RGB_PACK_INTERLEAVE - LOAD_ALPHA - RGB_PACK32(REG_RED, REG_GREEN, REG_BLUE, REG_ALPHA) - - YUV2RGB_ENDLOOP(4) - YUV2RGB_OPERANDS_ALPHA - YUV2RGB_ENDFUNC + RENAME(ff_yuva_420_bgr32)(index, image, pu - index, pv - index, &(c->redDither), py - 2 * index, pa - 2 * index); + } + return srcSliceH; } #endif -static inline int RENAME(yuv420_bgr32)(SwsContext *c, const uint8_t *src[], +static inline int RENAME(yuv420_rgb24)(SwsContext *c, const uint8_t *src[], int srcStride[], int srcSliceY, int srcSliceH, uint8_t *dst[], int dstStride[]) { int y, h_size, vshift; - YUV2RGB_LOOP(4) - - YUV2RGB_INITIAL_LOAD - YUV2RGB - RGB_PACK_INTERLEAVE - SET_EMPTY_ALPHA - RGB_PACK32(REG_BLUE, REG_GREEN, REG_RED, REG_ALPHA) + YUV2RGB_LOOP(3) - YUV2RGB_ENDLOOP(4) - YUV2RGB_OPERANDS - YUV2RGB_ENDFUNC + RENAME(ff_yuv_420_rgb24)(index, image, pu - index, pv - index, &(c->redDither), py - 2 * index); + } + return srcSliceH; } -#if HAVE_7REGS && CONFIG_SWSCALE_ALPHA -static inline int RENAME(yuva420_bgr32)(SwsContext *c, const uint8_t *src[], +static inline int RENAME(yuv420_bgr24)(SwsContext *c, const uint8_t *src[], int srcStride[], int srcSliceY, int srcSliceH, uint8_t *dst[], int dstStride[]) { int y, h_size, vshift; - YUV2RGB_LOOP(4) - - const uint8_t *pa = src[3] + y * srcStride[3]; - YUV2RGB_INITIAL_LOAD - YUV2RGB - RGB_PACK_INTERLEAVE - LOAD_ALPHA - RGB_PACK32(REG_BLUE, REG_GREEN, REG_RED, REG_ALPHA) + YUV2RGB_LOOP(3) - YUV2RGB_ENDLOOP(4) - YUV2RGB_OPERANDS_ALPHA - YUV2RGB_ENDFUNC + RENAME(ff_yuv_420_bgr24)(index, image, pu - index, pv - index, &(c->redDither), py - 2 * index); + } + return srcSliceH; } -#endif -#endif /* !COMPILE_TEMPLATE_MMXEXT */ diff --git a/libswscale/x86/yuv_2_rgb.asm b/libswscale/x86/yuv_2_rgb.asm new file mode 100644 index 00000000000..575a84d921e --- /dev/null +++ b/libswscale/x86/yuv_2_rgb.asm @@ -0,0 +1,383 @@ +;****************************************************************************** +;* software YUV to RGB converter +;* +;* Copyright (C) 2001-2007 Michael Niedermayer +;* (c) 2010 Konstantin Shishkov +;* +;* This file is part of FFmpeg. +;* +;* FFmpeg is free software; you can redistribute it and/or +;* modify it under the terms of the GNU Lesser General Public +;* License as published by the Free Software Foundation; either +;* version 2.1 of the License, or (at your option) any later version. +;* +;* FFmpeg is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;* Lesser General Public License for more details. +;* +;* You should have received a copy of the GNU Lesser General 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 + +; below variables are named like mask_dwXY, which means to preserve dword No.X & No.Y +mask_dw036 : db -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0 +mask_dw147 : db 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1 +mask_dw25 : db 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0 +rgb24_shuf1: db 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5, 10, 11 +rgb24_shuf2: db 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15, 4, 5 +rgb24_shuf3: db 4, 5, 10, 11, 0, 1, 6, 7, 12, 13, 2, 3, 8, 9, 14, 15 +pw_00ff: times 8 dw 255 +pb_f8: times 16 db 248 +pb_e0: times 16 db 224 +pb_03: times 16 db 3 +pb_07: times 16 db 7 + +mask_1101: dw -1, -1, 0, -1 +mask_0010: dw 0, 0, -1, 0 +mask_0110: dw 0, -1, -1, 0 +mask_1001: dw -1, 0, 0, -1 +mask_0100: dw 0, -1, 0, 0 + +SECTION .text + +;----------------------------------------------------------------------------- +; +; YUV420/YUVA420 to RGB/BGR 15/16/24/32 +; R = Y + ((vrCoff * (v - 128)) >> 8) +; G = Y - ((ugCoff * (u - 128) + vgCoff * (v - 128)) >> 8) +; B = Y + ((ubCoff * (u - 128)) >> 8) +; +;----------------------------------------------------------------------------- + +%macro MOV_H2L 1 +%if mmsize == 8 + psrlq %1, 32 +%else ; mmsize == 16 + psrldq %1, 8 +%endif +%endmacro + +%macro yuv2rgb_fn 3 + +%if %3 == 32 + %ifidn %1, yuva + %define parameters index, image, pu_index, pv_index, pointer_c_dither, py_2index, pa_2index + %define GPR_num 7 + %endif +%else + %define parameters index, image, pu_index, pv_index, pointer_c_dither, py_2index + %define GPR_num 6 +%endif + +%define m_green m2 +%define m_alpha m3 +%define m_y m6 +%define m_u m0 +%define m_v m1 +%ifidn %2, rgb +%define m_red m1 +%define m_blue m0 +%else +%define m_red m0 +%define m_blue m1 +%endif + +%if mmsize == 8 +%define time_num 1 +%define reg_num 8 +%define y_offset [pointer_c_ditherq + 8 * 8] +%define u_offset [pointer_c_ditherq + 9 * 8] +%define v_offset [pointer_c_ditherq + 10 * 8] +%define ug_coff [pointer_c_ditherq + 7 * 8] +%define vg_coff [pointer_c_ditherq + 6 * 8] +%define y_coff [pointer_c_ditherq + 3 * 8] +%define ub_coff [pointer_c_ditherq + 5 * 8] +%define vr_coff [pointer_c_ditherq + 4 * 8] +%elif mmsize == 16 +%define time_num 2 +%if ARCH_X86_32 +%define reg_num 8 +%define my_offset [pointer_c_ditherq + 8 * 8] +%define mu_offset [pointer_c_ditherq + 9 * 8] +%define mv_offset [pointer_c_ditherq + 10 * 8] +%define mug_coff [pointer_c_ditherq + 7 * 8] +%define mvg_coff [pointer_c_ditherq + 6 * 8] +%define my_coff [pointer_c_ditherq + 3 * 8] +%define mub_coff [pointer_c_ditherq + 5 * 8] +%define mvr_coff [pointer_c_ditherq + 4 * 8] +%else ; ARCH_X86_64 +%define reg_num 16 +%define y_offset m8 +%define u_offset m9 +%define v_offset m10 +%define ug_coff m11 +%define vg_coff m12 +%define y_coff m13 +%define ub_coff m14 +%define vr_coff m15 +%endif ; ARCH_X86_32/64 +%endif ; coeff define mmsize == 8/16 + +cglobal %1_420_%2%3, GPR_num, GPR_num, reg_num, parameters + +%if ARCH_X86_64 + movsxd indexq, indexd +%if mmsize == 16 + VBROADCASTSD y_offset, [pointer_c_ditherq + 8 * 8] + VBROADCASTSD u_offset, [pointer_c_ditherq + 9 * 8] + VBROADCASTSD v_offset, [pointer_c_ditherq + 10 * 8] + VBROADCASTSD ug_coff, [pointer_c_ditherq + 7 * 8] + VBROADCASTSD vg_coff, [pointer_c_ditherq + 6 * 8] + VBROADCASTSD y_coff, [pointer_c_ditherq + 3 * 8] + VBROADCASTSD ub_coff, [pointer_c_ditherq + 5 * 8] + VBROADCASTSD vr_coff, [pointer_c_ditherq + 4 * 8] +%endif +%endif + movu m_y, [py_2indexq + 2 * indexq] + movh m_u, [pu_indexq + indexq] + movh m_v, [pv_indexq + indexq] +.loop0: + pxor m4, m4 + mova m7, m6 + punpcklbw m0, m4 + punpcklbw m1, m4 + mova m2, [pw_00ff] + pand m6, m2 + psrlw m7, 8 + psllw m0, 3 + psllw m1, 3 + psllw m6, 3 + psllw m7, 3 +%if (ARCH_X86_32 && mmsize == 16) + VBROADCASTSD m2, mu_offset + VBROADCASTSD m3, mv_offset + VBROADCASTSD m4, my_offset + psubsw m0, m2 ; U = U - 128 + psubsw m1, m3 ; V = V - 128 + psubw m6, m4 + psubw m7, m4 + VBROADCASTSD m2, mug_coff + VBROADCASTSD m3, mvg_coff + VBROADCASTSD m4, my_coff + VBROADCASTSD m5, mub_coff + pmulhw m2, m0 + pmulhw m3, m1 + pmulhw m6, m4 + pmulhw m7, m4 + pmulhw m0, m5 + VBROADCASTSD m4, mvr_coff + pmulhw m1, m4 +%else ; ARCH_X86_64 || mmsize == 8 + psubsw m0, u_offset ; U = U - 128 + psubsw m1, v_offset ; V = V - 128 + psubw m6, y_offset + psubw m7, y_offset + mova m2, m0 + mova m3, m1 + pmulhw m2, ug_coff + pmulhw m3, vg_coff + pmulhw m6, y_coff + pmulhw m7, y_coff + pmulhw m0, ub_coff + pmulhw m1, vr_coff +%endif + paddsw m2, m3 + mova m3, m7 + mova m5, m7 + paddsw m3, m0 ; B1 B3 B5 B7 ... + paddsw m5, m1 ; R1 R3 R5 R7 ... + paddsw m7, m2 ; G1 G3 G4 G7 ... + paddsw m0, m6 ; B0 B2 B4 B6 ... + paddsw m1, m6 ; R0 R2 R4 R6 ... + paddsw m2, m6 ; G0 G2 G4 G6 ... + +%if %3 == 24 ; PACK RGB24 +%define depth 3 + packuswb m0, m3 ; R0 R2 R4 R6 ... R1 R3 R5 R7 ... + packuswb m1, m5 ; B0 B2 B4 B6 ... B1 B3 B5 B7 ... + packuswb m2, m7 ; G0 G2 G4 G6 ... G1 G3 G5 G7 ... + mova m3, m_red + mova m6, m_blue + MOV_H2L m_red + punpcklbw m3, m2 ; R0 G0 R2 G2 R4 G4 R6 G6 R8 G8 ... + punpcklbw m6, m_red ; B0 R1 B2 R3 B4 R5 B6 R7 B8 R9 ... + mova m5, m3 + punpckhbw m2, m_blue ; G1 B1 G3 B3 G5 B5 G7 B7 G9 B9 ... +%if mmsize == 8 + punpcklwd m3 ,m6 ; R0 G0 B0 R1 R2 G2 B2 R3 + punpckhwd m5, m6 ; R4 G4 B4 R5 R6 G6 B6 R7 +%if cpuflag(mmxext) + pshufw m1, m2, 0xc6 + pshufw m6, m3, 0x84 + pshufw m7, m5, 0x38 + pand m6, [mask_1101] ; R0 G0 B0 R1 -- -- R2 G2 + movq m0, m1 + pand m7, [mask_0110] ; -- -- R6 G6 B6 R7 -- -- + movq m2, m1 + pand m1, [mask_0100] ; -- -- G3 B3 -- -- -- -- + psrlq m3, 48 ; B2 R3 -- -- -- -- -- -- + pand m0, [mask_0010] ; -- -- -- -- G1 B1 -- -- + psllq m5, 32 ; -- -- -- -- R4 G4 B4 R5 + pand m2, [mask_1001] ; G5 B5 -- -- -- -- G7 B7 + por m1, m3 + por m0, m6 + por m1, m5 + por m2, m7 + movntq [imageq], m0 + movntq [imageq + 8], m1 + movntq [imageq + 16], m2 +%else ; cpuflag(mmx) + movd [imageq], m3 ; R0 G0 R2 G2 + movd [imageq + 4], m2 ; G1 B1 + psrlq m3, 32 + psrlq m2, 16 + movd [imageq + 6], m3 ; R2 G2 B2 R3 + movd [imageq + 10], m2 ; G3 B3 + psrlq m2, 16 + movd [imageq + 12], m5 ; R4 G4 B4 R5 + movd [imageq + 16], m2 ; G5 B5 + psrlq m5, 32 + movd [imageq + 20], m2 ; -- -- G7 B7 + movd [imageq + 18], m5 ; R6 G6 B6 R7 +%endif ; mmsize = 8 +%else ; mmsize == 16 + pshufb m3, [rgb24_shuf1] ; r0 g0 r6 g6 r12 g12 r2 g2 r8 g8 r14 g14 r4 g4 r10 g10 + pshufb m6, [rgb24_shuf2] ; b10 r11 b0 r1 b6 r7 b12 r13 b2 r3 b8 r9 b14 r15 b4 r5 + pshufb m2, [rgb24_shuf3] ; g5 b5 g11 b11 g1 b1 g7 b7 g13 b13 g3 b3 g9 b9 g15 b15 + mova m7, [mask_dw036] + mova m4, [mask_dw147] + mova m5, [mask_dw25] + pand m0, m7, m3 ; r0 g0 --- --- --- --- r2 g2 --- --- --- --- r4 g4 --- --- + pand m1, m4, m6 ; --- --- b0 r1 --- --- --- --- b2 r3 --- --- --- --- b4 r5 + por m0, m1 + pand m1, m5, m2 ; --- --- --- --- g1 b1 --- --- --- --- g3 b3 --- --- --- --- + por m0, m1 ; r0 g0 b0 r1 g1 b1 r2 g2 b2 r3 g3 b3 r4 g4 b4 r5 + pand m1, m7, m2 ; g5 b5 --- --- --- --- g7 b7 --- --- --- --- g9 b9 --- --- + pand m7, m6 ; b10 r11 --- --- --- --- b12 r13 --- --- --- --- b14 r15 --- --- + pand m6, m5 ; --- --- --- --- b6 r7 --- --- --- --- b8 r9 --- --- --- --- + por m1, m6 + pand m6, m4, m3 ; --- --- r6 g6 --- --- --- --- r8 g8 --- --- --- --- r10 g10 + pand m2, m4 ; --- --- g11 b11 --- --- --- --- g13 b13 --- --- --- --- g15 b15 + pand m3, m5 ; --- --- --- --- r12 g12 --- --- --- --- r14 g14 --- --- --- --- + por m2, m7 + por m1, m6 ; g5 b5 r6 g6 b6 r7 g7 b7 r8 g8 b8 r9 g9 b9 r10 g10 + por m2, m3 ; b10 r11 g11 b11 r12 g12 b12 r13 g13 b13 r14 g14 b14 r15 g15 b15 + mova [imageq], m0 + mova [imageq + 16], m1 + mova [imageq + 32], m2 +%endif ; mmsize = 16 +%else ; PACK RGB15/16/32 + packuswb m0, m1 + packuswb m3, m5 + packuswb m2, m2 + mova m1, m0 + packuswb m7, m7 + punpcklbw m0, m3 ; B0 B1 B2 B3 ... B7 + punpckhbw m1, m3 ; R0 R1 R2 R3 ... R7 + punpcklbw m2, m7 ; G0 G1 G2 G3 ... G7 +%if %3 == 32 ; PACK RGB32 +%define depth 4 +%ifidn %1, yuv + pcmpeqd m3, m3 ; Set alpha empty +%else + mova m3, [pa_2indexq + 2 * indexq] ; Load alpha +%endif + mova m5, m_blue + mova m6, m_red + punpckhbw m5, m_green + punpcklbw m_blue, m_green + punpckhbw m6, m_alpha + punpcklbw m_red, m_alpha + mova m_green, m_blue + mova m_alpha, m5 + punpcklwd m_blue, m_red + punpckhwd m_green, m_red + punpcklwd m5, m6 + punpckhwd m_alpha, m6 + mova [imageq + 0], m_blue + mova [imageq + 8 * time_num], m_green + mova [imageq + 16 * time_num], m5 + mova [imageq + 24 * time_num], m_alpha +%else ; PACK RGB15/16 +%define depth 2 +%if cpuflag(ssse3) + %define red_dither m3 + %define green_dither m4 + %define blue_dither m5 + VBROADCASTSD red_dither, [pointer_c_ditherq + 0 * 8] + VBROADCASTSD green_dither, [pointer_c_ditherq + 1 * 8] + VBROADCASTSD blue_dither, [pointer_c_ditherq + 2 * 8] +%else ; cpuflag(mmx/mmxext) +%define blue_dither [pointer_c_ditherq + 2 * 8] +%define green_dither [pointer_c_ditherq + 1 * 8] +%define red_dither [pointer_c_ditherq + 0 * 8] +%endif +%if %3 == 15 +%define gmask pb_03 +%define isRGB15 1 +%else +%define gmask pb_07 +%define isRGB15 0 +%endif + paddusb m0, blue_dither + paddusb m2, green_dither + paddusb m1, red_dither + pand m0, [pb_f8] + pand m1, [pb_f8] + mova m3, m2 + psllw m2, 3 - isRGB15 + psrlw m3, 5 + isRGB15 + psrlw m0, 3 + psrlw m1, isRGB15 + pand m2, [pb_e0] + pand m3, [gmask] + por m0, m2 + por m1, m3 + mova m2, m0 + punpcklbw m0, m1 + punpckhbw m2, m1 + mova [imageq], m0 + mova [imageq + 8 * time_num], m2 +%endif ; PACK RGB15/16 +%endif ; PACK RGB15/16/32 + +movu m_y, [py_2indexq + 2 * indexq + 8 * time_num] +movh m_v, [pv_indexq + indexq + 4 * time_num] +movh m_u, [pu_indexq + indexq + 4 * time_num] +add imageq, 8 * depth * time_num +add indexq, 4 * time_num +js .loop0 + +REP_RET + +%endmacro + +INIT_MMX mmx +yuv2rgb_fn yuv, rgb, 24 +yuv2rgb_fn yuv, bgr, 24 +yuv2rgb_fn yuv, rgb, 32 +yuv2rgb_fn yuv, bgr, 32 +yuv2rgb_fn yuva, rgb, 32 +yuv2rgb_fn yuva, bgr, 32 +yuv2rgb_fn yuv, rgb, 15 +yuv2rgb_fn yuv, rgb, 16 + +INIT_MMX mmxext +yuv2rgb_fn yuv, rgb, 24 +yuv2rgb_fn yuv, bgr, 24 + +INIT_XMM ssse3 +yuv2rgb_fn yuv, rgb, 24 +yuv2rgb_fn yuv, bgr, 24 +yuv2rgb_fn yuv, rgb, 32 +yuv2rgb_fn yuv, bgr, 32 +yuv2rgb_fn yuva, rgb, 32 +yuv2rgb_fn yuva, bgr, 32 +yuv2rgb_fn yuv, rgb, 15 +yuv2rgb_fn yuv, rgb, 16 diff --git a/libswscale/yuv2rgb.c b/libswscale/yuv2rgb.c index d0df061e4d9..588462504ec 100644 --- a/libswscale/yuv2rgb.c +++ b/libswscale/yuv2rgb.c @@ -138,10 +138,11 @@ const int *sws_getCoefficients(int colorspace) srcStride[2] *= 2; \ } \ for (y = 0; y < srcSliceH; y += 2) { \ + int yd = y + srcSliceY; \ dst_type *dst_1 = \ - (dst_type *)(dst[0] + (y + srcSliceY) * dstStride[0]); \ + (dst_type *)(dst[0] + (yd) * dstStride[0]); \ dst_type *dst_2 = \ - (dst_type *)(dst[0] + (y + srcSliceY + 1) * dstStride[0]); \ + (dst_type *)(dst[0] + (yd + 1) * dstStride[0]); \ dst_type av_unused *r, *g, *b; \ const uint8_t *py_1 = src[0] + y * srcStride[0]; \ const uint8_t *py_2 = py_1 + srcStride[0]; \ @@ -498,8 +499,8 @@ CLOSEYUV2RGBFUNC(8) // r, g, b, dst_1, dst_2 YUV2RGBFUNC(yuv2rgb_c_8_ordered_dither, uint8_t, 0) - const uint8_t *d32 = ff_dither_8x8_32[y & 7]; - const uint8_t *d64 = ff_dither_8x8_73[y & 7]; + const uint8_t *d32 = ff_dither_8x8_32[yd & 7]; + const uint8_t *d64 = ff_dither_8x8_73[yd & 7]; #define PUTRGB8(dst, src, i, o) \ Y = src[2 * i]; \ @@ -528,8 +529,8 @@ YUV2RGBFUNC(yuv2rgb_c_8_ordered_dither, uint8_t, 0) PUTRGB8(dst_1, py_1, 3, 6); ENDYUV2RGBLINE(8, 0) - const uint8_t *d32 = ff_dither_8x8_32[y & 7]; - const uint8_t *d64 = ff_dither_8x8_73[y & 7]; + const uint8_t *d32 = ff_dither_8x8_32[yd & 7]; + const uint8_t *d64 = ff_dither_8x8_73[yd & 7]; LOADCHROMA(0); PUTRGB8(dst_1, py_1, 0, 0); PUTRGB8(dst_2, py_2, 0, 0 + 8); @@ -539,8 +540,8 @@ ENDYUV2RGBLINE(8, 0) PUTRGB8(dst_1, py_1, 1, 2); ENDYUV2RGBLINE(8, 1) - const uint8_t *d32 = ff_dither_8x8_32[y & 7]; - const uint8_t *d64 = ff_dither_8x8_73[y & 7]; + const uint8_t *d32 = ff_dither_8x8_32[yd & 7]; + const uint8_t *d64 = ff_dither_8x8_73[yd & 7]; LOADCHROMA(0); PUTRGB8(dst_1, py_1, 0, 0); PUTRGB8(dst_2, py_2, 0, 0 + 8); @@ -549,8 +550,8 @@ ENDYUV2RGBFUNC() YUV2RGBFUNC(yuv2rgb_c_4_ordered_dither, uint8_t, 0) - const uint8_t * d64 = ff_dither_8x8_73[y & 7]; - const uint8_t *d128 = ff_dither_8x8_220[y & 7]; + const uint8_t * d64 = ff_dither_8x8_73[yd & 7]; + const uint8_t *d128 = ff_dither_8x8_220[yd & 7]; int acc; #define PUTRGB4D(dst, src, i, o) \ @@ -581,8 +582,8 @@ YUV2RGBFUNC(yuv2rgb_c_4_ordered_dither, uint8_t, 0) PUTRGB4D(dst_1, py_1, 3, 6); ENDYUV2RGBLINE(4, 0) - const uint8_t * d64 = ff_dither_8x8_73[y & 7]; - const uint8_t *d128 = ff_dither_8x8_220[y & 7]; + const uint8_t * d64 = ff_dither_8x8_73[yd & 7]; + const uint8_t *d128 = ff_dither_8x8_220[yd & 7]; int acc; LOADCHROMA(0); PUTRGB4D(dst_1, py_1, 0, 0); @@ -593,8 +594,8 @@ ENDYUV2RGBLINE(4, 0) PUTRGB4D(dst_1, py_1, 1, 2); ENDYUV2RGBLINE(4, 1) - const uint8_t * d64 = ff_dither_8x8_73[y & 7]; - const uint8_t *d128 = ff_dither_8x8_220[y & 7]; + const uint8_t * d64 = ff_dither_8x8_73[yd & 7]; + const uint8_t *d128 = ff_dither_8x8_220[yd & 7]; int acc; LOADCHROMA(0); PUTRGB4D(dst_1, py_1, 0, 0); @@ -602,8 +603,8 @@ ENDYUV2RGBLINE(4, 1) ENDYUV2RGBFUNC() YUV2RGBFUNC(yuv2rgb_c_4b_ordered_dither, uint8_t, 0) - const uint8_t *d64 = ff_dither_8x8_73[y & 7]; - const uint8_t *d128 = ff_dither_8x8_220[y & 7]; + const uint8_t *d64 = ff_dither_8x8_73[yd & 7]; + const uint8_t *d128 = ff_dither_8x8_220[yd & 7]; #define PUTRGB4DB(dst, src, i, o) \ Y = src[2 * i]; \ @@ -631,8 +632,8 @@ YUV2RGBFUNC(yuv2rgb_c_4b_ordered_dither, uint8_t, 0) PUTRGB4DB(dst_2, py_2, 3, 6 + 8); PUTRGB4DB(dst_1, py_1, 3, 6); ENDYUV2RGBLINE(8, 0) - const uint8_t *d64 = ff_dither_8x8_73[y & 7]; - const uint8_t *d128 = ff_dither_8x8_220[y & 7]; + const uint8_t *d64 = ff_dither_8x8_73[yd & 7]; + const uint8_t *d128 = ff_dither_8x8_220[yd & 7]; LOADCHROMA(0); PUTRGB4DB(dst_1, py_1, 0, 0); PUTRGB4DB(dst_2, py_2, 0, 0 + 8); @@ -641,15 +642,15 @@ ENDYUV2RGBLINE(8, 0) PUTRGB4DB(dst_2, py_2, 1, 2 + 8); PUTRGB4DB(dst_1, py_1, 1, 2); ENDYUV2RGBLINE(8, 1) - const uint8_t *d64 = ff_dither_8x8_73[y & 7]; - const uint8_t *d128 = ff_dither_8x8_220[y & 7]; + const uint8_t *d64 = ff_dither_8x8_73[yd & 7]; + const uint8_t *d128 = ff_dither_8x8_220[yd & 7]; LOADCHROMA(0); PUTRGB4DB(dst_1, py_1, 0, 0); PUTRGB4DB(dst_2, py_2, 0, 0 + 8); ENDYUV2RGBFUNC() YUV2RGBFUNC(yuv2rgb_c_1_ordered_dither, uint8_t, 0) - const uint8_t *d128 = ff_dither_8x8_220[y & 7]; + const uint8_t *d128 = ff_dither_8x8_220[yd & 7]; char out_1 = 0, out_2 = 0; g = c->table_gU[128 + YUVRGB_TABLE_HEADROOM] + c->table_gV[128 + YUVRGB_TABLE_HEADROOM]; diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000000..4766e56945b --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,11 @@ +/audiogen +/audiomatch +/base64 +/data/ +/pixfmts.mak +/rotozoom +/test_copy.ffmeta +/tiny_psnr +/tiny_ssim +/videogen +/vsynth1/ diff --git a/tests/Makefile b/tests/Makefile index 624292d4514..7844901e53c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -10,7 +10,8 @@ FFMPEG=ffmpeg$(PROGSSUF)$(EXESUF) $(AREF): CMP= APITESTSDIR := tests/api -FATE_OUTDIRS = tests/data tests/data/fate tests/data/filtergraphs tests/data/lavf tests/data/lavf-fate tests/data/pixfmt tests/vsynth1 $(APITESTSDIR) +DNNTESTSDIR := tests/dnn +FATE_OUTDIRS = tests/data tests/data/fate tests/data/filtergraphs tests/data/lavf tests/data/lavf-fate tests/data/pixfmt tests/vsynth1 $(APITESTSDIR) $(DNNTESTSDIR) OUTDIRS += $(FATE_OUTDIRS) $(VREF): tests/videogen$(HOSTEXESUF) | tests/vsynth1 @@ -84,7 +85,18 @@ FILTERDEMDECENCMUX = $(call ALLYES, $(1:%=%_FILTER) $(2)_DEMUXER $(3)_DECODER $( PARSERDEMDEC = $(call ALLYES, $(1)_PARSER $(2)_DEMUXER $(3)_DECODER) +# Allow overriding CONFIG_LARGE_TESTS via LARGE_TESTS, if set on the +# make command line. +ifeq ($(LARGE_TESTS), yes) +CONFIG_LARGE_TESTS:=yes +!CONFIG_LARGE_TESTS:= +else ifeq ($(LARGE_TESTS), no) +CONFIG_LARGE_TESTS:= +!CONFIG_LARGE_TESTS:=yes +endif + include $(SRC_PATH)/$(APITESTSDIR)/Makefile +include $(SRC_PATH)/$(DNNTESTSDIR)/Makefile include $(SRC_PATH)/tests/fate/acodec.mak include $(SRC_PATH)/tests/fate/vcodec.mak @@ -118,6 +130,7 @@ include $(SRC_PATH)/tests/fate/cover-art.mak include $(SRC_PATH)/tests/fate/dca.mak include $(SRC_PATH)/tests/fate/demux.mak include $(SRC_PATH)/tests/fate/dfa.mak +include $(SRC_PATH)/tests/fate/dnn.mak include $(SRC_PATH)/tests/fate/dnxhd.mak include $(SRC_PATH)/tests/fate/dpcm.mak include $(SRC_PATH)/tests/fate/ea.mak @@ -174,6 +187,7 @@ include $(SRC_PATH)/tests/fate/segment.mak include $(SRC_PATH)/tests/fate/source.mak include $(SRC_PATH)/tests/fate/speedhq.mak include $(SRC_PATH)/tests/fate/subtitles.mak +include $(SRC_PATH)/tests/fate/truehd.mak include $(SRC_PATH)/tests/fate/utvideo.mak include $(SRC_PATH)/tests/fate/video.mak include $(SRC_PATH)/tests/fate/voice.mak @@ -187,25 +201,29 @@ include $(SRC_PATH)/tests/fate/xvid.mak FATE_FFMPEG += $(FATE_FFMPEG-yes) $(FATE_AVCONV) $(FATE_AVCONV-yes) FATE-$(CONFIG_FFMPEG) += $(FATE_FFMPEG) FATE-$(CONFIG_FFPROBE) += $(FATE_FFPROBE) +FATE-$(call ALLYES, FFMPEG FFPROBE) += $(FATE_FFMPEG_FFPROBE) FATE_SAMPLES_AVCONV += $(FATE_SAMPLES_AVCONV-yes) FATE_SAMPLES_FFMPEG += $(FATE_SAMPLES_FFMPEG-yes) -FATE_EXTERN-$(CONFIG_FFMPEG) += $(FATE_SAMPLES_AVCONV) $(FATE_SAMPLES_FFMPEG) $(FATE_SAMPLES_FFPROBE) $(FATE_SAMPLES_FASTSTART) -FATE_EXTERN += $(FATE_EXTERN-yes) +FATE_EXTERN-$(CONFIG_FFMPEG) += $(FATE_SAMPLES_AVCONV) $(FATE_SAMPLES_FFMPEG) +FATE_EXTERN-$(CONFIG_FFPROBE) += $(FATE_SAMPLES_FFPROBE) +FATE_SAMPLES_FFMPEG_FFPROBE += $(FATE_SAMPLES_FFMPEG_FFPROBE-yes) +FATE_EXTERN-$(call ALLYES, FFMPEG FFPROBE) += $(FATE_SAMPLES_FFMPEG_FFPROBE) +FATE_EXTERN += $(FATE_EXTERN-yes) $(FATE_SAMPLES_FASTSTART) FATE += $(FATE-yes) RSYNC_OPTIONS-$(HAVE_RSYNC_CONTIMEOUT) += --contimeout=60 RSYNC_OPTIONS = -vrltLW --timeout=60 $(RSYNC_OPTIONS-yes) -$(FATE_FFMPEG) $(FATE_SAMPLES_AVCONV) $(FATE_SAMPLES_FFMPEG): ffmpeg$(PROGSSUF)$(EXESUF) +$(FATE_FFMPEG) $(FATE_FFMPEG_FFPROBE) $(FATE_SAMPLES_AVCONV) $(FATE_SAMPLES_FFMPEG) $(FATE_SAMPLES_FFMPEG_FFPROBE): ffmpeg$(PROGSSUF)$(EXESUF) -$(FATE_FFPROBE) $(FATE_SAMPLES_FFPROBE): ffprobe$(PROGSSUF)$(EXESUF) +$(FATE_FFPROBE) $(FATE_FFMPEG_FFPROBE) $(FATE_SAMPLES_FFPROBE) $(FATE_SAMPLES_FFMPEG_FFPROBE): ffprobe$(PROGSSUF)$(EXESUF) $(FATE_SAMPLES_FASTSTART): tools/qt-faststart$(EXESUF) +$(FATE_SAMPLES_DUMP_DATA): tools/venc_data_dump$(EXESUF) ifdef SAMPLES -FATE += $(FATE_FULL) $(FATE_FULL-yes) FATE += $(FATE_EXTERN) fate-rsync: rsync $(RSYNC_OPTIONS) rsync://fate-suite.ffmpeg.org/fate-suite/ $(SAMPLES) diff --git a/tests/api/.gitignore b/tests/api/.gitignore new file mode 100644 index 00000000000..bbab80ca73e --- /dev/null +++ b/tests/api/.gitignore @@ -0,0 +1 @@ +/*-test diff --git a/tests/api/api-flac-test.c b/tests/api/api-flac-test.c index e8e8cbf1e7c..ae6a9316d82 100644 --- a/tests/api/api-flac-test.c +++ b/tests/api/api-flac-test.c @@ -126,7 +126,7 @@ static int run_test(AVCodec *enc, AVCodec *dec, AVCodecContext *enc_ctx, in_frame->nb_samples = enc_ctx->frame_size; in_frame->format = enc_ctx->sample_fmt; in_frame->channel_layout = enc_ctx->channel_layout; - if (av_frame_get_buffer(in_frame, 32) != 0) { + if (av_frame_get_buffer(in_frame, 0) != 0) { av_log(NULL, AV_LOG_ERROR, "Can't allocate a buffer for input frame\n"); return AVERROR(ENOMEM); } diff --git a/tests/api/api-h264-slice-test.c b/tests/api/api-h264-slice-test.c index dee93b8349e..b7aa405b0d4 100644 --- a/tests/api/api-h264-slice-test.c +++ b/tests/api/api-h264-slice-test.c @@ -24,7 +24,6 @@ #include "config.h" -#include #include #include #include diff --git a/tests/api/api-threadmessage-test.c b/tests/api/api-threadmessage-test.c index 3c693a70d1e..b6a74f678bf 100644 --- a/tests/api/api-threadmessage-test.c +++ b/tests/api/api-threadmessage-test.c @@ -101,7 +101,7 @@ static void *sender_thread(void *arg) msg.frame->format = AV_PIX_FMT_RGBA; msg.frame->width = 320; msg.frame->height = 240; - ret = av_frame_get_buffer(msg.frame, 32); + ret = av_frame_get_buffer(msg.frame, 0); if (ret < 0) { av_frame_free(&msg.frame); break; diff --git a/tests/checkasm/.gitignore b/tests/checkasm/.gitignore new file mode 100644 index 00000000000..9ee0e5cfc11 --- /dev/null +++ b/tests/checkasm/.gitignore @@ -0,0 +1 @@ +/checkasm diff --git a/tests/checkasm/Makefile b/tests/checkasm/Makefile index f5780eedb24..9e9569777b1 100644 --- a/tests/checkasm/Makefile +++ b/tests/checkasm/Makefile @@ -22,6 +22,7 @@ AVCODECOBJS-$(CONFIG_DCA_DECODER) += synth_filter.o AVCODECOBJS-$(CONFIG_EXR_DECODER) += exrdsp.o AVCODECOBJS-$(CONFIG_HUFFYUV_DECODER) += huffyuvdsp.o AVCODECOBJS-$(CONFIG_JPEG2000_DECODER) += jpeg2000dsp.o +AVCODECOBJS-$(CONFIG_OPUS_DECODER) += opusdsp.o AVCODECOBJS-$(CONFIG_PIXBLOCKDSP) += pixblockdsp.o AVCODECOBJS-$(CONFIG_HEVC_DECODER) += hevc_add_res.o hevc_idct.o hevc_sao.o AVCODECOBJS-$(CONFIG_UTVIDEO_DECODER) += utvideodsp.o @@ -35,6 +36,7 @@ CHECKASMOBJS-$(CONFIG_AVCODEC) += $(AVCODECOBJS-yes) AVFILTEROBJS-$(CONFIG_AFIR_FILTER) += af_afir.o AVFILTEROBJS-$(CONFIG_BLEND_FILTER) += vf_blend.o AVFILTEROBJS-$(CONFIG_COLORSPACE_FILTER) += vf_colorspace.o +AVFILTEROBJS-$(CONFIG_EQ_FILTER) += vf_eq.o AVFILTEROBJS-$(CONFIG_GBLUR_FILTER) += vf_gblur.o AVFILTEROBJS-$(CONFIG_HFLIP_FILTER) += vf_hflip.o AVFILTEROBJS-$(CONFIG_THRESHOLD_FILTER) += vf_threshold.o @@ -43,7 +45,7 @@ AVFILTEROBJS-$(CONFIG_NLMEANS_FILTER) += vf_nlmeans.o CHECKASMOBJS-$(CONFIG_AVFILTER) += $(AVFILTEROBJS-yes) # swscale tests -SWSCALEOBJS += sw_rgb.o +SWSCALEOBJS += sw_rgb.o sw_scale.o CHECKASMOBJS-$(CONFIG_SWSCALE) += $(SWSCALEOBJS) diff --git a/tests/checkasm/aacpsdsp.c b/tests/checkasm/aacpsdsp.c index ea68b39fa97..2ceef4341f3 100644 --- a/tests/checkasm/aacpsdsp.c +++ b/tests/checkasm/aacpsdsp.c @@ -17,6 +17,7 @@ */ #include "libavcodec/aacpsdsp.h" +#include "libavutil/intfloat.h" #include "checkasm.h" @@ -34,6 +35,16 @@ #define EPS 0.005 +static void clear_less_significant_bits(INTFLOAT *buf, int len, int bits) +{ + int i; + for (i = 0; i < len; i++) { + union av_intfloat32 u = { .f = buf[i] }; + u.i &= (0xffffffff << bits); + buf[i] = u.f; + } +} + static void test_add_squares(void) { LOCAL_ALIGNED_16(INTFLOAT, dst0, [BUF_SIZE]); @@ -198,6 +209,13 @@ static void test_stereo_interpolate(PSDSPContext *psdsp) randomize((INTFLOAT *)h, 2 * 4); randomize((INTFLOAT *)h_step, 2 * 4); + // Clear the least significant 14 bits of h_step, to avoid + // divergence when accumulating h_step BUF_SIZE times into + // a float variable which may or may not have extra intermediate + // precision. Therefore clear roughly log2(BUF_SIZE) less + // significant bits, to get the same result regardless of any + // extra precision in the accumulator. + clear_less_significant_bits((INTFLOAT *)h_step, 2 * 4, 14); call_ref(l0, r0, h, h_step, BUF_SIZE); call_new(l1, r1, h, h_step, BUF_SIZE); diff --git a/tests/checkasm/aarch64/checkasm.S b/tests/checkasm/aarch64/checkasm.S index 89f2b77548b..6d3c7388016 100644 --- a/tests/checkasm/aarch64/checkasm.S +++ b/tests/checkasm/aarch64/checkasm.S @@ -23,29 +23,31 @@ #include "libavutil/aarch64/asm.S" const register_init, align=4 - .quad 0x21f86d66c8ca00ce - .quad 0x75b6ba21077c48ad - .quad 0xed56bb2dcb3c7736 - .quad 0x8bda43d3fd1a7e06 - .quad 0xb64a9c9e5d318408 - .quad 0xdf9a54b303f1d3a3 - .quad 0x4a75479abd64e097 - .quad 0x249214109d5d1c88 - .quad 0x1a1b2550a612b48c - .quad 0x79445c159ce79064 - .quad 0x2eed899d5a28ddcd - .quad 0x86b2536fcd8cf636 - .quad 0xb0856806085e7943 - .quad 0x3f2bf84fc0fcca4e - .quad 0xacbd382dcf5b8de2 - .quad 0xd229e1f5b281303f - .quad 0x71aeaff20b095fd9 - .quad 0xab63e2e11fa38ed9 + .quad 0x21f86d66c8ca00ce + .quad 0x75b6ba21077c48ad + .quad 0xed56bb2dcb3c7736 + .quad 0x8bda43d3fd1a7e06 + .quad 0xb64a9c9e5d318408 + .quad 0xdf9a54b303f1d3a3 + .quad 0x4a75479abd64e097 + .quad 0x249214109d5d1c88 + .quad 0x1a1b2550a612b48c + .quad 0x79445c159ce79064 + .quad 0x2eed899d5a28ddcd + .quad 0x86b2536fcd8cf636 + .quad 0xb0856806085e7943 + .quad 0x3f2bf84fc0fcca4e + .quad 0xacbd382dcf5b8de2 + .quad 0xd229e1f5b281303f + .quad 0x71aeaff20b095fd9 + .quad 0xab63e2e11fa38ed9 endconst -const error_message - .asciz "failed to preserve register" +const error_message_register + .asciz "failed to preserve register" +error_message_stack: + .asciz "stack clobbered" endconst @@ -55,103 +57,142 @@ endconst #define CLOBBER_STACK ((8*MAX_ARGS + 15) & ~15) function checkasm_stack_clobber, export=1 - mov x3, sp - mov x2, #CLOBBER_STACK + mov x3, sp + mov x2, #CLOBBER_STACK 1: - stp x0, x1, [sp, #-16]! - subs x2, x2, #16 - b.gt 1b - mov sp, x3 - ret + stp x0, x1, [sp, #-16]! + subs x2, x2, #16 + b.gt 1b + mov sp, x3 + ret endfunc -#define ARG_STACK ((8*(MAX_ARGS - 8) + 15) & ~15) +// + 16 for stack canary reference +#define ARG_STACK ((8*(MAX_ARGS - 8) + 15) & ~15 + 16) function checkasm_checked_call, export=1 - stp x29, x30, [sp, #-16]! - mov x29, sp - stp x19, x20, [sp, #-16]! - stp x21, x22, [sp, #-16]! - stp x23, x24, [sp, #-16]! - stp x25, x26, [sp, #-16]! - stp x27, x28, [sp, #-16]! - stp d8, d9, [sp, #-16]! - stp d10, d11, [sp, #-16]! - stp d12, d13, [sp, #-16]! - stp d14, d15, [sp, #-16]! - - movrel x9, register_init - ldp d8, d9, [x9], #16 - ldp d10, d11, [x9], #16 - ldp d12, d13, [x9], #16 - ldp d14, d15, [x9], #16 - ldp x19, x20, [x9], #16 - ldp x21, x22, [x9], #16 - ldp x23, x24, [x9], #16 - ldp x25, x26, [x9], #16 - ldp x27, x28, [x9], #16 - - sub sp, sp, #ARG_STACK + stp x29, x30, [sp, #-16]! + mov x29, sp + stp x19, x20, [sp, #-16]! + stp x21, x22, [sp, #-16]! + stp x23, x24, [sp, #-16]! + stp x25, x26, [sp, #-16]! + stp x27, x28, [sp, #-16]! + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + + movrel x9, register_init + ldp d8, d9, [x9], #16 + ldp d10, d11, [x9], #16 + ldp d12, d13, [x9], #16 + ldp d14, d15, [x9], #16 + ldp x19, x20, [x9], #16 + ldp x21, x22, [x9], #16 + ldp x23, x24, [x9], #16 + ldp x25, x26, [x9], #16 + ldp x27, x28, [x9], #16 + + sub sp, sp, #ARG_STACK .equ pos, 0 .rept MAX_ARGS-8 - // Skip the first 8 args, that are loaded into registers - ldr x9, [x29, #16 + 8*8 + pos] - str x9, [sp, #pos] + // Skip the first 8 args, that are loaded into registers + ldr x9, [x29, #16 + 8*8 + pos] + str x9, [sp, #pos] .equ pos, pos + 8 .endr - mov x12, x0 - ldp x0, x1, [x29, #16] - ldp x2, x3, [x29, #32] - ldp x4, x5, [x29, #48] - ldp x6, x7, [x29, #64] - blr x12 - add sp, sp, #ARG_STACK - stp x0, x1, [sp, #-16]! - movrel x9, register_init - movi v3.8h, #0 + // Fill x8-x17 with garbage. This doesn't have to be preserved, + // but avoids relying on them having any particular value. + movrel x9, register_init + ldp x10, x11, [x9], #32 + ldp x12, x13, [x9], #32 + ldp x14, x15, [x9], #32 + ldp x16, x17, [x9], #32 + ldp x8, x9, [x9] + + // For stack overflows, the callee is free to overwrite the parameters + // that were passed on the stack (if any), so we can only check after + // that point. First figure out how many parameters the function + // really took on the stack: + ldr w2, [x29, #16 + 8*8 + (MAX_ARGS-8)*8] + // Load the first non-parameter value from the stack, that should be + // left untouched by the function. Store a copy of it inverted, so that + // e.g. overwriting everything with zero would be noticed. + ldr x2, [sp, x2, lsl #3] + mvn x2, x2 + str x2, [sp, #ARG_STACK-8] + + // Load the in-register arguments + mov x12, x0 + ldp x0, x1, [x29, #16] + ldp x2, x3, [x29, #32] + ldp x4, x5, [x29, #48] + ldp x6, x7, [x29, #64] + // Call the target function + blr x12 + + // Load the number of stack parameters, stack canary and its reference + ldr w2, [x29, #16 + 8*8 + (MAX_ARGS-8)*8] + ldr x2, [sp, x2, lsl #3] + ldr x3, [sp, #ARG_STACK-8] + + add sp, sp, #ARG_STACK + stp x0, x1, [sp, #-16]! + + mvn x3, x3 + cmp x2, x3 + b.ne 2f + + movrel x9, register_init + movi v3.8h, #0 .macro check_reg_neon reg1, reg2 - ldr q1, [x9], #16 - uzp1 v2.2d, v\reg1\().2d, v\reg2\().2d - eor v1.16b, v1.16b, v2.16b - orr v3.16b, v3.16b, v1.16b + ldr q1, [x9], #16 + uzp1 v2.2d, v\reg1\().2d, v\reg2\().2d + eor v1.16b, v1.16b, v2.16b + orr v3.16b, v3.16b, v1.16b .endm - check_reg_neon 8, 9 - check_reg_neon 10, 11 - check_reg_neon 12, 13 - check_reg_neon 14, 15 - uqxtn v3.8b, v3.8h - umov x3, v3.d[0] + check_reg_neon 8, 9 + check_reg_neon 10, 11 + check_reg_neon 12, 13 + check_reg_neon 14, 15 + uqxtn v3.8b, v3.8h + umov x3, v3.d[0] .macro check_reg reg1, reg2 - ldp x0, x1, [x9], #16 - eor x0, x0, \reg1 - eor x1, x1, \reg2 - orr x3, x3, x0 - orr x3, x3, x1 + ldp x0, x1, [x9], #16 + eor x0, x0, \reg1 + eor x1, x1, \reg2 + orr x3, x3, x0 + orr x3, x3, x1 .endm - check_reg x19, x20 - check_reg x21, x22 - check_reg x23, x24 - check_reg x25, x26 - check_reg x27, x28 - - cbz x3, 0f - - movrel x0, error_message - bl X(checkasm_fail_func) + check_reg x19, x20 + check_reg x21, x22 + check_reg x23, x24 + check_reg x25, x26 + check_reg x27, x28 + + cbz x3, 0f + + movrel x0, error_message_register + b 1f +2: + movrel x0, error_message_stack +1: + bl X(checkasm_fail_func) 0: - ldp x0, x1, [sp], #16 - ldp d14, d15, [sp], #16 - ldp d12, d13, [sp], #16 - ldp d10, d11, [sp], #16 - ldp d8, d9, [sp], #16 - ldp x27, x28, [sp], #16 - ldp x25, x26, [sp], #16 - ldp x23, x24, [sp], #16 - ldp x21, x22, [sp], #16 - ldp x19, x20, [sp], #16 - ldp x29, x30, [sp], #16 - ret + ldp x0, x1, [sp], #16 + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + ldp x27, x28, [sp], #16 + ldp x25, x26, [sp], #16 + ldp x23, x24, [sp], #16 + ldp x21, x22, [sp], #16 + ldp x19, x20, [sp], #16 + ldp x29, x30, [sp], #16 + ret endfunc diff --git a/tests/checkasm/af_afir.c b/tests/checkasm/af_afir.c index e3fb76e8e05..8d1f815469b 100644 --- a/tests/checkasm/af_afir.c +++ b/tests/checkasm/af_afir.c @@ -53,7 +53,19 @@ static void test_fcmul_add(const float *src0, const float *src1, const float *sr call_ref(cdst, src1, src2, LEN); call_new(odst, src1, src2, LEN); for (i = 0; i <= LEN*2; i++) { - if (!float_near_abs_eps(cdst[i], odst[i], 6.2e-05)) { + int idx = i & ~1; + float cre = src2[idx]; + float cim = src2[idx + 1]; + float tre = src1[idx]; + float tim = src1[idx + 1]; + double t = fabs(src0[i]) + + fabs(tre) + fabs(tim) + fabs(cre) + fabs(cim) + + fabs(tre * cre) + fabs(tim * cim) + + fabs(tre * cim) + fabs(tim * cre) + + fabs(tre * cre - tim * cim) + + fabs(tre * cim + tim * cre) + + fabs(cdst[i]) + 1.0; + if (!float_near_abs_eps(cdst[i], odst[i], t * 2 * FLT_EPSILON)) { fprintf(stderr, "%d: %- .12f - %- .12f = % .12g\n", i, cdst[i], odst[i], cdst[i] - odst[i]); fail(); diff --git a/tests/checkasm/arm/checkasm.S b/tests/checkasm/arm/checkasm.S index 2051b290f69..601c2f66b87 100644 --- a/tests/checkasm/arm/checkasm.S +++ b/tests/checkasm/arm/checkasm.S @@ -29,22 +29,24 @@ ELF .eabi_attribute 10, 0 @ suppress Tag_FP_arch #endif const register_init, align=3 - .quad 0x21f86d66c8ca00ce - .quad 0x75b6ba21077c48ad - .quad 0xed56bb2dcb3c7736 - .quad 0x8bda43d3fd1a7e06 - .quad 0xb64a9c9e5d318408 - .quad 0xdf9a54b303f1d3a3 - .quad 0x4a75479abd64e097 - .quad 0x249214109d5d1c88 + .quad 0x21f86d66c8ca00ce + .quad 0x75b6ba21077c48ad + .quad 0xed56bb2dcb3c7736 + .quad 0x8bda43d3fd1a7e06 + .quad 0xb64a9c9e5d318408 + .quad 0xdf9a54b303f1d3a3 + .quad 0x4a75479abd64e097 + .quad 0x249214109d5d1c88 endconst const error_message_fpscr - .asciz "failed to preserve register FPSCR, changed bits: %x" + .asciz "failed to preserve register FPSCR, changed bits: %x" error_message_gpr: - .asciz "failed to preserve register r%d" + .asciz "failed to preserve register r%d" error_message_vfp: - .asciz "failed to preserve register d%d" + .asciz "failed to preserve register d%d" +error_message_stack: + .asciz "failed to preserve stack" endconst @ max number of args used by any asm function. @@ -52,113 +54,140 @@ endconst #define ARG_STACK 4*(MAX_ARGS - 4) -@ align the used stack space to 8 to preserve the stack alignment -#define ARG_STACK_A (((ARG_STACK + pushed + 7) & ~7) - pushed) +@ Align the used stack space to 8 to preserve the stack alignment. +@ +8 for stack canary reference. +#define ARG_STACK_A (((ARG_STACK + pushed + 7) & ~7) - pushed + 8) .macro clobbercheck variant .equ pushed, 4*9 function checkasm_checked_call_\variant, export=1 - push {r4-r11, lr} + push {r4-r11, lr} .ifc \variant, vfp - vpush {d8-d15} - fmrx r4, FPSCR - push {r4} + vpush {d8-d15} + fmrx r4, FPSCR + push {r4} .equ pushed, pushed + 16*4 + 4 .endif - movrel r12, register_init + movrel r12, register_init .ifc \variant, vfp - vldm r12, {d8-d15} + vldm r12, {d8-d15} .endif - ldm r12, {r4-r11} + ldm r12, {r4-r11} - sub sp, sp, #ARG_STACK_A + sub sp, sp, #ARG_STACK_A .equ pos, 0 .rept MAX_ARGS-4 - ldr r12, [sp, #ARG_STACK_A + pushed + 8 + pos] - str r12, [sp, #pos] + ldr r12, [sp, #ARG_STACK_A + pushed + 8 + pos] + str r12, [sp, #pos] .equ pos, pos + 4 .endr - mov r12, r0 - mov r0, r2 - mov r1, r3 - ldrd r2, r3, [sp, #ARG_STACK_A + pushed] - blx r12 - add sp, sp, #ARG_STACK_A - - push {r0, r1} - movrel r12, register_init + @ For stack overflows, the callee is free to overwrite the parameters + @ that were passed on the stack (if any), so we can only check after + @ that point. First figure out how many parameters the function + @ really took on the stack: + ldr r12, [sp, #ARG_STACK_A + pushed + 8 + 4*(MAX_ARGS-4)] + @ Load the first non-parameter value from the stack, that should be + @ left untouched by the function. Store a copy of it inverted, so that + @ e.g. overwriting everything with zero would be noticed. + ldr r12, [sp, r12, lsl #2] + mvn r12, r12 + str r12, [sp, #ARG_STACK_A - 4] + + mov r12, r0 + mov r0, r2 + mov r1, r3 + ldrd r2, r3, [sp, #ARG_STACK_A + pushed] + @ Call the target function + blx r12 + + @ Load the number of stack parameters, stack canary and its reference + ldr r12, [sp, #ARG_STACK_A + pushed + 8 + 4*(MAX_ARGS-4)] + ldr r2, [sp, r12, lsl #2] + ldr r3, [sp, #ARG_STACK_A - 4] + + add sp, sp, #ARG_STACK_A + push {r0, r1} + + mvn r3, r3 + cmp r2, r3 + bne 5f + + movrel r12, register_init .ifc \variant, vfp .macro check_reg_vfp, dreg, offset - ldrd r2, r3, [r12, #8 * (\offset)] - vmov r0, lr, \dreg - eor r2, r2, r0 - eor r3, r3, lr - orrs r2, r2, r3 - bne 4f + ldrd r2, r3, [r12, #8 * (\offset)] + vmov r0, lr, \dreg + eor r2, r2, r0 + eor r3, r3, lr + orrs r2, r2, r3 + bne 4f .endm .irp n, 8, 9, 10, 11, 12, 13, 14, 15 - @ keep track of the checked double/SIMD register - mov r1, #\n - check_reg_vfp d\n, \n-8 + @ keep track of the checked double/SIMD register + mov r1, #\n + check_reg_vfp d\n, \n-8 .endr .purgem check_reg_vfp - fmrx r1, FPSCR - ldr r3, [sp, #8] - eor r1, r1, r3 - @ Ignore changes in bits 0-4 and 7 - bic r1, r1, #0x9f - @ Ignore changes in the topmost 5 bits - bics r1, r1, #0xf8000000 - bne 3f + fmrx r1, FPSCR + ldr r3, [sp, #8] + eor r1, r1, r3 + @ Ignore changes in bits 0-4 and 7 + bic r1, r1, #0x9f + @ Ignore changes in the topmost 5 bits + bics r1, r1, #0xf8000000 + bne 3f .endif - @ keep track of the checked GPR - mov r1, #4 + @ keep track of the checked GPR + mov r1, #4 .macro check_reg reg1, reg2= - ldrd r2, r3, [r12], #8 - eors r2, r2, \reg1 - bne 2f - add r1, r1, #1 + ldrd r2, r3, [r12], #8 + eors r2, r2, \reg1 + bne 2f + add r1, r1, #1 .ifnb \reg2 - eors r3, r3, \reg2 - bne 2f + eors r3, r3, \reg2 + bne 2f .endif - add r1, r1, #1 + add r1, r1, #1 .endm - check_reg r4, r5 - check_reg r6, r7 + check_reg r4, r5 + check_reg r6, r7 @ r9 is a volatile register in the ios ABI #ifdef __APPLE__ - check_reg r8 + check_reg r8 #else - check_reg r8, r9 + check_reg r8, r9 #endif - check_reg r10, r11 + check_reg r10, r11 .purgem check_reg - b 0f + b 0f +5: + movrel r0, error_message_stack + b 1f 4: - movrel r0, error_message_vfp - b 1f + movrel r0, error_message_vfp + b 1f 3: - movrel r0, error_message_fpscr - b 1f + movrel r0, error_message_fpscr + b 1f 2: - movrel r0, error_message_gpr + movrel r0, error_message_gpr 1: - blx X(checkasm_fail_func) + bl X(checkasm_fail_func) 0: - pop {r0, r1} + pop {r0, r1} .ifc \variant, vfp - pop {r2} - fmxr FPSCR, r2 - vpop {d8-d15} + pop {r2} + fmxr FPSCR, r2 + vpop {d8-d15} .endif - pop {r4-r11, pc} + pop {r4-r11, pc} endfunc .endm diff --git a/tests/checkasm/checkasm.c b/tests/checkasm/checkasm.c index 3e2ec377bec..899f68bb323 100644 --- a/tests/checkasm/checkasm.c +++ b/tests/checkasm/checkasm.c @@ -42,7 +42,7 @@ #include #endif -#if HAVE_SETCONSOLETEXTATTRIBUTE +#if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE #include #define COLOR_RED FOREGROUND_RED #define COLOR_GREEN FOREGROUND_GREEN @@ -130,6 +130,9 @@ static const struct { #if CONFIG_LLVIDENCDSP { "llviddspenc", checkasm_check_llviddspenc }, #endif + #if CONFIG_OPUS_DECODER + { "opusdsp", checkasm_check_opusdsp }, + #endif #if CONFIG_PIXBLOCKDSP { "pixblockdsp", checkasm_check_pixblockdsp }, #endif @@ -162,6 +165,9 @@ static const struct { #if CONFIG_COLORSPACE_FILTER { "vf_colorspace", checkasm_check_colorspace }, #endif + #if CONFIG_EQ_FILTER + { "vf_eq", checkasm_check_vf_eq }, + #endif #if CONFIG_GBLUR_FILTER { "vf_gblur", checkasm_check_vf_gblur }, #endif @@ -177,6 +183,7 @@ static const struct { #endif #if CONFIG_SWSCALE { "sw_rgb", checkasm_check_sw_rgb }, + { "sw_scale", checkasm_check_sw_scale }, #endif #if CONFIG_AVUTIL { "fixed_dsp", checkasm_check_fixed_dsp }, @@ -262,6 +269,7 @@ static struct { int cpu_flag; const char *cpu_flag_name; const char *test_name; + int verbose; } state; /* PRNG state */ @@ -368,7 +376,7 @@ static void color_printf(int color, const char *fmt, ...) static int use_color = -1; va_list arg; -#if HAVE_SETCONSOLETEXTATTRIBUTE +#if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE static HANDLE con; static WORD org_attributes; @@ -397,7 +405,7 @@ static void color_printf(int color, const char *fmt, ...) va_end(arg); if (use_color) { -#if HAVE_SETCONSOLETEXTATTRIBUTE +#if HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE SetConsoleTextAttribute(con, org_attributes); #else fprintf(stderr, "\x1b[0m"); @@ -680,6 +688,8 @@ int main(int argc, char *argv[]) state.bench_pattern = ""; } else if (!strncmp(argv[1], "--test=", 7)) { state.test_name = argv[1] + 7; + } else if (!strcmp(argv[1], "--verbose") || !strcmp(argv[1], "-v")) { + state.verbose = 1; } else { seed = strtoul(argv[1], NULL, 10); } @@ -830,3 +840,42 @@ void checkasm_report(const char *name, ...) max_length = length; } } + +#define DEF_CHECKASM_CHECK_FUNC(type, fmt) \ +int checkasm_check_##type(const char *const file, const int line, \ + const type *buf1, ptrdiff_t stride1, \ + const type *buf2, ptrdiff_t stride2, \ + const int w, int h, const char *const name) \ +{ \ + int y = 0; \ + stride1 /= sizeof(*buf1); \ + stride2 /= sizeof(*buf2); \ + for (y = 0; y < h; y++) \ + if (memcmp(&buf1[y*stride1], &buf2[y*stride2], w*sizeof(*buf1))) \ + break; \ + if (y == h) \ + return 0; \ + checkasm_fail_func("%s:%d", file, line); \ + if (!state.verbose) \ + return 1; \ + fprintf(stderr, "%s:\n", name); \ + while (h--) { \ + for (int x = 0; x < w; x++) \ + fprintf(stderr, " " fmt, buf1[x]); \ + fprintf(stderr, " "); \ + for (int x = 0; x < w; x++) \ + fprintf(stderr, " " fmt, buf2[x]); \ + fprintf(stderr, " "); \ + for (int x = 0; x < w; x++) \ + fprintf(stderr, "%c", buf1[x] != buf2[x] ? 'x' : '.'); \ + buf1 += stride1; \ + buf2 += stride2; \ + fprintf(stderr, "\n"); \ + } \ + return 1; \ +} + +DEF_CHECKASM_CHECK_FUNC(uint8_t, "%02x") +DEF_CHECKASM_CHECK_FUNC(uint16_t, "%04x") +DEF_CHECKASM_CHECK_FUNC(int16_t, "%6d") +DEF_CHECKASM_CHECK_FUNC(int32_t, "%9d") diff --git a/tests/checkasm/checkasm.h b/tests/checkasm/checkasm.h index aed15b5fa4a..0190bc912c1 100644 --- a/tests/checkasm/checkasm.h +++ b/tests/checkasm/checkasm.h @@ -64,13 +64,16 @@ void checkasm_check_jpeg2000dsp(void); void checkasm_check_llviddsp(void); void checkasm_check_llviddspenc(void); void checkasm_check_nlmeans(void); +void checkasm_check_opusdsp(void); void checkasm_check_pixblockdsp(void); void checkasm_check_sbrdsp(void); void checkasm_check_synth_filter(void); void checkasm_check_sw_rgb(void); +void checkasm_check_sw_scale(void); void checkasm_check_utvideodsp(void); void checkasm_check_v210dec(void); void checkasm_check_v210enc(void); +void checkasm_check_vf_eq(void); void checkasm_check_vf_gblur(void); void checkasm_check_vf_hflip(void); void checkasm_check_vf_threshold(void); @@ -174,17 +177,22 @@ void checkasm_stack_clobber(uint64_t clobber, ...); void checkasm_checked_call_vfp(void *func, int dummy, ...); void checkasm_checked_call_novfp(void *func, int dummy, ...); extern void (*checkasm_checked_call)(void *func, int dummy, ...); -#define declare_new(ret, ...) ret (*checked_call)(void *, int dummy, __VA_ARGS__) = (void *)checkasm_checked_call; -#define call_new(...) checked_call(func_new, 0, __VA_ARGS__) +#define declare_new(ret, ...) ret (*checked_call)(void *, int dummy, __VA_ARGS__, \ + int, int, int, int, int, int, int, int, \ + int, int, int, int, int, int, int) = (void *)checkasm_checked_call; +#define call_new(...) checked_call(func_new, 0, __VA_ARGS__, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0) #elif ARCH_AARCH64 && !defined(__APPLE__) void checkasm_stack_clobber(uint64_t clobber, ...); void checkasm_checked_call(void *func, ...); -#define declare_new(ret, ...) ret (*checked_call)(void *, int, int, int, int, int, int, int, __VA_ARGS__)\ +#define declare_new(ret, ...) ret (*checked_call)(void *, int, int, int, int, int, int, int, __VA_ARGS__,\ + int, int, int, int, int, int, int, int,\ + int, int, int, int, int, int, int)\ = (void *)checkasm_checked_call; #define CLOB (UINT64_C(0xdeadbeefdeadbeef)) #define call_new(...) (checkasm_stack_clobber(CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,\ CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB),\ - checked_call(func_new, 0, 0, 0, 0, 0, 0, 0, __VA_ARGS__)) + checked_call(func_new, 0, 0, 0, 0, 0, 0, 0, __VA_ARGS__,\ + 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0)) #else #define declare_new(ret, ...) #define declare_new_float(ret, ...) @@ -255,4 +263,20 @@ typedef struct CheckasmPerf { #define PERF_STOP(t) while(0) #endif +#define DECL_CHECKASM_CHECK_FUNC(type) \ +int checkasm_check_##type(const char *const file, const int line, \ + const type *const buf1, const ptrdiff_t stride1, \ + const type *const buf2, const ptrdiff_t stride2, \ + const int w, const int h, const char *const name) + +DECL_CHECKASM_CHECK_FUNC(uint8_t); +DECL_CHECKASM_CHECK_FUNC(uint16_t); +DECL_CHECKASM_CHECK_FUNC(int16_t); +DECL_CHECKASM_CHECK_FUNC(int32_t); + +#define PASTE(a,b) a ## b +#define CONCAT(a,b) PASTE(a,b) + +#define checkasm_check(prefix, ...) CONCAT(checkasm_check_, prefix)(__FILE__, __LINE__, __VA_ARGS__) + #endif /* TESTS_CHECKASM_CHECKASM_H */ diff --git a/tests/checkasm/float_dsp.c b/tests/checkasm/float_dsp.c index 2abe4eccbde..a1616a61a8b 100644 --- a/tests/checkasm/float_dsp.c +++ b/tests/checkasm/float_dsp.c @@ -51,7 +51,8 @@ static void test_vector_fmul(const float *src0, const float *src1) call_ref(cdst, src0, src1, LEN); call_new(odst, src0, src1, LEN); for (i = 0; i < LEN; i++) { - if (!float_near_abs_eps(cdst[i], odst[i], FLT_EPSILON)) { + double t = fabs(src0[i]) + fabs(src1[i]) + fabs(src0[i] * src1[i]) + 1.0; + if (!float_near_abs_eps(cdst[i], odst[i], t * 2 * FLT_EPSILON)) { fprintf(stderr, "%d: %- .12f - %- .12f = % .12g\n", i, cdst[i], odst[i], cdst[i] - odst[i]); fail(); @@ -73,7 +74,8 @@ static void test_vector_dmul(const double *src0, const double *src1) call_ref(cdst, src0, src1, LEN); call_new(odst, src0, src1, LEN); for (i = 0; i < LEN; i++) { - if (!double_near_abs_eps(cdst[i], odst[i], DBL_EPSILON)) { + double t = fabs(src0[i]) + fabs(src1[i]) + fabs(src0[i] * src1[i]) + 1.0; + if (!double_near_abs_eps(cdst[i], odst[i], t * 2 * DBL_EPSILON)) { fprintf(stderr, "%d: %- .12f - %- .12f = % .12g\n", i, cdst[i], odst[i], cdst[i] - odst[i]); fail(); @@ -117,7 +119,8 @@ static void test_vector_fmul_scalar(const float *src0, const float *src1) call_ref(cdst, src0, src1[0], LEN); call_new(odst, src0, src1[0], LEN); for (i = 0; i < LEN; i++) { - if (!float_near_abs_eps(cdst[i], odst[i], FLT_EPSILON)) { + double t = fabs(src0[i]) + fabs(src1[0]) + fabs(src0[i] * src1[0]) + 1.0; + if (!float_near_abs_eps(cdst[i], odst[i], t * 2 * FLT_EPSILON)) { fprintf(stderr, "%d: %- .12f - %- .12f = % .12g\n", i, cdst[i], odst[i], cdst[i] - odst[i]); fail(); diff --git a/tests/checkasm/hevc_add_res.c b/tests/checkasm/hevc_add_res.c index e92c6b427b6..0a3bcbb7065 100644 --- a/tests/checkasm/hevc_add_res.c +++ b/tests/checkasm/hevc_add_res.c @@ -42,31 +42,42 @@ AV_WN16A(buf + j * 2, rnd() & 0x3FF); \ } while (0) -static void check_add_res(HEVCDSPContext h, int bit_depth) +static void compare_add_res(int size, ptrdiff_t stride, int overflow_test) { - int i; LOCAL_ALIGNED_32(int16_t, res0, [32 * 32]); LOCAL_ALIGNED_32(int16_t, res1, [32 * 32]); LOCAL_ALIGNED_32(uint8_t, dst0, [32 * 32 * 2]); LOCAL_ALIGNED_32(uint8_t, dst1, [32 * 32 * 2]); + declare_func_emms(AV_CPU_FLAG_MMX, void, uint8_t *dst, int16_t *res, ptrdiff_t stride); + + randomize_buffers(res0, size); + randomize_buffers2(dst0, size); + if (overflow_test) + res0[0] = 0x8000; + memcpy(res1, res0, sizeof(*res0) * size); + memcpy(dst1, dst0, sizeof(int16_t) * size); + + call_ref(dst0, res0, stride); + call_new(dst1, res1, stride); + if (memcmp(dst0, dst1, size)) + fail(); + bench_new(dst1, res1, stride); +} + +static void check_add_res(HEVCDSPContext h, int bit_depth) +{ + int i; + for (i = 2; i <= 5; i++) { int block_size = 1 << i; int size = block_size * block_size; ptrdiff_t stride = block_size << (bit_depth > 8); - declare_func_emms(AV_CPU_FLAG_MMX, void, uint8_t *dst, int16_t *res, ptrdiff_t stride); - - randomize_buffers(res0, size); - randomize_buffers2(dst0, size); - memcpy(res1, res0, sizeof(*res0) * size); - memcpy(dst1, dst0, sizeof(int16_t) * size); if (check_func(h.add_residual[i - 2], "hevc_add_res_%dx%d_%d", block_size, block_size, bit_depth)) { - call_ref(dst0, res0, stride); - call_new(dst1, res1, stride); - if (memcmp(dst0, dst1, size)) - fail(); - bench_new(dst1, res1, stride); + compare_add_res(size, stride, 0); + // overflow test for res = -32768 + compare_add_res(size, stride, 1); } } } diff --git a/tests/checkasm/opusdsp.c b/tests/checkasm/opusdsp.c new file mode 100644 index 00000000000..828ecf9ce2b --- /dev/null +++ b/tests/checkasm/opusdsp.c @@ -0,0 +1,103 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "libavcodec/opusdsp.h" + +#include "checkasm.h" + +#define randomize_float(buf, len) \ + do { \ + for (int i = 0; i < len; i++) { \ + float f = (float)rnd() / (UINT_MAX >> 5) - 16.0f; \ + buf[i] = f; \ + } \ + } while (0) + +#define EPS 0.005 +#define MAX_SIZE (960) + +/* period is between 15 and 1022, inclusive */ +static void test_postfilter(int period) +{ + LOCAL_ALIGNED(16, float, data0, [MAX_SIZE + 1024]); + LOCAL_ALIGNED(16, float, data1, [MAX_SIZE + 1024]); + + /* This filter can explode very easily, so use a tapset from the codec. + * In the codec these are usually multiplied by at least 0.09375f, + * so its outside the largest filter value, but the filter is still stable + * so use it. */ + float gains[3] = { 0.3066406250f, 0.2170410156f, 0.1296386719f }; + + /* The codec will always call with an offset which is aligned once + * (period + 2) is subtracted, but here we have to align it outselves. */ + int offset = FFALIGN(period + 2, 4); + + declare_func(void, float *data, int period, float *gains, int len); + + randomize_float(data0, MAX_SIZE + 1024); + memcpy(data1, data0, (MAX_SIZE + 1024)*sizeof(float)); + + call_ref(data0 + offset, period, gains, MAX_SIZE); + call_new(data1 + offset, period, gains, MAX_SIZE); + + if (!float_near_abs_eps_array(data0 + offset, data1 + offset, EPS, MAX_SIZE)) + fail(); + bench_new(data1 + offset, period, gains, MAX_SIZE); +} + +static void test_deemphasis(void) +{ + LOCAL_ALIGNED(16, float, src, [FFALIGN(MAX_SIZE, 4)]); + LOCAL_ALIGNED(16, float, dst0, [FFALIGN(MAX_SIZE, 4)]); + LOCAL_ALIGNED(16, float, dst1, [FFALIGN(MAX_SIZE, 4)]); + float coeff0 = (float)rnd() / (UINT_MAX >> 5) - 16.0f, coeff1 = coeff0; + + declare_func_float(float, float *out, float *in, float coeff, int len); + + randomize_float(src, MAX_SIZE); + + coeff0 = call_ref(dst0, src, coeff0, MAX_SIZE); + coeff1 = call_new(dst1, src, coeff1, MAX_SIZE); + + if (!float_near_abs_eps(coeff0, coeff1, EPS) || + !float_near_abs_eps_array(dst0, dst1, EPS, MAX_SIZE)) + fail(); + bench_new(dst1, src, coeff1, MAX_SIZE); +} + +void checkasm_check_opusdsp(void) +{ + OpusDSP ctx; + ff_opus_dsp_init(&ctx); + + if (check_func(ctx.postfilter, "postfilter_15")) + test_postfilter(15); + report("postfilter_15"); + + if (check_func(ctx.postfilter, "postfilter_512")) + test_postfilter(512); + report("postfilter_512"); + + if (check_func(ctx.postfilter, "postfilter_1022")) + test_postfilter(1022); + report("postfilter_1022"); + + if (check_func(ctx.deemphasis, "deemphasis")) + test_deemphasis(); + report("deemphasis"); +} diff --git a/tests/checkasm/pixblockdsp.c b/tests/checkasm/pixblockdsp.c index e14b0a90ded..79ab156d45d 100644 --- a/tests/checkasm/pixblockdsp.c +++ b/tests/checkasm/pixblockdsp.c @@ -44,13 +44,13 @@ } \ } while (0) -#define check_get_pixels(type) \ +#define check_get_pixels(type, aligned) \ do { \ int i; \ declare_func_emms(AV_CPU_FLAG_MMX, void, int16_t *block, const uint8_t *pixels, ptrdiff_t line_size); \ \ for (i = 0; i < BUF_UNITS; i++) { \ - int src_offset = i * 64 * sizeof(type) + 8 * i; /* Test various alignments */ \ + int src_offset = i * 64 * sizeof(type) + (aligned ? 8 : 1) * i; \ int dst_offset = i * 64; /* dst must be aligned */ \ randomize_buffers(); \ call_ref(dst0 + dst_offset, src10 + src_offset, 8); \ @@ -61,13 +61,13 @@ } \ } while (0) -#define check_diff_pixels(type) \ +#define check_diff_pixels(type, aligned) \ do { \ int i; \ declare_func_emms(AV_CPU_FLAG_MMX, void, int16_t *av_restrict block, const uint8_t *s1, const uint8_t *s2, ptrdiff_t stride); \ \ for (i = 0; i < BUF_UNITS; i++) { \ - int src_offset = i * 64 * sizeof(type) + 8 * i; /* Test various alignments */ \ + int src_offset = i * 64 * sizeof(type) + (aligned ? 8 : 1) * i; \ int dst_offset = i * 64; /* dst must be aligned */ \ randomize_buffers(); \ call_ref(dst0 + dst_offset, src10 + src_offset, src20 + src_offset, 8); \ @@ -96,12 +96,16 @@ void checkasm_check_pixblockdsp(void) ff_pixblockdsp_init(&h, &avctx); if (check_func(h.get_pixels, "get_pixels")) - check_get_pixels(uint8_t); + check_get_pixels(uint8_t, 1); + if (check_func(h.get_pixels_unaligned, "get_pixels_unaligned")) + check_get_pixels(uint8_t, 0); report("get_pixels"); if (check_func(h.diff_pixels, "diff_pixels")) - check_diff_pixels(uint8_t); + check_diff_pixels(uint8_t, 1); + if (check_func(h.diff_pixels_unaligned, "diff_pixels_unaligned")) + check_diff_pixels(uint8_t, 0); report("diff_pixels"); } diff --git a/tests/checkasm/sbrdsp.c b/tests/checkasm/sbrdsp.c index 558f452c9bd..516b9f0ec6f 100644 --- a/tests/checkasm/sbrdsp.c +++ b/tests/checkasm/sbrdsp.c @@ -17,6 +17,7 @@ */ #include "libavcodec/sbrdsp.h" +#include #include "checkasm.h" @@ -51,13 +52,14 @@ static void test_sum_square(void) INTFLOAT res0; INTFLOAT res1; LOCAL_ALIGNED_16(INTFLOAT, src, [256], [2]); + double t = 4 * 256; declare_func_float(INTFLOAT, INTFLOAT (*x)[2], int n); randomize((INTFLOAT *)src, 256 * 2); res0 = call_ref(src, 256); res1 = call_new(src, 256); - if (!float_near_abs_eps(res0, res1, EPS)) + if (!float_near_abs_eps(res0, res1, t * 2 * FLT_EPSILON)) fail(); bench_new(src, 256); } diff --git a/tests/checkasm/sw_rgb.c b/tests/checkasm/sw_rgb.c index 000420d8f7a..e5aad20c6da 100644 --- a/tests/checkasm/sw_rgb.c +++ b/tests/checkasm/sw_rgb.c @@ -111,6 +111,74 @@ static void check_uyvy_to_422p(void) } } +static void check_interleave_bytes(void) +{ + LOCAL_ALIGNED_16(uint8_t, src0_buf, [MAX_STRIDE*MAX_HEIGHT+1]); + LOCAL_ALIGNED_16(uint8_t, src1_buf, [MAX_STRIDE*MAX_HEIGHT+1]); + LOCAL_ALIGNED_16(uint8_t, dst0_buf, [2*MAX_STRIDE*MAX_HEIGHT+2]); + LOCAL_ALIGNED_16(uint8_t, dst1_buf, [2*MAX_STRIDE*MAX_HEIGHT+2]); + // Intentionally using unaligned buffers, as this function doesn't have + // any alignment requirements. + uint8_t *src0 = src0_buf + 1; + uint8_t *src1 = src1_buf + 1; + uint8_t *dst0 = dst0_buf + 2; + uint8_t *dst1 = dst1_buf + 2; + + declare_func_emms(AV_CPU_FLAG_MMX, void, const uint8_t *, const uint8_t *, + uint8_t *, int, int, int, int, int); + + randomize_buffers(src0, MAX_STRIDE * MAX_HEIGHT); + randomize_buffers(src1, MAX_STRIDE * MAX_HEIGHT); + + if (check_func(interleaveBytes, "interleave_bytes")) { + for (int i = 0; i <= 16; i++) { + // Try all widths [1,16], and try one random width. + + int w = i > 0 ? i : (1 + (rnd() % (MAX_STRIDE-2))); + int h = 1 + (rnd() % (MAX_HEIGHT-2)); + + int src0_offset = 0, src0_stride = MAX_STRIDE; + int src1_offset = 0, src1_stride = MAX_STRIDE; + int dst_offset = 0, dst_stride = 2 * MAX_STRIDE; + + memset(dst0, 0, 2 * MAX_STRIDE * MAX_HEIGHT); + memset(dst1, 0, 2 * MAX_STRIDE * MAX_HEIGHT); + + // Try different combinations of negative strides + if (i & 1) { + src0_offset = (h-1)*src0_stride; + src0_stride = -src0_stride; + } + if (i & 2) { + src1_offset = (h-1)*src1_stride; + src1_stride = -src1_stride; + } + if (i & 4) { + dst_offset = (h-1)*dst_stride; + dst_stride = -dst_stride; + } + + call_ref(src0 + src0_offset, src1 + src1_offset, dst0 + dst_offset, + w, h, src0_stride, src1_stride, dst_stride); + call_new(src0 + src0_offset, src1 + src1_offset, dst1 + dst_offset, + w, h, src0_stride, src1_stride, dst_stride); + // Check a one pixel-pair edge around the destination area, + // to catch overwrites past the end. + checkasm_check(uint8_t, dst0, 2*MAX_STRIDE, dst1, 2*MAX_STRIDE, + 2 * w + 2, h + 1, "dst"); + } + + bench_new(src0, src1, dst1, 127, MAX_HEIGHT, + MAX_STRIDE, MAX_STRIDE, 2*MAX_STRIDE); + } + if (check_func(interleaveBytes, "interleave_bytes_aligned")) { + // Bench the function in a more typical case, with aligned + // buffers and widths. + bench_new(src0_buf, src1_buf, dst1_buf, 128, MAX_HEIGHT, + MAX_STRIDE, MAX_STRIDE, 2*MAX_STRIDE); + } +} + void checkasm_check_sw_rgb(void) { ff_sws_rgb2rgb_init(); @@ -132,4 +200,7 @@ void checkasm_check_sw_rgb(void) check_uyvy_to_422p(); report("uyvytoyuv422"); + + check_interleave_bytes(); + report("interleave_bytes"); } diff --git a/tests/checkasm/sw_scale.c b/tests/checkasm/sw_scale.c new file mode 100644 index 00000000000..9efa2b4defc --- /dev/null +++ b/tests/checkasm/sw_scale.c @@ -0,0 +1,134 @@ +/* + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include "libavutil/common.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/mem.h" + +#include "libswscale/swscale.h" +#include "libswscale/swscale_internal.h" + +#include "checkasm.h" + +#define randomize_buffers(buf, size) \ + do { \ + int j; \ + for (j = 0; j < size; j+=4) \ + AV_WN32(buf + j, rnd()); \ + } while (0) + +#define SRC_PIXELS 128 + +static void check_hscale(void) +{ +#define MAX_FILTER_WIDTH 40 +#define FILTER_SIZES 5 + static const int filter_sizes[FILTER_SIZES] = { 4, 8, 16, 32, 40 }; + +#define HSCALE_PAIRS 2 + static const int hscale_pairs[HSCALE_PAIRS][2] = { + { 8, 14 }, + { 8, 18 }, + }; + + int i, j, fsi, hpi, width; + struct SwsContext *ctx; + + // padded + LOCAL_ALIGNED_32(uint8_t, src, [FFALIGN(SRC_PIXELS + MAX_FILTER_WIDTH - 1, 4)]); + LOCAL_ALIGNED_32(uint32_t, dst0, [SRC_PIXELS]); + LOCAL_ALIGNED_32(uint32_t, dst1, [SRC_PIXELS]); + + // padded + LOCAL_ALIGNED_32(int16_t, filter, [SRC_PIXELS * MAX_FILTER_WIDTH + MAX_FILTER_WIDTH]); + LOCAL_ALIGNED_32(int32_t, filterPos, [SRC_PIXELS]); + + // The dst parameter here is either int16_t or int32_t but we use void* to + // just cover both cases. + declare_func_emms(AV_CPU_FLAG_MMX, void, void *c, void *dst, int dstW, + const uint8_t *src, const int16_t *filter, + const int32_t *filterPos, int filterSize); + + ctx = sws_alloc_context(); + if (sws_init_context(ctx, NULL, NULL) < 0) + fail(); + + randomize_buffers(src, SRC_PIXELS + MAX_FILTER_WIDTH - 1); + + for (hpi = 0; hpi < HSCALE_PAIRS; hpi++) { + for (fsi = 0; fsi < FILTER_SIZES; fsi++) { + width = filter_sizes[fsi]; + + ctx->srcBpc = hscale_pairs[hpi][0]; + ctx->dstBpc = hscale_pairs[hpi][1]; + ctx->hLumFilterSize = ctx->hChrFilterSize = width; + + for (i = 0; i < SRC_PIXELS; i++) { + filterPos[i] = i; + + // These filter cofficients are chosen to try break two corner + // cases, namely: + // + // - Negative filter coefficients. The filters output signed + // values, and it should be possible to end up with negative + // output values. + // + // - Positive clipping. The hscale filter function has clipping + // at (1<<15) - 1 + // + // The coefficients sum to the 1.0 point for the hscale + // functions (1 << 14). + + for (j = 0; j < width; j++) { + filter[i * width + j] = -((1 << 14) / (width - 1)); + } + filter[i * width + (rnd() % width)] = ((1 << 15) - 1); + } + + for (i = 0; i < MAX_FILTER_WIDTH; i++) { + // These values should be unused in SIMD implementations but + // may still be read, random coefficients here should help show + // issues where they are used in error. + + filter[SRC_PIXELS * width + i] = rnd(); + } + ff_getSwsFunc(ctx); + + if (check_func(ctx->hcScale, "hscale_%d_to_%d_width%d", ctx->srcBpc, ctx->dstBpc + 1, width)) { + memset(dst0, 0, SRC_PIXELS * sizeof(dst0[0])); + memset(dst1, 0, SRC_PIXELS * sizeof(dst1[0])); + + call_ref(NULL, dst0, SRC_PIXELS, src, filter, filterPos, width); + call_new(NULL, dst1, SRC_PIXELS, src, filter, filterPos, width); + if (memcmp(dst0, dst1, SRC_PIXELS * sizeof(dst0[0]))) + fail(); + bench_new(NULL, dst0, SRC_PIXELS, src, filter, filterPos, width); + } + } + } + sws_freeContext(ctx); +} + +void checkasm_check_sw_scale(void) +{ + check_hscale(); + report("hscale"); +} diff --git a/tests/checkasm/vf_eq.c b/tests/checkasm/vf_eq.c new file mode 100644 index 00000000000..48dccddf779 --- /dev/null +++ b/tests/checkasm/vf_eq.c @@ -0,0 +1,79 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include "checkasm.h" +#include "libavfilter/avfilter.h" +#include "libavfilter/vf_eq.h" +#include "libavutil/intreadwrite.h" + +#define WIDTH 256 +#define HEIGHT 256 +#define SRC_STRIDE 256 +#define PIXELS (WIDTH * HEIGHT) +#define RANDOM_RANGE 80000 +#define SCALE 10000 + +#define randomize_buffers(buf, size) \ + do { \ + int j; \ + uint8_t *tmp_buf = (uint8_t *)buf;\ + for (j = 0; j< size; j++) \ + tmp_buf[j] = rnd() & 0xFF; \ + } while (0) + +static void check_eq(void) +{ + LOCAL_ALIGNED_32(uint8_t, src, [PIXELS]); + LOCAL_ALIGNED_32(uint8_t, dst_ref, [PIXELS]); + LOCAL_ALIGNED_32(uint8_t, dst_new, [PIXELS]); + int w = WIDTH; + int h = HEIGHT; + int src_stride = SRC_STRIDE; + int dst_stride = SRC_STRIDE; + EQParameters pa; + EQContext eq; + declare_func(void, EQParameters *param, uint8_t *dst, int dst_stride, + const uint8_t *src, int src_stride, int w, int h); + + double rand_contrast = (int)(rnd() % (RANDOM_RANGE * 2) - RANDOM_RANGE) / + (SCALE * 1.0); + double rand_brightness = (int)(rnd() % (SCALE * 2) - SCALE) / + (SCALE * 1.0); + pa.contrast = rand_contrast; + pa.brightness = rand_brightness; + + memset(dst_ref, 0, PIXELS); + memset(dst_new, 0, PIXELS); + randomize_buffers(src, PIXELS); + ff_eq_init(&eq); + + if (check_func(eq.process, "process")) { + call_ref(&pa, dst_ref, dst_stride, src, src_stride, w, h); + call_new(&pa, dst_new, dst_stride, src, src_stride, w, h); + if (memcmp(dst_ref, dst_new, PIXELS)) + fail(); + bench_new(&pa, dst_new, dst_stride, src, src_stride, w, h); + } +} + +void checkasm_check_vf_eq(void) +{ + check_eq(); + report("eq"); +} diff --git a/tests/dnn/.gitignore b/tests/dnn/.gitignore new file mode 100644 index 00000000000..1fcd2410b47 --- /dev/null +++ b/tests/dnn/.gitignore @@ -0,0 +1,6 @@ +/dnn-layer-conv2d-test +/dnn-layer-depth2space-test +/dnn-layer-maximum-test +/dnn-layer-pad-test +/dnn-layer-mathbinary-test +/dnn-layer-mathunary-test diff --git a/tests/dnn/Makefile b/tests/dnn/Makefile new file mode 100644 index 00000000000..64591b7851c --- /dev/null +++ b/tests/dnn/Makefile @@ -0,0 +1,16 @@ +DNNTESTPROGS += dnn-layer-pad +DNNTESTPROGS += dnn-layer-conv2d +DNNTESTPROGS += dnn-layer-depth2space +DNNTESTPROGS += dnn-layer-mathbinary +DNNTESTPROGS += dnn-layer-maximum +DNNTESTPROGS += dnn-layer-mathunary + +DNNTESTOBJS := $(DNNTESTOBJS:%=$(DNNTESTSDIR)%) $(DNNTESTPROGS:%=$(DNNTESTSDIR)/%-test.o) +DNNTESTPROGS := $(DNNTESTPROGS:%=$(DNNTESTSDIR)/%-test$(EXESUF)) +-include $(wildcard $(DNNTESTOBJS:.o=.d)) + +$(DNNTESTPROGS): %$(EXESUF): %.o $(FF_STATIC_DEP_LIBS) + $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(filter %.o,$^) $(FF_STATIC_DEP_LIBS) $(EXTRALIBS-avcodec) $(EXTRALIBS-avfilter) $(EXTRALIBS-avformat) $(EXTRALIBS-avutil) $(EXTRALIBS-swresample) $(EXTRALIBS) + +testclean:: + $(RM) $(addprefix $(DNNTESTSDIR)/,$(CLEANSUFFIXES) *-test$(EXESUF)) diff --git a/tests/dnn/dnn-layer-conv2d-test.c b/tests/dnn/dnn-layer-conv2d-test.c new file mode 100644 index 00000000000..2da01e5372a --- /dev/null +++ b/tests/dnn/dnn-layer-conv2d-test.c @@ -0,0 +1,240 @@ +/* + * 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 }; + + 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; + dnn_execute_layer_conv2d(operands, input_indexes, 1, ¶ms); + + 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 }; + + 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; + dnn_execute_layer_conv2d(operands, input_indexes, 1, ¶ms); + + 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/tests/dnn/dnn-layer-depth2space-test.c b/tests/dnn/dnn-layer-depth2space-test.c new file mode 100644 index 00000000000..5225ec7b7ac --- /dev/null +++ b/tests/dnn/dnn-layer-depth2space-test.c @@ -0,0 +1,102 @@ +/* + * 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; + dnn_execute_layer_depth2space(operands, input_indexes, 1, ¶ms); + + 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/tests/dnn/dnn-layer-mathbinary-test.c b/tests/dnn/dnn-layer-mathbinary-test.c new file mode 100644 index 00000000000..e7f8f8557cc --- /dev/null +++ b/tests/dnn/dnn-layer-mathbinary-test.c @@ -0,0 +1,209 @@ +/* + * 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; + 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; + dnn_execute_layer_math_binary(operands, input_indexes, 1, ¶ms); + + 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; + dnn_execute_layer_math_binary(operands, input_indexes, 1, ¶ms); + + 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; + dnn_execute_layer_math_binary(operands, input_indexes, 2, ¶ms); + + 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; + + return 0; +} diff --git a/tests/dnn/dnn-layer-mathunary-test.c b/tests/dnn/dnn-layer-mathunary-test.c new file mode 100644 index 00000000000..f032ca0684a --- /dev/null +++ b/tests/dnn/dnn-layer-mathunary-test.c @@ -0,0 +1,81 @@ +/* + * 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; + 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*2*3] = { + -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] = 2; + operands[0].dims[3] = 3; + operands[1].data = NULL; + + input_indexes[0] = 0; + dnn_execute_layer_math_unary(operands, input_indexes, 1, ¶ms); + + output = operands[1].data; + for (int i = 0; i < sizeof(input) / sizeof(float); ++i) { + float expected_output = get_expected(input[i], op); + if(fabs(output[i] - expected_output) > EPS) { + 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; + return 0; +} diff --git a/tests/dnn/dnn-layer-maximum-test.c b/tests/dnn/dnn-layer-maximum-test.c new file mode 100644 index 00000000000..06daf64481a --- /dev/null +++ b/tests/dnn/dnn-layer-maximum-test.c @@ -0,0 +1,71 @@ +/* + * 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; + dnn_execute_layer_maximum(operands, input_indexes, 1, ¶ms); + + 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/tests/dnn/dnn-layer-pad-test.c b/tests/dnn/dnn-layer-pad-test.c new file mode 100644 index 00000000000..ea8c824d1e9 --- /dev/null +++ b/tests/dnn/dnn-layer-pad-test.c @@ -0,0 +1,239 @@ +/* + * 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; + dnn_execute_layer_pad(operands, input_indexes, 1, ¶ms); + + 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; + dnn_execute_layer_pad(operands, input_indexes, 1, ¶ms); + + 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; + dnn_execute_layer_pad(operands, input_indexes, 1, ¶ms); + + 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/tests/fate-run.sh b/tests/fate-run.sh index 2f1991da52b..002944b0105 100755 --- a/tests/fate-run.sh +++ b/tests/fate-run.sh @@ -154,7 +154,7 @@ md5pipe(){ md5(){ encfile="${outdir}/${test}.out" cleanfiles="$cleanfiles $encfile" - ffmpeg "$@" $encfile + ffmpeg "$@" $(target_path $encfile) do_md5sum $encfile | awk '{print $1}' } @@ -192,6 +192,7 @@ enc_dec(){ enc_opt=$4 dec_fmt=$5 dec_opt=$6 + ffprobe_opts=$9 encfile="${outdir}/${test}.${enc_fmt}" decfile="${outdir}/${test}.out.${dec_fmt}" cleanfiles="$cleanfiles $decfile" @@ -207,6 +208,8 @@ enc_dec(){ -f $dec_fmt -y $tdecfile || return do_md5sum $decfile tests/tiny_psnr${HOSTEXECSUF} $srcfile $decfile $cmp_unit $cmp_shift + test -z $ffprobe_opts || \ + run ffprobe${PROGSUF}${EXECSUF} $ffprobe_opts -v 0 $tencfile || return } transcode(){ @@ -215,16 +218,19 @@ transcode(){ enc_fmt=$3 enc_opt=$4 final_decode=$5 + ffprobe_opts=$7 encfile="${outdir}/${test}.${enc_fmt}" - test "$7" = -keep || cleanfiles="$cleanfiles $encfile" + test "$6" = -keep || cleanfiles="$cleanfiles $encfile" tsrcfile=$(target_path $srcfile) tencfile=$(target_path $encfile) ffmpeg -f $src_fmt $DEC_OPTS -i $tsrcfile $ENC_OPTS $enc_opt $FLAGS \ -f $enc_fmt -y $tencfile || return do_md5sum $encfile echo $(wc -c $encfile) - ffmpeg $DEC_OPTS -i $encfile $ENC_OPTS $FLAGS $final_decode \ + ffmpeg $DEC_OPTS -i $tencfile $ENC_OPTS $FLAGS $final_decode \ -f framecrc - || return + test -z $ffprobe_opts || \ + run ffprobe${PROGSUF}${EXECSUF} $ffprobe_opts -v 0 $tencfile || return } stream_remux(){ @@ -233,14 +239,17 @@ stream_remux(){ enc_fmt=$3 stream_maps=$4 final_decode=$5 + ffprobe_opts=$7 encfile="${outdir}/${test}.${enc_fmt}" - test "$7" = -keep || cleanfiles="$cleanfiles $encfile" + test "$6" = -keep || cleanfiles="$cleanfiles $encfile" tsrcfile=$(target_path $srcfile) tencfile=$(target_path $encfile) ffmpeg -f $src_fmt -i $tsrcfile $stream_maps -codec copy $FLAGS \ -f $enc_fmt -y $tencfile || return - ffmpeg $DEC_OPTS -i $encfile $ENC_OPTS $FLAGS $final_decode \ + ffmpeg $DEC_OPTS -i $tencfile $ENC_OPTS $FLAGS $final_decode \ -f framecrc - || return + test -z $ffprobe_opts || \ + run ffprobe${PROGSUF}${EXECSUF} $ffprobe_opts -v 0 $tencfile || return } # FIXME: There is a certain duplication between the avconv-related helper @@ -296,7 +305,7 @@ lavf_container(){ outdir="tests/data/lavf" file=${outdir}/lavf.$t do_avconv $file $DEC_OPTS -f image2 -c:v pgmyuv -i $raw_src $DEC_OPTS -ar 44100 -f s16le $1 -i $pcm_src "$ENC_OPTS -metadata title=lavftest" -b:a 64k -t 1 -qscale:v 10 $2 - test $3 = "disable_crc" || + test "$3" = "disable_crc" || do_avconv_crc $file $DEC_OPTS -i $target_path/$file $3 } @@ -420,16 +429,16 @@ gapless(){ cleanfiles="$cleanfiles $decfile1 $decfile2 $decfile3" # test packet data - ffmpeg $extra_args -i "$sample" -bitexact -c:a copy -f framecrc -y $decfile1 + ffmpeg $extra_args -i "$sample" -bitexact -c:a copy -f framecrc -y $(target_path $decfile1) do_md5sum $decfile1 # test decoded (and cut) data ffmpeg $extra_args -i "$sample" -bitexact -f wav md5: # the same as above again, with seeking to the start - ffmpeg $extra_args -ss 0 -seek_timestamp 1 -i "$sample" -bitexact -c:a copy -f framecrc -y $decfile2 + ffmpeg $extra_args -ss 0 -seek_timestamp 1 -i "$sample" -bitexact -c:a copy -f framecrc -y $(target_path $decfile2) do_md5sum $decfile2 ffmpeg $extra_args -ss 0 -seek_timestamp 1 -i "$sample" -bitexact -f wav md5: # test packet data, with seeking to a specific position - ffmpeg $extra_args -ss 5 -seek_timestamp 1 -i "$sample" -bitexact -c:a copy -f framecrc -y $decfile3 + ffmpeg $extra_args -ss 5 -seek_timestamp 1 -i "$sample" -bitexact -c:a copy -f framecrc -y $(target_path $decfile3) do_md5sum $decfile3 } @@ -442,19 +451,19 @@ gaplessenc(){ cleanfiles="$cleanfiles $file1" # test data after reencoding - ffmpeg -i "$sample" -bitexact -map 0:a -c:a $codec -f $format -y "$file1" - probegaplessinfo "$file1" + ffmpeg -i "$sample" -bitexact -map 0:a -c:a $codec -f $format -y "$(target_path "$file1")" + probegaplessinfo "$(target_path "$file1")" } audio_match(){ sample=$(target_path $1) - trefile=$(target_path $2) + trefile=$2 extra_args=$3 decfile="${outdir}/${test}.wav" cleanfiles="$cleanfiles $decfile" - ffmpeg -i "$sample" -bitexact $extra_args -y $decfile + ffmpeg -i "$sample" -bitexact $extra_args -y $(target_path $decfile) tests/audiomatch${HOSTEXECSUF} $decfile $trefile } @@ -471,13 +480,20 @@ concat(){ awk "{gsub(/%SRCFILE%/, \"$sample\"); print}" $template > $concatfile if [ "$mode" = "md5" ]; then - run ffprobe${PROGSUF}${EXECSUF} -bitexact -show_streams -show_packets -v 0 -fflags keepside -safe 0 $extra_args $concatfile | tr -d '\r' > $packetfile + run ffprobe${PROGSUF}${EXECSUF} -bitexact -show_streams -show_packets -v 0 -safe 0 $extra_args $(target_path $concatfile) | tr -d '\r' > $packetfile do_md5sum $packetfile else - run ffprobe${PROGSUF}${EXECSUF} -bitexact -show_streams -show_packets -v 0 -of compact=p=0:nk=1 -fflags keepside -safe 0 $extra_args $concatfile + run ffprobe${PROGSUF}${EXECSUF} -bitexact -show_streams -show_packets -v 0 -of compact=p=0:nk=1 -safe 0 $extra_args $(target_path $concatfile) fi } +venc_data(){ + file=$1 + stream=$2 + frames=$3 + run tools/venc_data_dump${EXECSUF} ${file} ${stream} ${frames} ${threads} ${thread_type} +} + null(){ : } diff --git a/tests/fate/acodec.mak b/tests/fate/acodec.mak index 80d26de0f9c..bb6bfe5adab 100644 --- a/tests/fate/acodec.mak +++ b/tests/fate/acodec.mak @@ -46,6 +46,7 @@ fate-acodec-pcm-f%be: FMT = au FATE_ACODEC_ADPCM-$(call ENCDEC, ADPCM_ADX, ADX) += adx FATE_ACODEC_ADPCM-$(call ENCDEC, ADPCM_IMA_QT, AIFF) += ima_qt +FATE_ACODEC_ADPCM-$(call ENCDEC, ADPCM_IMA_SSI, KVAG) += ima_ssi FATE_ACODEC_ADPCM-$(call ENCDEC, ADPCM_IMA_WAV, WAV) += ima_wav FATE_ACODEC_ADPCM-$(call ENCDEC, ADPCM_MS, WAV) += ms FATE_ACODEC_ADPCM-$(call ENCDEC, ADPCM_SWF, FLV) += swf @@ -59,6 +60,7 @@ fate-acodec-adpcm-%: CODEC = adpcm_$(@:fate-acodec-adpcm-%=%) fate-acodec-adpcm-adx: FMT = adx fate-acodec-adpcm-ima_qt: FMT = aiff +fate-acodec-adpcm-ima_ssi: FMT = kvag fate-acodec-adpcm-ima_wav: FMT = wav fate-acodec-adpcm-ms: FMT = wav fate-acodec-adpcm-swf: FMT = flv diff --git a/tests/fate/adpcm.mak b/tests/fate/adpcm.mak index b64920d4fe7..bc2804477f1 100644 --- a/tests/fate/adpcm.mak +++ b/tests/fate/adpcm.mak @@ -88,5 +88,56 @@ fate-adpcm-vima: CMD = framecrc -i $(TARGET_SAMPLES)/smush/ronin_part.znm -vn FATE_ADPCM-$(call DEMDEC, STR, ADPCM_XA) += fate-adpcm-xa fate-adpcm-xa: CMD = framecrc -i $(TARGET_SAMPLES)/psx-str/abc000_cut.str -vn +FATE_ADPCM-$(call DEMDEC, ARGO_ASF, ADPCM_ARGO) += fate-adpcm-argo-mono +fate-adpcm-argo-mono: CMD = md5 -i $(TARGET_SAMPLES)/argo-asf/PWIN22M.ASF -f s16le + +FATE_ADPCM-$(call DEMDEC, ARGO_ASF, ADPCM_ARGO) += fate-adpcm-argo-stereo +fate-adpcm-argo-stereo: CMD = md5 -i $(TARGET_SAMPLES)/argo-asf/CBK2_cut.asf -f s16le + +FATE_ADPCM-$(call DEMDEC, KVAG, ADPCM_IMA_SSI) += fate-adpcm-ima-ssi-mono +fate-adpcm-ima-ssi-mono: CMD = md5 -i $(TARGET_SAMPLES)/kvag/mull1_cut.vag -f s16le + +FATE_ADPCM-$(call DEMDEC, KVAG, ADPCM_IMA_SSI) += fate-adpcm-ima-ssi-stereo +fate-adpcm-ima-ssi-stereo: CMD = md5 -i $(TARGET_SAMPLES)/kvag/credits_cut.vag -f s16le + +FATE_ADPCM-$(call DEMDEC, APM, ADPCM_IMA_APM) += fate-adpcm-ima-apm-mono +fate-adpcm-ima-apm-mono: CMD = md5 -i $(TARGET_SAMPLES)/apm/outro1.apm -f s16le + +FATE_ADPCM-$(call DEMDEC, APM, ADPCM_IMA_APM) += fate-adpcm-ima-apm-stereo +fate-adpcm-ima-apm-stereo: CMD = md5 -i $(TARGET_SAMPLES)/apm/AS01.apm -f s16le + +FATE_ADPCM-$(call DEMDEC, ALP, ADPCM_IMA_ALP) += fate-adpcm-ima-alp-mono +fate-adpcm-ima-alp-mono: CMD = md5 -i $(TARGET_SAMPLES)/alp/AD_P11.PCM -f s16le + +FATE_ADPCM-$(call DEMDEC, ALP, ADPCM_IMA_ALP) += fate-adpcm-ima-alp-stereo +fate-adpcm-ima-alp-stereo: CMD = md5 -i $(TARGET_SAMPLES)/alp/theme-cut.tun -f s16le + +FATE_ADPCM-$(call DEMDEC, PP_BNK, ADPCM_IMA_CUNNING) += fate-adpcm-ima-cunning-single +fate-adpcm-ima-cunning-single: CMD = md5 -y -i $(TARGET_SAMPLES)/pp_bnk/GD-cut.5c -f s16le + +FATE_ADPCM-$(call DEMDEC, PP_BNK, ADPCM_IMA_CUNNING) += fate-adpcm-ima-cunning-track0 +fate-adpcm-ima-cunning-track0: CMD = md5 -y -i $(TARGET_SAMPLES)/pp_bnk/VIDEOMOD-cut.11c -map 0:a:0 -f s16le + +FATE_ADPCM-$(call DEMDEC, PP_BNK, ADPCM_IMA_CUNNING) += fate-adpcm-ima-cunning-track1 +fate-adpcm-ima-cunning-track1: CMD = md5 -y -i $(TARGET_SAMPLES)/pp_bnk/VIDEOMOD-cut.11c -map 0:a:1 -f s16le + +FATE_ADPCM-$(call DEMDEC, PP_BNK, ADPCM_IMA_CUNNING) += fate-adpcm-ima-cunning-trunc-t1 +fate-adpcm-ima-cunning-trunc-t1: CMD = md5 -y -i $(TARGET_SAMPLES)/pp_bnk/VIDEOMOD-trunc-t1.11c -map 0:a:0 -f s16le + +FATE_ADPCM-$(call DEMDEC, PP_BNK, ADPCM_IMA_CUNNING) += fate-adpcm-ima-cunning-trunc-t2-track0 +fate-adpcm-ima-cunning-trunc-t2-track0: CMD = md5 -y -i $(TARGET_SAMPLES)/pp_bnk/VIDEOMOD-trunc-t2.11c -map 0:a:0 -f s16le + +FATE_ADPCM-$(call DEMDEC, PP_BNK, ADPCM_IMA_CUNNING) += fate-adpcm-ima-cunning-trunc-t2-track1 +fate-adpcm-ima-cunning-trunc-t2-track1: CMD = md5 -y -i $(TARGET_SAMPLES)/pp_bnk/VIDEOMOD-trunc-t2.11c -map 0:a:1 -f s16le + +FATE_ADPCM-$(call DEMDEC, PP_BNK, ADPCM_IMA_CUNNING) += fate-adpcm-ima-cunning-trunc-t2a-track0 +fate-adpcm-ima-cunning-trunc-t2a-track0: CMD = md5 -y -i $(TARGET_SAMPLES)/pp_bnk/VIDEOMOD-trunc-t2a.11c -map 0:a:0 -f s16le + +FATE_ADPCM-$(call DEMDEC, PP_BNK, ADPCM_IMA_CUNNING) += fate-adpcm-ima-cunning-trunc-t2a-track1 +fate-adpcm-ima-cunning-trunc-t2a-track1: CMD = md5 -y -i $(TARGET_SAMPLES)/pp_bnk/VIDEOMOD-trunc-t2a.11c -map 0:a:1 -f s16le + +FATE_ADPCM-$(call DEMDEC, PP_BNK, ADPCM_IMA_CUNNING) += fate-adpcm-ima-cunning-trunc-h2 +fate-adpcm-ima-cunning-trunc-h2: CMD = md5 -y -i $(TARGET_SAMPLES)/pp_bnk/VIDEOMOD-trunc-h2.11c -map 0:a:0 -f s16le + FATE_SAMPLES_AVCONV += $(FATE_ADPCM-yes) fate-adpcm: $(FATE_ADPCM-yes) diff --git a/tests/fate/als.mak b/tests/fate/als.mak index ff2badf5ed6..c7287dee86d 100644 --- a/tests/fate/als.mak +++ b/tests/fate/als.mak @@ -7,5 +7,9 @@ endef $(foreach N,$(ALS_SUITE),$(eval $(call FATE_ALS_SUITE,$(N)))) +FATE_ALS += fate-mpeg4-als-conformance-09 + +fate-mpeg4-als-conformance-09: CMD = crc -i $(TARGET_SAMPLES)/lossless-audio/als_09_512ch2k16b.mp4 + FATE_SAMPLES_AVCONV-$(call DEMDEC, MOV, ALS) += $(FATE_ALS) fate-als: $(FATE_ALS) diff --git a/tests/fate/avformat.mak b/tests/fate/avformat.mak deleted file mode 100644 index a12f9ccc711..00000000000 --- a/tests/fate/avformat.mak +++ /dev/null @@ -1,142 +0,0 @@ -FATE_LAVF-$(call ENCDEC, PCM_S16BE, AIFF) += aiff -FATE_LAVF-$(call ENCDEC, PCM_ALAW, PCM_ALAW) += alaw -FATE_LAVF-$(call ENCDEC, APNG, APNG) += apng -FATE_LAVF-$(call ENCDEC2, MSMPEG4V3, MP2, ASF) += asf -FATE_LAVF-$(call ENCDEC, PCM_S16BE_PLANAR, AST) += ast -FATE_LAVF-$(call ENCDEC, PCM_S16BE, AU) += au -FATE_LAVF-$(call ENCDEC2, MPEG4, MP2, AVI) += avi -FATE_LAVF-$(call ENCDEC, BMP, IMAGE2) += bmp -FATE_LAVF-$(call ENCDEC, PCM_S16BE, CAF) += caf -FATE_LAVF-$(call ENCDEC, DPX, IMAGE2) += dpx -FATE_LAVF-$(call ENCDEC2, DVVIDEO, PCM_S16LE, AVI) += dv_fmt -FATE_LAVF-$(call ENCDEC, FITS, FITS) += fits -FATE_LAVF-$(call ENCDEC, RAWVIDEO, FILMSTRIP) += flm -FATE_LAVF-$(call ENCDEC, FLV, FLV) += flv_fmt -FATE_LAVF-$(call ENCDEC, GIF, IMAGE2) += gif -FATE_LAVF-$(call ENCDEC2, MPEG2VIDEO, PCM_S16LE, GXF) += gxf -FATE_LAVF-$(call ENCDEC, PCM_S16LE, IRCAM) += ircam -FATE_LAVF-$(call ENCDEC, MJPEG, IMAGE2) += jpg -FATE_LAVF-$(call ENCMUX, TTA, MATROSKA_AUDIO) += mka -FATE_LAVF-$(call ENCDEC2, MPEG4, MP2, MATROSKA) += mkv -FATE_LAVF-$(call ENCDEC, ADPCM_YAMAHA, MMF) += mmf -FATE_LAVF-$(call ENCDEC2, MPEG4, PCM_ALAW, MOV) += mov ismv -FATE_LAVF-$(call ENCDEC2, MPEG1VIDEO, MP2, MPEG1SYSTEM MPEGPS) += mpg -FATE_LAVF-$(call ENCDEC, PCM_MULAW, PCM_MULAW) += mulaw -FATE_LAVF-$(call ENCDEC2, MPEG2VIDEO, PCM_S16LE, MXF) += mxf -FATE_LAVF-$(call ENCDEC2, MPEG2VIDEO, PCM_S16LE, MXF_D10 MXF) += mxf_d10 -FATE_LAVF-$(call ENCDEC2, DVVIDEO, PCM_S16LE, MXF) += mxf_dv25 -FATE_LAVF-$(call ENCDEC2, DVVIDEO, PCM_S16LE, MXF) += mxf_dvcpro50 -FATE_LAVF-$(call ENCDEC2, DNXHD, PCM_S16LE, MXF_OPATOM MXF) += mxf_opatom -FATE_LAVF-$(call ENCDEC2, DNXHD, PCM_S16LE, MXF_OPATOM MXF) += mxf_opatom_audio -FATE_LAVF-$(call ENCDEC2, MPEG4, MP2, NUT) += nut -FATE_LAVF-$(call ENCDEC, FLAC, OGG) += ogg -FATE_LAVF-$(call ENCDEC, PAM, IMAGE2) += pam -FATE_LAVF-$(call ENCDEC, PBM, IMAGE2PIPE) += pbmpipe -FATE_LAVF-$(call ENCDEC, PCX, IMAGE2) += pcx -FATE_LAVF-$(call ENCDEC, PGM, IMAGE2) += pgm -FATE_LAVF-$(call ENCDEC, PGM, IMAGE2PIPE) += pgmpipe -FATE_LAVF-$(call ENCDEC, PNG, IMAGE2) += png -FATE_LAVF-$(call ENCDEC, PPM, IMAGE2) += ppm -FATE_LAVF-$(call ENCDEC, PPM, IMAGE2PIPE) += ppmpipe -FATE_LAVF-$(call ENCMUX, RV10 AC3_FIXED, RM) += rm -FATE_LAVF-$(call ENCDEC, PCM_U8, RSO) += rso -FATE_LAVF-$(call ENCDEC, SGI, IMAGE2) += sgi -FATE_LAVF-$(call ENCMUX, MJPEG PCM_S16LE, SMJPEG) += smjpeg -FATE_LAVF-$(call ENCDEC, PCM_S16LE, SOX) += sox -FATE_LAVF-$(call ENCDEC, SUNRAST, IMAGE2) += sunrast -FATE_LAVF-$(call ENCDEC, FLV, SWF) += swf -FATE_LAVF-$(call ENCDEC, TARGA, IMAGE2) += tga -FATE_LAVF-$(call ENCDEC, TIFF, IMAGE2) += tiff -FATE_LAVF-$(call ENCDEC2, MPEG2VIDEO, MP2, MPEGTS) += ts -FATE_LAVF-$(call ENCDEC, TTA, TTA) += tta -FATE_LAVF-$(call ENCDEC, PCM_U8, VOC) += voc -FATE_LAVF-$(call ENCDEC, PCM_S16LE, VOC) += voc_s16 -FATE_LAVF-$(call ENCDEC, PCM_S16LE, WAV) += wav -FATE_LAVF-$(call ENCDEC, PCM_S16LE, WAV) += wav_peak -FATE_LAVF-$(call ENCDEC, PCM_S16LE, WAV) += wav_peak_only -FATE_LAVF-$(call ENCMUX, PCM_S16LE, W64) += w64 -FATE_LAVF-$(call ENCDEC, MP2, WTV) += wtv -FATE_LAVF-$(call ENCDEC, WAVPACK, WV) += wv -FATE_LAVF-$(call ENCDEC, XBM, IMAGE2) += xbm -FATE_LAVF-$(call ENCDEC, XWD, IMAGE2) += xwd -FATE_LAVF-$(CONFIG_YUV4MPEGPIPE_MUXER) += yuv4mpeg - -FATE_LAVF += $(FATE_LAVF-yes:%=fate-lavf-%) -FATE_LAVF_PIXFMT-$(CONFIG_SCALE_FILTER) += fate-lavf-pixfmt -FATE_LAVF += $(FATE_LAVF_PIXFMT-yes) - -$(FATE_LAVF): $(AREF) $(VREF) -$(FATE_LAVF): CMD = lavftest -$(FATE_LAVF): CMP = - -FATE_AVCONV += $(FATE_LAVF) -fate-lavf: $(FATE_LAVF) - -FATE_LAVF_FATE-$(call ALLYES, MATROSKA_DEMUXER OGG_MUXER) += ogg_vp3 -FATE_LAVF_FATE-$(call ALLYES, MATROSKA_DEMUXER OGV_MUXER) += ogg_vp8 -FATE_LAVF_FATE-$(call ALLYES, MOV_DEMUXER LATM_MUXER) += latm -FATE_LAVF_FATE-$(call ALLYES, MP3_DEMUXER MP3_MUXER) += mp3 -FATE_LAVF_FATE-$(call ALLYES, MOV_DEMUXER MOV_MUXER) += mov_qtrle_mace6 -FATE_LAVF_FATE-$(call ALLYES, AVI_DEMUXER AVI_MUXER) += avi_cram - -FATE_LAVF_FATE += $(FATE_LAVF_FATE-yes:%=fate-lavf-fate-%) -$(FATE_LAVF_FATE): CMD = lavffatetest - -FATE_SAMPLES_FFMPEG += $(FATE_LAVF_FATE) -fate-lavf-fate: $(FATE_LAVF_FATE) - -tests/data/mp4-to-ts.m3u8: TAG = GEN -tests/data/mp4-to-ts.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data - $(M)$(TARGET_EXEC) $(TARGET_PATH)/$< \ - -i $(TARGET_SAMPLES)/h264/interlaced_crop.mp4 \ - -f ssegment -segment_time 1 -map 0 -flags +bitexact -codec copy \ - -segment_list $(TARGET_PATH)/$@ -y $(TARGET_PATH)/tests/data/mp4-to-ts-%03d.ts 2>/dev/null - -tests/data/adts-to-mkv.m3u8: TAG = GEN -tests/data/adts-to-mkv.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data - $(M)$(TARGET_EXEC) $(TARGET_PATH)/$< \ - -i $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_mono_aac_lc.m4a \ - -f segment -segment_time 1 -map 0 -flags +bitexact -codec copy -segment_format_options live=1 \ - -segment_list $(TARGET_PATH)/$@ -y $(TARGET_PATH)/tests/data/adts-to-mkv-%03d.mkv 2>/dev/null - -tests/data/adts-to-mkv-header.mkv: TAG = GEN -tests/data/adts-to-mkv-header.mkv: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data - $(M)$(TARGET_EXEC) $(TARGET_PATH)/$< \ - -i $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_mono_aac_lc.m4a \ - -f segment -segment_time 1 -map 0 -flags +bitexact -codec copy -segment_format_options live=1 \ - -segment_header_filename $(TARGET_PATH)/tests/data/adts-to-mkv-header.mkv \ - -y $(TARGET_PATH)/tests/data/adts-to-mkv-header-%03d.mkv 2>/dev/null - -tests/data/adts-to-mkv-header-%.mkv: tests/data/adts-to-mkv-header.mkv ; - -FATE_SEGMENT_PARTS += 000 001 002 - -tests/data/adts-to-mkv-cated-all.mkv: TAG = GEN -tests/data/adts-to-mkv-cated-all.mkv: tests/data/adts-to-mkv-header.mkv $(FATE_SEGMENT_PARTS:%=tests/data/adts-to-mkv-header-%.mkv) | tests/data - $(M)cat $^ >$@ - -tests/data/adts-to-mkv-cated-%.mkv: TAG = GEN -tests/data/adts-to-mkv-cated-%.mkv: tests/data/adts-to-mkv-header.mkv tests/data/adts-to-mkv-header-%.mkv | tests/data - $(M)cat $^ >$@ - -FATE_SEGMENT += fate-segment-mp4-to-ts -fate-segment-mp4-to-ts: tests/data/mp4-to-ts.m3u8 -fate-segment-mp4-to-ts: CMD = framecrc -flags +bitexact -i $(TARGET_PATH)/tests/data/mp4-to-ts.m3u8 -c copy -FATE_SEGMENT-$(call ALLYES, MOV_DEMUXER H264_MP4TOANNEXB_BSF MPEGTS_MUXER MATROSKA_DEMUXER SEGMENT_MUXER HLS_DEMUXER) += fate-segment-mp4-to-ts - -FATE_SEGMENT += fate-segment-adts-to-mkv -fate-segment-adts-to-mkv: tests/data/adts-to-mkv.m3u8 -fate-segment-adts-to-mkv: CMD = framecrc -flags +bitexact -i $(TARGET_PATH)/tests/data/adts-to-mkv.m3u8 -c copy -fate-segment-adts-to-mkv: REF = $(SRC_PATH)/tests/ref/fate/segment-adts-to-mkv-header-all -FATE_SEGMENT-$(call ALLYES, AAC_DEMUXER AAC_ADTSTOASC_BSF MATROSKA_MUXER MATROSKA_DEMUXER SEGMENT_MUXER HLS_DEMUXER) += fate-segment-adts-to-mkv - -FATE_SEGMENT_ALLPARTS = $(FATE_SEGMENT_PARTS) -FATE_SEGMENT_ALLPARTS += all -FATE_SEGMENT_SPLIT += $(FATE_SEGMENT_ALLPARTS:%=fate-segment-adts-to-mkv-header-%) -$(foreach N,$(FATE_SEGMENT_ALLPARTS),$(eval $(N:%=fate-segment-adts-to-mkv-header-%): tests/data/adts-to-mkv-cated-$(N).mkv)) -fate-segment-adts-to-mkv-header-%: CMD = framecrc -flags +bitexact -i $(TARGET_PATH)/tests/data/$(@:fate-segment-adts-to-mkv-header-%=adts-to-mkv-cated-%).mkv -c copy -FATE_SEGMENT-$(call ALLYES, AAC_DEMUXER AAC_ADTSTOASC_BSF MATROSKA_MUXER MATROSKA_DEMUXER SEGMENT_MUXER HLS_DEMUXER) += $(FATE_SEGMENT_SPLIT) - -FATE_SAMPLES_FFMPEG += $(FATE_SEGMENT-yes) - -fate-segment: $(FATE_SEGMENT-yes) diff --git a/tests/fate/cbs.mak b/tests/fate/cbs.mak index f2ef04ceda1..ad9c85863b0 100644 --- a/tests/fate/cbs.mak +++ b/tests/fate/cbs.mak @@ -2,7 +2,7 @@ # arguments, it decomposes the stream fully and then recomposes it # without making any changes. -fate-cbs: fate-cbs-h264 fate-cbs-hevc fate-cbs-mpeg2 fate-cbs-vp9 +fate-cbs: fate-cbs-av1 fate-cbs-h264 fate-cbs-hevc fate-cbs-mpeg2 fate-cbs-vp9 FATE_CBS_DEPS = $(call ALLYES, $(1)_DEMUXER $(2)_PARSER $(3)_METADATA_BSF $(4)_DECODER $(5)_MUXER) @@ -12,6 +12,35 @@ FATE_CBS_$(1) += fate-cbs-$(1)-$(2) fate-cbs-$(1)-$(2): CMD = md5 -i $(TARGET_SAMPLES)/$(3) -c:v copy -y -bsf:v $(1)_metadata -f $(4) endef +# AV1 read/write + +FATE_CBS_AV1_CONFORMANCE_SAMPLES = \ + av1-1-b8-02-allintra.ivf \ + av1-1-b8-03-sizedown.ivf \ + av1-1-b8-03-sizeup.ivf \ + av1-1-b8-04-cdfupdate.ivf \ + av1-1-b8-05-mv.ivf \ + av1-1-b8-06-mfmv.ivf \ + av1-1-b8-22-svc-L1T2.ivf \ + av1-1-b8-22-svc-L2T1.ivf \ + av1-1-b8-22-svc-L2T2.ivf \ + av1-1-b8-23-film_grain-50.ivf \ + av1-1-b10-23-film_grain-50.ivf + +FATE_CBS_AV1_SAMPLES = \ + decode_model.ivf \ + frames_refs_short_signaling.ivf \ + non_uniform_tiling.ivf \ + seq_hdr_op_param_info.ivf \ + switch_frame.ivf + +$(foreach N,$(FATE_CBS_AV1_CONFORMANCE_SAMPLES),$(eval $(call FATE_CBS_TEST,av1,$(basename $(N)),av1-test-vectors/$(N),rawvideo))) +$(foreach N,$(FATE_CBS_AV1_SAMPLES),$(eval $(call FATE_CBS_TEST,av1,$(basename $(N)),av1/$(N),rawvideo))) + +FATE_CBS_AV1-$(call ALLYES, IVF_DEMUXER AV1_PARSER AV1_METADATA_BSF RAWVIDEO_MUXER) = $(FATE_CBS_av1) +FATE_SAMPLES_AVCONV += $(FATE_CBS_AV1-yes) +fate-cbs-av1: $(FATE_CBS_AV1-yes) + # H.264 read/write FATE_CBS_H264_CONFORMANCE_SAMPLES = \ diff --git a/tests/fate/checkasm.mak b/tests/fate/checkasm.mak index 618bde509f0..07f1d8238ec 100644 --- a/tests/fate/checkasm.mak +++ b/tests/fate/checkasm.mak @@ -19,14 +19,17 @@ FATE_CHECKASM = fate-checkasm-aacpsdsp \ fate-checkasm-jpeg2000dsp \ fate-checkasm-llviddsp \ fate-checkasm-llviddspenc \ + fate-checkasm-opusdsp \ fate-checkasm-pixblockdsp \ fate-checkasm-sbrdsp \ fate-checkasm-synth_filter \ fate-checkasm-sw_rgb \ + fate-checkasm-sw_scale \ fate-checkasm-v210dec \ fate-checkasm-v210enc \ fate-checkasm-vf_blend \ fate-checkasm-vf_colorspace \ + fate-checkasm-vf_eq \ fate-checkasm-vf_gblur \ fate-checkasm-vf_hflip \ fate-checkasm-vf_threshold \ diff --git a/tests/fate/demux.mak b/tests/fate/demux.mak index eb8d8c36555..9f3a6be2760 100644 --- a/tests/fate/demux.mak +++ b/tests/fate/demux.mak @@ -10,6 +10,9 @@ fate-adts-id3v2-two-tags-demux: CMD = framecrc -i $(TARGET_SAMPLES)/aac/id3v2_tw FATE_SAMPLES_DEMUX-$(CONFIG_AEA_DEMUXER) += fate-aea-demux fate-aea-demux: CMD = crc -i $(TARGET_SAMPLES)/aea/chirp.aea -c:a copy +FATE_SAMPLES_DEMUX-$(CONFIG_AV1_DEMUXER) += fate-av1-annexb-demux +fate-av1-annexb-demux: CMD = framecrc -i $(TARGET_SAMPLES)/av1/annexb.obu -c:v copy + FATE_SAMPLES_DEMUX-$(CONFIG_AST_DEMUXER) += fate-ast fate-ast: CMD = crc -i $(TARGET_SAMPLES)/ast/demo11_02_partial.ast -c copy diff --git a/tests/fate/dnn.mak b/tests/fate/dnn.mak new file mode 100644 index 00000000000..4a50b163820 --- /dev/null +++ b/tests/fate/dnn.mak @@ -0,0 +1,33 @@ +FATE_DNN += fate-dnn-layer-pad +fate-dnn-layer-pad: $(DNNTESTSDIR)/dnn-layer-pad-test$(EXESUF) +fate-dnn-layer-pad: CMD = run $(DNNTESTSDIR)/dnn-layer-pad-test$(EXESUF) +fate-dnn-layer-pad: CMP = null + +FATE_DNN += fate-dnn-layer-conv2d +fate-dnn-layer-conv2d: $(DNNTESTSDIR)/dnn-layer-conv2d-test$(EXESUF) +fate-dnn-layer-conv2d: CMD = run $(DNNTESTSDIR)/dnn-layer-conv2d-test$(EXESUF) +fate-dnn-layer-conv2d: CMP = null + +FATE_DNN += fate-dnn-layer-depth2space +fate-dnn-layer-depth2space: $(DNNTESTSDIR)/dnn-layer-depth2space-test$(EXESUF) +fate-dnn-layer-depth2space: CMD = run $(DNNTESTSDIR)/dnn-layer-depth2space-test$(EXESUF) +fate-dnn-layer-depth2space: CMP = null + +FATE_DNN += fate-dnn-layer-mathbinary +fate-dnn-layer-mathbinary: $(DNNTESTSDIR)/dnn-layer-mathbinary-test$(EXESUF) +fate-dnn-layer-mathbinary: CMD = run $(DNNTESTSDIR)/dnn-layer-mathbinary-test$(EXESUF) +fate-dnn-layer-mathbinary: CMP = null + +FATE_DNN += fate-dnn-layer-maximum +fate-dnn-layer-maximum: $(DNNTESTSDIR)/dnn-layer-maximum-test$(EXESUF) +fate-dnn-layer-maximum: CMD = run $(DNNTESTSDIR)/dnn-layer-maximum-test$(EXESUF) +fate-dnn-layer-maximum: CMP = null + +FATE_DNN += fate-dnn-layer-mathunary +fate-dnn-layer-mathunary: $(DNNTESTSDIR)/dnn-layer-mathunary-test$(EXESUF) +fate-dnn-layer-mathunary: CMD = run $(DNNTESTSDIR)/dnn-layer-mathunary-test$(EXESUF) +fate-dnn-layer-mathunary: CMP = null + +FATE-yes += $(FATE_DNN) + +fate-dnn: $(FATE_DNN) diff --git a/tests/fate/ffmpeg.mak b/tests/fate/ffmpeg.mak index 71ab2f1f630..0b0610f647d 100644 --- a/tests/fate/ffmpeg.mak +++ b/tests/fate/ffmpeg.mak @@ -56,6 +56,25 @@ fate-sub2video: CMD = framecrc \ -filter_complex "sws_flags=+accurate_rnd+bitexact\;[0:0]scale=720:480[v]\;[v][1:0]overlay[v2]" \ -map "[v2]" -c:v rawvideo -map 1:s -c:s dvdsub +# Very basic sub2video example, decode and convert to AVFrame with sub2video. +# Attempt to not touch timestamps. +FATE_SAMPLES_FFMPEG-$(call ALLYES, VOBSUB_DEMUXER DVDSUB_DECODER AVFILTER) += fate-sub2video_basic +fate-sub2video_basic: CMD = framecrc \ + -i $(TARGET_SAMPLES)/sub/vobsub.idx \ + -vsync passthrough -copyts \ + -filter_complex "sws_flags=+accurate_rnd+bitexact\;[0:s:0]scale" \ + -c:v rawvideo + +# Time-limited run with a sample that doesn't require seeking and +# contains samples within the initial period. +FATE_SAMPLES_FFMPEG-$(call ALLYES, SUP_DEMUXER PGSSUB_DECODER AVFILTER) += fate-sub2video_time_limited +fate-sub2video_time_limited: CMD = framecrc \ + -i $(TARGET_SAMPLES)/sub/pgs_sub.sup \ + -vsync passthrough -copyts \ + -t 15 \ + -filter_complex "sws_flags=+accurate_rnd+bitexact\;[0:s:0]scale" \ + -c:v rawvideo + FATE_FFMPEG-$(call ALLYES, PCM_S16LE_DEMUXER PCM_S16LE_MUXER PCM_S16LE_DECODER PCM_S16LE_ENCODER) += fate-unknown_layout-pcm fate-unknown_layout-pcm: $(AREF) fate-unknown_layout-pcm: CMD = md5 \ @@ -69,27 +88,27 @@ fate-unknown_layout-ac3: CMD = md5 \ FATE_STREAMCOPY-$(call ALLYES, EAC3_DEMUXER MOV_MUXER) += fate-copy-trac3074 -fate-copy-trac3074: $(TARGET_SAMPLES)/eac3/csi_miami_stereo_128_spx.eac3 +fate-copy-trac3074: $(SAMPLES)/eac3/csi_miami_stereo_128_spx.eac3 fate-copy-trac3074: CMD = transcode eac3 $(TARGET_SAMPLES)/eac3/csi_miami_stereo_128_spx.eac3\ mp4 "-codec copy -map 0" "-codec copy" FATE_STREAMCOPY-$(call ALLYES, MOV_DEMUXER MOV_MUXER) += fate-copy-trac236 -fate-copy-trac236: $(TARGET_SAMPLES)/mov/fcp_export8-236.mov +fate-copy-trac236: $(SAMPLES)/mov/fcp_export8-236.mov fate-copy-trac236: CMD = transcode mov $(TARGET_SAMPLES)/mov/fcp_export8-236.mov\ mov "-codec copy -map 0" FATE_STREAMCOPY-$(call ALLYES, MPEGTS_DEMUXER MXF_MUXER PCM_S16LE_ENCODER) += fate-copy-trac4914 -fate-copy-trac4914: $(TARGET_SAMPLES)/mpeg2/xdcam8mp2-1s_small.ts +fate-copy-trac4914: $(SAMPLES)/mpeg2/xdcam8mp2-1s_small.ts fate-copy-trac4914: CMD = transcode mpegts $(TARGET_SAMPLES)/mpeg2/xdcam8mp2-1s_small.ts\ mxf "-c:a pcm_s16le -c:v copy" FATE_STREAMCOPY-$(call ALLYES, MPEGTS_DEMUXER AVI_MUXER) += fate-copy-trac4914-avi -fate-copy-trac4914-avi: $(TARGET_SAMPLES)/mpeg2/xdcam8mp2-1s_small.ts +fate-copy-trac4914-avi: $(SAMPLES)/mpeg2/xdcam8mp2-1s_small.ts fate-copy-trac4914-avi: CMD = transcode mpegts $(TARGET_SAMPLES)/mpeg2/xdcam8mp2-1s_small.ts\ avi "-c:a copy -c:v copy" FATE_STREAMCOPY-$(call ALLYES, H264_DEMUXER AVI_MUXER) += fate-copy-trac2211-avi -fate-copy-trac2211-avi: $(TARGET_SAMPLES)/h264/bbc2.sample.h264 +fate-copy-trac2211-avi: $(SAMPLES)/h264/bbc2.sample.h264 fate-copy-trac2211-avi: CMD = transcode "h264 -r 14" $(TARGET_SAMPLES)/h264/bbc2.sample.h264\ avi "-c:a copy -c:v copy" @@ -98,30 +117,34 @@ fate-copy-apng: fate-lavf-apng fate-copy-apng: CMD = transcode apng tests/data/lavf/lavf.apng apng "-c:v copy" FATE_STREAMCOPY-$(call DEMMUX, OGG, OGG) += fate-limited_input_seek fate-limited_input_seek-copyts -fate-limited_input_seek: $(TARGET_SAMPLES)/vorbis/moog_small.ogg +fate-limited_input_seek: $(SAMPLES)/vorbis/moog_small.ogg fate-limited_input_seek: CMD = md5 -ss 1.5 -t 1.3 -i $(TARGET_SAMPLES)/vorbis/moog_small.ogg -c:a copy -fflags +bitexact -f ogg -fate-limited_input_seek-copyts: $(TARGET_SAMPLES)/vorbis/moog_small.ogg +fate-limited_input_seek-copyts: $(SAMPLES)/vorbis/moog_small.ogg fate-limited_input_seek-copyts: CMD = md5 -ss 1.5 -t 1.3 -i $(TARGET_SAMPLES)/vorbis/moog_small.ogg -c:a copy -copyts -fflags +bitexact -f ogg FATE_STREAMCOPY-$(call ALLYES, MOV_DEMUXER MOV_MUXER) += fate-copy-psp -fate-copy-psp: $(TARGET_SAMPLES)/h264/wwwq_cut.mp4 +fate-copy-psp: $(SAMPLES)/h264/wwwq_cut.mp4 fate-copy-psp: CMD = transcode "mov" $(TARGET_SAMPLES)/h264/wwwq_cut.mp4\ psp "-c copy" "-codec copy" +FATE_STREAMCOPY-$(CONFIG_FLV_DEMUXER) += fate-ffmpeg-streamloop +fate-ffmpeg-streamloop: $(SAMPLES)/flv/streamloop.flv +fate-ffmpeg-streamloop: CMD = framemd5 -stream_loop 2 -i $(TARGET_SAMPLES)/flv/streamloop.flv -c copy + fate-streamcopy: $(FATE_STREAMCOPY-yes) FATE_SAMPLES_FFMPEG-$(call ALLYES, MOV_DEMUXER MATROSKA_MUXER) += fate-rgb24-mkv -fate-rgb24-mkv: $(TARGET_SAMPLES)/qtrle/aletrek-rle.mov +fate-rgb24-mkv: $(SAMPLES)/qtrle/aletrek-rle.mov fate-rgb24-mkv: CMD = transcode "mov" $(TARGET_SAMPLES)/qtrle/aletrek-rle.mov\ matroska "-c:v rawvideo -pix_fmt rgb24 -allow_raw_vfw 1 -frames:v 1" FATE_SAMPLES_FFMPEG-$(call ALLYES, AAC_DEMUXER MOV_MUXER) += fate-adtstoasc_ticket3715 -fate-adtstoasc_ticket3715: $(TARGET_SAMPLES)/aac/foo.aac +fate-adtstoasc_ticket3715: $(SAMPLES)/aac/foo.aac fate-adtstoasc_ticket3715: CMD = transcode "aac" $(TARGET_SAMPLES)/aac/foo.aac\ mov "-c copy -bsf:a aac_adtstoasc" "-codec copy" FATE_SAMPLES_FFMPEG-$(call ALLYES, MOV_DEMUXER H264_MUXER H264_MP4TOANNEXB_BSF) += fate-h264_mp4toannexb_ticket2991 -fate-h264_mp4toannexb_ticket2991: $(TARGET_SAMPLES)/h264/wwwq_cut.mp4 +fate-h264_mp4toannexb_ticket2991: $(SAMPLES)/h264/wwwq_cut.mp4 fate-h264_mp4toannexb_ticket2991: CMD = transcode "mp4" $(TARGET_SAMPLES)/h264/wwwq_cut.mp4\ h264 "-c:v copy -bsf:v h264_mp4toannexb" "-codec copy" @@ -132,13 +155,13 @@ fate-h264_mp4toannexb_ticket5927_2: CMD = transcode "mp4" $(TARGET_SAMPLES)/h264 h264 "-c:v copy -an" "-c:v copy" FATE_SAMPLES_FFMPEG-$(call ALLYES, MPEGPS_DEMUXER AVI_MUXER REMOVE_EXTRADATA_BSF) += fate-ffmpeg-bsf-remove-k fate-ffmpeg-bsf-remove-r fate-ffmpeg-bsf-remove-e -fate-ffmpeg-bsf-remove-k: $(TARGET_SAMPLES)/mpeg2/matrixbench_mpeg2.lq1.mpg +fate-ffmpeg-bsf-remove-k: $(SAMPLES)/mpeg2/matrixbench_mpeg2.lq1.mpg fate-ffmpeg-bsf-remove-k: CMD = transcode "mpeg" $(TARGET_SAMPLES)/mpeg2/matrixbench_mpeg2.lq1.mpg\ avi "-vbsf remove_extra=k" "-codec copy" -fate-ffmpeg-bsf-remove-r: $(TARGET_SAMPLES)/mpeg2/matrixbench_mpeg2.lq1.mpg +fate-ffmpeg-bsf-remove-r: $(SAMPLES)/mpeg2/matrixbench_mpeg2.lq1.mpg fate-ffmpeg-bsf-remove-r: CMD = transcode "mpeg" $(TARGET_SAMPLES)/mpeg2/matrixbench_mpeg2.lq1.mpg\ avi "-vbsf remove_extra=keyframe" "-codec copy" -fate-ffmpeg-bsf-remove-e: $(TARGET_SAMPLES)/mpeg2/matrixbench_mpeg2.lq1.mpg +fate-ffmpeg-bsf-remove-e: $(SAMPLES)/mpeg2/matrixbench_mpeg2.lq1.mpg fate-ffmpeg-bsf-remove-e: CMD = transcode "mpeg" $(TARGET_SAMPLES)/mpeg2/matrixbench_mpeg2.lq1.mpg\ avi "-vbsf remove_extra=e" "-codec copy" @@ -146,7 +169,7 @@ fate-ffmpeg-bsf-remove-e: CMD = transcode "mpeg" $(TARGET_SAMPLES)/mpeg2/matrixb FATE_SAMPLES_FFMPEG-yes += $(FATE_STREAMCOPY-yes) FATE_TIME_BASE-$(call ALLYES, MPEGPS_DEMUXER MXF_MUXER) += fate-time_base -fate-time_base: $(TARGET_SAMPLES)/mpeg2/dvd_single_frame.vob +fate-time_base: $(SAMPLES)/mpeg2/dvd_single_frame.vob fate-time_base: CMD = md5 -i $(TARGET_SAMPLES)/mpeg2/dvd_single_frame.vob -an -sn -c:v copy -r 25 -time_base 1001:30000 -fflags +bitexact -f mxf FATE_SAMPLES_FFMPEG-yes += $(FATE_TIME_BASE-yes) diff --git a/tests/fate/ffprobe.mak b/tests/fate/ffprobe.mak index d5fb05cd683..c867bebf414 100644 --- a/tests/fate/ffprobe.mak +++ b/tests/fate/ffprobe.mak @@ -1,5 +1,5 @@ FFPROBE_TEST_FILE=tests/data/ffprobe-test.nut -FFPROBE_COMMAND=ffprobe$(PROGSSUF)$(EXESUF) -show_streams -show_packets -show_format -show_frames -bitexact $(FFPROBE_TEST_FILE) +FFPROBE_COMMAND=ffprobe$(PROGSSUF)$(EXESUF) -show_streams -show_packets -show_format -show_frames -bitexact $(TARGET_PATH)/$(FFPROBE_TEST_FILE) -print_filename $(FFPROBE_TEST_FILE) FATE_FFPROBE-$(CONFIG_AVDEVICE) += fate-ffprobe_compact fate-ffprobe_compact: $(FFPROBE_TEST_FILE) diff --git a/tests/fate/filter-audio.mak b/tests/fate/filter-audio.mak index f1db7b9f021..79b1536df08 100644 --- a/tests/fate/filter-audio.mak +++ b/tests/fate/filter-audio.mak @@ -186,10 +186,13 @@ FATE_AFILTER_SAMPLES-$(call FILTERDEMDECENCMUX, STEREOTOOLS, WAV, PCM_S16LE, PCM fate-filter-stereotools: SRC = $(TARGET_SAMPLES)/audio-reference/luckynight_2ch_44kHz_s16.wav fate-filter-stereotools: CMD = framecrc -i $(SRC) -frames:a 20 -af stereotools=mlev=0.015625 -FATE_AFILTER-$(call FILTERDEMDECENCMUX, TREMOLO, WAV, PCM_S16LE, PCM_S16LE, WAV) += fate-filter-tremolo +FATE_AFILTER_SAMPLES-$(call FILTERDEMDECENCMUX, TREMOLO, WAV, PCM_S16LE, PCM_S16LE, WAV) += fate-filter-tremolo fate-filter-tremolo: tests/data/asynth-44100-2.wav fate-filter-tremolo: SRC = $(TARGET_PATH)/tests/data/asynth-44100-2.wav -fate-filter-tremolo: CMD = framecrc -i $(SRC) -frames:a 20 -af tremolo +fate-filter-tremolo: CMD = ffmpeg -i $(SRC) -frames:a 20 -af tremolo -f wav -f s16le - +fate-filter-tremolo: REF = $(SAMPLES)/filter/tremolo.pcm +fate-filter-tremolo: CMP = oneoff +fate-filter-tremolo: CMP_UNIT = s16 FATE_AFILTER-$(call FILTERDEMDECENCMUX, COMPAND, WAV, PCM_S16LE, PCM_S16LE, WAV) += fate-filter-compand fate-filter-compand: tests/data/asynth-44100-2.wav @@ -312,47 +315,47 @@ FATE_AFILTER_SAMPLES-$(call FILTERDEMDECENCMUX, HDCD, FLAC, FLAC, PCM_S24LE, PCM fate-filter-hdcd-mix: SRC = $(TARGET_SAMPLES)/filter/hdcd-mix.flac fate-filter-hdcd-mix: CMD = md5 -i $(SRC) -af hdcd -f s24le fate-filter-hdcd-mix: CMP = oneline -fate-filter-hdcd-mix: REF = e7079913e90c124460cdbc712df5b84c +fate-filter-hdcd-mix: REF = 77443573e0bd3532de52a8bc0e825da7 # output will be different because of the gain mismatch in the second and third parts FATE_AFILTER_SAMPLES-$(call FILTERDEMDECENCMUX, HDCD, FLAC, FLAC, PCM_S24LE, PCM_S24LE) += fate-filter-hdcd-mix-psoff fate-filter-hdcd-mix-psoff: SRC = $(TARGET_SAMPLES)/filter/hdcd-mix.flac fate-filter-hdcd-mix-psoff: CMD = md5 -i $(SRC) -af hdcd=process_stereo=false -f s24le fate-filter-hdcd-mix-psoff: CMP = oneline -fate-filter-hdcd-mix-psoff: REF = bd0e81fe17696b825ee3515ab928e6bb +fate-filter-hdcd-mix-psoff: REF = 89e57885917a436b30855db4d478cefb # test the different analyze modes FATE_AFILTER_SAMPLES-$(call FILTERDEMDECENCMUX, HDCD, FLAC, FLAC, PCM_S24LE, PCM_S24LE) += fate-filter-hdcd-analyze-pe fate-filter-hdcd-analyze-pe: SRC = $(TARGET_SAMPLES)/filter/hdcd-mix.flac fate-filter-hdcd-analyze-pe: CMD = md5 -i $(SRC) -af hdcd=analyze_mode=pe -f s24le fate-filter-hdcd-analyze-pe: CMP = oneline -fate-filter-hdcd-analyze-pe: REF = bb83e97bbd0064b9b1c0ef2f2c8f0c77 +fate-filter-hdcd-analyze-pe: REF = 2d839d8a1cf73b10a566ce3d4cfaa79e FATE_AFILTER_SAMPLES-$(call FILTERDEMDECENCMUX, HDCD, FLAC, FLAC, PCM_S24LE, PCM_S24LE) += fate-filter-hdcd-analyze-lle fate-filter-hdcd-analyze-lle: SRC = $(TARGET_SAMPLES)/filter/hdcd-mix.flac fate-filter-hdcd-analyze-lle: CMD = md5 -i $(SRC) -af hdcd=analyze_mode=lle -f s24le fate-filter-hdcd-analyze-lle: CMP = oneline -fate-filter-hdcd-analyze-lle: REF = 121cc4a681aa0caef5c664fece7a3ddc +fate-filter-hdcd-analyze-lle: REF = b4b185332b7025c191062f49a2c015f1 FATE_AFILTER_SAMPLES-$(call FILTERDEMDECENCMUX, HDCD, FLAC, FLAC, PCM_S24LE, PCM_S24LE) += fate-filter-hdcd-analyze-cdt fate-filter-hdcd-analyze-cdt: SRC = $(TARGET_SAMPLES)/filter/hdcd-mix.flac fate-filter-hdcd-analyze-cdt: CMD = md5 -i $(SRC) -af hdcd=analyze_mode=cdt -f s24le fate-filter-hdcd-analyze-cdt: CMP = oneline -fate-filter-hdcd-analyze-cdt: REF = 12136e6a00dd532994f6edcc347af1d4 +fate-filter-hdcd-analyze-cdt: REF = afa6577675c63e87da3edbd442b7b6e2 FATE_AFILTER_SAMPLES-$(call FILTERDEMDECENCMUX, HDCD, FLAC, FLAC, PCM_S24LE, PCM_S24LE) += fate-filter-hdcd-analyze-tgm fate-filter-hdcd-analyze-tgm: SRC = $(TARGET_SAMPLES)/filter/hdcd-mix.flac fate-filter-hdcd-analyze-tgm: CMD = md5 -i $(SRC) -af hdcd=analyze_mode=tgm -f s24le fate-filter-hdcd-analyze-tgm: CMP = oneline -fate-filter-hdcd-analyze-tgm: REF = a3c39f62e9b9b42c9c440d0045d5fb2f +fate-filter-hdcd-analyze-tgm: REF = 285f0fd2249b4903cd5e1ad5ce004219 # the two additional analyze modes from libhdcd FATE_AFILTER_SAMPLES-$(call FILTERDEMDECENCMUX, HDCD, FLAC, FLAC, PCM_S24LE, PCM_S24LE) += fate-filter-hdcd-analyze-ltgm fate-filter-hdcd-analyze-ltgm: SRC = $(TARGET_SAMPLES)/filter/hdcd-mix.flac fate-filter-hdcd-analyze-ltgm: CMD = md5 -i $(SRC) -af hdcd=analyze_mode=lle:process_stereo=false -f s24le fate-filter-hdcd-analyze-ltgm: CMP = oneline -fate-filter-hdcd-analyze-ltgm: REF = 76ffd86b762b5a93332039f27e4c0c0e +fate-filter-hdcd-analyze-ltgm: REF = 404dc2301ea97e9f96c3d6d2ebcfeaa5 FATE_AFILTER_SAMPLES-$(call FILTERDEMDECENCMUX, HDCD, FLAC, FLAC, PCM_S24LE, PCM_S24LE) += fate-filter-hdcd-analyze-pel fate-filter-hdcd-analyze-pel: SRC = $(TARGET_SAMPLES)/filter/hdcd-mix.flac fate-filter-hdcd-analyze-pel: CMD = md5 -i $(SRC) -af hdcd=analyze_mode=pe:force_pe=true -f s24le fate-filter-hdcd-analyze-pel: CMP = oneline -fate-filter-hdcd-analyze-pel: REF = 8156c5a3658d789ab46447d62151f5e9 +fate-filter-hdcd-analyze-pel: REF = 9342983208ec1a7f2b3e332ac4dcb723 FATE_AFILTER_SAMPLES-$(call FILTERDEMDECENCMUX, HDCD, FLAC, FLAC, PCM_S24LE, PCM_S24LE) += fate-filter-hdcd-false-positive fate-filter-hdcd-false-positive: SRC = $(TARGET_SAMPLES)/filter/hdcd-false-positive.flac diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak index 1042e96e54b..cfeb53e5320 100644 --- a/tests/fate/filter-video.mak +++ b/tests/fate/filter-video.mak @@ -1,6 +1,6 @@ FATE_FILTER_SAMPLES-$(call ALLYES, SMJPEG_DEMUXER MJPEG_DECODER PERMS_FILTER OWDENOISE_FILTER) += fate-filter-owdenoise-sample fate-filter-owdenoise-sample: CMD = ffmpeg -idct simple -i $(TARGET_SAMPLES)/smjpeg/scenwin.mjpg -vf "trim=duration=0.5,perms=random,owdenoise=10:20:20:enable=not(between(t\,0.2\,1.2))" -an -f rawvideo - -fate-filter-owdenoise-sample: REF = $(TARGET_SAMPLES)/filter-reference/owdenoise-scenwin.raw +fate-filter-owdenoise-sample: REF = $(SAMPLES)/filter-reference/owdenoise-scenwin.raw fate-filter-owdenoise-sample: CMP_TARGET = 1 fate-filter-owdenoise-sample: FUZZ = 3539 fate-filter-owdenoise-sample: CMP = oneoff @@ -122,6 +122,10 @@ FATE_FILTER-$(call ALLYES, FRAMERATE_FILTER TESTSRC2_FILTER FORMAT_FILTER) += fa fate-filter-framerate-12bit-up: CMD = framecrc -lavfi testsrc2=r=50:d=1,format=pix_fmts=yuv422p12le,framerate=fps=60 -t 1 -pix_fmt yuv422p12le fate-filter-framerate-12bit-down: CMD = framecrc -lavfi testsrc2=r=60:d=1,format=pix_fmts=yuv422p12le,framerate=fps=50 -t 1 -pix_fmt yuv422p12le +FATE_FILTER-$(call ALLYES, MINTERPOLATE_FILTER TESTSRC2_FILTER) += fate-filter-minterpolate-up fate-filter-minterpolate-down +fate-filter-minterpolate-up: CMD = framecrc -lavfi testsrc2=r=2:d=10,framerate=fps=10 -t 1 +fate-filter-minterpolate-down: CMD = framecrc -lavfi testsrc2=r=2:d=10,framerate=fps=1 -t 1 + FATE_FILTER_VSYNTH-$(CONFIG_BOXBLUR_FILTER) += fate-filter-boxblur fate-filter-boxblur: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf boxblur=2:1 @@ -360,7 +364,7 @@ FATE_SHUFFLEPLANES += fate-filter-shuffleplanes-dup-luma fate-filter-shuffleplanes-dup-luma: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf format=yuva444p,shuffleplanes=0:0:0:0 FATE_SHUFFLEPLANES += fate-filter-shuffleplanes-swapuv -fate-filter-shuffleplanes-swapuv: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf shuffleplanes=0:2:1 +fate-filter-shuffleplanes-swapuv: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf shuffleplanes=0:2:1:0 FATE_FILTER_VSYNTH-$(CONFIG_SHUFFLEPLANES_FILTER) += $(FATE_SHUFFLEPLANES) @@ -392,6 +396,9 @@ fate-filter-trim-time: CMD = framecrc -i $(SRC) -vf trim=0:0.09 FATE_FILTER_VSYNTH-$(CONFIG_TRIM_FILTER) += $(FATE_TRIM) +FATE_FILTER-$(call ALLYES, TESTSRC2_FILTER UNTILE_FILTER) += fate-filter-untile +fate-filter-untile: CMD = framecrc -lavfi testsrc2=d=1:r=2,untile=2x2 + FATE_FILTER_VSYNTH-$(CONFIG_UNSHARP_FILTER) += fate-filter-unsharp fate-filter-unsharp: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf unsharp=11:11:-1.5:11:11:-1.5 @@ -424,9 +431,11 @@ FATE_FILTER_SAMPLES-$(call ALLYES, VMD_DEMUXER VMDVIDEO_DECODER FORMAT_FILTER PE fate-filter-gradfun-sample: tests/data/filtergraphs/gradfun fate-filter-gradfun-sample: CMD = framecrc -i $(TARGET_SAMPLES)/vmd/12.vmd -filter_script $(TARGET_PATH)/tests/data/filtergraphs/gradfun -an -frames:v 20 -FATE_FILTER-$(call ALLYES, TESTSRC_FILTER SINE_FILTER CONCAT_FILTER) += fate-filter-concat +FATE_FILTER-$(call ALLYES, TESTSRC_FILTER SINE_FILTER CONCAT_FILTER) += fate-filter-concat fate-filter-concat-vfr fate-filter-concat: tests/data/filtergraphs/concat fate-filter-concat: CMD = framecrc -filter_complex_script $(TARGET_PATH)/tests/data/filtergraphs/concat +fate-filter-concat-vfr: tests/data/filtergraphs/concat-vfr +fate-filter-concat-vfr: CMD = framecrc -filter_complex_script $(TARGET_PATH)/tests/data/filtergraphs/concat-vfr FATE_FILTER-$(call ALLYES, TESTSRC2_FILTER FPS_FILTER MPDECIMATE_FILTER) += fate-filter-mpdecimate fate-filter-mpdecimate: CMD = framecrc -lavfi testsrc2=r=2:d=10,fps=3,mpdecimate -r 3 -pix_fmt yuv420p @@ -482,11 +491,29 @@ fate-filter-scale2ref_keep_aspect: CMD = framemd5 -frames:v 5 -filter_complex_sc FATE_FILTER_VSYNTH-$(CONFIG_SCALE_FILTER) += fate-filter-scalechroma fate-filter-scalechroma: tests/data/vsynth1.yuv -fate-filter-scalechroma: CMD = framecrc -flags bitexact -s 352x288 -pix_fmt yuv444p -i tests/data/vsynth1.yuv -pix_fmt yuv420p -sws_flags +bitexact -vf scale=out_v_chr_pos=33:out_h_chr_pos=151 +fate-filter-scalechroma: CMD = framecrc -flags bitexact -s 352x288 -pix_fmt yuv444p -i $(TARGET_PATH)/tests/data/vsynth1.yuv -pix_fmt yuv420p -sws_flags +bitexact -vf scale=out_v_chr_pos=33:out_h_chr_pos=151 FATE_FILTER_VSYNTH-$(CONFIG_VFLIP_FILTER) += fate-filter-vflip fate-filter-vflip: CMD = video_filter "vflip" +FATE_FILTER_VSYNTH-$(CONFIG_COLORLEVELS_FILTER) += fate-filter-colorlevels +fate-filter-colorlevels: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf format=rgb24,colorlevels -flags +bitexact -sws_flags +accurate_rnd+bitexact + +FATE_FILTER_VSYNTH-$(CONFIG_COLORLEVELS_FILTER) += fate-filter-colorlevels-16 +fate-filter-colorlevels-16: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf format=rgb48,colorlevels -pix_fmt rgb48le -flags +bitexact -sws_flags +accurate_rnd+bitexact + +FATE_FILTER_VSYNTH-$(CONFIG_COLORBALANCE_FILTER) += fate-filter-colorbalance +fate-filter-colorbalance: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf format=rgb24,colorbalance=rs=.2 -flags +bitexact -sws_flags +accurate_rnd+bitexact -frames:v 3 + +FATE_FILTER_VSYNTH-$(CONFIG_COLORBALANCE_FILTER) += fate-filter-colorbalance-gbrap +fate-filter-colorbalance-gbrap: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf format=gbrap,colorbalance=gh=.2 -flags +bitexact -sws_flags +accurate_rnd+bitexact -frames:v 3 + +FATE_FILTER_VSYNTH-$(CONFIG_COLORBALANCE_FILTER) += fate-filter-colorbalance-rgba64 +fate-filter-colorbalance-rgba64: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf format=rgba64,colorbalance=rm=.2 -pix_fmt rgba64le -flags +bitexact -sws_flags +accurate_rnd+bitexact -frames:v 3 + +FATE_FILTER_VSYNTH-$(CONFIG_COLORBALANCE_FILTER) += fate-filter-colorbalance-gbrap-16 +fate-filter-colorbalance-gbrap-16: CMD = framecrc -c:v pgmyuv -i $(SRC) -vf format=gbrap,colorbalance=bh=.2 -pix_fmt gbrap -flags +bitexact -sws_flags +accurate_rnd+bitexact -frames:v 3 + FATE_FILTER_VSYNTH-$(CONFIG_COLORMATRIX_FILTER) += fate-filter-colormatrix1 fate-filter-colormatrix1: CMD = video_filter "colormatrix=bt601:smpte240m,colormatrix=smpte240m:fcc,colormatrix=fcc:bt601,colormatrix=bt601:fcc,colormatrix=fcc:smpte240m,colormatrix=smpte240m:bt709" @@ -535,6 +562,18 @@ fate-filter-pp4: CMD = video_filter "pp=be/ci" fate-filter-pp5: CMD = video_filter "pp=md" fate-filter-pp6: CMD = video_filter "pp=be/fd" +FATE_FILTER_VSYNTH-$(CONFIG_PP7_FILTER) += fate-filter-pp7 +fate-filter-pp7: fate-vsynth1-mpeg4-qprd +fate-filter-pp7: CMD = framecrc -flags bitexact -idct simple -i $(TARGET_PATH)/tests/data/fate/vsynth1-mpeg4-qprd.avi -frames:v 5 -flags +bitexact -vf "pp7" + +FATE_FILTER_VSYNTH-$(CONFIG_SPP_FILTER) += fate-filter-spp +fate-filter-spp: fate-vsynth1-mpeg4-qprd +fate-filter-spp: CMD = framecrc -flags bitexact -idct simple -i $(TARGET_PATH)/tests/data/fate/vsynth1-mpeg4-qprd.avi -frames:v 5 -flags +bitexact -vf "spp=idct=simple:dct=int" + +FATE_FILTER_VSYNTH-$(CONFIG_CODECVIEW_FILTER) += fate-filter-codecview +fate-filter-codecview: fate-vsynth1-mpeg4-qprd +fate-filter-codecview: CMD = framecrc -flags bitexact -idct simple -flags2 +export_mvs -i $(TARGET_PATH)/tests/data/fate/vsynth1-mpeg4-qprd.avi -frames:v 5 -flags +bitexact -vf codecview=mv=pf+bf+bb + FATE_FILTER_VSYNTH-$(call ALLYES, QP_FILTER PP_FILTER) += fate-filter-qp fate-filter-qp: CMD = video_filter "qp=17,pp=be/hb/vb/tn/l5/al" @@ -734,7 +773,13 @@ SCENEDETECT_DEPS = FFPROBE LAVFI_INDEV MOVIE_FILTER SELECT_FILTER SCALE_FILTER \ AVCODEC AVDEVICE MOV_DEMUXER SVQ3_DECODER ZLIB FATE_METADATA_FILTER-$(call ALLYES, $(SCENEDETECT_DEPS)) += fate-filter-metadata-scenedetect fate-filter-metadata-scenedetect: SRC = $(TARGET_SAMPLES)/svq3/Vertical400kbit.sorenson3.mov -fate-filter-metadata-scenedetect: CMD = run $(FILTER_METADATA_COMMAND) "sws_flags=+accurate_rnd+bitexact;movie='$(SRC)',select=gt(scene\,.4)" +fate-filter-metadata-scenedetect: CMD = run $(FILTER_METADATA_COMMAND) "sws_flags=+accurate_rnd+bitexact;movie='$(SRC)',select=gt(scene\,.25)" + +SCDET_DEPS = FFPROBE LAVFI_INDEV MOVIE_FILTER SCDET_FILTER SCALE_FILTER \ + AVCODEC AVDEVICE MOV_DEMUXER SVQ3_DECODER ZLIB +FATE_METADATA_FILTER-$(call ALLYES, $(SCDET_DEPS)) += fate-filter-metadata-scdet +fate-filter-metadata-scdet: SRC = $(TARGET_SAMPLES)/svq3/Vertical400kbit.sorenson3.mov +fate-filter-metadata-scdet: CMD = run $(FILTER_METADATA_COMMAND) "sws_flags=+accurate_rnd+bitexact;movie='$(SRC)',scdet=s=1" CROPDETECT_DEPS = FFPROBE LAVFI_INDEV MOVIE_FILTER CROPDETECT_FILTER SCALE_FILTER \ AVCODEC AVDEVICE MOV_DEMUXER H264_DECODER @@ -742,10 +787,19 @@ FATE_METADATA_FILTER-$(call ALLYES, $(CROPDETECT_DEPS)) += fate-filter-metadata- fate-filter-metadata-cropdetect: SRC = $(TARGET_SAMPLES)/filter/cropdetect.mp4 fate-filter-metadata-cropdetect: CMD = run $(FILTER_METADATA_COMMAND) "sws_flags=+accurate_rnd+bitexact;movie='$(SRC)',cropdetect=max_outliers=3" +FREEZEDETECT_DEPS = FFPROBE AVDEVICE LAVFI_INDEV MPTESTSRC_FILTER SCALE_FILTER FREEZEDETECT_FILTER +FATE_METADATA_FILTER-$(call ALLYES, $(FREEZEDETECT_DEPS)) += fate-filter-metadata-freezedetect +fate-filter-metadata-freezedetect: CMD = run $(FILTER_METADATA_COMMAND) "sws_flags=+accurate_rnd+bitexact;mptestsrc=r=25:d=10:m=51,freezedetect" + +SIGNALSTATS_DEPS = FFPROBE AVDEVICE LAVFI_INDEV COLOR_FILTER SCALE_FILTER SIGNALSTATS_FILTER +FATE_METADATA_FILTER-$(call ALLYES, $(SIGNALSTATS_DEPS)) += fate-filter-metadata-signalstats-yuv420p fate-filter-metadata-signalstats-yuv420p10 +fate-filter-metadata-signalstats-yuv420p: CMD = run $(FILTER_METADATA_COMMAND) "sws_flags=+accurate_rnd+bitexact;color=white:duration=1:r=1,signalstats" +fate-filter-metadata-signalstats-yuv420p10: CMD = run $(FILTER_METADATA_COMMAND) "sws_flags=+accurate_rnd+bitexact;color=white:duration=1:r=1,format=yuv420p10,signalstats" + SILENCEDETECT_DEPS = FFPROBE AVDEVICE LAVFI_INDEV AMOVIE_FILTER TTA_DEMUXER TTA_DECODER SILENCEDETECT_FILTER FATE_METADATA_FILTER-$(call ALLYES, $(SILENCEDETECT_DEPS)) += fate-filter-metadata-silencedetect fate-filter-metadata-silencedetect: SRC = $(TARGET_SAMPLES)/lossless-audio/inside.tta -fate-filter-metadata-silencedetect: CMD = run $(FILTER_METADATA_COMMAND) "amovie='$(SRC)',silencedetect=n=-33.5dB:d=.2" +fate-filter-metadata-silencedetect: CMD = run $(FILTER_METADATA_COMMAND) "amovie='$(SRC)',silencedetect=n=-33.5dB:d=0.2" EBUR128_METADATA_DEPS = FFPROBE AVDEVICE LAVFI_INDEV AMOVIE_FILTER FLAC_DEMUXER FLAC_DECODER EBUR128_FILTER FATE_METADATA_FILTER-$(call ALLYES, $(EBUR128_METADATA_DEPS)) += fate-filter-metadata-ebur128 diff --git a/tests/fate/gapless.mak b/tests/fate/gapless.mak index 91fddb4130a..3a82c3ef680 100644 --- a/tests/fate/gapless.mak +++ b/tests/fate/gapless.mak @@ -2,7 +2,7 @@ FATE_GAPLESS-$(CONFIG_MP3_DEMUXER) += fate-gapless-mp3 fate-gapless-mp3: CMD = gapless $(TARGET_SAMPLES)/gapless/gapless.mp3 "-c:a mp3" FATE_GAPLESS-$(CONFIG_MP3_DEMUXER) += fate-audiomatch-square-mp3 -fate-audiomatch-square-mp3: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/square3.mp3 $(TARGET_SAMPLES)/audiomatch/square3.wav +fate-audiomatch-square-mp3: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/square3.mp3 $(SAMPLES)/audiomatch/square3.wav FATE_GAPLESS-$(CONFIG_MOV_DEMUXER) += fate-audiomatch-square-aac FATE_GAPLESS-$(CONFIG_MOV_DEMUXER) += fate-audiomatch-afconvert-16000-mono-lc-adts fate-audiomatch-afconvert-16000-mono-lc-m4a @@ -40,57 +40,57 @@ FATE_GAPLESS-$(CONFIG_MOV_DEMUXER) += fate-audiomatch-nero-44100-stereo-lc-m4a FATE_GAPLESS-$(CONFIG_MOV_DEMUXER) += fate-audiomatch-quicktime7-44100-stereo-lc-mp4 fate-audiomatch-quicktimeX-44100-stereo-lc-m4a -fate-audiomatch-square-aac: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/square3.m4a $(TARGET_SAMPLES)/audiomatch/square3.wav - -fate-audiomatch-afconvert-16000-mono-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_mono_aac_lc.adts $(TARGET_SAMPLES)/audiomatch/tones_16000_mono.wav -fate-audiomatch-afconvert-16000-mono-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_mono_aac_lc.m4a $(TARGET_SAMPLES)/audiomatch/tones_16000_mono.wav -fate-audiomatch-afconvert-16000-mono-he-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_mono_aac_he.adts $(TARGET_SAMPLES)/audiomatch/tones_16000_mono.wav "-ac 1 -ar 16000" -fate-audiomatch-afconvert-16000-mono-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_mono_aac_he.m4a $(TARGET_SAMPLES)/audiomatch/tones_16000_mono.wav "-ac 1 -ar 16000" -fate-audiomatch-afconvert-16000-stereo-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_stereo_aac_lc.adts $(TARGET_SAMPLES)/audiomatch/tones_16000_stereo.wav -fate-audiomatch-afconvert-16000-stereo-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_stereo_aac_lc.m4a $(TARGET_SAMPLES)/audiomatch/tones_16000_stereo.wav -fate-audiomatch-afconvert-16000-stereo-he-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_stereo_aac_he.adts $(TARGET_SAMPLES)/audiomatch/tones_16000_stereo.wav "-ar 16000" -fate-audiomatch-afconvert-16000-stereo-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_stereo_aac_he.m4a $(TARGET_SAMPLES)/audiomatch/tones_16000_stereo.wav "-ar 16000" -fate-audiomatch-afconvert-16000-stereo-he2-adts:CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_stereo_aac_he2.adts $(TARGET_SAMPLES)/audiomatch/tones_16000_stereo.wav "-ar 16000" -fate-audiomatch-afconvert-16000-stereo-he2-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_stereo_aac_he2.m4a $(TARGET_SAMPLES)/audiomatch/tones_16000_stereo.wav "-ar 16000" -fate-audiomatch-afconvert-44100-mono-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_mono_aac_lc.adts $(TARGET_SAMPLES)/audiomatch/tones_44100_mono.wav -fate-audiomatch-afconvert-44100-mono-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_mono_aac_lc.m4a $(TARGET_SAMPLES)/audiomatch/tones_44100_mono.wav -fate-audiomatch-afconvert-44100-mono-he-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_mono_aac_he.adts $(TARGET_SAMPLES)/audiomatch/tones_44100_mono.wav "-ac 1" -fate-audiomatch-afconvert-44100-mono-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_mono_aac_he.m4a $(TARGET_SAMPLES)/audiomatch/tones_44100_mono.wav "-ac 1" -fate-audiomatch-afconvert-44100-stereo-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_stereo_aac_lc.adts $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav -fate-audiomatch-afconvert-44100-stereo-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_stereo_aac_lc.m4a $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav -fate-audiomatch-afconvert-44100-stereo-he-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_stereo_aac_he.adts $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav -fate-audiomatch-afconvert-44100-stereo-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_stereo_aac_he.m4a $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav -fate-audiomatch-afconvert-44100-stereo-he2-adts:CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_stereo_aac_he2.adts $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav -fate-audiomatch-afconvert-44100-stereo-he2-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_stereo_aac_he2.m4a $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav - -fate-audiomatch-dolby-44100-mono-lc-mp4: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_dolby_44100_mono_aac_lc.mp4 $(TARGET_SAMPLES)/audiomatch/tones_44100_mono.wav -fate-audiomatch-dolby-44100-mono-he-mp4: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_dolby_44100_mono_aac_he.mp4 $(TARGET_SAMPLES)/audiomatch/tones_44100_mono.wav "-ac 1" -fate-audiomatch-dolby-44100-stereo-lc-mp4: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_dolby_44100_stereo_aac_lc.mp4 $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav -fate-audiomatch-dolby-44100-stereo-he-mp4: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_dolby_44100_stereo_aac_he.mp4 $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav -fate-audiomatch-dolby-44100-stereo-he2-mp4: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_dolby_44100_stereo_aac_he2.mp4 $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav - -fate-audiomatch-faac-16000-mono-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_16000_mono_aac_lc.adts $(TARGET_SAMPLES)/audiomatch/tones_16000_mono.wav -fate-audiomatch-faac-16000-mono-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_16000_mono_aac_lc.m4a $(TARGET_SAMPLES)/audiomatch/tones_16000_mono.wav -fate-audiomatch-faac-16000-stereo-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_16000_stereo_aac_lc.adts $(TARGET_SAMPLES)/audiomatch/tones_16000_stereo.wav -fate-audiomatch-faac-16000-stereo-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_16000_stereo_aac_lc.m4a $(TARGET_SAMPLES)/audiomatch/tones_16000_stereo.wav -fate-audiomatch-faac-44100-mono-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_44100_mono_aac_lc.adts $(TARGET_SAMPLES)/audiomatch/tones_44100_mono.wav -fate-audiomatch-faac-44100-mono-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_44100_mono_aac_lc.m4a $(TARGET_SAMPLES)/audiomatch/tones_44100_mono.wav -fate-audiomatch-faac-44100-stereo-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_44100_stereo_aac_lc.adts $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav -fate-audiomatch-faac-44100-stereo-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_44100_stereo_aac_lc.m4a $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav - -fate-audiomatch-nero-16000-mono-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_16000_mono_aac_lc.m4a $(TARGET_SAMPLES)/audiomatch/tones_16000_mono.wav -fate-audiomatch-nero-16000-mono-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_16000_mono_aac_he.m4a $(TARGET_SAMPLES)/audiomatch/tones_16000_mono.wav -fate-audiomatch-nero-16000-stereo-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_16000_stereo_aac_lc.m4a $(TARGET_SAMPLES)/audiomatch/tones_16000_stereo.wav -fate-audiomatch-nero-16000-stereo-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_16000_stereo_aac_he.m4a $(TARGET_SAMPLES)/audiomatch/tones_16000_stereo.wav -fate-audiomatch-nero-16000-stereo-he2-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_16000_stereo_aac_he2.m4a $(TARGET_SAMPLES)/audiomatch/tones_16000_stereo.wav -fate-audiomatch-nero-44100-mono-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_44100_mono_aac_lc.m4a $(TARGET_SAMPLES)/audiomatch/tones_44100_mono.wav -fate-audiomatch-nero-44100-mono-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_44100_mono_aac_he.m4a $(TARGET_SAMPLES)/audiomatch/tones_44100_mono.wav -fate-audiomatch-nero-44100-stereo-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_44100_stereo_aac_lc.m4a $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav -fate-audiomatch-nero-44100-stereo-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_44100_stereo_aac_he.m4a $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav -fate-audiomatch-nero-44100-stereo-he2-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_44100_stereo_aac_he2.m4a $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav - -fate-audiomatch-quicktime7-44100-stereo-lc-mp4: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_quicktime7_44100_stereo_aac_lc.mp4 $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav -fate-audiomatch-quicktimeX-44100-stereo-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_quicktimeX_44100_stereo_aac_lc.m4a $(TARGET_SAMPLES)/audiomatch/tones_44100_stereo.wav +fate-audiomatch-square-aac: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/square3.m4a $(SAMPLES)/audiomatch/square3.wav + +fate-audiomatch-afconvert-16000-mono-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_mono_aac_lc.adts $(SAMPLES)/audiomatch/tones_16000_mono.wav +fate-audiomatch-afconvert-16000-mono-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_mono_aac_lc.m4a $(SAMPLES)/audiomatch/tones_16000_mono.wav +fate-audiomatch-afconvert-16000-mono-he-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_mono_aac_he.adts $(SAMPLES)/audiomatch/tones_16000_mono.wav "-ac 1 -ar 16000" +fate-audiomatch-afconvert-16000-mono-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_mono_aac_he.m4a $(SAMPLES)/audiomatch/tones_16000_mono.wav "-ac 1 -ar 16000" +fate-audiomatch-afconvert-16000-stereo-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_stereo_aac_lc.adts $(SAMPLES)/audiomatch/tones_16000_stereo.wav +fate-audiomatch-afconvert-16000-stereo-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_stereo_aac_lc.m4a $(SAMPLES)/audiomatch/tones_16000_stereo.wav +fate-audiomatch-afconvert-16000-stereo-he-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_stereo_aac_he.adts $(SAMPLES)/audiomatch/tones_16000_stereo.wav "-ar 16000" +fate-audiomatch-afconvert-16000-stereo-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_stereo_aac_he.m4a $(SAMPLES)/audiomatch/tones_16000_stereo.wav "-ar 16000" +fate-audiomatch-afconvert-16000-stereo-he2-adts:CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_stereo_aac_he2.adts $(SAMPLES)/audiomatch/tones_16000_stereo.wav "-ar 16000" +fate-audiomatch-afconvert-16000-stereo-he2-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_16000_stereo_aac_he2.m4a $(SAMPLES)/audiomatch/tones_16000_stereo.wav "-ar 16000" +fate-audiomatch-afconvert-44100-mono-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_mono_aac_lc.adts $(SAMPLES)/audiomatch/tones_44100_mono.wav +fate-audiomatch-afconvert-44100-mono-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_mono_aac_lc.m4a $(SAMPLES)/audiomatch/tones_44100_mono.wav +fate-audiomatch-afconvert-44100-mono-he-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_mono_aac_he.adts $(SAMPLES)/audiomatch/tones_44100_mono.wav "-ac 1" +fate-audiomatch-afconvert-44100-mono-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_mono_aac_he.m4a $(SAMPLES)/audiomatch/tones_44100_mono.wav "-ac 1" +fate-audiomatch-afconvert-44100-stereo-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_stereo_aac_lc.adts $(SAMPLES)/audiomatch/tones_44100_stereo.wav +fate-audiomatch-afconvert-44100-stereo-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_stereo_aac_lc.m4a $(SAMPLES)/audiomatch/tones_44100_stereo.wav +fate-audiomatch-afconvert-44100-stereo-he-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_stereo_aac_he.adts $(SAMPLES)/audiomatch/tones_44100_stereo.wav +fate-audiomatch-afconvert-44100-stereo-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_stereo_aac_he.m4a $(SAMPLES)/audiomatch/tones_44100_stereo.wav +fate-audiomatch-afconvert-44100-stereo-he2-adts:CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_stereo_aac_he2.adts $(SAMPLES)/audiomatch/tones_44100_stereo.wav +fate-audiomatch-afconvert-44100-stereo-he2-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_afconvert_44100_stereo_aac_he2.m4a $(SAMPLES)/audiomatch/tones_44100_stereo.wav + +fate-audiomatch-dolby-44100-mono-lc-mp4: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_dolby_44100_mono_aac_lc.mp4 $(SAMPLES)/audiomatch/tones_44100_mono.wav +fate-audiomatch-dolby-44100-mono-he-mp4: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_dolby_44100_mono_aac_he.mp4 $(SAMPLES)/audiomatch/tones_44100_mono.wav "-ac 1" +fate-audiomatch-dolby-44100-stereo-lc-mp4: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_dolby_44100_stereo_aac_lc.mp4 $(SAMPLES)/audiomatch/tones_44100_stereo.wav +fate-audiomatch-dolby-44100-stereo-he-mp4: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_dolby_44100_stereo_aac_he.mp4 $(SAMPLES)/audiomatch/tones_44100_stereo.wav +fate-audiomatch-dolby-44100-stereo-he2-mp4: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_dolby_44100_stereo_aac_he2.mp4 $(SAMPLES)/audiomatch/tones_44100_stereo.wav + +fate-audiomatch-faac-16000-mono-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_16000_mono_aac_lc.adts $(SAMPLES)/audiomatch/tones_16000_mono.wav +fate-audiomatch-faac-16000-mono-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_16000_mono_aac_lc.m4a $(SAMPLES)/audiomatch/tones_16000_mono.wav +fate-audiomatch-faac-16000-stereo-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_16000_stereo_aac_lc.adts $(SAMPLES)/audiomatch/tones_16000_stereo.wav +fate-audiomatch-faac-16000-stereo-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_16000_stereo_aac_lc.m4a $(SAMPLES)/audiomatch/tones_16000_stereo.wav +fate-audiomatch-faac-44100-mono-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_44100_mono_aac_lc.adts $(SAMPLES)/audiomatch/tones_44100_mono.wav +fate-audiomatch-faac-44100-mono-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_44100_mono_aac_lc.m4a $(SAMPLES)/audiomatch/tones_44100_mono.wav +fate-audiomatch-faac-44100-stereo-lc-adts: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_44100_stereo_aac_lc.adts $(SAMPLES)/audiomatch/tones_44100_stereo.wav +fate-audiomatch-faac-44100-stereo-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_faac_44100_stereo_aac_lc.m4a $(SAMPLES)/audiomatch/tones_44100_stereo.wav + +fate-audiomatch-nero-16000-mono-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_16000_mono_aac_lc.m4a $(SAMPLES)/audiomatch/tones_16000_mono.wav +fate-audiomatch-nero-16000-mono-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_16000_mono_aac_he.m4a $(SAMPLES)/audiomatch/tones_16000_mono.wav +fate-audiomatch-nero-16000-stereo-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_16000_stereo_aac_lc.m4a $(SAMPLES)/audiomatch/tones_16000_stereo.wav +fate-audiomatch-nero-16000-stereo-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_16000_stereo_aac_he.m4a $(SAMPLES)/audiomatch/tones_16000_stereo.wav +fate-audiomatch-nero-16000-stereo-he2-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_16000_stereo_aac_he2.m4a $(SAMPLES)/audiomatch/tones_16000_stereo.wav +fate-audiomatch-nero-44100-mono-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_44100_mono_aac_lc.m4a $(SAMPLES)/audiomatch/tones_44100_mono.wav +fate-audiomatch-nero-44100-mono-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_44100_mono_aac_he.m4a $(SAMPLES)/audiomatch/tones_44100_mono.wav +fate-audiomatch-nero-44100-stereo-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_44100_stereo_aac_lc.m4a $(SAMPLES)/audiomatch/tones_44100_stereo.wav +fate-audiomatch-nero-44100-stereo-he-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_44100_stereo_aac_he.m4a $(SAMPLES)/audiomatch/tones_44100_stereo.wav +fate-audiomatch-nero-44100-stereo-he2-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_nero_44100_stereo_aac_he2.m4a $(SAMPLES)/audiomatch/tones_44100_stereo.wav + +fate-audiomatch-quicktime7-44100-stereo-lc-mp4: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_quicktime7_44100_stereo_aac_lc.mp4 $(SAMPLES)/audiomatch/tones_44100_stereo.wav +fate-audiomatch-quicktimeX-44100-stereo-lc-m4a: CMD = audio_match $(TARGET_SAMPLES)/audiomatch/tones_quicktimeX_44100_stereo_aac_lc.m4a $(SAMPLES)/audiomatch/tones_44100_stereo.wav FATE_GAPLESS = $(FATE_GAPLESS-yes) diff --git a/tests/fate/gif.mak b/tests/fate/gif.mak index 9bd9631e379..16d4286a0a5 100644 --- a/tests/fate/gif.mak +++ b/tests/fate/gif.mak @@ -16,7 +16,7 @@ fate-gif-deal: CMD = framecrc -i $(TARGET_SAMPLES)/gif/deal.gif -vsync cfr -pix_ fate-gifenc%: fate-gif-color fate-gifenc%: PIXFMT = $(word 3, $(subst -, ,$(@))) fate-gifenc%: SRC = $(TARGET_SAMPLES)/gif/tc217.gif -fate-gifenc%: CMD = framecrc -i $(SRC) -c:v gif -pix_fmt $(PIXFMT) +fate-gifenc%: CMD = framecrc -i $(SRC) -c:v gif -pix_fmt $(PIXFMT) -sws_flags +accurate_rnd+bitexact FATE_GIF_ENC_PIXFMT = rgb8 bgr8 rgb4_byte bgr4_byte gray pal8 FATE_GIF_ENC-$(call ENCDEC, GIF, GIF) = $(FATE_GIF_ENC_PIXFMT:%=fate-gifenc-%) diff --git a/tests/fate/h264.mak b/tests/fate/h264.mak index f14b46c6e05..13a596e52b4 100644 --- a/tests/fate/h264.mak +++ b/tests/fate/h264.mak @@ -196,7 +196,8 @@ FATE_H264 := $(FATE_H264:%=fate-h264-conformance-%) \ fate-h264-3386 \ fate-h264-missing-frame \ fate-h264-ref-pic-mod-overflow \ - fate-h264-timecode + fate-h264-timecode \ + fate-h264-encparams FATE_H264-$(call DEMDEC, H264, H264) += $(FATE_H264) FATE_H264-$(call DEMDEC, MOV, H264) += fate-h264-crop-to-container @@ -446,3 +447,6 @@ fate-h264-timecode: CMD = framecrc -i $(TARGET_SAM fate-h264-reinit-%: CMD = framecrc -i $(TARGET_SAMPLES)/h264/$(@:fate-h264-%=%).h264 -vf format=yuv444p10le,scale=w=352:h=288 fate-h264-dts_5frames: CMD = probeframes $(TARGET_SAMPLES)/h264/dts_5frames.mkv + +fate-h264-encparams: CMD = venc_data $(TARGET_SAMPLES)/h264-conformance/FRext/FRExt_MMCO4_Sony_B.264 0 1 +FATE_SAMPLES_DUMP_DATA += fate-h264-encparams diff --git a/tests/fate/hevc.mak b/tests/fate/hevc.mak index 559c3898bc8..65c5a262e93 100644 --- a/tests/fate/hevc.mak +++ b/tests/fate/hevc.mak @@ -160,6 +160,8 @@ HEVC_SAMPLES_422_10BIT = \ HEVC_SAMPLES_422_10BIN = \ Main_422_10_A_RExt_Sony_1 \ + +HEVC_SAMPLES_422_10BIN_LARGE = \ Main_422_10_B_RExt_Sony_1 \ HEVC_SAMPLES_444_8BIT = \ @@ -169,6 +171,8 @@ HEVC_SAMPLES_444_12BIT = \ IPCM_B_RExt_NEC \ PERSIST_RPARAM_A_RExt_Sony_1\ PERSIST_RPARAM_A_RExt_Sony_3\ + +HEVC_SAMPLES_444_12BIT_LARGE = \ SAO_A_RExt_MediaTek_1 \ @@ -206,6 +210,11 @@ FATE_HEVC += fate-hevc-conformance-$(1) fate-hevc-conformance-$(1): CMD = framecrc -flags unaligned -i $(TARGET_SAMPLES)/hevc-conformance/$(1).bin -pix_fmt yuv422p10le endef +define FATE_HEVC_TEST_422_10BIN_LARGE +FATE_HEVC_LARGE += fate-hevc-conformance-$(1) +fate-hevc-conformance-$(1): CMD = framecrc -flags unaligned -i $(TARGET_SAMPLES)/hevc-conformance/$(1).bin -pix_fmt yuv422p10le +endef + define FATE_HEVC_TEST_444_8BIT FATE_HEVC += fate-hevc-conformance-$(1) fate-hevc-conformance-$(1): CMD = framecrc -flags unaligned -i $(TARGET_SAMPLES)/hevc-conformance/$(1).bit -pix_fmt yuv444p @@ -216,18 +225,22 @@ FATE_HEVC += fate-hevc-conformance-$(1) fate-hevc-conformance-$(1): CMD = framecrc -flags unaligned -i $(TARGET_SAMPLES)/hevc-conformance/$(1).bit -pix_fmt yuv444p12le endef +define FATE_HEVC_TEST_444_12BIT_LARGE +FATE_HEVC_LARGE += fate-hevc-conformance-$(1) +fate-hevc-conformance-$(1): CMD = framecrc -flags unaligned -i $(TARGET_SAMPLES)/hevc-conformance/$(1).bit -pix_fmt yuv444p12le +endef + $(foreach N,$(HEVC_SAMPLES),$(eval $(call FATE_HEVC_TEST,$(N)))) $(foreach N,$(HEVC_SAMPLES_10BIT),$(eval $(call FATE_HEVC_TEST_10BIT,$(N)))) $(foreach N,$(HEVC_SAMPLES_422_10BIT),$(eval $(call FATE_HEVC_TEST_422_10BIT,$(N)))) $(foreach N,$(HEVC_SAMPLES_422_10BIN),$(eval $(call FATE_HEVC_TEST_422_10BIN,$(N)))) +$(foreach N,$(HEVC_SAMPLES_422_10BIN_LARGE),$(eval $(call FATE_HEVC_TEST_422_10BIN_LARGE,$(N)))) $(foreach N,$(HEVC_SAMPLES_444_8BIT),$(eval $(call FATE_HEVC_TEST_444_8BIT,$(N)))) $(foreach N,$(HEVC_SAMPLES_444_12BIT),$(eval $(call FATE_HEVC_TEST_444_12BIT,$(N)))) +$(foreach N,$(HEVC_SAMPLES_444_12BIT_LARGE),$(eval $(call FATE_HEVC_TEST_444_12BIT_LARGE,$(N)))) fate-hevc-paramchange-yuv420p-yuv420p10: CMD = framecrc -vsync 0 -i $(TARGET_SAMPLES)/hevc/paramchange_yuv420p_yuv420p10.hevc -sws_flags area+accurate_rnd+bitexact -FATE_HEVC += fate-hevc-paramchange-yuv420p-yuv420p10 - -fate-hevc-paired-fields: CMD = probeframes -show_entries frame=interlaced_frame,top_field_first $(TARGET_SAMPLES)/hevc/paired_fields.hevc -FATE_HEVC_FFPROBE-$(call DEMDEC, HEVC, HEVC) += fate-hevc-paired-fields +FATE_HEVC_LARGE += fate-hevc-paramchange-yuv420p-yuv420p10 tests/data/hevc-mp4.mov: TAG = GEN tests/data/hevc-mp4.mov: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data @@ -244,18 +257,25 @@ fate-hevc-skiploopfilter: CMD = framemd5 -skip_loop_filter nokey -i $(TARGET_SAM FATE_HEVC += fate-hevc-skiploopfilter FATE_HEVC-$(call DEMDEC, HEVC, HEVC) += $(FATE_HEVC) +FATE_HEVC-$(call ALLYES, HEVC_DEMUXER HEVC_DECODER LARGE_TESTS) += $(FATE_HEVC_LARGE) # this sample has two stsd entries and needs to reload extradata FATE_HEVC-$(call DEMDEC, MOV, HEVC) += fate-hevc-extradata-reload fate-hevc-extradata-reload: CMD = framemd5 -i $(TARGET_SAMPLES)/hevc/extradata-reload-multi-stsd.mov -sws_flags bitexact +fate-hevc-paired-fields: CMD = probeframes -show_entries frame=interlaced_frame,top_field_first $(TARGET_SAMPLES)/hevc/paired_fields.hevc +FATE_HEVC_FFPROBE-$(call DEMDEC, HEVC, HEVC) += fate-hevc-paired-fields + fate-hevc-monochrome-crop: CMD = probeframes -show_entries frame=width,height:stream=width,height $(TARGET_SAMPLES)/hevc/hevc-monochrome.hevc FATE_HEVC_FFPROBE-$(call DEMDEC, HEVC, HEVC) += fate-hevc-monochrome-crop fate-hevc-two-first-slice: CMD = threads=2 framemd5 -i $(TARGET_SAMPLES)/hevc/two_first_slice.mp4 -sws_flags bitexact -t 00:02.00 -an FATE_HEVC-$(call DEMDEC, MOV, HEVC) += fate-hevc-two-first-slice +fate-hevc-cabac-tudepth: CMD = framecrc -flags unaligned -i $(TARGET_SAMPLES)/hevc/cbf_cr_cb_TUDepth_4_circle.h265 -pix_fmt yuv444p +FATE_HEVC-$(call DEMDEC, HEVC, HEVC) += fate-hevc-cabac-tudepth + FATE_SAMPLES_AVCONV += $(FATE_HEVC-yes) FATE_SAMPLES_FFPROBE += $(FATE_HEVC_FFPROBE-yes) diff --git a/tests/fate/hlsenc.mak b/tests/fate/hlsenc.mak index 98d67f96dfb..43f93c8c5bb 100644 --- a/tests/fate/hlsenc.mak +++ b/tests/fate/hlsenc.mak @@ -5,7 +5,7 @@ tests/data/live_no_endlist.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data -hls_flags omit_endlist -codec:a mp2fixed -hls_segment_filename $(TARGET_PATH)/tests/data/live_no_endlist_%03d.ts \ $(TARGET_PATH)/tests/data/live_no_endlist.m3u8 2>/dev/null -FATE_AFILTER-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-live-no-endlist +FATE_HLSENC-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-live-no-endlist fate-hls-live-no-endlist: tests/data/live_no_endlist.m3u8 fate-hls-live-no-endlist: SRC = $(TARGET_PATH)/tests/data/live_no_endlist.m3u8 fate-hls-live-no-endlist: CMD = md5 -i $(SRC) -af hdcd=process_stereo=false -t 6 -f s24le @@ -19,7 +19,7 @@ tests/data/live_last_endlist.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data -codec:a mp2fixed -hls_segment_filename $(TARGET_PATH)/tests/data/live_last_endlist_%03d.ts \ $(TARGET_PATH)/tests/data/live_last_endlist.m3u8 2>/dev/null -FATE_AFILTER-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-live-last-endlist +FATE_HLSENC-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-live-last-endlist fate-hls-live-last-endlist: tests/data/live_last_endlist.m3u8 fate-hls-live-last-endlist: SRC = $(TARGET_PATH)/tests/data/live_last_endlist.m3u8 fate-hls-live-last-endlist: CMD = md5 -i $(SRC) -af hdcd=process_stereo=false -t 6 -f s24le @@ -34,7 +34,7 @@ tests/data/live_endlist.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data -hls_list_size 0 -codec:a mp2fixed -hls_segment_filename $(TARGET_PATH)/tests/data/live_endlist_%d.ts \ $(TARGET_PATH)/tests/data/live_endlist.m3u8 2>/dev/null -FATE_AFILTER-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-live-endlist +FATE_HLSENC-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-live-endlist fate-hls-live-endlist: tests/data/live_endlist.m3u8 fate-hls-live-endlist: SRC = $(TARGET_PATH)/tests/data/live_endlist.m3u8 fate-hls-live-endlist: CMD = md5 -i $(SRC) -af hdcd=process_stereo=false -t 20 -f s24le @@ -48,10 +48,21 @@ tests/data/hls_segment_size.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data -hls_list_size 0 -codec:a mp2fixed -hls_segment_filename $(TARGET_PATH)/tests/data/hls_segment_size_%d.ts \ $(TARGET_PATH)/tests/data/hls_segment_size.m3u8 2>/dev/null -FATE_AFILTER-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-segment-size +FATE_HLSENC-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-segment-size fate-hls-segment-size: tests/data/hls_segment_size.m3u8 fate-hls-segment-size: CMD = framecrc -flags +bitexact -i $(TARGET_PATH)/tests/data/hls_segment_size.m3u8 -vf setpts=N*23 +tests/data/hls_segment_single.m3u8: TAG = GEN +tests/data/hls_segment_single.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data + $(M)$(TARGET_EXEC) $(TARGET_PATH)/$< \ + -f lavfi -i "aevalsrc=cos(2*PI*t)*sin(2*PI*(440+4*t)*t):d=20" -f hls -hls_flags single_file -map 0 \ + -hls_list_size 0 -codec:a mp2fixed -hls_segment_filename $(TARGET_PATH)/tests/data/hls_segment_single.ts \ + $(TARGET_PATH)/tests/data/hls_segment_single.m3u8 2>/dev/null + +FATE_HLSENC-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-segment-single +fate-hls-segment-single: tests/data/hls_segment_single.m3u8 +fate-hls-segment-single: CMD = framecrc -flags +bitexact -i $(TARGET_PATH)/tests/data/hls_segment_single.m3u8 -vf setpts=N*23 + tests/data/hls_init_time.m3u8: TAG = GEN tests/data/hls_init_time.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data $(M)$(TARGET_EXEC) $(TARGET_PATH)/$< \ @@ -59,7 +70,7 @@ tests/data/hls_init_time.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data -hls_list_size 5 -codec:a mp2fixed -hls_segment_filename $(TARGET_PATH)/tests/data/hls_init_time_%d.ts \ $(TARGET_PATH)/tests/data/hls_init_time.m3u8 2>/dev/null -FATE_AFILTER-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-init-time +FATE_HLSENC-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-init-time fate-hls-init-time: tests/data/hls_init_time.m3u8 fate-hls-init-time: CMD = framecrc -flags +bitexact -i $(TARGET_PATH)/tests/data/hls_init_time.m3u8 -vf setpts=N*23 @@ -70,7 +81,7 @@ tests/data/hls_list_size.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data -hls_list_size 4 -codec:a mp2fixed -hls_segment_filename $(TARGET_PATH)/tests/data/hls_list_size_%d.ts \ $(TARGET_PATH)/tests/data/hls_list_size.m3u8 2>/dev/null -FATE_AFILTER-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-list-size +FATE_HLSENC-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-list-size fate-hls-list-size: tests/data/hls_list_size.m3u8 fate-hls-list-size: CMD = framecrc -flags +bitexact -i $(TARGET_PATH)/tests/data/hls_list_size.m3u8 -vf setpts=N*23 @@ -82,7 +93,9 @@ tests/data/hls_segment_type_fmp4.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data -hls_time 1 -hls_segment_filename "$(TARGET_PATH)/tests/data/hls_fmp4_%d.m4s" \ $(TARGET_PATH)/tests/data/hls_fmp4.m3u8 2>/dev/null -FATE_AFILTER-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-fmp4 +FATE_HLSENC-$(call ALLYES, HLS_DEMUXER MPEGTS_MUXER MPEGTS_DEMUXER AEVALSRC_FILTER LAVFI_INDEV MP2FIXED_ENCODER) += fate-hls-fmp4 fate-hls-fmp4: tests/data/hls_segment_type_fmp4.m3u8 fate-hls-fmp4: CMD = framecrc -flags +bitexact -i $(TARGET_PATH)/tests/data/hls_fmp4.m3u8 -vf setpts=N*23 +FATE_FFMPEG += $(FATE_HLSENC-yes) +fate-hlsenc: $(FATE_HLSENC-yes) diff --git a/tests/fate/image.mak b/tests/fate/image.mak index 121405aab9c..44fd06c294a 100644 --- a/tests/fate/image.mak +++ b/tests/fate/image.mak @@ -98,154 +98,154 @@ FATE_SAMPLES_AVCONV-$(call PARSERDEMDEC, DPX, IMAGE2PIPE, DPX) += fate-dpxparser fate-dpxparser: CMD = framecrc -f image2pipe -i $(TARGET_SAMPLES)/dpx/lena_4x_concat.dpx -sws_flags +accurate_rnd+bitexact FATE_EXR += fate-exr-slice-raw -fate-exr-slice-raw: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_slice_raw.exr -pix_fmt rgba64le +fate-exr-slice-raw: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_slice_raw.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-slice-rle -fate-exr-slice-rle: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_slice_rle.exr -pix_fmt rgba64le +fate-exr-slice-rle: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_slice_rle.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-slice-zip1 -fate-exr-slice-zip1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_slice_zip1.exr -pix_fmt rgba64le +fate-exr-slice-zip1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_slice_zip1.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-slice-zip16 -fate-exr-slice-zip16: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_slice_zip16.exr -pix_fmt rgba64le +fate-exr-slice-zip16: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_slice_zip16.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-slice-pxr24 -fate-exr-slice-pxr24: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_slice_pxr24.exr -pix_fmt rgb48le +fate-exr-slice-pxr24: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_slice_pxr24.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-pxr24-float-12x8 -fate-exr-rgb-scanline-pxr24-float-12x8: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_pxr24_float_12x8.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-pxr24-float-12x8: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_pxr24_float_12x8.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgba-multiscanline-half-b44 -fate-exr-rgba-multiscanline-half-b44: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_multiscanline_half_b44.exr -pix_fmt rgba64le +fate-exr-rgba-multiscanline-half-b44: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_multiscanline_half_b44.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-scanline-float-b44 -fate-exr-rgb-scanline-float-b44: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_float_b44.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-float-b44: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_float_b44.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-half-b44-12x8 -fate-exr-rgb-scanline-half-b44-12x8: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_b44_12x8.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-half-b44-12x8: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_b44_12x8.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-half-b44-13x9 -fate-exr-rgb-scanline-half-b44-13x9: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_b44_13x9.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-half-b44-13x9: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_b44_13x9.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-tile-float-raw-12x8 -fate-exr-rgb-tile-float-raw-12x8: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_float_raw_12x8.exr -pix_fmt rgb48le +fate-exr-rgb-tile-float-raw-12x8: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_float_raw_12x8.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-tile-float-raw-150x130 -fate-exr-rgb-tile-float-raw-150x130: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_float_raw_150x130.exr -pix_fmt rgb48le +fate-exr-rgb-tile-float-raw-150x130: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_float_raw_150x130.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-tile-half-raw-12x8 -fate-exr-rgb-tile-half-raw-12x8: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_half_raw_12x8.exr -pix_fmt rgb48le +fate-exr-rgb-tile-half-raw-12x8: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_half_raw_12x8.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgba-scanline-float-half-b44-13x9-l1 -fate-exr-rgba-scanline-float-half-b44-13x9-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44_13x9.exr -pix_fmt rgba64le +fate-exr-rgba-scanline-float-half-b44-13x9-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44_13x9.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgba-scanline-float-half-b44-13x9-l2 -fate-exr-rgba-scanline-float-half-b44-13x9-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44_13x9.exr -pix_fmt rgba64le +fate-exr-rgba-scanline-float-half-b44-13x9-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44_13x9.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgba-scanline-float-half-b44-12x8-l1 -fate-exr-rgba-scanline-float-half-b44-12x8-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44_12x8.exr -pix_fmt rgba64le +fate-exr-rgba-scanline-float-half-b44-12x8-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44_12x8.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgba-scanline-float-half-b44-12x8-l2 -fate-exr-rgba-scanline-float-half-b44-12x8-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44_12x8.exr -pix_fmt rgba64le +fate-exr-rgba-scanline-float-half-b44-12x8-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44_12x8.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgba-scanline-float-half-b44a-12x8-l1 -fate-exr-rgba-scanline-float-half-b44a-12x8-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44a_12x8.exr -pix_fmt rgba64le +fate-exr-rgba-scanline-float-half-b44a-12x8-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44a_12x8.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgba-scanline-float-half-b44a-12x8-l2 -fate-exr-rgba-scanline-float-half-b44a-12x8-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44a_12x8.exr -pix_fmt rgba64le +fate-exr-rgba-scanline-float-half-b44a-12x8-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44a_12x8.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgba-scanline-float-half-b44a-13x9-l1 -fate-exr-rgba-scanline-float-half-b44a-13x9-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44a_13x9.exr -pix_fmt rgba64le +fate-exr-rgba-scanline-float-half-b44a-13x9-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44a_13x9.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgba-scanline-float-half-b44a-13x9-l2 -fate-exr-rgba-scanline-float-half-b44a-13x9-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44a_13x9.exr -pix_fmt rgba64le +fate-exr-rgba-scanline-float-half-b44a-13x9-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgba_scanline_float_half_b44a_13x9.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-tile-pxr24-float-half-l1 -fate-exr-rgb-tile-pxr24-float-half-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_pxr24_float_half.exr -pix_fmt rgb48le +fate-exr-rgb-tile-pxr24-float-half-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_pxr24_float_half.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-tile-pxr24-float-half-l2 -fate-exr-rgb-tile-pxr24-float-half-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_tile_pxr24_float_half.exr -pix_fmt rgba64le +fate-exr-rgb-tile-pxr24-float-half-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_tile_pxr24_float_half.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-tile-pxr24-half-float-l1 -fate-exr-rgb-tile-pxr24-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_pxr24_half_float.exr -pix_fmt rgb48le +fate-exr-rgb-tile-pxr24-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_pxr24_half_float.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-tile-pxr24-half-float-l2 -fate-exr-rgb-tile-pxr24-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_tile_pxr24_half_float.exr -pix_fmt rgba64le +fate-exr-rgb-tile-pxr24-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_tile_pxr24_half_float.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-tile-half-float-b44-12x8-l1 -fate-exr-rgb-tile-half-float-b44-12x8-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_half_float_b44_12x8.exr -pix_fmt rgb48le +fate-exr-rgb-tile-half-float-b44-12x8-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_half_float_b44_12x8.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-tile-half-float-b44-12x8-l2 -fate-exr-rgb-tile-half-float-b44-12x8-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_tile_half_float_b44_12x8.exr -pix_fmt rgba64le +fate-exr-rgb-tile-half-float-b44-12x8-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_tile_half_float_b44_12x8.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-tile-zip-half-float-l1 -fate-exr-rgb-tile-zip-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_zip_half_float.exr -pix_fmt rgb48le +fate-exr-rgb-tile-zip-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_zip_half_float.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-tile-zip-half-float-l2 -fate-exr-rgb-tile-zip-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_tile_zip_half_float.exr -pix_fmt rgba64le +fate-exr-rgb-tile-zip-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_tile_zip_half_float.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-tile-zip1-half-float-l1 -fate-exr-rgb-tile-zip1-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_zip1_half_float.exr -pix_fmt rgb48le +fate-exr-rgb-tile-zip1-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_zip1_half_float.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-tile-zip1-half-float-l2 -fate-exr-rgb-tile-zip1-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_tile_zip1_half_float.exr -pix_fmt rgba64le +fate-exr-rgb-tile-zip1-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_tile_zip1_half_float.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-tile-rle-half-float-l1 -fate-exr-rgb-tile-rle-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_rle_half_float.exr -pix_fmt rgb48le +fate-exr-rgb-tile-rle-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_rle_half_float.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-tile-rle-half-float-l2 -fate-exr-rgb-tile-rle-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_tile_rle_half_float.exr -pix_fmt rgba64le +fate-exr-rgb-tile-rle-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_tile_rle_half_float.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-tile-raw-half-float-l1 -fate-exr-rgb-tile-raw-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_raw_half_float.exr -pix_fmt rgb48le +fate-exr-rgb-tile-raw-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_raw_half_float.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-tile-raw-half-float-l2 -fate-exr-rgb-tile-raw-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_tile_raw_half_float.exr -pix_fmt rgba64le +fate-exr-rgb-tile-raw-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_tile_raw_half_float.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-scanline-b44-half-float-12x8-l1 -fate-exr-rgb-scanline-b44-half-float-12x8-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_b44_half_float_12x8.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-b44-half-float-12x8-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_b44_half_float_12x8.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-b44-half-float-12x8-l2 -fate-exr-rgb-scanline-b44-half-float-12x8-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_scanline_b44_half_float_12x8.exr -pix_fmt rgba64le +fate-exr-rgb-scanline-b44-half-float-12x8-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_scanline_b44_half_float_12x8.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-scanline-pxr24-half-float-l1 -fate-exr-rgb-scanline-pxr24-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_pxr24_half_float.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-pxr24-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_pxr24_half_float.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-pxr24-half-float-l2 -fate-exr-rgb-scanline-pxr24-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_scanline_pxr24_half_float.exr -pix_fmt rgba64le +fate-exr-rgb-scanline-pxr24-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_scanline_pxr24_half_float.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-scanline-pxr24-float-half-l1 -fate-exr-rgb-scanline-pxr24-float-half-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_pxr24_float_half.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-pxr24-float-half-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_pxr24_float_half.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-pxr24-float-half-l2 -fate-exr-rgb-scanline-pxr24-float-half-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_scanline_pxr24_float_half.exr -pix_fmt rgba64le +fate-exr-rgb-scanline-pxr24-float-half-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_scanline_pxr24_float_half.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-scanline-pxr24-half-uint32-13x9 fate-exr-rgb-scanline-pxr24-half-uint32-13x9: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_pxr24_half_uint32_13x9.exr -pix_fmt rgb48le FATE_EXR += fate-exr-rgb-scanline-zip-half-float-l1 -fate-exr-rgb-scanline-zip-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_zip_half_float.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-zip-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_zip_half_float.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-zip-half-float-l2 -fate-exr-rgb-scanline-zip-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_scanline_zip_half_float.exr -pix_fmt rgba64le +fate-exr-rgb-scanline-zip-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_scanline_zip_half_float.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-scanline-zip1-half-float-l1 -fate-exr-rgb-scanline-zip1-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_zip1_half_float.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-zip1-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_zip1_half_float.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-zip1-half-float-l2 -fate-exr-rgb-scanline-zip1-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_scanline_zip1_half_float.exr -pix_fmt rgba64le +fate-exr-rgb-scanline-zip1-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_scanline_zip1_half_float.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-scanline-rle-half-float-l1 -fate-exr-rgb-scanline-rle-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_rle_half_float.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-rle-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_rle_half_float.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-rle-half-float-l2 -fate-exr-rgb-scanline-rle-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_scanline_rle_half_float.exr -pix_fmt rgba64le +fate-exr-rgb-scanline-rle-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_scanline_rle_half_float.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-scanline-raw-half-float-l1 -fate-exr-rgb-scanline-raw-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_raw_half_float.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-raw-half-float-l1: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_raw_half_float.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-raw-half-float-l2 -fate-exr-rgb-scanline-raw-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_scanline_raw_half_float.exr -pix_fmt rgba64le +fate-exr-rgb-scanline-raw-half-float-l2: CMD = framecrc -layer "VRaySamplerInfo" -i $(TARGET_SAMPLES)/exr/rgb_scanline_raw_half_float.exr -pix_fmt gbrapf32le FATE_EXR += fate-exr-rgb-scanline-b44-uint32 fate-exr-rgb-scanline-b44-uint32: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_b44_uint32.exr -pix_fmt rgb48le @@ -254,38 +254,38 @@ FATE_EXR += fate-exr-rgb-scanline-pxr24-uint32 fate-exr-rgb-scanline-pxr24-uint32: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_pxr24_uint32.exr -pix_fmt rgb48le FATE_EXR += fate-exr-rgb-scanline-zip1-half-float-l1-zero-offsets -fate-exr-rgb-scanline-zip1-half-float-l1-zero-offsets: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_zip1_half_float_zero_offsets.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-zip1-half-float-l1-zero-offsets: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_zip1_half_float_zero_offsets.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-half-piz-bw -fate-exr-rgb-scanline-half-piz-bw: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_piz_bw.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-half-piz-bw: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_piz_bw.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-half-piz-color -fate-exr-rgb-scanline-half-piz-color: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_piz_color.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-half-piz-color: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_piz_color.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-half-piz-dw-t01 -fate-exr-rgb-scanline-half-piz-dw-t01: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_piz_dw_t01.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-half-piz-dw-t01: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_piz_dw_t01.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-float-piz-48x32 -fate-exr-rgb-scanline-float-piz-48x32: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_float_piz_48x32.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-float-piz-48x32: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_float_piz_48x32.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-scanline-none-negative-red -fate-exr-rgb-scanline-none-negative-red: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_none_negative_red.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-none-negative-red: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_none_negative_red.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgb-b44a-half-negative-4x4 -fate-exr-rgb-b44a-half-negative-4x4: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_b44a_half_negative_4x4.exr -pix_fmt rgb48le +fate-exr-rgb-b44a-half-negative-4x4: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_b44a_half_negative_4x4.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-y-tile-zip-half-12x8 -fate-exr-y-tile-zip-half-12x8: CMD = framecrc -i $(TARGET_SAMPLES)/exr/y_tile_zip_half_12x8.exr -pix_fmt gray16le +fate-exr-y-tile-zip-half-12x8: CMD = framecrc -i $(TARGET_SAMPLES)/exr/y_tile_zip_half_12x8.exr -pix_fmt grayf32le FATE_EXR += fate-exr-y-scanline-zip-half-12x8 -fate-exr-y-scanline-zip-half-12x8: CMD = framecrc -i $(TARGET_SAMPLES)/exr/y_scanline_zip_half_12x8.exr -pix_fmt gray16le +fate-exr-y-scanline-zip-half-12x8: CMD = framecrc -i $(TARGET_SAMPLES)/exr/y_scanline_zip_half_12x8.exr -pix_fmt grayf32le FATE_EXR += fate-exr-rgb-scanline-half-piz-dw-t08 -fate-exr-rgb-scanline-half-piz-dw-t08: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_piz_dw_t08.exr -pix_fmt rgb48le +fate-exr-rgb-scanline-half-piz-dw-t08: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_piz_dw_t08.exr -pix_fmt gbrpf32le FATE_EXR += fate-exr-rgba-zip16-16x32-flag4 -fate-exr-rgba-zip16-16x32-flag4: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_zip16_16x32_flag4.exr -pix_fmt rgba64le +fate-exr-rgba-zip16-16x32-flag4: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_zip16_16x32_flag4.exr -pix_fmt gbrapf32le FATE_EXR-$(call DEMDEC, IMAGE2, EXR) += $(FATE_EXR) diff --git a/tests/fate/lavf-container.mak b/tests/fate/lavf-container.mak index 22387d04d85..9e0eed48514 100644 --- a/tests/fate/lavf-container.mak +++ b/tests/fate/lavf-container.mak @@ -56,6 +56,9 @@ fate-lavf-wtv: CMD = lavf_container "" "-c:a mp2 -threads 1" FATE_AVCONV += $(FATE_LAVF_CONTAINER) fate-lavf-container fate-lavf: $(FATE_LAVF_CONTAINER) +FATE_LAVF_CONTAINER_FATE-$(call ALLYES, IVF_DEMUXER AV1_PARSER MOV_MUXER) += av1.mp4 +FATE_LAVF_CONTAINER_FATE-$(call ALLYES, IVF_DEMUXER AV1_PARSER MATROSKA_MUXER) += av1.mkv +FATE_LAVF_CONTAINER_FATE-$(call ALLYES, H264_DEMUXER H264_PARSER MOV_MUXER) += h264.mp4 FATE_LAVF_CONTAINER_FATE-$(call ALLYES, MATROSKA_DEMUXER OGG_MUXER) += vp3.ogg FATE_LAVF_CONTAINER_FATE-$(call ALLYES, MATROSKA_DEMUXER OGV_MUXER) += vp8.ogg FATE_LAVF_CONTAINER_FATE-$(call ALLYES, MOV_DEMUXER LATM_MUXER) += latm @@ -68,6 +71,9 @@ FATE_LAVF_CONTAINER_FATE = $(FATE_LAVF_CONTAINER_FATE-yes:%=fate-lavf-fate-%) $(FATE_LAVF_CONTAINER_FATE): REF = $(SRC_PATH)/tests/ref/lavf-fate/$(@:fate-lavf-fate-%=%) $(FATE_LAVF_CONTAINER_FATE): $(AREF) $(VREF) +fate-lavf-fate-av1.mp4: CMD = lavf_container_fate "av1-test-vectors/av1-1-b8-05-mv.ivf" "" "-c:v copy" +fate-lavf-fate-av1.mkv: CMD = lavf_container_fate "av1-test-vectors/av1-1-b8-05-mv.ivf" "" "-c:v copy" +fate-lavf-fate-h264.mp4: CMD = lavf_container_fate "h264/intra_refresh.h264" "" "-c:v copy" fate-lavf-fate-vp3.ogg: CMD = lavf_container_fate "vp3/coeff_level64.mkv" "-idct auto" fate-lavf-fate-vp8.ogg: CMD = lavf_container_fate "vp8/RRSF49-short.webm" "" "-acodec copy" fate-lavf-fate-latm: CMD = lavf_container_fate "aac/al04_44.mp4" "" "-acodec copy" diff --git a/tests/fate/lavf-video.mak b/tests/fate/lavf-video.mak index 7a70ac90ce0..f6e98246c43 100644 --- a/tests/fate/lavf-video.mak +++ b/tests/fate/lavf-video.mak @@ -6,7 +6,7 @@ FATE_LAVF_VIDEO-$(call ENCDEC, FITS, FITS) += gbrp.fits FATE_LAVF_VIDEO-$(call ENCDEC, FITS, FITS) += gbrap.fits FATE_LAVF_VIDEO-$(call ENCDEC, FITS, FITS) += gbrp16be.fits FATE_LAVF_VIDEO-$(call ENCDEC, FITS, FITS) += gbrap16be.fits -FATE_LAVF_VIDEO-$(call ENCDEC, GIF, FITS) += gif +FATE_LAVF_VIDEO-$(call ENCDEC, GIF, GIF) += gif FATE_LAVF_VIDEO-$(CONFIG_YUV4MPEGPIPE_MUXER) += y4m FATE_LAVF_VIDEO = $(FATE_LAVF_VIDEO-yes:%=fate-lavf-%) diff --git a/tests/fate/libavcodec.mak b/tests/fate/libavcodec.mak index d9b26bae93b..747dae37041 100644 --- a/tests/fate/libavcodec.mak +++ b/tests/fate/libavcodec.mak @@ -53,7 +53,7 @@ fate-h264-levels: REF = /dev/null FATE_LIBAVCODEC-$(CONFIG_HEVC_METADATA_BSF) += fate-h265-levels fate-h265-levels: libavcodec/tests/h265_levels$(EXESUF) -fate-h265-levels: CMD = run libavcodec/tests/h265_levels +fate-h265-levels: CMD = run libavcodec/tests/h265_levels$(EXESUF) fate-h265-levels: REF = /dev/null FATE_LIBAVCODEC-$(CONFIG_IIRFILTER) += fate-iirfilter diff --git a/tests/fate/lossless-audio.mak b/tests/fate/lossless-audio.mak index d29285328d5..66ac6d89723 100644 --- a/tests/fate/lossless-audio.mak +++ b/tests/fate/lossless-audio.mak @@ -13,12 +13,6 @@ fate-lossless-shorten: CMD = md5 -i $(TARGET_SAMPLES)/lossless-audio/luckynight- FATE_SAMPLES_LOSSLESS_AUDIO-$(call DEMDEC, TAK, TAK) += fate-lossless-tak fate-lossless-tak: CMD = crc -i $(TARGET_SAMPLES)/lossless-audio/luckynight-partial.tak -FATE_TRUEHD = fate-lossless-truehd-5.1 fate-lossless-truehd-5.1-downmix-2.0 -fate-lossless-truehd-5.1: CMD = md5 -f truehd -i $(TARGET_SAMPLES)/lossless-audio/truehd_5.1.raw -f s32le -fate-lossless-truehd-5.1-downmix-2.0: CMD = md5 -f truehd -request_channel_layout 2 -i $(TARGET_SAMPLES)/lossless-audio/truehd_5.1.raw -f s32le -fate-lossless-truehd: $(FATE_TRUEHD) -FATE_SAMPLES_LOSSLESS_AUDIO-$(call DEMDEC, TRUEHD, TRUEHD) += $(FATE_TRUEHD) - FATE_SAMPLES_LOSSLESS_AUDIO-$(call DEMDEC, TTA, TTA) += fate-lossless-tta fate-lossless-tta: CMD = crc -i $(TARGET_SAMPLES)/lossless-audio/inside.tta diff --git a/tests/fate/matroska.mak b/tests/fate/matroska.mak index 98a49a4d74f..17fbf466537 100644 --- a/tests/fate/matroska.mak +++ b/tests/fate/matroska.mak @@ -1,13 +1,65 @@ +FATE_MATROSKA-$(call ALLYES, MATROSKA_DEMUXER ZLIB) += fate-matroska-prores-zlib +fate-matroska-prores-zlib: CMD = framecrc -i $(TARGET_SAMPLES)/mkv/prores_zlib.mkv -c:v copy + +# This tests that the matroska demuxer correctly adds the icpf header atom +# upon demuxing; it also tests bz2 decompression and unknown-length cluster. +FATE_MATROSKA-$(call ALLYES, MATROSKA_DEMUXER BZLIB) += fate-matroska-prores-header-insertion-bz2 +fate-matroska-prores-header-insertion-bz2: CMD = framecrc -i $(TARGET_SAMPLES)/mkv/prores_bz2.mkv -map 0 -c copy + # This tests that the matroska demuxer supports modifying the colorspace # properties in remuxing (-c:v copy) # It also tests automatic insertion of the vp9_superframe bitstream filter FATE_MATROSKA-$(call DEMMUX, MATROSKA, MATROSKA) += fate-matroska-remux fate-matroska-remux: CMD = md5pipe -i $(TARGET_SAMPLES)/vp9-test-vectors/vp90-2-2pass-akiyo.webm -color_trc 4 -c:v copy -fflags +bitexact -strict -2 -f matroska fate-matroska-remux: CMP = oneline -fate-matroska-remux: REF = e5457e5fa606d564a54914bd12f426c8 +fate-matroska-remux: REF = 26fabd90326e3de4bb6afe1b216ce232 + +FATE_MATROSKA-$(call ALLYES, MATROSKA_DEMUXER VORBIS_PARSER) += fate-matroska-xiph-lacing +fate-matroska-xiph-lacing: CMD = framecrc -i $(TARGET_SAMPLES)/mkv/xiph_lacing.mka -c:a copy + +# This tests that the Matroska demuxer correctly demuxes WavPack +# without CodecPrivate; it also tests zlib compressed WavPack. +FATE_MATROSKA-$(call ALLYES, MATROSKA_DEMUXER ZLIB) += fate-matroska-wavpack-missing-codecprivate +fate-matroska-wavpack-missing-codecprivate: CMD = framecrc -i $(TARGET_SAMPLES)/mkv/wavpack_missing_codecprivate.mka -c copy + +# This tests that the matroska demuxer supports decompressing +# zlib compressed tracks (both the CodecPrivate as well as the actual frames). +FATE_MATROSKA-$(call ALLYES, MATROSKA_DEMUXER ZLIB) += fate-matroska-zlib-decompression +fate-matroska-zlib-decompression: CMD = framecrc -i $(TARGET_SAMPLES)/mkv/subtitle_zlib.mks -c:s copy + +# This tests that the matroska demuxer can decompress lzo compressed tracks. +FATE_MATROSKA-$(call ALLYES, MATROSKA_DEMUXER LZO) += fate-matroska-lzo-decompression +fate-matroska-lzo-decompression: CMD = framecrc -i $(TARGET_SAMPLES)/mkv/lzo.mka -c copy + +# This tests that the matroska demuxer correctly propagates +# the channel layout contained in vorbis comments in the CodecPrivate +# of flac tracks. It also tests header removal compression. +FATE_MATROSKA-$(call ALLYES, MATROSKA_DEMUXER FLAC_PARSER) += fate-matroska-flac-channel-mapping +fate-matroska-flac-channel-mapping: CMD = framecrc -i $(TARGET_SAMPLES)/mkv/flac_channel_layouts.mka -map 0 -c:a copy + +# This tests that the Matroska muxer writes the channel layout +# of FLAC tracks as a Vorbis comment in the CodecPrivate if necessary +# and that FLAC extradata is correctly updated when a packet +# with sidedata containing new extradata is encountered. +# Furthermore it tests everything the matroska-flac-channel-mapping test +# tests and it also tests the FLAC decoder and encoder, in particular +# the latter's ability to send updated extradata. +FATE_MATROSKA-$(call ALLYES, FLAC_DECODER FLAC_ENCODER FLAC_PARSER \ + MATROSKA_DEMUXER MATROSKA_MUXER) += fate-matroska-flac-extradata-update +fate-matroska-flac-extradata-update: CMD = transcode matroska $(TARGET_SAMPLES)/mkv/flac_channel_layouts.mka \ + matroska "-map 0 -map 0:0 -c flac -frames:a:2 8" "-map 0 -c copy" + +# This test tests demuxing Vorbis and chapters from ogg and muxing it in and +# demuxing it from Matroska/WebM. It furthermore tests the WebM muxer, in +# particular its DASH mode. Finally, it tests writing the Cues at the front. +FATE_MATROSKA_FFMPEG_FFPROBE-$(call ALLYES, MATROSKA_DEMUXER OGG_DEMUXER \ + VORBIS_DECODER VORBIS_PARSER WEBM_MUXER) \ + += fate-webm-dash-chapters +fate-webm-dash-chapters: CMD = transcode ogg $(TARGET_SAMPLES)/vorbis/vorbis_chapter_extension_demo.ogg webm "-c copy -cluster_time_limit 1500 -dash 1 -dash_track_number 124 -reserve_index_space 400" "-c copy -t 0.5" "" -show_chapters FATE_MATROSKA_FFPROBE-$(call ALLYES, MATROSKA_DEMUXER) += fate-matroska-spherical-mono fate-matroska-spherical-mono: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -show_entries stream_side_data_list -select_streams v -v 0 $(TARGET_SAMPLES)/mkv/spherical.mkv FATE_SAMPLES_AVCONV += $(FATE_MATROSKA-yes) FATE_SAMPLES_FFPROBE += $(FATE_MATROSKA_FFPROBE-yes) +FATE_SAMPLES_FFMPEG_FFPROBE += $(FATE_MATROSKA_FFMPEG_FFPROBE-yes) diff --git a/tests/fate/mov.mak b/tests/fate/mov.mak index 8ed66cf1355..7a721d7c957 100644 --- a/tests/fate/mov.mak +++ b/tests/fate/mov.mak @@ -119,7 +119,7 @@ fate-mov-guess-delay-1: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -show_entries stre fate-mov-guess-delay-2: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -show_entries stream=has_b_frames -select_streams v $(TARGET_SAMPLES)/h264/h264_3bf_pyramid_nobsrestriction.mp4 fate-mov-guess-delay-3: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -show_entries stream=has_b_frames -select_streams v $(TARGET_SAMPLES)/h264/h264_4bf_pyramid_nobsrestriction.mp4 -fate-mov-faststart-4gb-overflow: CMD = run tools/qt-faststart$(EXESUF) $(TARGET_SAMPLES)/mov/faststart-4gb-overflow.mov faststart-4gb-overflow-output.mov > /dev/null ; do_md5sum faststart-4gb-overflow-output.mov | cut -d " " -f1 ; rm faststart-4gb-overflow-output.mov +fate-mov-faststart-4gb-overflow: CMD = run tools/qt-faststart$(EXESUF) $(TARGET_SAMPLES)/mov/faststart-4gb-overflow.mov $(TARGET_PATH)/faststart-4gb-overflow-output.mov > /dev/null ; do_md5sum faststart-4gb-overflow-output.mov | cut -d " " -f1 ; rm faststart-4gb-overflow-output.mov fate-mov-faststart-4gb-overflow: CMP = oneline fate-mov-faststart-4gb-overflow: REF = bc875921f151871e787c4b4023269b29 diff --git a/tests/fate/mpegps.mak b/tests/fate/mpegps.mak index cec1ea77fa1..87a8e1480e8 100644 --- a/tests/fate/mpegps.mak +++ b/tests/fate/mpegps.mak @@ -1,6 +1,6 @@ # This tests that a 16-bit pcm_dvd stream is correctly remuxed in mpegps FATE_MPEGPS-$(call DEMMUX, MPEGPS, MPEG1SYSTEM) += fate-mpegps-remuxed-pcm-demux -fate-mpegps-remuxed-pcm-demux: $(TARGET_SAMPLES)/mpegps/pcm_aud.mpg +fate-mpegps-remuxed-pcm-demux: $(SAMPLES)/mpegps/pcm_aud.mpg fate-mpegps-remuxed-pcm-demux: CMD = stream_remux "mpeg" "$(TARGET_SAMPLES)/mpegps/pcm_aud.mpg" "mpeg" "-map 0:a:0" "-codec copy" FATE_SAMPLES_FFMPEG += $(FATE_MPEGPS-yes) diff --git a/tests/fate/mxf.mak b/tests/fate/mxf.mak index 62e4ec01cb6..4aafc1f578b 100644 --- a/tests/fate/mxf.mak +++ b/tests/fate/mxf.mak @@ -34,19 +34,19 @@ fate-mxf-probe-dv25: SRC = $(TARGET_SAMPLES)/mxf/Avid-00005.mxf fate-mxf-probe-dv25: CMD = run $(PROBE_FORMAT_STREAMS_COMMAND) -i "$(SRC)" FATE_MXF_REEL_NAME-$(call ENCDEC2, MPEG2VIDEO, PCM_S16LE, MXF) += fate-mxf-reel_name -fate-mxf-reel_name: $(TARGET_SAMPLES)/mxf/Sony-00001.mxf +fate-mxf-reel_name: $(SAMPLES)/mxf/Sony-00001.mxf fate-mxf-reel_name: CMD = md5 -y -i $(TARGET_SAMPLES)/mxf/Sony-00001.mxf -c copy -timecode 00:00:00:00 -metadata "reel_name=test_reel" -fflags +bitexact -f mxf FATE_MXF_USER_COMMENTS-$(call ENCDEC2, MPEG2VIDEO, PCM_S16LE, MXF) += fate-mxf-user-comments -fate-mxf-user-comments: $(TARGET_SAMPLES)/mxf/Sony-00001.mxf +fate-mxf-user-comments: $(SAMPLES)/mxf/Sony-00001.mxf fate-mxf-user-comments: CMD = md5 -y -i $(TARGET_SAMPLES)/mxf/Sony-00001.mxf -c copy -metadata "comment_test=value" -fflags +bitexact -f mxf FATE_MXF_D10_USER_COMMENTS-$(call ENCDEC2, MPEG2VIDEO, PCM_S16LE, MXF) += fate-mxf-d10-user-comments -fate-mxf-d10-user-comments: $(TARGET_SAMPLES)/mxf/Sony-00001.mxf +fate-mxf-d10-user-comments: $(SAMPLES)/mxf/Sony-00001.mxf fate-mxf-d10-user-comments: CMD = md5 -y -i $(TARGET_SAMPLES)/mxf/Sony-00001.mxf -c copy -metadata "comment_test=value" -store_user_comments 1 -fflags +bitexact -f mxf_d10 FATE_MXF_OPATOM_USER_COMMENTS-$(call ENCDEC2, MPEG2VIDEO, PCM_S16LE, MXF) += fate-mxf-opatom-user-comments -fate-mxf-opatom-user-comments: $(TARGET_SAMPLES)/mxf/Sony-00001.mxf +fate-mxf-opatom-user-comments: $(SAMPLES)/mxf/Sony-00001.mxf fate-mxf-opatom-user-comments: CMD = md5 -y -i $(TARGET_SAMPLES)/mxf/Sony-00001.mxf -an -vcodec copy -metadata "comment_test=value" -fflags +bitexact -f mxf_opatom FATE_MXF-$(CONFIG_MXF_DEMUXER) += $(FATE_MXF) diff --git a/tests/fate/seek.mak b/tests/fate/seek.mak index a15c9e919ae..98d2b54674f 100644 --- a/tests/fate/seek.mak +++ b/tests/fate/seek.mak @@ -64,7 +64,6 @@ FATE_SEEK_VSYNTH_LENA-$(call ENCDEC, ASV1, AVI) += asv1 FATE_SEEK_VSYNTH_LENA-$(call ENCDEC, ASV2, AVI) += asv2 FATE_SEEK_VSYNTH_LENA-$(call ENCDEC, DNXHD, DNXHD) += dnxhd-720p FATE_SEEK_VSYNTH_LENA-$(call ENCDEC, DNXHD, DNXHD) += dnxhd-720p-rd -FATE_SEEK_VSYNTH_LENA-$(call ENCDEC, DNXHD, DNXHD) += dnxhd-4k-hr-lb FATE_SEEK_VSYNTH_LENA-$(call ENCDEC, DNXHD, MOV) += dnxhd-1080i FATE_SEEK_VSYNTH_LENA-$(call ENCDEC, DVVIDEO, DV) += dv FATE_SEEK_VSYNTH_LENA-$(call ENCDEC, DVVIDEO, DV) += dv-411 @@ -80,6 +79,8 @@ FATE_SEEK_VSYNTH_LENA-$(call ENCDEC, JPEGLS, AVI) += jpegls FATE_SEEK_VSYNTH_LENA-$(call ENCDEC, LJPEG MJPEG, AVI) += ljpeg FATE_SEEK_VSYNTH_LENA-$(call ENCDEC, MJPEG, AVI) += mjpeg +FATE_SEEK_VSYNTH_LENA-$(call ALLYES, DNXHD_ENCODER DNXHD_DECODER LARGE_TESTS) += dnxhd-4k-hr-lb + FATE_SEEK_VSYNTH_LENA-$(call ENCDEC, MPEG1VIDEO, MPEG1VIDEO MPEGVIDEO) += \ mpeg1 \ mpeg1b @@ -260,7 +261,7 @@ fate-seek-extra-mp4: CMD = run libavformat/tests/seek$(EXESUF) $(TARGET_SAMPLES fate-seek-empty-edit-mp4: CMD = run libavformat/tests/seek$(EXESUF) $(TARGET_SAMPLES)/mov/empty_edit_5s.mp4 -duration 15 -frames 4 fate-seek-test-iibbibb-mp4: CMD = run libavformat/tests/seek$(EXESUF) $(TARGET_SAMPLES)/mov/test_iibbibb.mp4 -duration 13 -frames 4 fate-seek-test-iibbibb-neg-ctts-mp4: CMD = run libavformat/tests/seek$(EXESUF) $(TARGET_SAMPLES)/mov/test_iibbibb_neg_ctts.mp4 -duration 13 -frames 4 -fate-seek-cache-pipe: CMD = cat $(TARGET_SAMPLES)/gapless/gapless.mp3 | run libavformat/tests/seek$(EXESUF) cache:pipe:0 -read_ahead_limit -1 +fate-seek-cache-pipe: CMD = cat $(SAMPLES)/gapless/gapless.mp3 | run libavformat/tests/seek$(EXESUF) cache:pipe:0 -read_ahead_limit -1 fate-seek-mkv-codec-delay: CMD = run libavformat/tests/seek$(EXESUF) $(TARGET_SAMPLES)/mkv/codec_delay_opus.mkv FATE_SEEK_EXTRA += $(FATE_SEEK_EXTRA-yes) diff --git a/tests/fate/truehd.mak b/tests/fate/truehd.mak new file mode 100644 index 00000000000..e672716527b --- /dev/null +++ b/tests/fate/truehd.mak @@ -0,0 +1,17 @@ +FATE_TRUEHD-$(call DEMDEC, TRUEHD, TRUEHD) += fate-truehd-5.1 +fate-truehd-5.1: CMD = md5pipe -f truehd -i $(TARGET_SAMPLES)/lossless-audio/truehd_5.1.raw -f s32le +fate-truehd-5.1: CMP = oneline +fate-truehd-5.1: REF = 95d8aac39dd9f0d7fb83dc7b6f88df35 + +FATE_TRUEHD-$(call DEMDEC, TRUEHD, TRUEHD) += fate-truehd-5.1-downmix-2.0 +fate-truehd-5.1-downmix-2.0: CMD = md5pipe -f truehd -request_channel_layout 2 -i $(TARGET_SAMPLES)/lossless-audio/truehd_5.1.raw -f s32le +fate-truehd-5.1-downmix-2.0: CMP = oneline +fate-truehd-5.1-downmix-2.0: REF = a269aee0051d4400c9117136f08c9767 + +FATE_TRUEHD-$(call ALLYES, TRUEHD_DEMUXER TRUEHD_MUXER TRUEHD_CORE_BSF) += fate-truehd-core-bsf +fate-truehd-core-bsf: CMD = md5pipe -i $(TARGET_SAMPLES)/truehd/atmos.thd -c:a copy -bsf:a truehd_core -fflags +bitexact -f truehd +fate-truehd-core-bsf: CMP = oneline +fate-truehd-core-bsf: REF = 3aa5d0c7825051f3657b71fd6135183b + +FATE_SAMPLES_AUDIO += $(FATE_TRUEHD-yes) +fate-truehd: $(FATE_TRUEHD-yes) diff --git a/tests/fate/vcodec.mak b/tests/fate/vcodec.mak index 2705d2227ac..1e9c0d5647a 100644 --- a/tests/fate/vcodec.mak +++ b/tests/fate/vcodec.mak @@ -29,13 +29,15 @@ FATE_VCODEC-$(call ENCDEC, DNXHD, DNXHD) += dnxhd-720p \ dnxhd-720p-rd \ dnxhd-720p-10bit \ dnxhd-720p-hr-lb \ - dnxhd-4k-hr-lb \ - dnxhd-uhd-hr-sq \ - dnxhd-2k-hr-hq \ dnxhd-edge1-hr \ dnxhd-edge2-hr \ dnxhd-edge3-hr +FATE_VCODEC-$(call ALLYES, DNXHD_ENCODER DNXHD_DECODER LARGE_TESTS) += dnxhd-4k-hr-lb \ + dnxhd-2k-hr-hq \ + dnxhd-uhd-hr-sq + + FATE_VCODEC-$(call ENCDEC, VC2 DIRAC, MOV) += vc2-420p vc2-420p10 vc2-420p12 \ vc2-422p vc2-422p10 vc2-422p12 \ vc2-444p vc2-444p10 vc2-444p12 \ @@ -135,7 +137,7 @@ fate-vsynth%-dnxhd-hr-hq-mov: ENCOPTS = -s 2kflat -profile:v dnxhr_hq \ fate-vsynth%-dnxhd-hr-hq-mov: DECOPTS = -sws_flags area+accurate_rnd+bitexact fate-vsynth%-dnxhd-hr-hq-mov: FMT = mov -FATE_VCODEC-$(call ENCDEC, DVVIDEO, DV) += dv dv-411 dv-50 +FATE_VCODEC-$(call ENCDEC, DVVIDEO, DV) += dv dv-411 dv-50 dv-hd dv-fhd fate-vsynth%-dv: CODEC = dvvideo fate-vsynth%-dv: ENCOPTS = -dct int -s pal fate-vsynth%-dv: FMT = dv @@ -152,6 +154,18 @@ fate-vsynth%-dv-50: ENCOPTS = -dct int -s pal -pix_fmt yuv422p \ fate-vsynth%-dv-50: DECOPTS = -sws_flags neighbor fate-vsynth%-dv-50: FMT = dv +fate-vsynth%-dv-fhd: CODEC = dvvideo +fate-vsynth%-dv-fhd: ENCOPTS = -dct int -s 1440x1080 -pix_fmt yuv422p \ + -sws_flags neighbor +fate-vsynth%-dv-fhd: DECOPTS = -sws_flags neighbor +fate-vsynth%-dv-fhd: FMT = dv + +fate-vsynth%-dv-hd: CODEC = dvvideo +fate-vsynth%-dv-hd: ENCOPTS = -dct int -s 960x720 -pix_fmt yuv422p \ + -sws_flags neighbor +fate-vsynth%-dv-hd: DECOPTS = -sws_flags neighbor +fate-vsynth%-dv-hd: FMT = dv + FATE_VCODEC-$(call ENCDEC, FFV1, AVI) += ffv1 ffv1-v0 \ ffv1-v3-yuv420p ffv1-v3-yuv422p10 ffv1-v3-yuv444p16 \ ffv1-v3-bgr0 ffv1-v3-rgb48 @@ -309,7 +323,7 @@ fate-vsynth%-mpeg4-nr: ENCOPTS = -qscale 8 -flags +mv4 -mbd rd \ fate-vsynth%-mpeg4-nsse: ENCOPTS = -qscale 7 -cmp nsse -subcmp nsse \ -mbcmp nsse -precmp nsse \ - -skipcmp nsse + -skip_cmp nsse fate-vsynth%-mpeg4-qpel: ENCOPTS = -qscale 7 -flags +mv4+qpel -mbd 2 \ -bf 2 -cmp 1 -subcmp 2 diff --git a/tests/fate/video.mak b/tests/fate/video.mak index be1458c8bb1..d2d43e518dd 100644 --- a/tests/fate/video.mak +++ b/tests/fate/video.mak @@ -232,6 +232,9 @@ fate-mimic: CMD = framecrc -idct simple -i $(TARGET_SAMPLES)/mimic/mimic2-womanl FATE_VIDEO-$(call DEMDEC, MOV, MJPEGB) += fate-mjpegb fate-mjpegb: CMD = framecrc -idct simple -fflags +bitexact -i $(TARGET_SAMPLES)/mjpegb/mjpegb_part.mov -an +FATE_VIDEO-$(call DEMDEC, AVI, MJPEG) += fate-mjpeg-ticket3229 +fate-mjpeg-ticket3229: CMD = framecrc -idct simple -fflags +bitexact -i $(TARGET_SAMPLES)/mjpeg/mjpeg_field_order.avi -an + FATE_VIDEO-$(call DEMDEC, MVI, MOTIONPIXELS) += fate-motionpixels fate-motionpixels: CMD = framecrc -i $(TARGET_SAMPLES)/motion-pixels/INTRO-partial.MVI -an -pix_fmt rgb24 -frames:v 111 diff --git a/tests/fate/vpx.mak b/tests/fate/vpx.mak index 3b3da18feb9..88ccc70a5a1 100644 --- a/tests/fate/vpx.mak +++ b/tests/fate/vpx.mak @@ -71,10 +71,10 @@ FATE_VP8-$(CONFIG_WEBM_DASH_MANIFEST_DEMUXER) += fate-webm-dash-manifest-represe fate-webm-dash-manifest-representations: CMD = run $(FFMPEG) -nostdin -f webm_dash_manifest -i $(TARGET_SAMPLES)/vp8/dash_video1.webm -f webm_dash_manifest -i $(TARGET_SAMPLES)/vp8/dash_video4.webm -c copy -map 0 -map 1 -f webm_dash_manifest -adaptation_sets "id=0,streams=0,1" - FATE_VP8-$(CONFIG_WEBM_DASH_MANIFEST_DEMUXER) += fate-webm-dash-manifest-live -fate-webm-dash-manifest-live: CMD = run $(FFMPEG) -nostdin -f webm_dash_manifest -live 1 -i $(TARGET_SAMPLES)/vp8/dash_live_video_360.hdr -f webm_dash_manifest -live 1 -i $(TARGET_SAMPLES)/vp8/dash_live_audio_171.hdr -c copy -map 0 -map 1 -f webm_dash_manifest -live 1 -adaptation_sets "id=0,streams=0 id=1,streams=1" -chunk_start_index 1 -chunk_duration_ms 5000 -time_shift_buffer_depth 7200 -minimum_update_period 60 -debug_mode 1 - +fate-webm-dash-manifest-live: CMD = run $(FFMPEG) -nostdin -f webm_dash_manifest -live 1 -i $(TARGET_SAMPLES)/vp8/dash_live_video_360.hdr -f webm_dash_manifest -live 1 -i $(TARGET_SAMPLES)/vp8/dash_live_audio_171.hdr -c copy -map 0 -map 1 -fflags +bitexact -f webm_dash_manifest -live 1 -adaptation_sets "id=0,streams=0 id=1,streams=1" -chunk_start_index 1 -chunk_duration_ms 5000 -time_shift_buffer_depth 7200 -minimum_update_period 60 - FATE_VP8-$(CONFIG_WEBM_DASH_MANIFEST_DEMUXER) += fate-webm-dash-manifest-live-bandwidth -fate-webm-dash-manifest-live-bandwidth: CMD = run $(FFMPEG) -nostdin -f webm_dash_manifest -live 1 -bandwidth 100 -i $(TARGET_SAMPLES)/vp8/dash_live_video_360.hdr -f webm_dash_manifest -live 1 -bandwidth 200 -i $(TARGET_SAMPLES)/vp8/dash_live_audio_171.hdr -c copy -map 0 -map 1 -f webm_dash_manifest -live 1 -adaptation_sets "id=0,streams=0 id=1,streams=1" -chunk_start_index 1 -chunk_duration_ms 5000 -time_shift_buffer_depth 7200 -minimum_update_period 60 -debug_mode 1 - +fate-webm-dash-manifest-live-bandwidth: CMD = run $(FFMPEG) -nostdin -f webm_dash_manifest -live 1 -bandwidth 100 -i $(TARGET_SAMPLES)/vp8/dash_live_video_360.hdr -f webm_dash_manifest -live 1 -bandwidth 200 -i $(TARGET_SAMPLES)/vp8/dash_live_audio_171.hdr -c copy -map 0 -map 1 -fflags +bitexact -f webm_dash_manifest -live 1 -adaptation_sets "id=0,streams=0 id=1,streams=1" -chunk_start_index 1 -chunk_duration_ms 5000 -time_shift_buffer_depth 7200 -minimum_update_period 60 - FATE_VP8-$(call DEMDEC, MATROSKA, VP8) += fate-vp8-2451 fate-vp8-2451: CMD = framecrc -flags +bitexact -i $(TARGET_SAMPLES)/vp8/RRSF49-short.webm -vsync cfr -an @@ -163,5 +163,10 @@ FATE_VP9-$(CONFIG_IVF_DEMUXER) += fate-vp9-05-resize fate-vp9-05-resize: CMD = framemd5 -i $(TARGET_SAMPLES)/vp9-test-vectors/vp90-2-05-resize.ivf -s 352x288 -sws_flags bitexact+bilinear fate-vp9-05-resize: REF = $(SRC_PATH)/tests/ref/fate/vp9-05-resize +fate-vp9-encparams: CMD = venc_data $(TARGET_SAMPLES)/vp9-test-vectors/vp90-2-segmentation-aq-akiyo.webm 0 5 +FATE_SAMPLES_DUMP_DATA += fate-vp9-encparams + +FATE_VP9-$(CONFIG_MATROSKA_DEMUXER) += fate-vp9-encparams + FATE_SAMPLES_AVCONV-$(CONFIG_VP9_DECODER) += $(FATE_VP9-yes) fate-vp9: $(FATE_VP9-yes) diff --git a/tests/fate/wavpack.mak b/tests/fate/wavpack.mak index 286e17f2ae5..96b4d16bff9 100644 --- a/tests/fate/wavpack.mak +++ b/tests/fate/wavpack.mak @@ -91,12 +91,12 @@ fate-wavpack-matroskamode: CMD = md5 -i $(TARGET_SAMPLES)/wavpack/special/matros FATE_WAVPACK-$(call DEMMUX, WV, MATROSKA) += fate-wavpack-matroska_mux-mono fate-wavpack-matroska_mux-mono: CMD = md5pipe -i $(TARGET_SAMPLES)/wavpack/num_channels/mono_16bit_int.wv -c copy -fflags +bitexact -f matroska fate-wavpack-matroska_mux-mono: CMP = oneline -fate-wavpack-matroska_mux-mono: REF = c5a2b46d8b31c9c2e7bfb436f9d56f02 +fate-wavpack-matroska_mux-mono: REF = 0a10b6cd4d64c25ced75f52d1c6929a7 FATE_WAVPACK-$(call DEMMUX, WV, MATROSKA) += fate-wavpack-matroska_mux-61 fate-wavpack-matroska_mux-61: CMD = md5pipe -i $(TARGET_SAMPLES)/wavpack/num_channels/eva_2.22_6.1_16bit-partial.wv -c copy -fflags +bitexact -f matroska fate-wavpack-matroska_mux-61: CMP = oneline -fate-wavpack-matroska_mux-61: REF = 22fc2a591be22657d19cab89ad6eef55 +fate-wavpack-matroska_mux-61: REF = fee4bb1d6cf2d54f78eddc4483e7c990 FATE_SAMPLES_AVCONV += $(FATE_WAVPACK-yes) fate-wavpack: $(FATE_WAVPACK-yes) diff --git a/tests/filtergraphs/concat-vfr b/tests/filtergraphs/concat-vfr new file mode 100644 index 00000000000..e15cb968459 --- /dev/null +++ b/tests/filtergraphs/concat-vfr @@ -0,0 +1,8 @@ +testsrc=r=5:n=1:d=2 [v1]; +sine=440:b=2:d=1 [a1]; +testsrc=r=15:n=1:d=1 [v2]; +sine=622:b=2:d=2 [a2]; +testsrc=r=8:n=1:d=1 [v3]; +sine=880:b=2:d=1 [a3]; + +[v1][a1][v2][a2][v3][a3] concat=v=1:a=1:n=3 diff --git a/tests/lavf-regression.sh b/tests/lavf-regression.sh deleted file mode 100755 index 45c877e4ac1..00000000000 --- a/tests/lavf-regression.sh +++ /dev/null @@ -1,425 +0,0 @@ -#!/bin/sh -# -# automatic regression test for libavformat -# -# -#set -x - -set -e - -. $(dirname $0)/regression-funcs.sh - -eval do_$test=y - -ENC_OPTS="$ENC_OPTS -metadata title=lavftest" - -do_lavf_fate() -{ - file=${outfile}lavf.$1 - input="${target_samples}/$2" - do_avconv $file $DEC_OPTS -i "$input" $ENC_OPTS -vcodec copy -acodec copy - do_avconv_crc $file $DEC_OPTS -i $target_path/$file $3 -} - -do_lavf() -{ - file=${outfile}lavf.$1 - do_avconv $file $DEC_OPTS -f image2 -vcodec pgmyuv -i $raw_src $DEC_OPTS -ar 44100 -f s16le $2 -i $pcm_src $ENC_OPTS -b:a 64k -t 1 -qscale:v 10 $3 - do_avconv_crc $file $DEC_OPTS -i $target_path/$file $4 -} - -do_lavf_timecode_nodrop() { do_lavf $1 "" "$2 -timecode 02:56:14:13"; } -do_lavf_timecode_drop() { do_lavf $1 "" "$2 -timecode 02:56:14.13 -r 30000/1001"; } - -do_lavf_timecode() -{ - do_lavf_timecode_nodrop "$@" - do_lavf_timecode_drop "$@" - do_lavf $1 "" "$2" -} - -do_streamed_images() -{ - file=${outfile}${1}pipe.$1 - do_avconv $file $DEC_OPTS -f image2 -vcodec pgmyuv -i $raw_src -f image2pipe $ENC_OPTS -t 1 -qscale 10 - do_avconv_crc $file $DEC_OPTS -f image2pipe -i $target_path/$file -} - -do_image_formats() -{ - outfile="$datadir/images/$1/" - mkdir -p "$outfile" - file=${outfile}%02d.$1 - run_avconv $DEC_OPTS -f image2 -vcodec pgmyuv -i $raw_src $2 $ENC_OPTS -frames 13 -y -qscale 10 $target_path/$file - do_md5sum ${outfile}02.$1 - do_avconv_crc $file $DEC_OPTS -i $target_path/$file $3 - echo $(wc -c ${outfile}02.$1) -} - -do_audio_only() -{ - file=${outfile}lavf.$1 - do_avconv $file $DEC_OPTS $2 -ar 44100 -f s16le -i $pcm_src $ENC_OPTS -t 1 -qscale 10 $3 - do_avconv_crc $file $DEC_OPTS $4 -i $target_path/$file -} - -if [ -n "$do_avi" ] ; then -do_lavf avi "" "-acodec mp2 -ar 44100 -ab 64k -threads 1" -fi - -if [ -n "$do_asf" ] ; then -do_lavf asf "" "-acodec mp2 -ar 44100 -ab 64k" "-r 25" -fi - -if [ -n "$do_rm" ] ; then -file=${outfile}lavf.rm -do_avconv $file $DEC_OPTS -f image2 -vcodec pgmyuv -i $raw_src $DEC_OPTS -ar 44100 -f s16le -i $pcm_src $ENC_OPTS -t 1 -qscale 10 -acodec ac3_fixed -ab 64k -# broken -#do_avconv_crc $file -i $target_path/$file -fi - -if [ -n "$do_mpg" ] ; then -do_lavf_timecode mpg "-ab 64k -ar 44100 -threads 1" -fi - -if [ -n "$do_mxf" ] ; then -do_lavf_timecode mxf "-ar 48000 -bf 2 -threads 1" -fi - -if [ -n "$do_mxf_d10" ]; then -do_lavf mxf_d10 "-ar 48000 -ac 2" "-r 25 -vf scale=720:576,pad=720:608:0:32 -vcodec mpeg2video -g 0 -flags +ildct+low_delay -dc 10 -non_linear_quant 1 -intra_vlc 1 -qscale 1 -ps 1 -qmin 1 -rc_max_vbv_use 1 -rc_min_vbv_use 1 -pix_fmt yuv422p -minrate 30000k -maxrate 30000k -b 30000k -bufsize 1200000 -top 1 -rc_init_occupancy 1200000 -qmax 12 -f mxf_d10" -fi - -if [ -n "$do_mxf_dv25" ]; then -do_lavf mxf_dv25 "-ar 48000 -ac 2" "-r 25 -vf scale=720:576,setdar=4/3 -vcodec dvvideo -pix_fmt yuv420p -b 25000k -top 0 -f mxf" -fi - -if [ -n "$do_mxf_dvcpro50" ]; then -do_lavf mxf_dvcpro50 "-ar 48000 -ac 2" "-r 25 -vf scale=720:576,setdar=16/9 -vcodec dvvideo -pix_fmt yuv422p -b 50000k -top 0 -f mxf" -fi - -if [ -n "$do_mxf_opatom" ]; then -do_lavf mxf_opatom "" "-s 1920x1080 -vcodec dnxhd -pix_fmt yuv422p -vb 36M -f mxf_opatom -map 0" -fi - -if [ -n "$do_mxf_opatom_audio" ]; then -do_lavf mxf_opatom_audio "-ar 48000 -ac 1" "-f mxf_opatom -mxf_audio_edit_rate 25 -map 1" -fi - -if [ -n "$do_ts" ] ; then -do_lavf ts "" "-ab 64k -mpegts_transport_stream_id 42 -ar 44100 -threads 1" -fi - -if [ -n "$do_swf" ] ; then -do_lavf swf "" "-an" -fi - -if [ -n "$do_ffm" ] ; then -do_lavf ffm "" "-ar 44100 -threads 1" -fi - -if [ -n "$do_flm" ] ; then -do_lavf flm "" "-pix_fmt rgba" -fi - -if [ -n "$do_flv_fmt" ] ; then -do_lavf flv "" "-an" -fi - -if [ -n "$do_mov" ] ; then -mov_common_opt="-acodec pcm_alaw -vcodec mpeg4 -threads 1" -do_lavf mov "" "-movflags +rtphint $mov_common_opt" -do_lavf_timecode mov "-movflags +faststart $mov_common_opt" -do_lavf_timecode mp4 "-vcodec mpeg4 -an -threads 1" -fi - -if [ -n "$do_ismv" ] ; then -do_lavf_timecode ismv "-an -vcodec mpeg4 -threads 1" -fi - -if [ -n "$do_dv_fmt" ] ; then -do_lavf_timecode_nodrop dv "-ar 48000 -r 25 -s pal -ac 2" -do_lavf_timecode_drop dv "-ar 48000 -pix_fmt yuv411p -s ntsc -ac 2" -do_lavf dv "-ar 48000 -channel_layout stereo" "-r 25 -s pal" -fi - -if [ -n "$do_gxf" ] ; then -do_lavf_timecode_nodrop gxf "-ar 48000 -r 25 -s pal -ac 1 -threads 1" -do_lavf_timecode_drop gxf "-ar 48000 -s ntsc -ac 1 -threads 1" -do_lavf gxf "-ar 48000" "-r 25 -s pal -ac 1 -threads 1" -fi - -if [ -n "$do_nut" ] ; then -do_lavf nut "" "-acodec mp2 -ab 64k -ar 44100 -threads 1" -fi - -if [ -n "$do_mka" ] ; then -do_audio_only mka "" "-c:a tta" -fi - -if [ -n "$do_mkv" ] ; then -do_lavf mkv "" "-acodec mp2 -ab 64k -vcodec mpeg4 \ - -attach ${raw_src%/*}/00.pgm -metadata:s:t mimetype=image/x-portable-greymap -threads 1" -do_lavf mkv "" "-acodec mp2 -ab 64k -vcodec mpeg4 -ar 44100 -threads 1" -fi - -if [ -n "$do_mp3" ] ; then -do_lavf_fate mp3 "mp3-conformance/he_32khz.bit" "-acodec copy" -fi - -if [ -n "$do_latm" ] ; then -do_lavf_fate latm "aac/al04_44.mp4" "-acodec copy" -fi - -if [ -n "$do_ogg_vp3" ] ; then -# -idct simple causes different results on different systems -DEC_OPTS="$DEC_OPTS -idct auto" -do_lavf_fate ogg "vp3/coeff_level64.mkv" -fi - -if [ -n "$do_ogg_vp8" ] ; then -do_lavf_fate ogv "vp8/RRSF49-short.webm" "-acodec copy" -fi - -if [ -n "$do_mov_qtrle_mace6" ] ; then -DEC_OPTS="$DEC_OPTS -idct auto" -do_lavf_fate mov "qtrle/Animation-16Greys.mov" -fi - -if [ -n "$do_avi_cram" ] ; then -DEC_OPTS="$DEC_OPTS -idct auto" -do_lavf_fate avi "cram/toon.avi" -fi - -if [ -n "$do_wtv" ] ; then -do_lavf wtv "" "-acodec mp2 -threads 1" -fi - - -# streamed images -# mjpeg -#file=${outfile}lavf.mjpeg -#do_avconv $file -t 1 -qscale 10 -f image2 -vcodec pgmyuv -i $raw_src -#do_avconv_crc $file -i $target_path/$file - -if [ -n "$do_pbmpipe" ] ; then -do_streamed_images pbm -fi - -if [ -n "$do_pgmpipe" ] ; then -do_streamed_images pgm -fi - -if [ -n "$do_ppmpipe" ] ; then -do_streamed_images ppm -fi - -if [ -n "$do_gif" ] ; then -file=${outfile}lavf.gif -do_avconv $file $DEC_OPTS -f image2 -vcodec pgmyuv -i $raw_src $ENC_OPTS -t 1 -qscale 10 -pix_fmt rgb24 -do_avconv_crc $file $DEC_OPTS -i $target_path/$file -pix_fmt rgb24 -fi - -if [ -n "$do_apng" ] ; then -file=${outfile}lavf.apng -do_avconv $file $DEC_OPTS -f image2 -vcodec pgmyuv -i $raw_src $ENC_OPTS -t 1 -pix_fmt rgb24 -do_avconv_crc $file $DEC_OPTS -i $target_path/$file -pix_fmt rgb24 -file_copy=${outfile}lavf.copy.apng -do_avconv $file_copy $DEC_OPTS -i $file $ENC_OPTS -c copy -do_avconv_crc $file_copy $DEC_OPTS -i $target_path/$file_copy -file=${outfile}lavf.png -do_avconv $file $DEC_OPTS -f image2 -vcodec pgmyuv -i $raw_src $ENC_OPTS -pix_fmt rgb24 -frames:v 1 -f apng -do_avconv_crc $file $DEC_OPTS -i $target_path/$file -pix_fmt rgb24 -fi - -if [ -n "$do_yuv4mpeg" ] ; then -file=${outfile}lavf.y4m -do_avconv $file $DEC_OPTS -f image2 -vcodec pgmyuv -i $raw_src $ENC_OPTS -t 1 -qscale 10 -#do_avconv_crc $file -i $target_path/$file -fi - -if [ -n "$do_fits" ] ; then -pix_fmts="gray gray16be gbrp gbrap gbrp16be gbrap16be" -for pix_fmt in $pix_fmts ; do - file=${outfile}${pix_fmt}lavf.fits - do_avconv $file $DEC_OPTS -f image2 -vcodec pgmyuv -i $raw_src $ENC_OPTS -pix_fmt $pix_fmt - do_avconv_crc $file $DEC_OPTS -i $target_path/$file -pix_fmt $pix_fmt -done -fi - -# image formats - -if [ -n "$do_pgm" ] ; then -do_image_formats pgm -fi - -if [ -n "$do_ppm" ] ; then -do_image_formats ppm -fi - -if [ -n "$do_png" ] ; then -do_image_formats png -do_image_formats png "-pix_fmt gray16be" -do_image_formats png "-pix_fmt rgb48be" -fi - -if [ -n "$do_xbm" ] ; then -do_image_formats xbm -fi - -if [ -n "$do_bmp" ] ; then -do_image_formats bmp -fi - -if [ -n "$do_tga" ] ; then -do_image_formats tga -fi - -if [ -n "$do_tiff" ] ; then -do_image_formats tiff "-pix_fmt rgb24" -fi - -if [ -n "$do_sgi" ] ; then -do_image_formats sgi -fi - -if [ -n "$do_jpg" ] ; then -do_image_formats jpg "-pix_fmt yuvj420p" -fi - -if [ -n "$do_pam" ] ; then -do_image_formats pam -do_image_formats pam "-pix_fmt rgba" -do_image_formats pam "-pix_fmt gray" -do_image_formats pam "-pix_fmt gray16be" "-pix_fmt gray16be" -do_image_formats pam "-pix_fmt rgb48be" "-pix_fmt rgb48be" -do_image_formats pam "-pix_fmt monob" -fi - -if [ -n "$do_pcx" ] ; then -do_image_formats pcx -fi - -if [ -n "$do_dpx" ] ; then -do_image_formats dpx -do_image_formats dpx "-pix_fmt gbrp10le" "-pix_fmt gbrp10le" -do_image_formats dpx "-pix_fmt gbrp12le" "-pix_fmt gbrp12le" -do_image_formats dpx "-pix_fmt rgb48le" -do_image_formats dpx "-pix_fmt rgb48le -bits_per_raw_sample 10" "-pix_fmt rgb48le" -do_image_formats dpx "-pix_fmt rgba64le" -fi - -if [ -n "$do_xwd" ] ; then -do_image_formats xwd -do_image_formats xwd "-pix_fmt rgba" -do_image_formats xwd "-pix_fmt rgb565be" -do_image_formats xwd "-pix_fmt rgb555be" -do_image_formats xwd "-pix_fmt rgb8" -do_image_formats xwd "-pix_fmt rgb4_byte" -do_image_formats xwd "-pix_fmt gray" -do_image_formats xwd "-pix_fmt monow" -fi - -if [ -n "$do_sunrast" ] ; then -do_image_formats sun -fi - -# audio only - -if [ -n "$do_wav" ] ; then -do_audio_only wav -fi - -if [ -n "$do_wav_peak" ] ; then -do_audio_only peak.wav "" "-write_peak on" -fi - -if [ -n "$do_wav_peak_only" ] ; then -file=${outfile}lavf.peak_only.wav -do_avconv $file $DEC_OPTS -ar 44100 -f s16le -i $pcm_src $ENC_OPTS -t 1 -qscale 10 -write_peak only -fi - -if [ -n "$do_alaw" ] ; then -do_audio_only al "" "" "-ar 44100" -fi - -if [ -n "$do_mulaw" ] ; then -do_audio_only ul "" "" "-ar 44100" -fi - -if [ -n "$do_au" ] ; then -do_audio_only au -fi - -if [ -n "$do_mmf" ] ; then -do_audio_only mmf -fi - -if [ -n "$do_aiff" ] ; then -do_audio_only aif -fi - -if [ -n "$do_voc" ] ; then -do_audio_only voc "" "-acodec pcm_u8" -fi - -if [ -n "$do_voc_s16" ] ; then -do_audio_only s16.voc "-ac 2" "-acodec pcm_s16le" -fi - -if [ -n "$do_ogg" ] ; then -do_audio_only ogg "" "-c:a flac" -fi - -if [ -n "$do_rso" ] ; then -do_audio_only rso -fi - -if [ -n "$do_smjpeg" ] ; then -do_lavf smjpeg "" "-f smjpeg" -fi - -if [ -n "$do_sox" ] ; then -do_audio_only sox -fi - -if [ -n "$do_tta" ] ; then -do_audio_only tta -fi - -if [ -n "$do_caf" ] ; then -do_audio_only caf -fi - -if [ -n "$do_ast" ] ; then -do_audio_only ast "-ac 2" "-loopstart 1 -loopend 10" -fi - -if [ -n "$do_ircam" ] ; then -do_audio_only ircam -fi - -if [ -n "$do_w64" ] ; then -do_audio_only w64 -fi - -if [ -n "$do_wv" ] ; then -do_audio_only wv -fi - -# pix_fmt conversions - -if [ -n "$do_pixfmt" ] ; then -outfile="$datadir/pixfmt/" -mkdir -p "$outfile" -conversions="yuv420p yuv422p yuv444p yuyv422 yuv410p yuv411p yuvj420p \ - yuvj422p yuvj444p rgb24 bgr24 rgb32 rgb565 rgb555 gray monow \ - monob yuv440p yuvj440p" -for pix_fmt in $conversions ; do - file=${outfile}${pix_fmt}.yuv - run_avconv $DEC_OPTS -r 1 -f image2 -vcodec pgmyuv -i $raw_src \ - $ENC_OPTS -f rawvideo -t 1 -s 352x288 -pix_fmt $pix_fmt $target_path/$raw_dst - do_avconv $file $DEC_OPTS -f rawvideo -s 352x288 -pix_fmt $pix_fmt -i $target_path/$raw_dst \ - $ENC_OPTS -f rawvideo -s 352x288 -pix_fmt yuv444p -done -fi diff --git a/tests/ref/acodec/adpcm-adx b/tests/ref/acodec/adpcm-adx index 8c401001b84..2c8550fb726 100644 --- a/tests/ref/acodec/adpcm-adx +++ b/tests/ref/acodec/adpcm-adx @@ -1,4 +1,4 @@ -6bf1a8e5ec9cc958a31cb2b1b66bfc75 *tests/data/fate/acodec-adpcm-adx.adx -297720 tests/data/fate/acodec-adpcm-adx.adx +c257001314241b469a6512616fd56548 *tests/data/fate/acodec-adpcm-adx.adx +297738 tests/data/fate/acodec-adpcm-adx.adx 5b5a436ec9d528d6eb0bebaf667521b0 *tests/data/fate/acodec-adpcm-adx.out.wav stddev: 2549.93 PSNR: 28.20 MAXDIFF:57514 bytes: 1058400/ 1058432 diff --git a/tests/ref/acodec/adpcm-adx-trellis b/tests/ref/acodec/adpcm-adx-trellis index 039f69f9db8..f6f5d768f50 100644 --- a/tests/ref/acodec/adpcm-adx-trellis +++ b/tests/ref/acodec/adpcm-adx-trellis @@ -1,4 +1,4 @@ -6bf1a8e5ec9cc958a31cb2b1b66bfc75 *tests/data/fate/acodec-adpcm-adx-trellis.adx -297720 tests/data/fate/acodec-adpcm-adx-trellis.adx +c257001314241b469a6512616fd56548 *tests/data/fate/acodec-adpcm-adx-trellis.adx +297738 tests/data/fate/acodec-adpcm-adx-trellis.adx 5b5a436ec9d528d6eb0bebaf667521b0 *tests/data/fate/acodec-adpcm-adx-trellis.out.wav stddev: 2549.93 PSNR: 28.20 MAXDIFF:57514 bytes: 1058400/ 1058432 diff --git a/tests/ref/acodec/adpcm-ima_ssi b/tests/ref/acodec/adpcm-ima_ssi new file mode 100644 index 00000000000..63cc9ce2773 --- /dev/null +++ b/tests/ref/acodec/adpcm-ima_ssi @@ -0,0 +1,4 @@ +91c6a0d8eb3544e402af17a1b77a22e5 *tests/data/fate/acodec-adpcm-ima_ssi.kvag +264614 tests/data/fate/acodec-adpcm-ima_ssi.kvag +201607bf7610f062b9a1e6524354c569 *tests/data/fate/acodec-adpcm-ima_ssi.out.wav +stddev: 904.76 PSNR: 37.20 MAXDIFF:34029 bytes: 1058400/ 1058400 diff --git a/tests/ref/acodec/s302m b/tests/ref/acodec/s302m index 63e39edef91..2919ed6e55b 100644 --- a/tests/ref/acodec/s302m +++ b/tests/ref/acodec/s302m @@ -1,4 +1,4 @@ -165d022ab86306d069797acff0c1e295 *tests/data/fate/acodec-s302m.mpegts -1589164 tests/data/fate/acodec-s302m.mpegts +0bf5457fd41a22fc5cdd99ae5ad4e273 *tests/data/fate/acodec-s302m.mpegts +1527688 tests/data/fate/acodec-s302m.mpegts 31f25a0020fd9017de9c3c608316854b *tests/data/fate/acodec-s302m.out.wav stddev: 986.94 PSNR: 36.44 MAXDIFF:18571 bytes: 1058400/ 1056708 diff --git a/tests/ref/fate/aac-autobsf-adtstoasc b/tests/ref/fate/aac-autobsf-adtstoasc index 9bf9dfe78fb..bfb18aedd24 100644 --- a/tests/ref/fate/aac-autobsf-adtstoasc +++ b/tests/ref/fate/aac-autobsf-adtstoasc @@ -1,5 +1,5 @@ -b09fc2f554712adbf84fe7899eb679d4 *tests/data/fate/aac-autobsf-adtstoasc.matroska -6695 tests/data/fate/aac-autobsf-adtstoasc.matroska +5088977b7f13181d707a4077771e4552 *tests/data/fate/aac-autobsf-adtstoasc.matroska +6648 tests/data/fate/aac-autobsf-adtstoasc.matroska #extradata 0: 2, 0x0030001c #tb 0: 1/1000 #media_type 0: audio diff --git a/tests/ref/fate/adpcm-argo-mono b/tests/ref/fate/adpcm-argo-mono new file mode 100644 index 00000000000..b9a142f3f48 --- /dev/null +++ b/tests/ref/fate/adpcm-argo-mono @@ -0,0 +1 @@ +3876d24a376b1ab1f6d207b7abda635e diff --git a/tests/ref/fate/adpcm-argo-stereo b/tests/ref/fate/adpcm-argo-stereo new file mode 100644 index 00000000000..e0efa5192bf --- /dev/null +++ b/tests/ref/fate/adpcm-argo-stereo @@ -0,0 +1 @@ +53ad310be0175583a5c3c0e5fe341b18 diff --git a/tests/ref/fate/adpcm-ima-alp-mono b/tests/ref/fate/adpcm-ima-alp-mono new file mode 100644 index 00000000000..4110a24c3ca --- /dev/null +++ b/tests/ref/fate/adpcm-ima-alp-mono @@ -0,0 +1 @@ +288d1ce2e92240ccebfe2586fc22d01b diff --git a/tests/ref/fate/adpcm-ima-alp-stereo b/tests/ref/fate/adpcm-ima-alp-stereo new file mode 100644 index 00000000000..8cf281e92b5 --- /dev/null +++ b/tests/ref/fate/adpcm-ima-alp-stereo @@ -0,0 +1 @@ +a100877e7e02b8a6ad63154682c844f4 diff --git a/tests/ref/fate/adpcm-ima-apm-mono b/tests/ref/fate/adpcm-ima-apm-mono new file mode 100644 index 00000000000..c97e09f7594 --- /dev/null +++ b/tests/ref/fate/adpcm-ima-apm-mono @@ -0,0 +1 @@ +a5cb56a035ab4b79adceba6fe4a428d2 diff --git a/tests/ref/fate/adpcm-ima-apm-stereo b/tests/ref/fate/adpcm-ima-apm-stereo new file mode 100644 index 00000000000..28bd689758f --- /dev/null +++ b/tests/ref/fate/adpcm-ima-apm-stereo @@ -0,0 +1 @@ +e7ceb7f846f831b0bb3728d8ec18fdd4 diff --git a/tests/ref/fate/adpcm-ima-cunning-single b/tests/ref/fate/adpcm-ima-cunning-single new file mode 100644 index 00000000000..49a8308093d --- /dev/null +++ b/tests/ref/fate/adpcm-ima-cunning-single @@ -0,0 +1 @@ +dd6ba6151c3e74d09be3c54005465aab diff --git a/tests/ref/fate/adpcm-ima-cunning-track0 b/tests/ref/fate/adpcm-ima-cunning-track0 new file mode 100644 index 00000000000..bd489cfb454 --- /dev/null +++ b/tests/ref/fate/adpcm-ima-cunning-track0 @@ -0,0 +1 @@ +fb8db1eef33860c1adde4932e7a250ac diff --git a/tests/ref/fate/adpcm-ima-cunning-track1 b/tests/ref/fate/adpcm-ima-cunning-track1 new file mode 100644 index 00000000000..f79013c9de4 --- /dev/null +++ b/tests/ref/fate/adpcm-ima-cunning-track1 @@ -0,0 +1 @@ +4b2f9c416ae676526754c82f2a669c91 diff --git a/tests/ref/fate/adpcm-ima-cunning-trunc-h2 b/tests/ref/fate/adpcm-ima-cunning-trunc-h2 new file mode 100644 index 00000000000..bd489cfb454 --- /dev/null +++ b/tests/ref/fate/adpcm-ima-cunning-trunc-h2 @@ -0,0 +1 @@ +fb8db1eef33860c1adde4932e7a250ac diff --git a/tests/ref/fate/adpcm-ima-cunning-trunc-t1 b/tests/ref/fate/adpcm-ima-cunning-trunc-t1 new file mode 100644 index 00000000000..4669e388555 --- /dev/null +++ b/tests/ref/fate/adpcm-ima-cunning-trunc-t1 @@ -0,0 +1 @@ +a0eaad31febdcf1cfb94a4d418e1e140 diff --git a/tests/ref/fate/adpcm-ima-cunning-trunc-t2-track0 b/tests/ref/fate/adpcm-ima-cunning-trunc-t2-track0 new file mode 100644 index 00000000000..bd489cfb454 --- /dev/null +++ b/tests/ref/fate/adpcm-ima-cunning-trunc-t2-track0 @@ -0,0 +1 @@ +fb8db1eef33860c1adde4932e7a250ac diff --git a/tests/ref/fate/adpcm-ima-cunning-trunc-t2-track1 b/tests/ref/fate/adpcm-ima-cunning-trunc-t2-track1 new file mode 100644 index 00000000000..df9edc403d3 --- /dev/null +++ b/tests/ref/fate/adpcm-ima-cunning-trunc-t2-track1 @@ -0,0 +1 @@ +d41d8cd98f00b204e9800998ecf8427e diff --git a/tests/ref/fate/adpcm-ima-cunning-trunc-t2a-track0 b/tests/ref/fate/adpcm-ima-cunning-trunc-t2a-track0 new file mode 100644 index 00000000000..bd489cfb454 --- /dev/null +++ b/tests/ref/fate/adpcm-ima-cunning-trunc-t2a-track0 @@ -0,0 +1 @@ +fb8db1eef33860c1adde4932e7a250ac diff --git a/tests/ref/fate/adpcm-ima-cunning-trunc-t2a-track1 b/tests/ref/fate/adpcm-ima-cunning-trunc-t2a-track1 new file mode 100644 index 00000000000..77d4b09e62a --- /dev/null +++ b/tests/ref/fate/adpcm-ima-cunning-trunc-t2a-track1 @@ -0,0 +1 @@ +af0d42f01108333ab356061f9cab9403 diff --git a/tests/ref/fate/adpcm-ima-ssi-mono b/tests/ref/fate/adpcm-ima-ssi-mono new file mode 100644 index 00000000000..18a7232ced0 --- /dev/null +++ b/tests/ref/fate/adpcm-ima-ssi-mono @@ -0,0 +1 @@ +18decd2cd6da6957f0b323f7598b2d42 diff --git a/tests/ref/fate/adpcm-ima-ssi-stereo b/tests/ref/fate/adpcm-ima-ssi-stereo new file mode 100644 index 00000000000..4e2d622faab --- /dev/null +++ b/tests/ref/fate/adpcm-ima-ssi-stereo @@ -0,0 +1 @@ +c4be97a96aea1d6b15f1fe7d97ef7db8 diff --git a/tests/ref/fate/api-mjpeg-codec-param b/tests/ref/fate/api-mjpeg-codec-param index 0815919d7d9..82e3313aa97 100644 --- a/tests/ref/fate/api-mjpeg-codec-param +++ b/tests/ref/fate/api-mjpeg-codec-param @@ -4,6 +4,7 @@ stream=0, decode=0 bt=4000000 flags=0x00000000 flags2=0x00000000 + export_side_data=0x00000000 time_base=0/1 g=12 ar=0 @@ -136,6 +137,7 @@ stream=0, decode=0 pixel_format=yuvj422p video_size=400x225 max_pixels=2147483647 + max_samples=2147483647 hwaccel_flags=0x00000001 extra_hw_frames=-1 discard_damaged_percentage=95 @@ -145,6 +147,7 @@ stream=0, decode=1 bt=4000000 flags=0x00000000 flags2=0x00000000 + export_side_data=0x00000000 time_base=0/1 g=12 ar=0 @@ -277,6 +280,7 @@ stream=0, decode=1 pixel_format=yuvj422p video_size=400x225 max_pixels=2147483647 + max_samples=2147483647 hwaccel_flags=0x00000001 extra_hw_frames=-1 discard_damaged_percentage=95 diff --git a/tests/ref/fate/api-png-codec-param b/tests/ref/fate/api-png-codec-param index a47d0963da0..7adaa5260d2 100644 --- a/tests/ref/fate/api-png-codec-param +++ b/tests/ref/fate/api-png-codec-param @@ -4,6 +4,7 @@ stream=0, decode=0 bt=4000000 flags=0x00000000 flags2=0x00000000 + export_side_data=0x00000000 time_base=0/1 g=12 ar=0 @@ -136,6 +137,7 @@ stream=0, decode=0 pixel_format=rgba video_size=128x128 max_pixels=2147483647 + max_samples=2147483647 hwaccel_flags=0x00000001 extra_hw_frames=-1 discard_damaged_percentage=95 @@ -145,6 +147,7 @@ stream=0, decode=1 bt=4000000 flags=0x00000000 flags2=0x00000000 + export_side_data=0x00000000 time_base=0/1 g=12 ar=0 @@ -277,6 +280,7 @@ stream=0, decode=1 pixel_format=rgba video_size=128x128 max_pixels=2147483647 + max_samples=2147483647 hwaccel_flags=0x00000001 extra_hw_frames=-1 discard_damaged_percentage=95 diff --git a/tests/ref/fate/av1-annexb-demux b/tests/ref/fate/av1-annexb-demux new file mode 100644 index 00000000000..139a893ec1a --- /dev/null +++ b/tests/ref/fate/av1-annexb-demux @@ -0,0 +1,16 @@ +#extradata 0: 13, 0x0e4c033a +#tb 0: 1/1200000 +#media_type 0: video +#codec_id 0: av1 +#dimensions 0: 300x300 +#sar 0: 0/1 +0, 0, 0, 48000, 12691, 0xf0adcc79 +0, 48000, 48000, 48000, 4975, 0x1742a45f, F=0x0 +0, 96000, 96000, 48000, 928, 0x7408be1a, F=0x0 +0, 144000, 144000, 48000, 702, 0x1d4464ca, F=0x0 +0, 192000, 192000, 48000, 1372, 0x37999ccf, F=0x0 +0, 240000, 240000, 48000, 1085, 0x06b61548, F=0x0 +0, 288000, 288000, 48000, 1533, 0x5e05dea3, F=0x0 +0, 336000, 336000, 48000, 1416, 0x31d4b906, F=0x0 +0, 384000, 384000, 48000, 108, 0xf52a2c59, F=0x0 +0, 432000, 432000, 48000, 1740, 0x1f065d09, F=0x0 diff --git a/tests/ref/fate/binsub-mksenc b/tests/ref/fate/binsub-mksenc index 4b13ef217a8..4740569d9c5 100644 --- a/tests/ref/fate/binsub-mksenc +++ b/tests/ref/fate/binsub-mksenc @@ -1 +1 @@ -a5811caa0caba3a3f9a449b91569745c +5706cf33f9ded660eb404275ec136127 diff --git a/tests/ref/fate/binsub-movtextenc b/tests/ref/fate/binsub-movtextenc index dacee0931ed..a8f94b7227d 100644 --- a/tests/ref/fate/binsub-movtextenc +++ b/tests/ref/fate/binsub-movtextenc @@ -1 +1 @@ -66b25412f7ca699ee525ba162246edb6 +fc6d07679ac1f718aa50de687924cd97 diff --git a/tests/ref/fate/cavs b/tests/ref/fate/cavs index ddcbe04d152..89c2b4b0b4f 100644 --- a/tests/ref/fate/cavs +++ b/tests/ref/fate/cavs @@ -172,4 +172,4 @@ 0, 166, 166, 1, 622080, 0x05496a5d 0, 167, 167, 1, 622080, 0xdcb4cee8 0, 168, 168, 1, 622080, 0xb41172e5 -0, 169, 169, 1, 622080, 0x56c72478 +0, 169, 169, 1, 622080, 0x84ff3af9 diff --git a/tests/ref/fate/cbs-av1-av1-1-b10-23-film_grain-50 b/tests/ref/fate/cbs-av1-av1-1-b10-23-film_grain-50 new file mode 100644 index 00000000000..500e6558030 --- /dev/null +++ b/tests/ref/fate/cbs-av1-av1-1-b10-23-film_grain-50 @@ -0,0 +1 @@ +0ab934a437181d0275dc6c26bb9f6281 diff --git a/tests/ref/fate/cbs-av1-av1-1-b8-02-allintra b/tests/ref/fate/cbs-av1-av1-1-b8-02-allintra new file mode 100644 index 00000000000..11abf2f336a --- /dev/null +++ b/tests/ref/fate/cbs-av1-av1-1-b8-02-allintra @@ -0,0 +1 @@ +134b447b04086088de4da127a97731f3 diff --git a/tests/ref/fate/cbs-av1-av1-1-b8-03-sizedown b/tests/ref/fate/cbs-av1-av1-1-b8-03-sizedown new file mode 100644 index 00000000000..7188d16dadf --- /dev/null +++ b/tests/ref/fate/cbs-av1-av1-1-b8-03-sizedown @@ -0,0 +1 @@ +e5924930773efdbbd82da02c96747f27 diff --git a/tests/ref/fate/cbs-av1-av1-1-b8-03-sizeup b/tests/ref/fate/cbs-av1-av1-1-b8-03-sizeup new file mode 100644 index 00000000000..9d767bbedeb --- /dev/null +++ b/tests/ref/fate/cbs-av1-av1-1-b8-03-sizeup @@ -0,0 +1 @@ +0348fba6ebf6caadfe80b19a6ad93caa diff --git a/tests/ref/fate/cbs-av1-av1-1-b8-04-cdfupdate b/tests/ref/fate/cbs-av1-av1-1-b8-04-cdfupdate new file mode 100644 index 00000000000..9325deac36b --- /dev/null +++ b/tests/ref/fate/cbs-av1-av1-1-b8-04-cdfupdate @@ -0,0 +1 @@ +aec87cd950fb985b1e345d0366709aea diff --git a/tests/ref/fate/cbs-av1-av1-1-b8-05-mv b/tests/ref/fate/cbs-av1-av1-1-b8-05-mv new file mode 100644 index 00000000000..aa4ab5138b1 --- /dev/null +++ b/tests/ref/fate/cbs-av1-av1-1-b8-05-mv @@ -0,0 +1 @@ +33f548eeef87e12b93b9bf4a3b79c70e diff --git a/tests/ref/fate/cbs-av1-av1-1-b8-06-mfmv b/tests/ref/fate/cbs-av1-av1-1-b8-06-mfmv new file mode 100644 index 00000000000..ede34aede06 --- /dev/null +++ b/tests/ref/fate/cbs-av1-av1-1-b8-06-mfmv @@ -0,0 +1 @@ +2e20870d44ba5ec5a8e1450b287e20b4 diff --git a/tests/ref/fate/cbs-av1-av1-1-b8-22-svc-L1T2 b/tests/ref/fate/cbs-av1-av1-1-b8-22-svc-L1T2 new file mode 100644 index 00000000000..d17f202fdc7 --- /dev/null +++ b/tests/ref/fate/cbs-av1-av1-1-b8-22-svc-L1T2 @@ -0,0 +1 @@ +f7138eaa1e572260a8a34f73f91e058a diff --git a/tests/ref/fate/cbs-av1-av1-1-b8-22-svc-L2T1 b/tests/ref/fate/cbs-av1-av1-1-b8-22-svc-L2T1 new file mode 100644 index 00000000000..a23f3cbd4e2 --- /dev/null +++ b/tests/ref/fate/cbs-av1-av1-1-b8-22-svc-L2T1 @@ -0,0 +1 @@ +4f51af7abcf75eba35ab1c4796793681 diff --git a/tests/ref/fate/cbs-av1-av1-1-b8-22-svc-L2T2 b/tests/ref/fate/cbs-av1-av1-1-b8-22-svc-L2T2 new file mode 100644 index 00000000000..7b6a77d980b --- /dev/null +++ b/tests/ref/fate/cbs-av1-av1-1-b8-22-svc-L2T2 @@ -0,0 +1 @@ +d52adb1719a0422782e40352e44c6cb0 diff --git a/tests/ref/fate/cbs-av1-av1-1-b8-23-film_grain-50 b/tests/ref/fate/cbs-av1-av1-1-b8-23-film_grain-50 new file mode 100644 index 00000000000..c0daa8d1cbe --- /dev/null +++ b/tests/ref/fate/cbs-av1-av1-1-b8-23-film_grain-50 @@ -0,0 +1 @@ +99a635753d7e4e7deb99fd2ba866818e diff --git a/tests/ref/fate/cbs-av1-decode_model b/tests/ref/fate/cbs-av1-decode_model new file mode 100644 index 00000000000..3d7695804f3 --- /dev/null +++ b/tests/ref/fate/cbs-av1-decode_model @@ -0,0 +1 @@ +171ebf527c4cd57179d6a4e5c4f23ce4 diff --git a/tests/ref/fate/cbs-av1-frames_refs_short_signaling b/tests/ref/fate/cbs-av1-frames_refs_short_signaling new file mode 100644 index 00000000000..15a76aef886 --- /dev/null +++ b/tests/ref/fate/cbs-av1-frames_refs_short_signaling @@ -0,0 +1 @@ +d1b05cf934aeda64b25a93423904c14d diff --git a/tests/ref/fate/cbs-av1-non_uniform_tiling b/tests/ref/fate/cbs-av1-non_uniform_tiling new file mode 100644 index 00000000000..257ae07d611 --- /dev/null +++ b/tests/ref/fate/cbs-av1-non_uniform_tiling @@ -0,0 +1 @@ +3e204ee8a71273cf0247f48e977e64b7 diff --git a/tests/ref/fate/cbs-av1-seq_hdr_op_param_info b/tests/ref/fate/cbs-av1-seq_hdr_op_param_info new file mode 100644 index 00000000000..85d4f9b0715 --- /dev/null +++ b/tests/ref/fate/cbs-av1-seq_hdr_op_param_info @@ -0,0 +1 @@ +10e7bdd7cab67f203520e44b28a6477c diff --git a/tests/ref/fate/cbs-av1-switch_frame b/tests/ref/fate/cbs-av1-switch_frame new file mode 100644 index 00000000000..07b0fd6b36b --- /dev/null +++ b/tests/ref/fate/cbs-av1-switch_frame @@ -0,0 +1 @@ +156b5297ca32c18183ca41a102a09a02 diff --git a/tests/ref/fate/concat-demuxer-extended-lavf-mxf b/tests/ref/fate/concat-demuxer-extended-lavf-mxf index 2fb5fce4b1c..e49915e4074 100644 --- a/tests/ref/fate/concat-demuxer-extended-lavf-mxf +++ b/tests/ref/fate/concat-demuxer-extended-lavf-mxf @@ -1 +1 @@ -a6fb9c37dc71cb43eb9664a8ae9f1c66 *tests/data/fate/concat-demuxer-extended-lavf-mxf.ffprobe +861b9c23587d0a09caa78c3651faf5a0 *tests/data/fate/concat-demuxer-extended-lavf-mxf.ffprobe diff --git a/tests/ref/fate/concat-demuxer-extended-lavf-mxf_d10 b/tests/ref/fate/concat-demuxer-extended-lavf-mxf_d10 index 60d729b3dae..e3e76f217a8 100644 --- a/tests/ref/fate/concat-demuxer-extended-lavf-mxf_d10 +++ b/tests/ref/fate/concat-demuxer-extended-lavf-mxf_d10 @@ -1 +1 @@ -cb7c8eac6f8917e39658e1fa4a250da8 *tests/data/fate/concat-demuxer-extended-lavf-mxf_d10.ffprobe +d66177ea3922692bc91cd0f8aa907650 *tests/data/fate/concat-demuxer-extended-lavf-mxf_d10.ffprobe diff --git a/tests/ref/fate/concat-demuxer-simple1-lavf-mxf b/tests/ref/fate/concat-demuxer-simple1-lavf-mxf index d18e35b7ba1..a590f6f24b1 100644 --- a/tests/ref/fate/concat-demuxer-simple1-lavf-mxf +++ b/tests/ref/fate/concat-demuxer-simple1-lavf-mxf @@ -120,5 +120,5 @@ audio|1|65280|1.360000|65280|1.360000|1920|0.040000|N/A|N/A|3840|207872|K_|1 Strings Metadata video|0|37|1.480000|34|1.360000|1|0.040000|N/A|N/A|24786|212480|K_|1 Strings Metadata -0|mpeg2video|4|video|1/25|[0][0][0][0]|0x0000|352|288|0|0|1|1:1|11:9|yuv420p|8|tv|unknown|unknown|unknown|left|progressive|N/A|1|N/A|25/1|25/1|1/25|N/A|N/A|N/A|N/A|N/A|N/A|N/A|N/A|N/A|51|0|0|0|0|0|0|0|0|0|0|0|0|0x060A2B340101010501010D001300000000000000000000000000000000000001 +0|mpeg2video|4|video|1/25|[0][0][0][0]|0x0000|352|288|0|0|0|1|1:1|11:9|yuv420p|8|tv|unknown|unknown|unknown|left|progressive|N/A|1|N/A|25/1|25/1|1/25|N/A|N/A|N/A|N/A|N/A|N/A|N/A|N/A|N/A|51|0|0|0|0|0|0|0|0|0|0|0|0|0x060A2B340101010501010D001300000000000000000000000000000000000001 1|pcm_s16le|unknown|audio|1/48000|[0][0][0][0]|0x0000|s16|48000|1|unknown|16|N/A|0/0|0/0|1/48000|0|0.000000|N/A|N/A|768000|N/A|N/A|N/A|N/A|50|0|0|0|0|0|0|0|0|0|0|0|0|0x060A2B340101010501010D001300000000000000000000000000000000000001 diff --git a/tests/ref/fate/concat-demuxer-simple1-lavf-mxf_d10 b/tests/ref/fate/concat-demuxer-simple1-lavf-mxf_d10 index e83d1bf847e..79ce1e2306f 100644 --- a/tests/ref/fate/concat-demuxer-simple1-lavf-mxf_d10 +++ b/tests/ref/fate/concat-demuxer-simple1-lavf-mxf_d10 @@ -78,5 +78,5 @@ video|0|34|1.360000|34|1.360000|1|0.040000|N/A|N/A|150000|1924096|K_|1 Strings Metadata audio|1|65280|1.360000|65280|1.360000|1920|0.040000|N/A|N/A|7680|2074624|K_|1 Strings Metadata -0|mpeg2video|0|video|1/25|[0][0][0][0]|0x0000|720|608|0|0|0|1:1|45:38|yuv422p|5|tv|unknown|unknown|unknown|topleft|tt|N/A|1|N/A|25/1|25/1|1/25|0|0.000000|N/A|N/A|30000000|N/A|N/A|N/A|N/A|35|0|0|0|0|0|0|0|0|0|0|0|0|0x060A2B340101010501010D001300000000000000000000000000000000000001 +0|mpeg2video|0|video|1/25|[0][0][0][0]|0x0000|720|608|0|0|0|0|1:1|45:38|yuv422p|5|tv|unknown|unknown|unknown|topleft|tt|N/A|1|N/A|25/1|25/1|1/25|0|0.000000|N/A|N/A|30000000|N/A|N/A|N/A|N/A|35|0|0|0|0|0|0|0|0|0|0|0|0|0x060A2B340101010501010D001300000000000000000000000000000000000001 1|pcm_s16le|unknown|audio|1/48000|[0][0][0][0]|0x0000|s16|48000|2|unknown|16|N/A|0/0|0/0|1/48000|0|0.000000|N/A|N/A|1536000|N/A|N/A|N/A|N/A|35|0|0|0|0|0|0|0|0|0|0|0|0|0x060A2B340101010501010D001300000000000000000000000000000000000001 diff --git a/tests/ref/fate/concat-demuxer-simple2-lavf-ts b/tests/ref/fate/concat-demuxer-simple2-lavf-ts index e5cf18bbcee..a364b761d13 100644 --- a/tests/ref/fate/concat-demuxer-simple2-lavf-ts +++ b/tests/ref/fate/concat-demuxer-simple2-lavf-ts @@ -1,20 +1,20 @@ video|1|982|0.010911|-2618|-0.029089|3600|0.040000|N/A|N/A|24801|564|K_MPEGTS Stream ID -video|1|4582|0.050911|982|0.010911|3600|0.040000|N/A|N/A|16429|27072|__MPEGTS Stream ID +video|1|4582|0.050911|982|0.010911|3600|0.040000|N/A|N/A|16429|25944|__MPEGTS Stream ID -video|1|8182|0.090911|4582|0.050911|3600|0.040000|N/A|N/A|14508|44932|__MPEGTS Stream ID +video|1|8182|0.090911|4582|0.050911|3600|0.040000|N/A|N/A|14508|42864|__MPEGTS Stream ID -video|1|11782|0.130911|8182|0.090911|3600|0.040000|N/A|N/A|12622|60536|__MPEGTS Stream ID +video|1|11782|0.130911|8182|0.090911|3600|0.040000|N/A|N/A|12622|58092|__MPEGTS Stream ID -video|1|15382|0.170911|11782|0.130911|3600|0.040000|N/A|N/A|13393|74260|__MPEGTS Stream ID +video|1|15382|0.170911|11782|0.130911|3600|0.040000|N/A|N/A|13393|71064|__MPEGTS Stream ID -video|1|18982|0.210911|15382|0.170911|3600|0.040000|N/A|N/A|13092|88924|__MPEGTS Stream ID +video|1|18982|0.210911|15382|0.170911|3600|0.040000|N/A|N/A|13092|84788|__MPEGTS Stream ID -video|1|22582|0.250911|18982|0.210911|3600|0.040000|N/A|N/A|12755|102836|__MPEGTS Stream ID +video|1|22582|0.250911|18982|0.210911|3600|0.040000|N/A|N/A|12755|98700|__MPEGTS Stream ID -video|1|26182|0.290911|22582|0.250911|3600|0.040000|N/A|N/A|12023|116748|__MPEGTS Stream ID +video|1|26182|0.290911|22582|0.250911|3600|0.040000|N/A|N/A|12023|111860|__MPEGTS Stream ID -audio|0|0|0.000000|0|0.000000|2351|0.026122|N/A|N/A|208|159988|K_MPEGTS Stream ID +audio|0|0|0.000000|0|0.000000|2351|0.026122|N/A|N/A|208|152844|K_MPEGTS Stream ID audio|0|2351|0.026122|2351|0.026122|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|4702|0.052244|4702|0.052244|2351|0.026122|N/A|N/A|209|N/A|K_ @@ -29,27 +29,27 @@ audio|0|23510|0.261222|23510|0.261222|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|25861|0.287344|25861|0.287344|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|28212|0.313467|28212|0.313467|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|30563|0.339589|30563|0.339589|2351|0.026122|N/A|N/A|209|N/A|K_ -video|1|29782|0.330911|26182|0.290911|3600|0.040000|N/A|N/A|14098|130096|__MPEGTS Stream ID +video|1|29782|0.330911|26182|0.290911|3600|0.040000|N/A|N/A|14098|124268|__MPEGTS Stream ID -video|1|33382|0.370911|29782|0.330911|3600|0.040000|N/A|N/A|13329|145324|__MPEGTS Stream ID +video|1|33382|0.370911|29782|0.330911|3600|0.040000|N/A|N/A|13329|139120|__MPEGTS Stream ID -video|1|36982|0.410911|33382|0.370911|3600|0.040000|N/A|N/A|12135|162996|__MPEGTS Stream ID +video|1|36982|0.410911|33382|0.370911|3600|0.040000|N/A|N/A|12135|155852|__MPEGTS Stream ID -video|1|40582|0.450911|36982|0.410911|3600|0.040000|N/A|N/A|12282|176344|__MPEGTS Stream ID +video|1|40582|0.450911|36982|0.410911|3600|0.040000|N/A|N/A|12282|168448|__MPEGTS Stream ID -video|1|44182|0.490911|40582|0.450911|3600|0.040000|N/A|N/A|24786|189692|K_MPEGTS Stream ID +video|1|44182|0.490911|40582|0.450911|3600|0.040000|N/A|N/A|24786|181420|K_MPEGTS Stream ID -video|1|47782|0.530911|44182|0.490911|3600|0.040000|N/A|N/A|17440|216388|__MPEGTS Stream ID +video|1|47782|0.530911|44182|0.490911|3600|0.040000|N/A|N/A|17440|206988|__MPEGTS Stream ID -video|1|51382|0.570911|47782|0.530911|3600|0.040000|N/A|N/A|15019|235000|__MPEGTS Stream ID +video|1|51382|0.570911|47782|0.530911|3600|0.040000|N/A|N/A|15019|224848|__MPEGTS Stream ID -video|1|54982|0.610911|51382|0.570911|3600|0.040000|N/A|N/A|13449|251356|__MPEGTS Stream ID +video|1|54982|0.610911|51382|0.570911|3600|0.040000|N/A|N/A|13449|240640|__MPEGTS Stream ID -video|1|58582|0.650911|54982|0.610911|3600|0.040000|N/A|N/A|12398|266020|__MPEGTS Stream ID +video|1|58582|0.650911|54982|0.610911|3600|0.040000|N/A|N/A|12398|254552|__MPEGTS Stream ID -video|1|62182|0.690911|58582|0.650911|3600|0.040000|N/A|N/A|13455|279744|__MPEGTS Stream ID +video|1|62182|0.690911|58582|0.650911|3600|0.040000|N/A|N/A|13455|267336|__MPEGTS Stream ID -audio|0|32915|0.365722|32915|0.365722|2351|0.026122|N/A|N/A|209|322608|K_MPEGTS Stream ID +audio|0|32915|0.365722|32915|0.365722|2351|0.026122|N/A|N/A|209|308508|K_MPEGTS Stream ID audio|0|35266|0.391844|35266|0.391844|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|37617|0.417967|37617|0.417967|2351|0.026122|N/A|N/A|209|N/A|K_ @@ -64,17 +64,17 @@ audio|0|56425|0.626944|56425|0.626944|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|58776|0.653067|58776|0.653067|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|61127|0.679189|61127|0.679189|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|63478|0.705311|63478|0.705311|2351|0.026122|N/A|N/A|209|N/A|K_ -video|1|65782|0.730911|62182|0.690911|3600|0.040000|N/A|N/A|13836|294408|__MPEGTS Stream ID +video|1|65782|0.730911|62182|0.690911|3600|0.040000|N/A|N/A|13836|281624|__MPEGTS Stream ID -video|1|69382|0.770911|65782|0.730911|3600|0.040000|N/A|N/A|12163|309448|__MPEGTS Stream ID +video|1|69382|0.770911|65782|0.730911|3600|0.040000|N/A|N/A|12163|295912|__MPEGTS Stream ID -video|1|72982|0.810911|69382|0.770911|3600|0.040000|N/A|N/A|12692|325992|__MPEGTS Stream ID +video|1|72982|0.810911|69382|0.770911|3600|0.040000|N/A|N/A|12692|311516|__MPEGTS Stream ID -video|1|76582|0.850911|72982|0.810911|3600|0.040000|N/A|N/A|10824|339528|__MPEGTS Stream ID +video|1|76582|0.850911|72982|0.810911|3600|0.040000|N/A|N/A|10824|325052|__MPEGTS Stream ID -video|1|80182|0.890911|76582|0.850911|3600|0.040000|N/A|N/A|11286|351372|__MPEGTS Stream ID +video|1|80182|0.890911|76582|0.850911|3600|0.040000|N/A|N/A|11286|336144|__MPEGTS Stream ID -audio|0|65829|0.731433|65829|0.731433|2351|0.026122|N/A|N/A|209|404576|K_MPEGTS Stream ID +audio|0|65829|0.731433|65829|0.731433|2351|0.026122|N/A|N/A|209|386716|K_MPEGTS Stream ID audio|0|68180|0.757556|68180|0.757556|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|70531|0.783678|70531|0.783678|2351|0.026122|N/A|N/A|209|N/A|K_ @@ -86,26 +86,26 @@ audio|0|82286|0.914289|82286|0.914289|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|84637|0.940411|84637|0.940411|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|86988|0.966533|86988|0.966533|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|89339|0.992656|89339|0.992656|2351|0.026122|N/A|N/A|209|N/A|K_ -video|1|83782|0.930911|80182|0.890911|3600|0.040000|N/A|N/A|12678|363592|__MPEGTS Stream ID +video|1|83782|0.930911|80182|0.890911|3600|0.040000|N/A|N/A|12678|347800|__MPEGTS Stream ID -video|1|87382|0.970911|83782|0.930911|3600|0.040000|N/A|N/A|24711|377880|K_ +video|1|87382|0.970911|83782|0.930911|3600|0.040000|N/A|N/A|24711|361336|K_ video|1|91964|1.021822|88364|0.981822|3600|0.040000|N/A|N/A|24801|564|K_MPEGTS Stream ID -video|1|95564|1.061822|91964|1.021822|3600|0.040000|N/A|N/A|16429|27072|__MPEGTS Stream ID +video|1|95564|1.061822|91964|1.021822|3600|0.040000|N/A|N/A|16429|25944|__MPEGTS Stream ID -video|1|99164|1.101822|95564|1.061822|3600|0.040000|N/A|N/A|14508|44932|__MPEGTS Stream ID +video|1|99164|1.101822|95564|1.061822|3600|0.040000|N/A|N/A|14508|42864|__MPEGTS Stream ID -video|1|102764|1.141822|99164|1.101822|3600|0.040000|N/A|N/A|12622|60536|__MPEGTS Stream ID +video|1|102764|1.141822|99164|1.101822|3600|0.040000|N/A|N/A|12622|58092|__MPEGTS Stream ID -video|1|106364|1.181822|102764|1.141822|3600|0.040000|N/A|N/A|13393|74260|__MPEGTS Stream ID +video|1|106364|1.181822|102764|1.141822|3600|0.040000|N/A|N/A|13393|71064|__MPEGTS Stream ID -video|1|109964|1.221822|106364|1.181822|3600|0.040000|N/A|N/A|13092|88924|__MPEGTS Stream ID +video|1|109964|1.221822|106364|1.181822|3600|0.040000|N/A|N/A|13092|84788|__MPEGTS Stream ID -video|1|113564|1.261822|109964|1.221822|3600|0.040000|N/A|N/A|12755|102836|__MPEGTS Stream ID +video|1|113564|1.261822|109964|1.221822|3600|0.040000|N/A|N/A|12755|98700|__MPEGTS Stream ID -video|1|117164|1.301822|113564|1.261822|3600|0.040000|N/A|N/A|12023|116748|__MPEGTS Stream ID +video|1|117164|1.301822|113564|1.261822|3600|0.040000|N/A|N/A|12023|111860|__MPEGTS Stream ID -audio|0|90982|1.010911|90982|1.010911|2351|0.026122|N/A|N/A|208|159988|K_MPEGTS Stream ID +audio|0|90982|1.010911|90982|1.010911|2351|0.026122|N/A|N/A|208|152844|K_MPEGTS Stream ID audio|0|93333|1.037033|93333|1.037033|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|95684|1.063156|95684|1.063156|2351|0.026122|N/A|N/A|209|N/A|K_ @@ -120,27 +120,27 @@ audio|0|114492|1.272133|114492|1.272133|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|116843|1.298256|116843|1.298256|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|119194|1.324378|119194|1.324378|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|121545|1.350500|121545|1.350500|2351|0.026122|N/A|N/A|209|N/A|K_ -video|1|120764|1.341822|117164|1.301822|3600|0.040000|N/A|N/A|14098|130096|__MPEGTS Stream ID +video|1|120764|1.341822|117164|1.301822|3600|0.040000|N/A|N/A|14098|124268|__MPEGTS Stream ID -video|1|124364|1.381822|120764|1.341822|3600|0.040000|N/A|N/A|13329|145324|__MPEGTS Stream ID +video|1|124364|1.381822|120764|1.341822|3600|0.040000|N/A|N/A|13329|139120|__MPEGTS Stream ID -video|1|127964|1.421822|124364|1.381822|3600|0.040000|N/A|N/A|12135|162996|__MPEGTS Stream ID +video|1|127964|1.421822|124364|1.381822|3600|0.040000|N/A|N/A|12135|155852|__MPEGTS Stream ID -video|1|131564|1.461822|127964|1.421822|3600|0.040000|N/A|N/A|12282|176344|__MPEGTS Stream ID +video|1|131564|1.461822|127964|1.421822|3600|0.040000|N/A|N/A|12282|168448|__MPEGTS Stream ID -video|1|135164|1.501822|131564|1.461822|3600|0.040000|N/A|N/A|24786|189692|K_MPEGTS Stream ID +video|1|135164|1.501822|131564|1.461822|3600|0.040000|N/A|N/A|24786|181420|K_MPEGTS Stream ID -video|1|138764|1.541822|135164|1.501822|3600|0.040000|N/A|N/A|17440|216388|__MPEGTS Stream ID +video|1|138764|1.541822|135164|1.501822|3600|0.040000|N/A|N/A|17440|206988|__MPEGTS Stream ID -video|1|142364|1.581822|138764|1.541822|3600|0.040000|N/A|N/A|15019|235000|__MPEGTS Stream ID +video|1|142364|1.581822|138764|1.541822|3600|0.040000|N/A|N/A|15019|224848|__MPEGTS Stream ID -video|1|145964|1.621822|142364|1.581822|3600|0.040000|N/A|N/A|13449|251356|__MPEGTS Stream ID +video|1|145964|1.621822|142364|1.581822|3600|0.040000|N/A|N/A|13449|240640|__MPEGTS Stream ID -video|1|149564|1.661822|145964|1.621822|3600|0.040000|N/A|N/A|12398|266020|__MPEGTS Stream ID +video|1|149564|1.661822|145964|1.621822|3600|0.040000|N/A|N/A|12398|254552|__MPEGTS Stream ID -video|1|153164|1.701822|149564|1.661822|3600|0.040000|N/A|N/A|13455|279744|__MPEGTS Stream ID +video|1|153164|1.701822|149564|1.661822|3600|0.040000|N/A|N/A|13455|267336|__MPEGTS Stream ID -audio|0|123897|1.376633|123897|1.376633|2351|0.026122|N/A|N/A|209|322608|K_MPEGTS Stream ID +audio|0|123897|1.376633|123897|1.376633|2351|0.026122|N/A|N/A|209|308508|K_MPEGTS Stream ID audio|0|126248|1.402756|126248|1.402756|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|128599|1.428878|128599|1.428878|2351|0.026122|N/A|N/A|209|N/A|K_ @@ -155,17 +155,17 @@ audio|0|147407|1.637856|147407|1.637856|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|149758|1.663978|149758|1.663978|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|152109|1.690100|152109|1.690100|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|154460|1.716222|154460|1.716222|2351|0.026122|N/A|N/A|209|N/A|K_ -video|1|156764|1.741822|153164|1.701822|3600|0.040000|N/A|N/A|13836|294408|__MPEGTS Stream ID +video|1|156764|1.741822|153164|1.701822|3600|0.040000|N/A|N/A|13836|281624|__MPEGTS Stream ID -video|1|160364|1.781822|156764|1.741822|3600|0.040000|N/A|N/A|12163|309448|__MPEGTS Stream ID +video|1|160364|1.781822|156764|1.741822|3600|0.040000|N/A|N/A|12163|295912|__MPEGTS Stream ID -video|1|163964|1.821822|160364|1.781822|3600|0.040000|N/A|N/A|12692|325992|__MPEGTS Stream ID +video|1|163964|1.821822|160364|1.781822|3600|0.040000|N/A|N/A|12692|311516|__MPEGTS Stream ID -video|1|167564|1.861822|163964|1.821822|3600|0.040000|N/A|N/A|10824|339528|__MPEGTS Stream ID +video|1|167564|1.861822|163964|1.821822|3600|0.040000|N/A|N/A|10824|325052|__MPEGTS Stream ID -video|1|171164|1.901822|167564|1.861822|3600|0.040000|N/A|N/A|11286|351372|__MPEGTS Stream ID +video|1|171164|1.901822|167564|1.861822|3600|0.040000|N/A|N/A|11286|336144|__MPEGTS Stream ID -audio|0|156811|1.742344|156811|1.742344|2351|0.026122|N/A|N/A|209|404576|K_MPEGTS Stream ID +audio|0|156811|1.742344|156811|1.742344|2351|0.026122|N/A|N/A|209|386716|K_MPEGTS Stream ID audio|0|159162|1.768467|159162|1.768467|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|161513|1.794589|161513|1.794589|2351|0.026122|N/A|N/A|209|N/A|K_ @@ -177,16 +177,16 @@ audio|0|173268|1.925200|173268|1.925200|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|175619|1.951322|175619|1.951322|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|177970|1.977444|177970|1.977444|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|180321|2.003567|180321|2.003567|2351|0.026122|N/A|N/A|209|N/A|K_ -video|1|174764|1.941822|171164|1.901822|3600|0.040000|N/A|N/A|12678|363592|__MPEGTS Stream ID +video|1|174764|1.941822|171164|1.901822|3600|0.040000|N/A|N/A|12678|347800|__MPEGTS Stream ID -video|1|178364|1.981822|174764|1.941822|3600|0.040000|N/A|N/A|24711|377880|K_ -video|1|139582|1.550911|135982|1.510911|3600|0.040000|N/A|N/A|12692|325992|__MPEGTS Stream ID +video|1|178364|1.981822|174764|1.941822|3600|0.040000|N/A|N/A|24711|361336|K_ +video|1|139582|1.550911|135982|1.510911|3600|0.040000|N/A|N/A|12692|311516|__MPEGTS Stream ID -video|1|143182|1.590911|139582|1.550911|3600|0.040000|N/A|N/A|10824|339528|__MPEGTS Stream ID +video|1|143182|1.590911|139582|1.550911|3600|0.040000|N/A|N/A|10824|325052|__MPEGTS Stream ID -video|1|146782|1.630911|143182|1.590911|3600|0.040000|N/A|N/A|11286|351372|__MPEGTS Stream ID +video|1|146782|1.630911|143182|1.590911|3600|0.040000|N/A|N/A|11286|336144|__MPEGTS Stream ID -audio|0|132429|1.471433|132429|1.471433|2351|0.026122|N/A|N/A|209|404576|K_MPEGTS Stream ID +audio|0|132429|1.471433|132429|1.471433|2351|0.026122|N/A|N/A|209|386716|K_MPEGTS Stream ID audio|0|134780|1.497556|134780|1.497556|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|137131|1.523678|137131|1.523678|2351|0.026122|N/A|N/A|209|N/A|K_ @@ -198,18 +198,18 @@ audio|0|148886|1.654289|148886|1.654289|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|151237|1.680411|151237|1.680411|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|153588|1.706533|153588|1.706533|2351|0.026122|N/A|N/A|209|N/A|K_ audio|0|155939|1.732656|155939|1.732656|2351|0.026122|N/A|N/A|209|N/A|K_ -video|1|150382|1.670911|146782|1.630911|3600|0.040000|N/A|N/A|12678|363592|__MPEGTS Stream ID +video|1|150382|1.670911|146782|1.630911|3600|0.040000|N/A|N/A|12678|347800|__MPEGTS Stream ID -video|1|153982|1.710911|150382|1.670911|3600|0.040000|N/A|N/A|24711|377880|K_ -video|1|161182|1.790911|157582|1.750911|3600|0.040000|N/A|N/A|12135|162996|__MPEGTS Stream ID +video|1|153982|1.710911|150382|1.670911|3600|0.040000|N/A|N/A|24711|361336|K_ +video|1|161182|1.790911|157582|1.750911|3600|0.040000|N/A|N/A|12135|155852|__MPEGTS Stream ID -video|1|164782|1.830911|161182|1.790911|3600|0.040000|N/A|N/A|12282|176344|__MPEGTS Stream ID +video|1|164782|1.830911|161182|1.790911|3600|0.040000|N/A|N/A|12282|168448|__MPEGTS Stream ID -video|1|168382|1.870911|164782|1.830911|3600|0.040000|N/A|N/A|24786|189692|K_MPEGTS Stream ID +video|1|168382|1.870911|164782|1.830911|3600|0.040000|N/A|N/A|24786|181420|K_MPEGTS Stream ID -video|1|171982|1.910911|168382|1.870911|3600|0.040000|N/A|N/A|17440|216388|__MPEGTS Stream ID +video|1|171982|1.910911|168382|1.870911|3600|0.040000|N/A|N/A|17440|206988|__MPEGTS Stream ID -video|1|175582|1.950911|171982|1.910911|3600|0.040000|N/A|N/A|15019|235000|__MPEGTS Stream ID +video|1|175582|1.950911|171982|1.910911|3600|0.040000|N/A|N/A|15019|224848|__MPEGTS Stream ID 0|mp2|unknown|audio|1/44100|[3][0][0][0]|0x0003|s16p|44100|1|mono|0|N/A|0/0|0/0|1/90000|0|0.000000|N/A|N/A|64000|N/A|N/A|N/A|N/A|89|0|0|0|0|0|0|0|0|0|0|0|0 -1|mpeg2video|4|video|1/25|[2][0][0][0]|0x0002|352|288|0|0|1|1:1|11:9|yuv420p|8|tv|unknown|unknown|unknown|left|progressive|N/A|1|N/A|25/1|25/1|1/90000|N/A|N/A|N/A|N/A|N/A|N/A|N/A|N/A|N/A|60|0|0|0|0|0|0|0|0|0|0|0|0 +1|mpeg2video|4|video|1/25|[2][0][0][0]|0x0002|352|288|0|0|0|1|1:1|11:9|yuv420p|8|tv|unknown|unknown|unknown|left|progressive|N/A|1|N/A|25/1|25/1|1/90000|N/A|N/A|N/A|N/A|N/A|N/A|N/A|N/A|N/A|60|0|0|0|0|0|0|0|0|0|0|0|0 diff --git a/tests/ref/fate/copy-psp b/tests/ref/fate/copy-psp index 44ec4612658..8b2cef87faa 100644 --- a/tests/ref/fate/copy-psp +++ b/tests/ref/fate/copy-psp @@ -1,4 +1,4 @@ -65a177552e03123c9a62ddb942970d05 *tests/data/fate/copy-psp.psp +8578401522773d0832f538ac915ad0b0 *tests/data/fate/copy-psp.psp 2041445 tests/data/fate/copy-psp.psp #extradata 0: 51, 0xaf6d1012 #extradata 1: 2, 0x00b200a1 diff --git a/tests/ref/fate/copy-trac236 b/tests/ref/fate/copy-trac236 index 2ac05e63e67..1583ae5704c 100644 --- a/tests/ref/fate/copy-trac236 +++ b/tests/ref/fate/copy-trac236 @@ -1,4 +1,4 @@ -959a4d78c6c11936e361fc3101a013eb *tests/data/fate/copy-trac236.mov +984a33c6292e3d35e2cfdfbf66d8e82b *tests/data/fate/copy-trac236.mov 630860 tests/data/fate/copy-trac236.mov #tb 0: 100/2997 #media_type 0: video diff --git a/tests/ref/fate/exr-rgb-b44a-half-negative-4x4 b/tests/ref/fate/exr-rgb-b44a-half-negative-4x4 index 2ff4e9742b7..82e3f74cd56 100644 --- a/tests/ref/fate/exr-rgb-b44a-half-negative-4x4 +++ b/tests/ref/fate/exr-rgb-b44a-half-negative-4x4 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 4x4 #sar 0: 1/1 -0, 0, 0, 1, 96, 0x70600260 +0, 0, 0, 1, 192, 0x5b8e39c0 diff --git a/tests/ref/fate/exr-rgb-scanline-b44-half-float-12x8-l1 b/tests/ref/fate/exr-rgb-scanline-b44-half-float-12x8-l1 index a19b500c901..e900164b8ce 100644 --- a/tests/ref/fate/exr-rgb-scanline-b44-half-float-12x8-l1 +++ b/tests/ref/fate/exr-rgb-scanline-b44-half-float-12x8-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0xa1a70fac +0, 0, 0, 1, 1152, 0x577d5150 diff --git a/tests/ref/fate/exr-rgb-scanline-b44-half-float-12x8-l2 b/tests/ref/fate/exr-rgb-scanline-b44-half-float-12x8-l2 index 444fb566d23..f577b7ad35b 100644 --- a/tests/ref/fate/exr-rgb-scanline-b44-half-float-12x8-l2 +++ b/tests/ref/fate/exr-rgb-scanline-b44-half-float-12x8-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 768, 0x22f77b1c +0, 0, 0, 1, 1536, 0xb8d48218 diff --git a/tests/ref/fate/exr-rgb-scanline-float-b44 b/tests/ref/fate/exr-rgb-scanline-float-b44 index 91b0e1fba13..6c1671377e9 100644 --- a/tests/ref/fate/exr-rgb-scanline-float-b44 +++ b/tests/ref/fate/exr-rgb-scanline-float-b44 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x39c8e03e +0, 0, 0, 1, 1152, 0x18e0f4d9 diff --git a/tests/ref/fate/exr-rgb-scanline-float-piz-48x32 b/tests/ref/fate/exr-rgb-scanline-float-piz-48x32 index d1a5d90ee2f..d9501e75940 100644 --- a/tests/ref/fate/exr-rgb-scanline-float-piz-48x32 +++ b/tests/ref/fate/exr-rgb-scanline-float-piz-48x32 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 48x32 #sar 0: 1/1 -0, 0, 0, 1, 9216, 0x5b3f7a8e +0, 0, 0, 1, 18432, 0x479e42a8 diff --git a/tests/ref/fate/exr-rgb-scanline-half-b44-12x8 b/tests/ref/fate/exr-rgb-scanline-half-b44-12x8 index 8464203e7a0..2a63877ab90 100644 --- a/tests/ref/fate/exr-rgb-scanline-half-b44-12x8 +++ b/tests/ref/fate/exr-rgb-scanline-half-b44-12x8 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x506469f9 +0, 0, 0, 1, 1152, 0xe84d5b9d diff --git a/tests/ref/fate/exr-rgb-scanline-half-b44-13x9 b/tests/ref/fate/exr-rgb-scanline-half-b44-13x9 index 675d2f09490..a8136368ef9 100644 --- a/tests/ref/fate/exr-rgb-scanline-half-b44-13x9 +++ b/tests/ref/fate/exr-rgb-scanline-half-b44-13x9 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 13x9 #sar 0: 1/1 -0, 0, 0, 1, 702, 0x6914838a +0, 0, 0, 1, 1404, 0x252cc156 diff --git a/tests/ref/fate/exr-rgb-scanline-half-piz-bw b/tests/ref/fate/exr-rgb-scanline-half-piz-bw index 6e08d528eba..8dcab4196e8 100644 --- a/tests/ref/fate/exr-rgb-scanline-half-piz-bw +++ b/tests/ref/fate/exr-rgb-scanline-half-piz-bw @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 34x27 #sar 0: 1/1 -0, 0, 0, 1, 5508, 0x36d15e2e +0, 0, 0, 1, 11016, 0x1644e1f9 diff --git a/tests/ref/fate/exr-rgb-scanline-half-piz-color b/tests/ref/fate/exr-rgb-scanline-half-piz-color index 21510792ff1..0a7af94d7aa 100644 --- a/tests/ref/fate/exr-rgb-scanline-half-piz-color +++ b/tests/ref/fate/exr-rgb-scanline-half-piz-color @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 34x40 #sar 0: 1/1 -0, 0, 0, 1, 8160, 0x9dd67b7d +0, 0, 0, 1, 16320, 0xc40939ad diff --git a/tests/ref/fate/exr-rgb-scanline-half-piz-dw-t01 b/tests/ref/fate/exr-rgb-scanline-half-piz-dw-t01 index e135aed3ba4..6a77d850467 100644 --- a/tests/ref/fate/exr-rgb-scanline-half-piz-dw-t01 +++ b/tests/ref/fate/exr-rgb-scanline-half-piz-dw-t01 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 400x300 #sar 0: 1/1 -0, 0, 0, 1, 720000, 0xe50fc9f8 +0, 0, 0, 1, 1440000, 0x4800b00b diff --git a/tests/ref/fate/exr-rgb-scanline-half-piz-dw-t08 b/tests/ref/fate/exr-rgb-scanline-half-piz-dw-t08 index a443c98d9de..7cf760334eb 100644 --- a/tests/ref/fate/exr-rgb-scanline-half-piz-dw-t08 +++ b/tests/ref/fate/exr-rgb-scanline-half-piz-dw-t08 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 501x401 #sar 0: 1/1 -0, 0, 0, 1, 1205406, 0xc45cc9f8 +0, 0, 0, 1, 2410812, 0x2dd1b00b diff --git a/tests/ref/fate/exr-rgb-scanline-none-negative-red b/tests/ref/fate/exr-rgb-scanline-none-negative-red index 8c399b2f5e6..ae45873547d 100644 --- a/tests/ref/fate/exr-rgb-scanline-none-negative-red +++ b/tests/ref/fate/exr-rgb-scanline-none-negative-red @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 4x4 #sar 0: 1/1 -0, 0, 0, 1, 96, 0x27bc131b +0, 0, 0, 1, 192, 0xc87b5685 diff --git a/tests/ref/fate/exr-rgb-scanline-pxr24-float-12x8 b/tests/ref/fate/exr-rgb-scanline-pxr24-float-12x8 index 13946d8e032..1a2e6e77f85 100644 --- a/tests/ref/fate/exr-rgb-scanline-pxr24-float-12x8 +++ b/tests/ref/fate/exr-rgb-scanline-pxr24-float-12x8 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x7120e072 +0, 0, 0, 1, 1152, 0x046e63d0 diff --git a/tests/ref/fate/exr-rgb-scanline-pxr24-float-half-l1 b/tests/ref/fate/exr-rgb-scanline-pxr24-float-half-l1 index 1f97a2fcb1e..df84ed39aa6 100644 --- a/tests/ref/fate/exr-rgb-scanline-pxr24-float-half-l1 +++ b/tests/ref/fate/exr-rgb-scanline-pxr24-float-half-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0xbd350af8 +0, 0, 0, 1, 1152, 0xd9575c2d diff --git a/tests/ref/fate/exr-rgb-scanline-pxr24-float-half-l2 b/tests/ref/fate/exr-rgb-scanline-pxr24-float-half-l2 index e57f026f647..4b50d0fd5e1 100644 --- a/tests/ref/fate/exr-rgb-scanline-pxr24-float-half-l2 +++ b/tests/ref/fate/exr-rgb-scanline-pxr24-float-half-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 768, 0xadb27043 +0, 0, 0, 1, 1536, 0x04e1137d diff --git a/tests/ref/fate/exr-rgb-scanline-pxr24-half-float-l1 b/tests/ref/fate/exr-rgb-scanline-pxr24-half-float-l1 index af042043e7e..ec431d3d85f 100644 --- a/tests/ref/fate/exr-rgb-scanline-pxr24-half-float-l1 +++ b/tests/ref/fate/exr-rgb-scanline-pxr24-half-float-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x5ede004c +0, 0, 0, 1, 1152, 0x8f8c4a81 diff --git a/tests/ref/fate/exr-rgb-scanline-pxr24-half-float-l2 b/tests/ref/fate/exr-rgb-scanline-pxr24-half-float-l2 index 048e30d4d52..3b6aa35b749 100644 --- a/tests/ref/fate/exr-rgb-scanline-pxr24-half-float-l2 +++ b/tests/ref/fate/exr-rgb-scanline-pxr24-half-float-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 768, 0x78317b56 +0, 0, 0, 1, 1536, 0x03fb2399 diff --git a/tests/ref/fate/exr-rgb-scanline-pxr24-half-uint32-13x9 b/tests/ref/fate/exr-rgb-scanline-pxr24-half-uint32-13x9 index 50b85c8d1c9..523ed9c88bc 100644 --- a/tests/ref/fate/exr-rgb-scanline-pxr24-half-uint32-13x9 +++ b/tests/ref/fate/exr-rgb-scanline-pxr24-half-uint32-13x9 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 13x9 #sar 0: 9/10 -0, 0, 0, 1, 702, 0xf0212f1d +0, 0, 0, 1, 702, 0x68c1450d diff --git a/tests/ref/fate/exr-rgb-scanline-raw-half-float-l1 b/tests/ref/fate/exr-rgb-scanline-raw-half-float-l1 index af042043e7e..ec431d3d85f 100644 --- a/tests/ref/fate/exr-rgb-scanline-raw-half-float-l1 +++ b/tests/ref/fate/exr-rgb-scanline-raw-half-float-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x5ede004c +0, 0, 0, 1, 1152, 0x8f8c4a81 diff --git a/tests/ref/fate/exr-rgb-scanline-raw-half-float-l2 b/tests/ref/fate/exr-rgb-scanline-raw-half-float-l2 index 444fb566d23..f577b7ad35b 100644 --- a/tests/ref/fate/exr-rgb-scanline-raw-half-float-l2 +++ b/tests/ref/fate/exr-rgb-scanline-raw-half-float-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 768, 0x22f77b1c +0, 0, 0, 1, 1536, 0xb8d48218 diff --git a/tests/ref/fate/exr-rgb-scanline-rle-half-float-l1 b/tests/ref/fate/exr-rgb-scanline-rle-half-float-l1 index af042043e7e..ec431d3d85f 100644 --- a/tests/ref/fate/exr-rgb-scanline-rle-half-float-l1 +++ b/tests/ref/fate/exr-rgb-scanline-rle-half-float-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x5ede004c +0, 0, 0, 1, 1152, 0x8f8c4a81 diff --git a/tests/ref/fate/exr-rgb-scanline-rle-half-float-l2 b/tests/ref/fate/exr-rgb-scanline-rle-half-float-l2 index 444fb566d23..f577b7ad35b 100644 --- a/tests/ref/fate/exr-rgb-scanline-rle-half-float-l2 +++ b/tests/ref/fate/exr-rgb-scanline-rle-half-float-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 768, 0x22f77b1c +0, 0, 0, 1, 1536, 0xb8d48218 diff --git a/tests/ref/fate/exr-rgb-scanline-zip-half-float-l1 b/tests/ref/fate/exr-rgb-scanline-zip-half-float-l1 index af042043e7e..ec431d3d85f 100644 --- a/tests/ref/fate/exr-rgb-scanline-zip-half-float-l1 +++ b/tests/ref/fate/exr-rgb-scanline-zip-half-float-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x5ede004c +0, 0, 0, 1, 1152, 0x8f8c4a81 diff --git a/tests/ref/fate/exr-rgb-scanline-zip-half-float-l2 b/tests/ref/fate/exr-rgb-scanline-zip-half-float-l2 index 444fb566d23..f577b7ad35b 100644 --- a/tests/ref/fate/exr-rgb-scanline-zip-half-float-l2 +++ b/tests/ref/fate/exr-rgb-scanline-zip-half-float-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 768, 0x22f77b1c +0, 0, 0, 1, 1536, 0xb8d48218 diff --git a/tests/ref/fate/exr-rgb-scanline-zip1-half-float-l1 b/tests/ref/fate/exr-rgb-scanline-zip1-half-float-l1 index af042043e7e..ec431d3d85f 100644 --- a/tests/ref/fate/exr-rgb-scanline-zip1-half-float-l1 +++ b/tests/ref/fate/exr-rgb-scanline-zip1-half-float-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x5ede004c +0, 0, 0, 1, 1152, 0x8f8c4a81 diff --git a/tests/ref/fate/exr-rgb-scanline-zip1-half-float-l1-zero-offsets b/tests/ref/fate/exr-rgb-scanline-zip1-half-float-l1-zero-offsets index af042043e7e..ec431d3d85f 100644 --- a/tests/ref/fate/exr-rgb-scanline-zip1-half-float-l1-zero-offsets +++ b/tests/ref/fate/exr-rgb-scanline-zip1-half-float-l1-zero-offsets @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x5ede004c +0, 0, 0, 1, 1152, 0x8f8c4a81 diff --git a/tests/ref/fate/exr-rgb-scanline-zip1-half-float-l2 b/tests/ref/fate/exr-rgb-scanline-zip1-half-float-l2 index 444fb566d23..f577b7ad35b 100644 --- a/tests/ref/fate/exr-rgb-scanline-zip1-half-float-l2 +++ b/tests/ref/fate/exr-rgb-scanline-zip1-half-float-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 768, 0x22f77b1c +0, 0, 0, 1, 1536, 0xb8d48218 diff --git a/tests/ref/fate/exr-rgb-tile-float-raw-12x8 b/tests/ref/fate/exr-rgb-tile-float-raw-12x8 index abe41b8a7a7..c1ef672477f 100644 --- a/tests/ref/fate/exr-rgb-tile-float-raw-12x8 +++ b/tests/ref/fate/exr-rgb-tile-float-raw-12x8 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x6b950ce3 +0, 0, 0, 1, 1152, 0xea4ae7b7 diff --git a/tests/ref/fate/exr-rgb-tile-float-raw-150x130 b/tests/ref/fate/exr-rgb-tile-float-raw-150x130 index 3ac5c28df10..44f05f9acb4 100644 --- a/tests/ref/fate/exr-rgb-tile-float-raw-150x130 +++ b/tests/ref/fate/exr-rgb-tile-float-raw-150x130 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 150x130 #sar 0: 1/1 -0, 0, 0, 1, 117000, 0xabc5eab2 +0, 0, 0, 1, 234000, 0xeb8582d5 diff --git a/tests/ref/fate/exr-rgb-tile-half-float-b44-12x8-l1 b/tests/ref/fate/exr-rgb-tile-half-float-b44-12x8-l1 index a19b500c901..e900164b8ce 100644 --- a/tests/ref/fate/exr-rgb-tile-half-float-b44-12x8-l1 +++ b/tests/ref/fate/exr-rgb-tile-half-float-b44-12x8-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0xa1a70fac +0, 0, 0, 1, 1152, 0x577d5150 diff --git a/tests/ref/fate/exr-rgb-tile-half-float-b44-12x8-l2 b/tests/ref/fate/exr-rgb-tile-half-float-b44-12x8-l2 index 444fb566d23..f577b7ad35b 100644 --- a/tests/ref/fate/exr-rgb-tile-half-float-b44-12x8-l2 +++ b/tests/ref/fate/exr-rgb-tile-half-float-b44-12x8-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 768, 0x22f77b1c +0, 0, 0, 1, 1536, 0xb8d48218 diff --git a/tests/ref/fate/exr-rgb-tile-half-raw-12x8 b/tests/ref/fate/exr-rgb-tile-half-raw-12x8 index f59e248d695..f2ff3619497 100644 --- a/tests/ref/fate/exr-rgb-tile-half-raw-12x8 +++ b/tests/ref/fate/exr-rgb-tile-half-raw-12x8 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x667903f5 +0, 0, 0, 1, 1152, 0xd3614640 diff --git a/tests/ref/fate/exr-rgb-tile-pxr24-float-half-l1 b/tests/ref/fate/exr-rgb-tile-pxr24-float-half-l1 index 1f97a2fcb1e..df84ed39aa6 100644 --- a/tests/ref/fate/exr-rgb-tile-pxr24-float-half-l1 +++ b/tests/ref/fate/exr-rgb-tile-pxr24-float-half-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0xbd350af8 +0, 0, 0, 1, 1152, 0xd9575c2d diff --git a/tests/ref/fate/exr-rgb-tile-pxr24-float-half-l2 b/tests/ref/fate/exr-rgb-tile-pxr24-float-half-l2 index e57f026f647..4b50d0fd5e1 100644 --- a/tests/ref/fate/exr-rgb-tile-pxr24-float-half-l2 +++ b/tests/ref/fate/exr-rgb-tile-pxr24-float-half-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 768, 0xadb27043 +0, 0, 0, 1, 1536, 0x04e1137d diff --git a/tests/ref/fate/exr-rgb-tile-pxr24-half-float-l1 b/tests/ref/fate/exr-rgb-tile-pxr24-half-float-l1 index af042043e7e..ec431d3d85f 100644 --- a/tests/ref/fate/exr-rgb-tile-pxr24-half-float-l1 +++ b/tests/ref/fate/exr-rgb-tile-pxr24-half-float-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x5ede004c +0, 0, 0, 1, 1152, 0x8f8c4a81 diff --git a/tests/ref/fate/exr-rgb-tile-pxr24-half-float-l2 b/tests/ref/fate/exr-rgb-tile-pxr24-half-float-l2 index 048e30d4d52..3b6aa35b749 100644 --- a/tests/ref/fate/exr-rgb-tile-pxr24-half-float-l2 +++ b/tests/ref/fate/exr-rgb-tile-pxr24-half-float-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 768, 0x78317b56 +0, 0, 0, 1, 1536, 0x03fb2399 diff --git a/tests/ref/fate/exr-rgb-tile-raw-half-float-l1 b/tests/ref/fate/exr-rgb-tile-raw-half-float-l1 index af042043e7e..ec431d3d85f 100644 --- a/tests/ref/fate/exr-rgb-tile-raw-half-float-l1 +++ b/tests/ref/fate/exr-rgb-tile-raw-half-float-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x5ede004c +0, 0, 0, 1, 1152, 0x8f8c4a81 diff --git a/tests/ref/fate/exr-rgb-tile-raw-half-float-l2 b/tests/ref/fate/exr-rgb-tile-raw-half-float-l2 index 444fb566d23..f577b7ad35b 100644 --- a/tests/ref/fate/exr-rgb-tile-raw-half-float-l2 +++ b/tests/ref/fate/exr-rgb-tile-raw-half-float-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 768, 0x22f77b1c +0, 0, 0, 1, 1536, 0xb8d48218 diff --git a/tests/ref/fate/exr-rgb-tile-rle-half-float-l1 b/tests/ref/fate/exr-rgb-tile-rle-half-float-l1 index af042043e7e..ec431d3d85f 100644 --- a/tests/ref/fate/exr-rgb-tile-rle-half-float-l1 +++ b/tests/ref/fate/exr-rgb-tile-rle-half-float-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x5ede004c +0, 0, 0, 1, 1152, 0x8f8c4a81 diff --git a/tests/ref/fate/exr-rgb-tile-rle-half-float-l2 b/tests/ref/fate/exr-rgb-tile-rle-half-float-l2 index 444fb566d23..f577b7ad35b 100644 --- a/tests/ref/fate/exr-rgb-tile-rle-half-float-l2 +++ b/tests/ref/fate/exr-rgb-tile-rle-half-float-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 768, 0x22f77b1c +0, 0, 0, 1, 1536, 0xb8d48218 diff --git a/tests/ref/fate/exr-rgb-tile-zip-half-float-l1 b/tests/ref/fate/exr-rgb-tile-zip-half-float-l1 index af042043e7e..ec431d3d85f 100644 --- a/tests/ref/fate/exr-rgb-tile-zip-half-float-l1 +++ b/tests/ref/fate/exr-rgb-tile-zip-half-float-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x5ede004c +0, 0, 0, 1, 1152, 0x8f8c4a81 diff --git a/tests/ref/fate/exr-rgb-tile-zip-half-float-l2 b/tests/ref/fate/exr-rgb-tile-zip-half-float-l2 index 444fb566d23..f577b7ad35b 100644 --- a/tests/ref/fate/exr-rgb-tile-zip-half-float-l2 +++ b/tests/ref/fate/exr-rgb-tile-zip-half-float-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 768, 0x22f77b1c +0, 0, 0, 1, 1536, 0xb8d48218 diff --git a/tests/ref/fate/exr-rgb-tile-zip1-half-float-l1 b/tests/ref/fate/exr-rgb-tile-zip1-half-float-l1 index af042043e7e..ec431d3d85f 100644 --- a/tests/ref/fate/exr-rgb-tile-zip1-half-float-l1 +++ b/tests/ref/fate/exr-rgb-tile-zip1-half-float-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 576, 0x5ede004c +0, 0, 0, 1, 1152, 0x8f8c4a81 diff --git a/tests/ref/fate/exr-rgb-tile-zip1-half-float-l2 b/tests/ref/fate/exr-rgb-tile-zip1-half-float-l2 index 444fb566d23..f577b7ad35b 100644 --- a/tests/ref/fate/exr-rgb-tile-zip1-half-float-l2 +++ b/tests/ref/fate/exr-rgb-tile-zip1-half-float-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 768, 0x22f77b1c +0, 0, 0, 1, 1536, 0xb8d48218 diff --git a/tests/ref/fate/exr-rgba-multiscanline-half-b44 b/tests/ref/fate/exr-rgba-multiscanline-half-b44 index 4b9a88ba522..964bf2e65e4 100644 --- a/tests/ref/fate/exr-rgba-multiscanline-half-b44 +++ b/tests/ref/fate/exr-rgba-multiscanline-half-b44 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 935x251 #sar 0: 1/1 -0, 0, 0, 1, 1877480, 0x6f28b860 +0, 0, 0, 1, 3754960, 0x4d48a1b2 diff --git a/tests/ref/fate/exr-rgba-scanline-float-half-b44-12x8-l1 b/tests/ref/fate/exr-rgba-scanline-float-half-b44-12x8-l1 index cd25f618f41..f2b35237e41 100644 --- a/tests/ref/fate/exr-rgba-scanline-float-half-b44-12x8-l1 +++ b/tests/ref/fate/exr-rgba-scanline-float-half-b44-12x8-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 9/10 -0, 0, 0, 1, 768, 0x1de5c7f1 +0, 0, 0, 1, 1536, 0xd5802c0c diff --git a/tests/ref/fate/exr-rgba-scanline-float-half-b44-12x8-l2 b/tests/ref/fate/exr-rgba-scanline-float-half-b44-12x8-l2 index ecfba85e83d..825bfafd693 100644 --- a/tests/ref/fate/exr-rgba-scanline-float-half-b44-12x8-l2 +++ b/tests/ref/fate/exr-rgba-scanline-float-half-b44-12x8-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 9/10 -0, 0, 0, 1, 768, 0xe08ca6d3 +0, 0, 0, 1, 1536, 0x0fca2ff9 diff --git a/tests/ref/fate/exr-rgba-scanline-float-half-b44-13x9-l1 b/tests/ref/fate/exr-rgba-scanline-float-half-b44-13x9-l1 index 3e82e2aaa43..e200b2f9f0c 100644 --- a/tests/ref/fate/exr-rgba-scanline-float-half-b44-13x9-l1 +++ b/tests/ref/fate/exr-rgba-scanline-float-half-b44-13x9-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 13x9 #sar 0: 9/10 -0, 0, 0, 1, 936, 0xdcb42186 +0, 0, 0, 1, 1872, 0xe9968f61 diff --git a/tests/ref/fate/exr-rgba-scanline-float-half-b44-13x9-l2 b/tests/ref/fate/exr-rgba-scanline-float-half-b44-13x9-l2 index f237d17d3f4..ee5579c0395 100644 --- a/tests/ref/fate/exr-rgba-scanline-float-half-b44-13x9-l2 +++ b/tests/ref/fate/exr-rgba-scanline-float-half-b44-13x9-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 13x9 #sar 0: 9/10 -0, 0, 0, 1, 936, 0x7f710bf5 +0, 0, 0, 1, 1872, 0xf99e750e diff --git a/tests/ref/fate/exr-rgba-scanline-float-half-b44a-12x8-l1 b/tests/ref/fate/exr-rgba-scanline-float-half-b44a-12x8-l1 index 43313e37126..fdfa6d22fde 100644 --- a/tests/ref/fate/exr-rgba-scanline-float-half-b44a-12x8-l1 +++ b/tests/ref/fate/exr-rgba-scanline-float-half-b44a-12x8-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 9/10 -0, 0, 0, 1, 768, 0xe200c160 +0, 0, 0, 1, 1536, 0x4dec255f diff --git a/tests/ref/fate/exr-rgba-scanline-float-half-b44a-12x8-l2 b/tests/ref/fate/exr-rgba-scanline-float-half-b44a-12x8-l2 index ecfba85e83d..825bfafd693 100644 --- a/tests/ref/fate/exr-rgba-scanline-float-half-b44a-12x8-l2 +++ b/tests/ref/fate/exr-rgba-scanline-float-half-b44a-12x8-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 9/10 -0, 0, 0, 1, 768, 0xe08ca6d3 +0, 0, 0, 1, 1536, 0x0fca2ff9 diff --git a/tests/ref/fate/exr-rgba-scanline-float-half-b44a-13x9-l1 b/tests/ref/fate/exr-rgba-scanline-float-half-b44a-13x9-l1 index e43c31ebd54..20dc4bd8d54 100644 --- a/tests/ref/fate/exr-rgba-scanline-float-half-b44a-13x9-l1 +++ b/tests/ref/fate/exr-rgba-scanline-float-half-b44a-13x9-l1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 13x9 #sar 0: 9/10 -0, 0, 0, 1, 936, 0x911718ac +0, 0, 0, 1, 1872, 0x84b88e40 diff --git a/tests/ref/fate/exr-rgba-scanline-float-half-b44a-13x9-l2 b/tests/ref/fate/exr-rgba-scanline-float-half-b44a-13x9-l2 index f237d17d3f4..ee5579c0395 100644 --- a/tests/ref/fate/exr-rgba-scanline-float-half-b44a-13x9-l2 +++ b/tests/ref/fate/exr-rgba-scanline-float-half-b44a-13x9-l2 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 13x9 #sar 0: 9/10 -0, 0, 0, 1, 936, 0x7f710bf5 +0, 0, 0, 1, 1872, 0xf99e750e diff --git a/tests/ref/fate/exr-rgba-zip16-16x32-flag4 b/tests/ref/fate/exr-rgba-zip16-16x32-flag4 index e34aa711eab..66ecafc777b 100644 --- a/tests/ref/fate/exr-rgba-zip16-16x32-flag4 +++ b/tests/ref/fate/exr-rgba-zip16-16x32-flag4 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 16x32 #sar 0: 1/1 -0, 0, 0, 1, 4096, 0xf90ab1e9 +0, 0, 0, 1, 8192, 0x87767180 diff --git a/tests/ref/fate/exr-slice-pxr24 b/tests/ref/fate/exr-slice-pxr24 index 99fbc050745..79dfa546f4a 100644 --- a/tests/ref/fate/exr-slice-pxr24 +++ b/tests/ref/fate/exr-slice-pxr24 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 800x800 #sar 0: 1/1 -0, 0, 0, 1, 3840000, 0xdcfb341d +0, 0, 0, 1, 7680000, 0x98f60162 diff --git a/tests/ref/fate/exr-slice-raw b/tests/ref/fate/exr-slice-raw index cbe124b3e16..c7096e4d2ad 100644 --- a/tests/ref/fate/exr-slice-raw +++ b/tests/ref/fate/exr-slice-raw @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 587x675 #sar 0: 1/1 -0, 0, 0, 1, 3169800, 0x6a356d0d +0, 0, 0, 1, 6339600, 0x4f2b496b diff --git a/tests/ref/fate/exr-slice-rle b/tests/ref/fate/exr-slice-rle index cbe124b3e16..c7096e4d2ad 100644 --- a/tests/ref/fate/exr-slice-rle +++ b/tests/ref/fate/exr-slice-rle @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 587x675 #sar 0: 1/1 -0, 0, 0, 1, 3169800, 0x6a356d0d +0, 0, 0, 1, 6339600, 0x4f2b496b diff --git a/tests/ref/fate/exr-slice-zip1 b/tests/ref/fate/exr-slice-zip1 index cbe124b3e16..c7096e4d2ad 100644 --- a/tests/ref/fate/exr-slice-zip1 +++ b/tests/ref/fate/exr-slice-zip1 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 587x675 #sar 0: 1/1 -0, 0, 0, 1, 3169800, 0x6a356d0d +0, 0, 0, 1, 6339600, 0x4f2b496b diff --git a/tests/ref/fate/exr-slice-zip16 b/tests/ref/fate/exr-slice-zip16 index cbe124b3e16..c7096e4d2ad 100644 --- a/tests/ref/fate/exr-slice-zip16 +++ b/tests/ref/fate/exr-slice-zip16 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 587x675 #sar 0: 1/1 -0, 0, 0, 1, 3169800, 0x6a356d0d +0, 0, 0, 1, 6339600, 0x4f2b496b diff --git a/tests/ref/fate/exr-y-scanline-zip-half-12x8 b/tests/ref/fate/exr-y-scanline-zip-half-12x8 index 2a62164256c..f6ef06a2a05 100644 --- a/tests/ref/fate/exr-y-scanline-zip-half-12x8 +++ b/tests/ref/fate/exr-y-scanline-zip-half-12x8 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 192, 0xdd5759b5 +0, 0, 0, 1, 384, 0x911475c4 diff --git a/tests/ref/fate/exr-y-tile-zip-half-12x8 b/tests/ref/fate/exr-y-tile-zip-half-12x8 index 2a62164256c..f6ef06a2a05 100644 --- a/tests/ref/fate/exr-y-tile-zip-half-12x8 +++ b/tests/ref/fate/exr-y-tile-zip-half-12x8 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 12x8 #sar 0: 1/1 -0, 0, 0, 1, 192, 0xdd5759b5 +0, 0, 0, 1, 384, 0x911475c4 diff --git a/tests/ref/fate/ffmpeg-filter_colorkey b/tests/ref/fate/ffmpeg-filter_colorkey index effc13b12f3..25c3fc09121 100644 --- a/tests/ref/fate/ffmpeg-filter_colorkey +++ b/tests/ref/fate/ffmpeg-filter_colorkey @@ -3,13 +3,13 @@ #codec_id 0: rawvideo #dimensions 0: 720x576 #sar 0: 0/1 -0, 0, 0, 1, 622080, 0x4e30accb -0, 1, 1, 1, 622080, 0x7d941c14 -0, 2, 2, 1, 622080, 0xf7451c5b -0, 3, 3, 1, 622080, 0xb2c74319 -0, 4, 4, 1, 622080, 0xc9b80b79 -0, 5, 5, 1, 622080, 0x92ce1194 -0, 6, 6, 1, 622080, 0x43ae99ac -0, 7, 7, 1, 622080, 0x4ec3a554 -0, 8, 8, 1, 622080, 0x3200250c -0, 9, 9, 1, 622080, 0x94ebb3f3 +0, 0, 0, 1, 622080, 0x5775bb12 +0, 1, 1, 1, 622080, 0x84d8395a +0, 2, 2, 1, 622080, 0x599d3a6b +0, 3, 3, 1, 622080, 0x1df1e9dc +0, 4, 4, 1, 622080, 0xf3255836 +0, 5, 5, 1, 622080, 0xf46bc26e +0, 6, 6, 1, 622080, 0x76d491da +0, 7, 7, 1, 622080, 0x2c9f3ca6 +0, 8, 8, 1, 622080, 0x5713e203 +0, 9, 9, 1, 622080, 0x47776493 diff --git a/tests/ref/fate/ffmpeg-streamloop b/tests/ref/fate/ffmpeg-streamloop new file mode 100644 index 00000000000..d22199d583b --- /dev/null +++ b/tests/ref/fate/ffmpeg-streamloop @@ -0,0 +1,85 @@ +#format: frame checksums +#version: 2 +#hash: MD5 +#extradata 0, 42, eb4ec433b0b59dcc24620891ef779635 +#tb 0: 1/1000 +#media_type 0: video +#codec_id 0: h264 +#dimensions 0: 320x240 +#sar 0: 1/1 +#stream#, dts, pts, duration, size, hash +0, -80, 0, 0, 5131, fb6a475e53addc2ffa79e1b75de81050 +0, -40, 160, 40, 2479, fc64a1f7054d22741fffc68891ff4887 +0, 0, 80, 40, 1354, 344ee1f5e8f0fe2785639dd530cf85ca +0, 40, 40, 40, 965, cbea2691414063d4e1199b781586cac4 +0, 80, 120, 40, 1031, 69245957d44a7a9211e5d6fe872dec52 +0, 120, 320, 40, 2165, 6ddd38bbf1c85be10e1b17d5578b26f5 +0, 160, 240, 40, 1214, 981b555d6f810dc6b3faa4efb6beebaf +0, 200, 200, 40, 942, 492ec5f73a56fcffff81ad1ec800dc53 +0, 240, 280, 40, 945, 1763cb0f5f1d4a054d54803715de83b3 +0, 280, 480, 40, 2026, 7a208cad2e04578047482a8874ba567a +0, 320, 400, 40, 1275, a29002e40743ccdddd66f47e3b98e276 +0, 360, 360, 40, 851, c764eb2d5cb752cd607e81b69f78b799 +0, 400, 440, 40, 897, a01da8a716eae006ea6b24d8acb89fd7 +0, 440, 640, 40, 1764, 18ddd7bae5732b44fdef045f21e90f46 +0, 480, 560, 40, 1328, 7f42357fdc0df07ba22c1f803baa6fdc +0, 520, 520, 40, 952, 83827ebe12f9104a72627b52170ff9e5 +0, 560, 600, 40, 1003, 41f8be66b69231439540e4cf0b07558c +0, 600, 800, 40, 1839, 929c25e4201d66c471482d090db24b5b +0, 640, 720, 40, 1133, bdc6f506ac07ff78cc33f33956de6efb +0, 680, 680, 40, 984, 5264d530093275ade55efc816dea5c7e +0, 720, 760, 40, 802, fd5f7a018a8c940d5978b36e2e6e9801 +0, 760, 960, 40, 1454, 4eac58bac2b54a8fcdaaaf16e7167917 +0, 800, 880, 40, 1269, 79632bd3f7325797f34874621f3a6dd3 +0, 840, 840, 40, 1013, 8a48271049e24a9a86fa48300bc75d34 +0, 880, 920, 40, 981, 759407123505076c31688ec8740d2db9 +0, 920, 1000, 40, 5131, fb6a475e53addc2ffa79e1b75de81050 +0, 960, 1160, 40, 2479, fc64a1f7054d22741fffc68891ff4887 +0, 1000, 1080, 40, 1354, 344ee1f5e8f0fe2785639dd530cf85ca +0, 1040, 1040, 40, 965, cbea2691414063d4e1199b781586cac4 +0, 1080, 1120, 40, 1031, 69245957d44a7a9211e5d6fe872dec52 +0, 1120, 1320, 40, 2165, 6ddd38bbf1c85be10e1b17d5578b26f5 +0, 1160, 1240, 40, 1214, 981b555d6f810dc6b3faa4efb6beebaf +0, 1200, 1200, 40, 942, 492ec5f73a56fcffff81ad1ec800dc53 +0, 1240, 1280, 40, 945, 1763cb0f5f1d4a054d54803715de83b3 +0, 1280, 1480, 40, 2026, 7a208cad2e04578047482a8874ba567a +0, 1320, 1400, 40, 1275, a29002e40743ccdddd66f47e3b98e276 +0, 1360, 1360, 40, 851, c764eb2d5cb752cd607e81b69f78b799 +0, 1400, 1440, 40, 897, a01da8a716eae006ea6b24d8acb89fd7 +0, 1440, 1640, 40, 1764, 18ddd7bae5732b44fdef045f21e90f46 +0, 1480, 1560, 40, 1328, 7f42357fdc0df07ba22c1f803baa6fdc +0, 1520, 1520, 40, 952, 83827ebe12f9104a72627b52170ff9e5 +0, 1560, 1600, 40, 1003, 41f8be66b69231439540e4cf0b07558c +0, 1600, 1800, 40, 1839, 929c25e4201d66c471482d090db24b5b +0, 1640, 1720, 40, 1133, bdc6f506ac07ff78cc33f33956de6efb +0, 1680, 1680, 40, 984, 5264d530093275ade55efc816dea5c7e +0, 1720, 1760, 40, 802, fd5f7a018a8c940d5978b36e2e6e9801 +0, 1760, 1960, 40, 1454, 4eac58bac2b54a8fcdaaaf16e7167917 +0, 1800, 1880, 40, 1269, 79632bd3f7325797f34874621f3a6dd3 +0, 1840, 1840, 40, 1013, 8a48271049e24a9a86fa48300bc75d34 +0, 1880, 1920, 40, 981, 759407123505076c31688ec8740d2db9 +0, 1920, 2000, 40, 5131, fb6a475e53addc2ffa79e1b75de81050 +0, 1960, 2160, 40, 2479, fc64a1f7054d22741fffc68891ff4887 +0, 2000, 2080, 40, 1354, 344ee1f5e8f0fe2785639dd530cf85ca +0, 2040, 2040, 40, 965, cbea2691414063d4e1199b781586cac4 +0, 2080, 2120, 40, 1031, 69245957d44a7a9211e5d6fe872dec52 +0, 2120, 2320, 40, 2165, 6ddd38bbf1c85be10e1b17d5578b26f5 +0, 2160, 2240, 40, 1214, 981b555d6f810dc6b3faa4efb6beebaf +0, 2200, 2200, 40, 942, 492ec5f73a56fcffff81ad1ec800dc53 +0, 2240, 2280, 40, 945, 1763cb0f5f1d4a054d54803715de83b3 +0, 2280, 2480, 40, 2026, 7a208cad2e04578047482a8874ba567a +0, 2320, 2400, 40, 1275, a29002e40743ccdddd66f47e3b98e276 +0, 2360, 2360, 40, 851, c764eb2d5cb752cd607e81b69f78b799 +0, 2400, 2440, 40, 897, a01da8a716eae006ea6b24d8acb89fd7 +0, 2440, 2640, 40, 1764, 18ddd7bae5732b44fdef045f21e90f46 +0, 2480, 2560, 40, 1328, 7f42357fdc0df07ba22c1f803baa6fdc +0, 2520, 2520, 40, 952, 83827ebe12f9104a72627b52170ff9e5 +0, 2560, 2600, 40, 1003, 41f8be66b69231439540e4cf0b07558c +0, 2600, 2800, 40, 1839, 929c25e4201d66c471482d090db24b5b +0, 2640, 2720, 40, 1133, bdc6f506ac07ff78cc33f33956de6efb +0, 2680, 2680, 40, 984, 5264d530093275ade55efc816dea5c7e +0, 2720, 2760, 40, 802, fd5f7a018a8c940d5978b36e2e6e9801 +0, 2760, 2960, 40, 1454, 4eac58bac2b54a8fcdaaaf16e7167917 +0, 2800, 2880, 40, 1269, 79632bd3f7325797f34874621f3a6dd3 +0, 2840, 2840, 40, 1013, 8a48271049e24a9a86fa48300bc75d34 +0, 2880, 2920, 40, 981, 759407123505076c31688ec8740d2db9 diff --git a/tests/ref/fate/ffprobe_compact b/tests/ref/fate/ffprobe_compact index 4a0f4ee7764..03ae21394a3 100644 --- a/tests/ref/fate/ffprobe_compact +++ b/tests/ref/fate/ffprobe_compact @@ -27,6 +27,6 @@ frame|media_type=video|stream_index=1|key_frame=1|pkt_pts=6144|pkt_pts_time=0.12 packet|codec_type=video|stream_index=2|pts=6144|pts_time=0.120000|dts=6144|dts_time=0.120000|duration=2048|duration_time=0.040000|convergence_duration=N/A|convergence_duration_time=N/A|size=30000|pos=1023544|flags=K_ frame|media_type=video|stream_index=2|key_frame=1|pkt_pts=6144|pkt_pts_time=0.120000|pkt_dts=6144|pkt_dts_time=0.120000|best_effort_timestamp=6144|best_effort_timestamp_time=0.120000|pkt_duration=2048|pkt_duration_time=0.040000|pkt_pos=1023544|pkt_size=30000|width=100|height=100|pix_fmt=rgb24|sample_aspect_ratio=1:1|pict_type=I|coded_picture_number=0|display_picture_number=0|interlaced_frame=0|top_field_first=0|repeat_pict=0|color_range=unknown|color_space=unknown|color_primaries=unknown|color_transfer=unknown|chroma_location=unspecified stream|index=0|codec_name=pcm_s16le|profile=unknown|codec_type=audio|codec_time_base=1/44100|codec_tag_string=PSD[16]|codec_tag=0x10445350|sample_fmt=s16|sample_rate=44100|channels=1|channel_layout=unknown|bits_per_sample=16|id=N/A|r_frame_rate=0/0|avg_frame_rate=0/0|time_base=1/44100|start_pts=0|start_time=0.000000|duration_ts=N/A|duration=N/A|bit_rate=705600|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=6|nb_read_packets=6|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|tag:E=mc²|tag:encoder=Lavc pcm_s16le -stream|index=1|codec_name=rawvideo|profile=unknown|codec_type=video|codec_time_base=1/25|codec_tag_string=RGB[24]|codec_tag=0x18424752|width=320|height=240|coded_width=320|coded_height=240|has_b_frames=0|sample_aspect_ratio=1:1|display_aspect_ratio=4:3|pix_fmt=rgb24|level=-99|color_range=unknown|color_space=unknown|color_transfer=unknown|color_primaries=unknown|chroma_location=unspecified|field_order=unknown|timecode=N/A|refs=1|id=N/A|r_frame_rate=25/1|avg_frame_rate=25/1|time_base=1/51200|start_pts=0|start_time=0.000000|duration_ts=N/A|duration=N/A|bit_rate=N/A|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=4|nb_read_packets=4|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|tag:title=foobar|tag:duration_ts=field-and-tags-conflict-attempt|tag:encoder=Lavc rawvideo -stream|index=2|codec_name=rawvideo|profile=unknown|codec_type=video|codec_time_base=1/25|codec_tag_string=RGB[24]|codec_tag=0x18424752|width=100|height=100|coded_width=100|coded_height=100|has_b_frames=0|sample_aspect_ratio=1:1|display_aspect_ratio=1:1|pix_fmt=rgb24|level=-99|color_range=unknown|color_space=unknown|color_transfer=unknown|color_primaries=unknown|chroma_location=unspecified|field_order=unknown|timecode=N/A|refs=1|id=N/A|r_frame_rate=25/1|avg_frame_rate=25/1|time_base=1/51200|start_pts=0|start_time=0.000000|duration_ts=N/A|duration=N/A|bit_rate=N/A|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=4|nb_read_packets=4|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|tag:encoder=Lavc rawvideo +stream|index=1|codec_name=rawvideo|profile=unknown|codec_type=video|codec_time_base=1/25|codec_tag_string=RGB[24]|codec_tag=0x18424752|width=320|height=240|coded_width=320|coded_height=240|closed_captions=0|has_b_frames=0|sample_aspect_ratio=1:1|display_aspect_ratio=4:3|pix_fmt=rgb24|level=-99|color_range=unknown|color_space=unknown|color_transfer=unknown|color_primaries=unknown|chroma_location=unspecified|field_order=unknown|timecode=N/A|refs=1|id=N/A|r_frame_rate=25/1|avg_frame_rate=25/1|time_base=1/51200|start_pts=0|start_time=0.000000|duration_ts=N/A|duration=N/A|bit_rate=N/A|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=4|nb_read_packets=4|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|tag:title=foobar|tag:duration_ts=field-and-tags-conflict-attempt|tag:encoder=Lavc rawvideo +stream|index=2|codec_name=rawvideo|profile=unknown|codec_type=video|codec_time_base=1/25|codec_tag_string=RGB[24]|codec_tag=0x18424752|width=100|height=100|coded_width=100|coded_height=100|closed_captions=0|has_b_frames=0|sample_aspect_ratio=1:1|display_aspect_ratio=1:1|pix_fmt=rgb24|level=-99|color_range=unknown|color_space=unknown|color_transfer=unknown|color_primaries=unknown|chroma_location=unspecified|field_order=unknown|timecode=N/A|refs=1|id=N/A|r_frame_rate=25/1|avg_frame_rate=25/1|time_base=1/51200|start_pts=0|start_time=0.000000|duration_ts=N/A|duration=N/A|bit_rate=N/A|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=4|nb_read_packets=4|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|tag:encoder=Lavc rawvideo format|filename=tests/data/ffprobe-test.nut|nb_streams=3|nb_programs=0|format_name=nut|start_time=0.000000|duration=0.120000|size=1053624|bit_rate=70241600|probe_score=100|tag:title=ffprobe test file|tag:comment='A comment with CSV, XML & JSON special chars': |tag:comment2=I ♥ Üñîçød€ diff --git a/tests/ref/fate/ffprobe_csv b/tests/ref/fate/ffprobe_csv index dfbeb40e20e..7240d1ee6f2 100644 --- a/tests/ref/fate/ffprobe_csv +++ b/tests/ref/fate/ffprobe_csv @@ -27,6 +27,6 @@ frame,video,1,1,6144,0.120000,6144,0.120000,6144,0.120000,2048,0.040000,793120,2 packet,video,2,6144,0.120000,6144,0.120000,2048,0.040000,N/A,N/A,30000,1023544,K_ frame,video,2,1,6144,0.120000,6144,0.120000,6144,0.120000,2048,0.040000,1023544,30000,100,100,rgb24,1:1,I,0,0,0,0,0,unknown,unknown,unknown,unknown,unspecified stream,0,pcm_s16le,unknown,audio,1/44100,PSD[16],0x10445350,s16,44100,1,unknown,16,N/A,0/0,0/0,1/44100,0,0.000000,N/A,N/A,705600,N/A,N/A,N/A,6,6,0,0,0,0,0,0,0,0,0,0,0,0,mc²,Lavc pcm_s16le -stream,1,rawvideo,unknown,video,1/25,RGB[24],0x18424752,320,240,320,240,0,1:1,4:3,rgb24,-99,unknown,unknown,unknown,unknown,unspecified,unknown,N/A,1,N/A,25/1,25/1,1/51200,0,0.000000,N/A,N/A,N/A,N/A,N/A,N/A,4,4,0,0,0,0,0,0,0,0,0,0,0,0,foobar,field-and-tags-conflict-attempt,Lavc rawvideo -stream,2,rawvideo,unknown,video,1/25,RGB[24],0x18424752,100,100,100,100,0,1:1,1:1,rgb24,-99,unknown,unknown,unknown,unknown,unspecified,unknown,N/A,1,N/A,25/1,25/1,1/51200,0,0.000000,N/A,N/A,N/A,N/A,N/A,N/A,4,4,0,0,0,0,0,0,0,0,0,0,0,0,Lavc rawvideo +stream,1,rawvideo,unknown,video,1/25,RGB[24],0x18424752,320,240,320,240,0,0,1:1,4:3,rgb24,-99,unknown,unknown,unknown,unknown,unspecified,unknown,N/A,1,N/A,25/1,25/1,1/51200,0,0.000000,N/A,N/A,N/A,N/A,N/A,N/A,4,4,0,0,0,0,0,0,0,0,0,0,0,0,foobar,field-and-tags-conflict-attempt,Lavc rawvideo +stream,2,rawvideo,unknown,video,1/25,RGB[24],0x18424752,100,100,100,100,0,0,1:1,1:1,rgb24,-99,unknown,unknown,unknown,unknown,unspecified,unknown,N/A,1,N/A,25/1,25/1,1/51200,0,0.000000,N/A,N/A,N/A,N/A,N/A,N/A,4,4,0,0,0,0,0,0,0,0,0,0,0,0,Lavc rawvideo format,tests/data/ffprobe-test.nut,3,0,nut,0.000000,0.120000,1053624,70241600,100,ffprobe test file,"'A comment with CSV, XML & JSON special chars': ",I ♥ Üñîçød€ diff --git a/tests/ref/fate/ffprobe_default b/tests/ref/fate/ffprobe_default index 7562867e19c..0c2908c5ae2 100644 --- a/tests/ref/fate/ffprobe_default +++ b/tests/ref/fate/ffprobe_default @@ -616,6 +616,7 @@ width=320 height=240 coded_width=320 coded_height=240 +closed_captions=0 has_b_frames=0 sample_aspect_ratio=1:1 display_aspect_ratio=4:3 @@ -671,6 +672,7 @@ width=100 height=100 coded_width=100 coded_height=100 +closed_captions=0 has_b_frames=0 sample_aspect_ratio=1:1 display_aspect_ratio=1:1 diff --git a/tests/ref/fate/ffprobe_flat b/tests/ref/fate/ffprobe_flat index fc82ee925d2..b2437ce49ef 100644 --- a/tests/ref/fate/ffprobe_flat +++ b/tests/ref/fate/ffprobe_flat @@ -557,6 +557,7 @@ streams.stream.1.width=320 streams.stream.1.height=240 streams.stream.1.coded_width=320 streams.stream.1.coded_height=240 +streams.stream.1.closed_captions=0 streams.stream.1.has_b_frames=0 streams.stream.1.sample_aspect_ratio="1:1" streams.stream.1.display_aspect_ratio="4:3" @@ -610,6 +611,7 @@ streams.stream.2.width=100 streams.stream.2.height=100 streams.stream.2.coded_width=100 streams.stream.2.coded_height=100 +streams.stream.2.closed_captions=0 streams.stream.2.has_b_frames=0 streams.stream.2.sample_aspect_ratio="1:1" streams.stream.2.display_aspect_ratio="1:1" diff --git a/tests/ref/fate/ffprobe_ini b/tests/ref/fate/ffprobe_ini index a78690cc29a..f2cff7627a4 100644 --- a/tests/ref/fate/ffprobe_ini +++ b/tests/ref/fate/ffprobe_ini @@ -622,6 +622,7 @@ width=320 height=240 coded_width=320 coded_height=240 +closed_captions=0 has_b_frames=0 sample_aspect_ratio=1\:1 display_aspect_ratio=4\:3 @@ -681,6 +682,7 @@ width=100 height=100 coded_width=100 coded_height=100 +closed_captions=0 has_b_frames=0 sample_aspect_ratio=1\:1 display_aspect_ratio=1\:1 diff --git a/tests/ref/fate/ffprobe_json b/tests/ref/fate/ffprobe_json index 4bb93d07e16..7b88e1a84d6 100644 --- a/tests/ref/fate/ffprobe_json +++ b/tests/ref/fate/ffprobe_json @@ -569,6 +569,7 @@ "height": 240, "coded_width": 320, "coded_height": 240, + "closed_captions": 0, "has_b_frames": 0, "sample_aspect_ratio": "1:1", "display_aspect_ratio": "4:3", @@ -613,6 +614,7 @@ "height": 100, "coded_width": 100, "coded_height": 100, + "closed_captions": 0, "has_b_frames": 0, "sample_aspect_ratio": "1:1", "display_aspect_ratio": "1:1", diff --git a/tests/ref/fate/ffprobe_xml b/tests/ref/fate/ffprobe_xml index d2c5ecdf734..ccea2874b29 100644 --- a/tests/ref/fate/ffprobe_xml +++ b/tests/ref/fate/ffprobe_xml @@ -37,13 +37,13 @@ - + - + diff --git a/tests/ref/fate/filter-codecview b/tests/ref/fate/filter-codecview new file mode 100644 index 00000000000..4f6ba741ae0 --- /dev/null +++ b/tests/ref/fate/filter-codecview @@ -0,0 +1,10 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 352x288 +#sar 0: 1/1 +0, 1, 1, 1, 152064, 0x69a58723 +0, 2, 2, 1, 152064, 0xb42d52c3 +0, 3, 3, 1, 152064, 0x9376ce65 +0, 4, 4, 1, 152064, 0x796543f9 +0, 5, 5, 1, 152064, 0x5027b118 diff --git a/tests/ref/fate/filter-colorbalance b/tests/ref/fate/filter-colorbalance new file mode 100644 index 00000000000..0be1f1c855a --- /dev/null +++ b/tests/ref/fate/filter-colorbalance @@ -0,0 +1,8 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 352x288 +#sar 0: 0/1 +0, 0, 0, 1, 304128, 0x3451c8eb +0, 1, 1, 1, 304128, 0x2107daa0 +0, 2, 2, 1, 304128, 0xcda7aa33 diff --git a/tests/ref/fate/filter-colorbalance-gbrap b/tests/ref/fate/filter-colorbalance-gbrap new file mode 100644 index 00000000000..2e99601829c --- /dev/null +++ b/tests/ref/fate/filter-colorbalance-gbrap @@ -0,0 +1,8 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 352x288 +#sar 0: 0/1 +0, 0, 0, 1, 405504, 0xad0f2d82 +0, 1, 1, 1, 405504, 0x81d7d31f +0, 2, 2, 1, 405504, 0x40c5e836 diff --git a/tests/ref/fate/filter-colorbalance-gbrap-16 b/tests/ref/fate/filter-colorbalance-gbrap-16 new file mode 100644 index 00000000000..4ce323557b8 --- /dev/null +++ b/tests/ref/fate/filter-colorbalance-gbrap-16 @@ -0,0 +1,8 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 352x288 +#sar 0: 0/1 +0, 0, 0, 1, 405504, 0xa97c136c +0, 1, 1, 1, 405504, 0x0a120697 +0, 2, 2, 1, 405504, 0x85b133ff diff --git a/tests/ref/fate/filter-colorbalance-rgba64 b/tests/ref/fate/filter-colorbalance-rgba64 new file mode 100644 index 00000000000..f6fcfee2503 --- /dev/null +++ b/tests/ref/fate/filter-colorbalance-rgba64 @@ -0,0 +1,8 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 352x288 +#sar 0: 0/1 +0, 0, 0, 1, 811008, 0xa67db91d +0, 1, 1, 1, 811008, 0x0846578a +0, 2, 2, 1, 811008, 0x77af61f8 diff --git a/tests/ref/fate/filter-colorlevels b/tests/ref/fate/filter-colorlevels new file mode 100644 index 00000000000..ceb8cb21274 --- /dev/null +++ b/tests/ref/fate/filter-colorlevels @@ -0,0 +1,55 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 352x288 +#sar 0: 0/1 +0, 0, 0, 1, 304128, 0x348bb7a0 +0, 1, 1, 1, 304128, 0xaf9634d7 +0, 2, 2, 1, 304128, 0x81161fd3 +0, 3, 3, 1, 304128, 0x6839b383 +0, 4, 4, 1, 304128, 0xa55299b8 +0, 5, 5, 1, 304128, 0x66fb65b3 +0, 6, 6, 1, 304128, 0xe6be2a99 +0, 7, 7, 1, 304128, 0xfb33cb55 +0, 8, 8, 1, 304128, 0x51ab3d74 +0, 9, 9, 1, 304128, 0x67dc44ee +0, 10, 10, 1, 304128, 0x2eac3b50 +0, 11, 11, 1, 304128, 0xd4a4c377 +0, 12, 12, 1, 304128, 0x1eefe29c +0, 13, 13, 1, 304128, 0x3a84d488 +0, 14, 14, 1, 304128, 0x70d3b165 +0, 15, 15, 1, 304128, 0x578e58d4 +0, 16, 16, 1, 304128, 0x08bba87e +0, 17, 17, 1, 304128, 0xccc86c47 +0, 18, 18, 1, 304128, 0x70bf9aa2 +0, 19, 19, 1, 304128, 0x3fc3d5b5 +0, 20, 20, 1, 304128, 0xef52590b +0, 21, 21, 1, 304128, 0x4f7adde0 +0, 22, 22, 1, 304128, 0xc076ef54 +0, 23, 23, 1, 304128, 0xed2bba2d +0, 24, 24, 1, 304128, 0x6fce6367 +0, 25, 25, 1, 304128, 0x71fe3c07 +0, 26, 26, 1, 304128, 0x6395fc7c +0, 27, 27, 1, 304128, 0xa800ea2c +0, 28, 28, 1, 304128, 0x758dfa57 +0, 29, 29, 1, 304128, 0x7fbba7c6 +0, 30, 30, 1, 304128, 0x2842e2a7 +0, 31, 31, 1, 304128, 0xafc2787c +0, 32, 32, 1, 304128, 0x01e9a76b +0, 33, 33, 1, 304128, 0x32a2377b +0, 34, 34, 1, 304128, 0x10e0af2f +0, 35, 35, 1, 304128, 0x8ab789ca +0, 36, 36, 1, 304128, 0x3a3a5c1d +0, 37, 37, 1, 304128, 0xeecb3f50 +0, 38, 38, 1, 304128, 0x317f8a3f +0, 39, 39, 1, 304128, 0x0b90ba72 +0, 40, 40, 1, 304128, 0x1d6a509e +0, 41, 41, 1, 304128, 0x11416b22 +0, 42, 42, 1, 304128, 0x325014ed +0, 43, 43, 1, 304128, 0xb36a830c +0, 44, 44, 1, 304128, 0xf67fd957 +0, 45, 45, 1, 304128, 0x091ad040 +0, 46, 46, 1, 304128, 0xd6b4b00e +0, 47, 47, 1, 304128, 0x440b3f2a +0, 48, 48, 1, 304128, 0x7c329040 +0, 49, 49, 1, 304128, 0xf6619a69 diff --git a/tests/ref/fate/filter-colorlevels-16 b/tests/ref/fate/filter-colorlevels-16 new file mode 100644 index 00000000000..eb948a9d46e --- /dev/null +++ b/tests/ref/fate/filter-colorlevels-16 @@ -0,0 +1,55 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 352x288 +#sar 0: 0/1 +0, 0, 0, 1, 608256, 0x60865484 +0, 1, 1, 1, 608256, 0x3c9bad01 +0, 2, 2, 1, 608256, 0x809b6b5d +0, 3, 3, 1, 608256, 0x4d873004 +0, 4, 4, 1, 608256, 0xff1651c8 +0, 5, 5, 1, 608256, 0x48fd0353 +0, 6, 6, 1, 608256, 0x184ce147 +0, 7, 7, 1, 608256, 0x9802722f +0, 8, 8, 1, 608256, 0x9413376e +0, 9, 9, 1, 608256, 0xfbb733e9 +0, 10, 10, 1, 608256, 0x573ae5a4 +0, 11, 11, 1, 608256, 0x8697c4a4 +0, 12, 12, 1, 608256, 0xc7b364c1 +0, 13, 13, 1, 608256, 0x512770ec +0, 14, 14, 1, 608256, 0x2adee98f +0, 15, 15, 1, 608256, 0xcfb7d642 +0, 16, 16, 1, 608256, 0xc303accb +0, 17, 17, 1, 608256, 0xc738fee1 +0, 18, 18, 1, 608256, 0xd28c5669 +0, 19, 19, 1, 608256, 0xd3ce495a +0, 20, 20, 1, 608256, 0x34fe368d +0, 21, 21, 1, 608256, 0xbafe49a4 +0, 22, 22, 1, 608256, 0x68da4a93 +0, 23, 23, 1, 608256, 0xfd632bde +0, 24, 24, 1, 608256, 0x92275713 +0, 25, 25, 1, 608256, 0xcb569e86 +0, 26, 26, 1, 608256, 0x84674f8a +0, 27, 27, 1, 608256, 0xd8b7f0d5 +0, 28, 28, 1, 608256, 0xe3e6f966 +0, 29, 29, 1, 608256, 0x084d3d4c +0, 30, 30, 1, 608256, 0x6e9c5c63 +0, 31, 31, 1, 608256, 0x71c71484 +0, 32, 32, 1, 608256, 0xa89dedd1 +0, 33, 33, 1, 608256, 0x4c481ca6 +0, 34, 34, 1, 608256, 0xa52bc63b +0, 35, 35, 1, 608256, 0x59f0efa5 +0, 36, 36, 1, 608256, 0xafb82d42 +0, 37, 37, 1, 608256, 0x887b8458 +0, 38, 38, 1, 608256, 0x8a2c6984 +0, 39, 39, 1, 608256, 0xe75e4737 +0, 40, 40, 1, 608256, 0xe41ed81c +0, 41, 41, 1, 608256, 0xbf816b8d +0, 42, 42, 1, 608256, 0x8cd1af16 +0, 43, 43, 1, 608256, 0x31883468 +0, 44, 44, 1, 608256, 0x380c2bf8 +0, 45, 45, 1, 608256, 0x25e7483e +0, 46, 46, 1, 608256, 0xe73edd67 +0, 47, 47, 1, 608256, 0x3e9670ef +0, 48, 48, 1, 608256, 0xd5b871fd +0, 49, 49, 1, 608256, 0xe075789f diff --git a/tests/ref/fate/filter-concat-vfr b/tests/ref/fate/filter-concat-vfr new file mode 100644 index 00000000000..0dece67a07c --- /dev/null +++ b/tests/ref/fate/filter-concat-vfr @@ -0,0 +1,224 @@ +#tb 0: 1/1000000 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 320x240 +#sar 0: 1/1 +#tb 1: 1/44100 +#media_type 1: audio +#codec_id 1: pcm_s16le +#sample_rate 1: 44100 +#channel_layout 1: 4 +#channel_layout_name 1: mono +0, 0, 0, 0, 230400, 0x88c4d19a +1, 0, 0, 1024, 2048, 0xb3f10192 +1, 1024, 1024, 1024, 2048, 0xb340fe4e +1, 2048, 2048, 1024, 2048, 0x0a5f0111 +1, 3072, 3072, 1024, 2048, 0x51be06b8 +1, 4096, 4096, 1024, 2048, 0x71a1ffcb +1, 5120, 5120, 1024, 2048, 0x7f64f50f +1, 6144, 6144, 1024, 2048, 0x70a8fa17 +1, 7168, 7168, 1024, 2048, 0x0dad072a +1, 8192, 8192, 1024, 2048, 0x5e810c51 +0, 200000, 200000, 0, 230400, 0x0d77c977 +1, 9216, 9216, 1024, 2048, 0xbe5bf462 +1, 10240, 10240, 1024, 2048, 0xbcd9faeb +1, 11264, 11264, 1024, 2048, 0x0d5bfe9c +1, 12288, 12288, 1024, 2048, 0x97d80297 +1, 13312, 13312, 1024, 2048, 0xba0f0894 +1, 14336, 14336, 1024, 2048, 0xcc22f291 +1, 15360, 15360, 1024, 2048, 0x11a9fa03 +1, 16384, 16384, 1024, 2048, 0x9a920378 +1, 17408, 17408, 1024, 2048, 0x901b0525 +0, 400000, 400000, 0, 230400, 0x242629d7 +1, 18432, 18432, 1024, 2048, 0x74b2003f +1, 19456, 19456, 1024, 2048, 0xa20ef3ed +1, 20480, 20480, 1024, 2048, 0x44cef9de +1, 21504, 21504, 1024, 2048, 0x4b2e039b +1, 22528, 22528, 1024, 2048, 0x198509a1 +1, 23552, 23552, 1024, 2048, 0xcab6f9e5 +1, 24576, 24576, 1024, 2048, 0x67f8f608 +1, 25600, 25600, 1024, 2048, 0x8d7f03fa +0, 600000, 600000, 0, 230400, 0x62cdc018 +1, 26624, 26624, 1024, 2048, 0x3e1e0566 +1, 27648, 27648, 1024, 2048, 0x2cfe0308 +1, 28672, 28672, 1024, 2048, 0x1ceaf702 +1, 29696, 29696, 1024, 2048, 0x38a9f3d1 +1, 30720, 30720, 1024, 2048, 0x6c3306b7 +1, 31744, 31744, 1024, 2048, 0x600f0579 +1, 32768, 32768, 1024, 2048, 0x3e5afa28 +1, 33792, 33792, 1024, 2048, 0x053ff47a +1, 34816, 34816, 1024, 2048, 0x0d28fed9 +0, 800000, 800000, 0, 230400, 0x248ad058 +1, 35840, 35840, 1024, 2048, 0x279805cc +1, 36864, 36864, 1024, 2048, 0xb16a0a12 +1, 37888, 37888, 1024, 2048, 0xb45af340 +1, 38912, 38912, 1024, 2048, 0x1834f972 +1, 39936, 39936, 1024, 2048, 0xb5d206ae +1, 40960, 40960, 1024, 2048, 0xc5760375 +1, 41984, 41984, 1024, 2048, 0x503800ce +1, 43008, 43008, 1024, 2048, 0xa3bbf4af +1, 44032, 44032, 68, 136, 0xc8d751c7 +0, 1000000, 1000000, 0, 230400, 0x223d134f +1, 44100, 44100, 9600, 19200, 0x00000000 +0, 1200000, 1200000, 0, 230400, 0xbf1c3d34 +1, 53700, 53700, 9600, 19200, 0x00000000 +0, 1400000, 1400000, 0, 230400, 0xae0efe96 +1, 63300, 63300, 9600, 19200, 0x00000000 +0, 1600000, 1600000, 0, 230400, 0x0cd624d1 +1, 72900, 72900, 9600, 19200, 0x00000000 +0, 1800000, 1800000, 0, 230400, 0x6dedf2c0 +1, 82500, 82500, 5700, 11400, 0x00000000 +0, 2000000, 2000000, 0, 230400, 0x88c4d19a +1, 88200, 88200, 1024, 2048, 0x283efb3a +1, 89224, 89224, 1024, 2048, 0x7692fb8f +1, 90248, 90248, 1024, 2048, 0xbaaafcc0 +0, 2066667, 2066667, 0, 230400, 0x5bbc2f63 +1, 91272, 91272, 1024, 2048, 0xadc8017e +1, 92296, 92296, 1024, 2048, 0x4f4dffdc +1, 93320, 93320, 1024, 2048, 0x7ffbff48 +0, 2133333, 2133333, 0, 230400, 0x3becbfad +1, 94344, 94344, 1024, 2048, 0x2f990719 +1, 95368, 95368, 1024, 2048, 0xe2caf65c +1, 96392, 96392, 1024, 2048, 0x825208e4 +0, 2200000, 2200000, 0, 230400, 0x0d77c977 +1, 97416, 97416, 1024, 2048, 0xf563f13b +1, 98440, 98440, 1024, 2048, 0x855d03e9 +1, 99464, 99464, 1024, 2048, 0x0ba9fa4b +0, 2266667, 2266667, 0, 230400, 0x436cf4b2 +1, 100488, 100488, 1024, 2048, 0x83e1fb92 +1, 101512, 101512, 1024, 2048, 0x1162f965 +1, 102536, 102536, 1024, 2048, 0x0cfef73d +0, 2333333, 2333333, 0, 230400, 0x39210f27 +1, 103560, 103560, 1024, 2048, 0x5688ff75 +1, 104584, 104584, 1024, 2048, 0xf6c0ede9 +1, 105608, 105608, 1024, 2048, 0xfdb20602 +0, 2400000, 2400000, 0, 230400, 0x242629d7 +1, 106632, 106632, 1024, 2048, 0x40c5f17b +1, 107656, 107656, 1024, 2048, 0x559600b1 +1, 108680, 108680, 1024, 2048, 0xccc3f930 +0, 2466667, 2466667, 0, 230400, 0x771c2293 +1, 109704, 109704, 1024, 2048, 0xdc800045 +1, 110728, 110728, 1024, 2048, 0xdce4fb3e +0, 2533333, 2533333, 0, 230400, 0xec2af9a9 +1, 111752, 111752, 1024, 2048, 0x1e5efba9 +1, 112776, 112776, 1024, 2048, 0x8c2e0832 +1, 113800, 113800, 1024, 2048, 0x5c42f66d +0, 2600000, 2600000, 0, 230400, 0x62cdc018 +1, 114824, 114824, 1024, 2048, 0x08e20b1e +1, 115848, 115848, 1024, 2048, 0x4cf7f903 +1, 116872, 116872, 1024, 2048, 0xe6b90794 +0, 2666667, 2666667, 0, 230400, 0xf02c8693 +1, 117896, 117896, 1024, 2048, 0x5956f8e6 +1, 118920, 118920, 1024, 2048, 0x6632ff16 +1, 119944, 119944, 1024, 2048, 0x46c8fe11 +0, 2733333, 2733333, 0, 230400, 0x14436efb +1, 120968, 120968, 1024, 2048, 0x7431f732 +1, 121992, 121992, 1024, 2048, 0xa258049f +1, 123016, 123016, 1024, 2048, 0xdb71f00e +0, 2800000, 2800000, 0, 230400, 0x248ad058 +1, 124040, 124040, 1024, 2048, 0xa89b0359 +1, 125064, 125064, 1024, 2048, 0xe0aff0f2 +1, 126088, 126088, 1024, 2048, 0xc33e0085 +0, 2866667, 2866667, 0, 230400, 0xe87f6c52 +1, 127112, 127112, 1024, 2048, 0x9d09f379 +1, 128136, 128136, 1024, 2048, 0x8c78fd06 +1, 129160, 129160, 1024, 2048, 0x532bfbdd +0, 2933333, 2933333, 0, 230400, 0x6a0c196b +1, 130184, 130184, 1024, 2048, 0xfc36f5cd +1, 131208, 131208, 1024, 2048, 0x2e8f0699 +1, 132232, 132232, 1024, 2048, 0x52382578 +1, 133256, 133256, 1024, 2048, 0x97ed1a28 +1, 134280, 134280, 1024, 2048, 0xabcdf73f +1, 135304, 135304, 1024, 2048, 0x3a24082c +1, 136328, 136328, 1024, 2048, 0xbe1cfc3d +1, 137352, 137352, 1024, 2048, 0xad5800a5 +1, 138376, 138376, 1024, 2048, 0x90b80522 +1, 139400, 139400, 1024, 2048, 0x1fa1f912 +1, 140424, 140424, 1024, 2048, 0x733a0878 +1, 141448, 141448, 1024, 2048, 0x9a3eee47 +1, 142472, 142472, 1024, 2048, 0x5d900759 +1, 143496, 143496, 1024, 2048, 0x1287f540 +1, 144520, 144520, 1024, 2048, 0x941cfe5d +1, 145544, 145544, 1024, 2048, 0x1587f8a9 +1, 146568, 146568, 1024, 2048, 0xb9e7f888 +1, 147592, 147592, 1024, 2048, 0xe9defbe2 +1, 148616, 148616, 1024, 2048, 0x3a5ef312 +1, 149640, 149640, 1024, 2048, 0xdcbe0544 +1, 150664, 150664, 1024, 2048, 0xbe51ecc5 +1, 151688, 151688, 1024, 2048, 0x21a60721 +1, 152712, 152712, 1024, 2048, 0xf29ff318 +1, 153736, 153736, 1024, 2048, 0xcd4c02ea +1, 154760, 154760, 1024, 2048, 0xa424faac +1, 155784, 155784, 1024, 2048, 0xbaedfdab +1, 156808, 156808, 1024, 2048, 0xcbff047c +1, 157832, 157832, 1024, 2048, 0x9ac8f96b +1, 158856, 158856, 1024, 2048, 0x43220bee +1, 159880, 159880, 1024, 2048, 0x547bf351 +1, 160904, 160904, 1024, 2048, 0x7dd10d6e +1, 161928, 161928, 1024, 2048, 0x77cbf603 +1, 162952, 162952, 1024, 2048, 0xb6fcff50 +1, 163976, 163976, 1024, 2048, 0x927bfde5 +1, 165000, 165000, 1024, 2048, 0x5bd0fca5 +1, 166024, 166024, 1024, 2048, 0x672cff2a +1, 167048, 167048, 1024, 2048, 0x3e3ef01c +1, 168072, 168072, 1024, 2048, 0xe52607af +1, 169096, 169096, 1024, 2048, 0x66bceaf5 +1, 170120, 170120, 1024, 2048, 0xe065046b +1, 171144, 171144, 1024, 2048, 0x350bf21f +1, 172168, 172168, 1024, 2048, 0x60b1fca4 +1, 173192, 173192, 1024, 2048, 0x8b1efa55 +1, 174216, 174216, 1024, 2048, 0xf86ff855 +1, 175240, 175240, 1024, 2048, 0x6934061b +1, 176264, 176264, 136, 272, 0x4a458a45 +0, 4000000, 4000000, 0, 230400, 0x88c4d19a +1, 176400, 176400, 1024, 2048, 0xdb0cfe95 +1, 177424, 177424, 1024, 2048, 0xcff3fdf1 +1, 178448, 178448, 1024, 2048, 0x070cf585 +1, 179472, 179472, 1024, 2048, 0xe9b8007f +1, 180496, 180496, 1024, 2048, 0xc51ffd64 +1, 181520, 181520, 1024, 2048, 0xede2fbf9 +0, 4125000, 4125000, 0, 230400, 0x05c1b733 +1, 182544, 182544, 1024, 2048, 0x51510410 +1, 183568, 183568, 1024, 2048, 0x198af498 +1, 184592, 184592, 1024, 2048, 0xae3603a2 +1, 185616, 185616, 1024, 2048, 0x6200f7a1 +1, 186640, 186640, 1024, 2048, 0xe6e3fe32 +0, 4250000, 4250000, 0, 230400, 0x0446ec19 +1, 187664, 187664, 1024, 2048, 0xb2e2fd77 +1, 188688, 188688, 1024, 2048, 0x063dff2f +1, 189712, 189712, 1024, 2048, 0xa89ffe21 +1, 190736, 190736, 1024, 2048, 0x9e6ffa6d +1, 191760, 191760, 1024, 2048, 0x028b004e +1, 192784, 192784, 1024, 2048, 0x57edfa23 +0, 4375000, 4375000, 0, 230400, 0x0f9b1744 +1, 193808, 193808, 1024, 2048, 0x6d8efe1f +1, 194832, 194832, 1024, 2048, 0x774bfe54 +1, 195856, 195856, 1024, 2048, 0xa931fcfb +1, 196880, 196880, 1024, 2048, 0x3505004b +1, 197904, 197904, 1024, 2048, 0x5001f576 +0, 4500000, 4500000, 0, 230400, 0x30cf070a +1, 198928, 198928, 1024, 2048, 0x78ea049b +1, 199952, 199952, 1024, 2048, 0xd45bf733 +1, 200976, 200976, 1024, 2048, 0x6395fead +1, 202000, 202000, 1024, 2048, 0xc126015e +1, 203024, 203024, 1024, 2048, 0xbecff8aa +0, 4625000, 4625000, 0, 230400, 0x9175aaa9 +1, 204048, 204048, 1024, 2048, 0x0fea06c3 +1, 205072, 205072, 1024, 2048, 0xdea6f351 +1, 206096, 206096, 1024, 2048, 0x35b808f0 +1, 207120, 207120, 1024, 2048, 0x5487ee73 +1, 208144, 208144, 1024, 2048, 0xac69050e +1, 209168, 209168, 1024, 2048, 0xcc5ffb00 +0, 4750000, 4750000, 0, 230400, 0x597f5628 +1, 210192, 210192, 1024, 2048, 0x328c00cb +1, 211216, 211216, 1024, 2048, 0xa707fd82 +1, 212240, 212240, 1024, 2048, 0xe442f73d +1, 213264, 213264, 1024, 2048, 0x545c0418 +1, 214288, 214288, 1024, 2048, 0x744ff3f7 +0, 4875000, 4875000, 0, 230400, 0x38a45a85 +1, 215312, 215312, 1024, 2048, 0x01aa04fd +1, 216336, 216336, 1024, 2048, 0xa885f7cd +1, 217360, 217360, 1024, 2048, 0xcfca04f4 +1, 218384, 218384, 1024, 2048, 0x67fdf91b +1, 219408, 219408, 1024, 2048, 0xce2b001d +1, 220432, 220432, 68, 136, 0x33e64a0d diff --git a/tests/ref/fate/filter-hue b/tests/ref/fate/filter-hue deleted file mode 100644 index 2f1ae619dd9..00000000000 --- a/tests/ref/fate/filter-hue +++ /dev/null @@ -1 +0,0 @@ -hue 57463dd9bc17156a51b704dd7271c863 diff --git a/tests/ref/fate/filter-metadata-freezedetect b/tests/ref/fate/filter-metadata-freezedetect new file mode 100644 index 00000000000..a0ee38e4588 --- /dev/null +++ b/tests/ref/fate/filter-metadata-freezedetect @@ -0,0 +1,251 @@ +pkt_pts=0 +pkt_pts=1 +pkt_pts=2 +pkt_pts=3 +pkt_pts=4 +pkt_pts=5 +pkt_pts=6 +pkt_pts=7 +pkt_pts=8 +pkt_pts=9 +pkt_pts=10 +pkt_pts=11 +pkt_pts=12 +pkt_pts=13 +pkt_pts=14 +pkt_pts=15 +pkt_pts=16 +pkt_pts=17 +pkt_pts=18 +pkt_pts=19 +pkt_pts=20 +pkt_pts=21 +pkt_pts=22 +pkt_pts=23 +pkt_pts=24 +pkt_pts=25 +pkt_pts=26 +pkt_pts=27 +pkt_pts=28 +pkt_pts=29 +pkt_pts=30 +pkt_pts=31 +pkt_pts=32 +pkt_pts=33 +pkt_pts=34 +pkt_pts=35 +pkt_pts=36 +pkt_pts=37 +pkt_pts=38 +pkt_pts=39 +pkt_pts=40 +pkt_pts=41 +pkt_pts=42 +pkt_pts=43 +pkt_pts=44 +pkt_pts=45 +pkt_pts=46 +pkt_pts=47 +pkt_pts=48 +pkt_pts=49 +pkt_pts=50 +pkt_pts=51 +pkt_pts=52 +pkt_pts=53 +pkt_pts=54 +pkt_pts=55 +pkt_pts=56 +pkt_pts=57 +pkt_pts=58 +pkt_pts=59 +pkt_pts=60 +pkt_pts=61 +pkt_pts=62 +pkt_pts=63 +pkt_pts=64 +pkt_pts=65 +pkt_pts=66 +pkt_pts=67 +pkt_pts=68 +pkt_pts=69 +pkt_pts=70 +pkt_pts=71 +pkt_pts=72 +pkt_pts=73 +pkt_pts=74 +pkt_pts=75 +pkt_pts=76 +pkt_pts=77 +pkt_pts=78 +pkt_pts=79 +pkt_pts=80 +pkt_pts=81 +pkt_pts=82 +pkt_pts=83 +pkt_pts=84 +pkt_pts=85 +pkt_pts=86 +pkt_pts=87 +pkt_pts=88 +pkt_pts=89 +pkt_pts=90 +pkt_pts=91 +pkt_pts=92 +pkt_pts=93 +pkt_pts=94 +pkt_pts=95 +pkt_pts=96 +pkt_pts=97 +pkt_pts=98 +pkt_pts=99 +pkt_pts=100 +pkt_pts=101 +pkt_pts=102 +pkt_pts=103 +pkt_pts=104 +pkt_pts=105 +pkt_pts=106 +pkt_pts=107 +pkt_pts=108 +pkt_pts=109 +pkt_pts=110 +pkt_pts=111 +pkt_pts=112 +pkt_pts=113 +pkt_pts=114 +pkt_pts=115 +pkt_pts=116 +pkt_pts=117 +pkt_pts=118 +pkt_pts=119 +pkt_pts=120 +pkt_pts=121 +pkt_pts=122 +pkt_pts=123 +pkt_pts=124 +pkt_pts=125 +pkt_pts=126 +pkt_pts=127 +pkt_pts=128 +pkt_pts=129 +pkt_pts=130 +pkt_pts=131 +pkt_pts=132 +pkt_pts=133 +pkt_pts=134 +pkt_pts=135 +pkt_pts=136 +pkt_pts=137 +pkt_pts=138 +pkt_pts=139 +pkt_pts=140 +pkt_pts=141 +pkt_pts=142 +pkt_pts=143 +pkt_pts=144 +pkt_pts=145 +pkt_pts=146 +pkt_pts=147 +pkt_pts=148 +pkt_pts=149 +pkt_pts=150 +pkt_pts=151 +pkt_pts=152 +pkt_pts=153|tag:lavfi.freezedetect.freeze_start=4.12|tag:lavfi.freezedetect.freeze_duration=2|tag:lavfi.freezedetect.freeze_end=6.12 +pkt_pts=154 +pkt_pts=155 +pkt_pts=156 +pkt_pts=157 +pkt_pts=158 +pkt_pts=159 +pkt_pts=160 +pkt_pts=161 +pkt_pts=162 +pkt_pts=163 +pkt_pts=164 +pkt_pts=165 +pkt_pts=166 +pkt_pts=167 +pkt_pts=168 +pkt_pts=169 +pkt_pts=170 +pkt_pts=171 +pkt_pts=172 +pkt_pts=173 +pkt_pts=174 +pkt_pts=175 +pkt_pts=176 +pkt_pts=177 +pkt_pts=178 +pkt_pts=179 +pkt_pts=180 +pkt_pts=181 +pkt_pts=182 +pkt_pts=183 +pkt_pts=184 +pkt_pts=185 +pkt_pts=186 +pkt_pts=187 +pkt_pts=188 +pkt_pts=189 +pkt_pts=190 +pkt_pts=191 +pkt_pts=192 +pkt_pts=193 +pkt_pts=194 +pkt_pts=195 +pkt_pts=196 +pkt_pts=197 +pkt_pts=198 +pkt_pts=199 +pkt_pts=200 +pkt_pts=201 +pkt_pts=202 +pkt_pts=203 +pkt_pts=204|tag:lavfi.freezedetect.freeze_start=6.16|tag:lavfi.freezedetect.freeze_duration=2|tag:lavfi.freezedetect.freeze_end=8.16 +pkt_pts=205 +pkt_pts=206 +pkt_pts=207 +pkt_pts=208 +pkt_pts=209 +pkt_pts=210 +pkt_pts=211 +pkt_pts=212 +pkt_pts=213 +pkt_pts=214 +pkt_pts=215 +pkt_pts=216 +pkt_pts=217 +pkt_pts=218 +pkt_pts=219 +pkt_pts=220 +pkt_pts=221 +pkt_pts=222 +pkt_pts=223 +pkt_pts=224 +pkt_pts=225 +pkt_pts=226 +pkt_pts=227 +pkt_pts=228 +pkt_pts=229 +pkt_pts=230 +pkt_pts=231 +pkt_pts=232 +pkt_pts=233 +pkt_pts=234 +pkt_pts=235 +pkt_pts=236 +pkt_pts=237 +pkt_pts=238 +pkt_pts=239 +pkt_pts=240 +pkt_pts=241 +pkt_pts=242 +pkt_pts=243 +pkt_pts=244 +pkt_pts=245 +pkt_pts=246 +pkt_pts=247 +pkt_pts=248 +pkt_pts=249 +pkt_pts=250 diff --git a/tests/ref/fate/filter-metadata-scdet b/tests/ref/fate/filter-metadata-scdet new file mode 100644 index 00000000000..58090c2a00e --- /dev/null +++ b/tests/ref/fate/filter-metadata-scdet @@ -0,0 +1,11 @@ +pkt_pts=1620|tag:lavfi.scd.mafd=60.175|tag:lavfi.scd.score=59.252|tag:lavfi.scd.time=2.7 +pkt_pts=4140|tag:lavfi.scd.mafd=44.209|tag:lavfi.scd.score=36.070|tag:lavfi.scd.time=6.9 +pkt_pts=5800|tag:lavfi.scd.mafd=55.819|tag:lavfi.scd.score=55.819|tag:lavfi.scd.time=9.66667 +pkt_pts=6720|tag:lavfi.scd.mafd=22.505|tag:lavfi.scd.score=18.580|tag:lavfi.scd.time=11.2 +pkt_pts=8160|tag:lavfi.scd.mafd=49.444|tag:lavfi.scd.score=49.240|tag:lavfi.scd.time=13.6 +pkt_pts=9760|tag:lavfi.scd.mafd=51.801|tag:lavfi.scd.score=51.497|tag:lavfi.scd.time=16.2667 +pkt_pts=14080|tag:lavfi.scd.mafd=34.337|tag:lavfi.scd.score=34.165|tag:lavfi.scd.time=23.4667 +pkt_pts=15700|tag:lavfi.scd.mafd=58.315|tag:lavfi.scd.score=58.310|tag:lavfi.scd.time=26.1667 +pkt_pts=18500|tag:lavfi.scd.mafd=19.603|tag:lavfi.scd.score=16.504|tag:lavfi.scd.time=30.8333 +pkt_pts=20040|tag:lavfi.scd.mafd=19.060|tag:lavfi.scd.score=13.764|tag:lavfi.scd.time=33.4 +pkt_pts=21760|tag:lavfi.scd.mafd=64.551|tag:lavfi.scd.score=64.451|tag:lavfi.scd.time=36.2667 diff --git a/tests/ref/fate/filter-metadata-scenedetect b/tests/ref/fate/filter-metadata-scenedetect index d04054a9515..36c033bc0e0 100644 --- a/tests/ref/fate/filter-metadata-scenedetect +++ b/tests/ref/fate/filter-metadata-scenedetect @@ -1,10 +1,11 @@ pkt_pts=1620|tag:lavfi.scene_score=1.000000 -pkt_pts=4140|tag:lavfi.scene_score=0.875036 +pkt_pts=4140|tag:lavfi.scene_score=0.923403 pkt_pts=5800|tag:lavfi.scene_score=1.000000 -pkt_pts=6720|tag:lavfi.scene_score=0.461625 +pkt_pts=6720|tag:lavfi.scene_score=0.475643 pkt_pts=8160|tag:lavfi.scene_score=1.000000 pkt_pts=9760|tag:lavfi.scene_score=1.000000 -pkt_pts=14080|tag:lavfi.scene_score=0.838916 +pkt_pts=14080|tag:lavfi.scene_score=0.874623 pkt_pts=15700|tag:lavfi.scene_score=1.000000 -pkt_pts=18500|tag:lavfi.scene_score=0.474948 +pkt_pts=18500|tag:lavfi.scene_score=0.422509 +pkt_pts=20040|tag:lavfi.scene_score=0.352360 pkt_pts=21760|tag:lavfi.scene_score=1.000000 diff --git a/tests/ref/fate/filter-metadata-signalstats-yuv420p b/tests/ref/fate/filter-metadata-signalstats-yuv420p new file mode 100644 index 00000000000..2e76134b3cf --- /dev/null +++ b/tests/ref/fate/filter-metadata-signalstats-yuv420p @@ -0,0 +1 @@ +pkt_pts=0|tag:lavfi.signalstats.YMIN=235|tag:lavfi.signalstats.YLOW=235|tag:lavfi.signalstats.YAVG=235|tag:lavfi.signalstats.YHIGH=235|tag:lavfi.signalstats.YMAX=235|tag:lavfi.signalstats.UMIN=128|tag:lavfi.signalstats.ULOW=128|tag:lavfi.signalstats.UAVG=128|tag:lavfi.signalstats.UHIGH=128|tag:lavfi.signalstats.UMAX=128|tag:lavfi.signalstats.VMIN=128|tag:lavfi.signalstats.VLOW=128|tag:lavfi.signalstats.VAVG=128|tag:lavfi.signalstats.VHIGH=128|tag:lavfi.signalstats.VMAX=128|tag:lavfi.signalstats.SATMIN=0|tag:lavfi.signalstats.SATLOW=0|tag:lavfi.signalstats.SATAVG=0|tag:lavfi.signalstats.SATHIGH=0|tag:lavfi.signalstats.SATMAX=0|tag:lavfi.signalstats.HUEMED=180|tag:lavfi.signalstats.HUEAVG=180|tag:lavfi.signalstats.YDIF=0|tag:lavfi.signalstats.UDIF=0|tag:lavfi.signalstats.VDIF=0|tag:lavfi.signalstats.YBITDEPTH=6|tag:lavfi.signalstats.UBITDEPTH=1|tag:lavfi.signalstats.VBITDEPTH=1 diff --git a/tests/ref/fate/filter-metadata-signalstats-yuv420p10 b/tests/ref/fate/filter-metadata-signalstats-yuv420p10 new file mode 100644 index 00000000000..f9a794799d5 --- /dev/null +++ b/tests/ref/fate/filter-metadata-signalstats-yuv420p10 @@ -0,0 +1 @@ +pkt_pts=0|tag:lavfi.signalstats.YMIN=940|tag:lavfi.signalstats.YLOW=940|tag:lavfi.signalstats.YAVG=940|tag:lavfi.signalstats.YHIGH=940|tag:lavfi.signalstats.YMAX=940|tag:lavfi.signalstats.UMIN=512|tag:lavfi.signalstats.ULOW=512|tag:lavfi.signalstats.UAVG=512|tag:lavfi.signalstats.UHIGH=512|tag:lavfi.signalstats.UMAX=512|tag:lavfi.signalstats.VMIN=512|tag:lavfi.signalstats.VLOW=512|tag:lavfi.signalstats.VAVG=512|tag:lavfi.signalstats.VHIGH=512|tag:lavfi.signalstats.VMAX=512|tag:lavfi.signalstats.SATMIN=0|tag:lavfi.signalstats.SATLOW=0|tag:lavfi.signalstats.SATAVG=0|tag:lavfi.signalstats.SATHIGH=0|tag:lavfi.signalstats.SATMAX=0|tag:lavfi.signalstats.HUEMED=180|tag:lavfi.signalstats.HUEAVG=180|tag:lavfi.signalstats.YDIF=0|tag:lavfi.signalstats.UDIF=0|tag:lavfi.signalstats.VDIF=0|tag:lavfi.signalstats.YBITDEPTH=6|tag:lavfi.signalstats.UBITDEPTH=1|tag:lavfi.signalstats.VBITDEPTH=1 diff --git a/tests/ref/fate/filter-minterpolate-down b/tests/ref/fate/filter-minterpolate-down new file mode 100644 index 00000000000..4eab7aab85f --- /dev/null +++ b/tests/ref/fate/filter-minterpolate-down @@ -0,0 +1,6 @@ +#tb 0: 1/1 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 320x240 +#sar 0: 1/1 +0, 0, 0, 1, 115200, 0x3744b3ed diff --git a/tests/ref/fate/filter-minterpolate-up b/tests/ref/fate/filter-minterpolate-up new file mode 100644 index 00000000000..a276bf660dc --- /dev/null +++ b/tests/ref/fate/filter-minterpolate-up @@ -0,0 +1,15 @@ +#tb 0: 1/10 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 320x240 +#sar 0: 1/1 +0, 0, 0, 1, 115200, 0x3744b3ed +0, 1, 1, 1, 115200, 0xec1fdfa0 +0, 2, 2, 1, 115200, 0xa17f0d74 +0, 3, 3, 1, 115200, 0xd72532a9 +0, 4, 4, 1, 115200, 0x032e60f8 +0, 5, 5, 1, 115200, 0x6e318ba0 +0, 6, 6, 1, 115200, 0x76018292 +0, 7, 7, 1, 115200, 0x89e27599 +0, 8, 8, 1, 115200, 0x68536eac +0, 9, 9, 1, 115200, 0xc3ac62a8 diff --git a/tests/ref/fate/filter-overlay-dvdsub-2397 b/tests/ref/fate/filter-overlay-dvdsub-2397 index b86a2184b72..483e5fa4e04 100644 --- a/tests/ref/fate/filter-overlay-dvdsub-2397 +++ b/tests/ref/fate/filter-overlay-dvdsub-2397 @@ -490,368 +490,368 @@ 1, 3877, 3877, 10, 2013, 0x95a39f9c 1, 3887, 3887, 10, 2013, 0x4f7ea123 1, 3897, 3897, 10, 2013, 0x9efb9ba1 -0, 117, 117, 1, 518400, 0x949e1e8b +0, 117, 117, 1, 518400, 0xbf8523da 1, 3907, 3907, 10, 2013, 0xf395b2cd 1, 3917, 3917, 10, 2013, 0x261a881e 1, 3927, 3927, 10, 2013, 0x7f2d9f72 1, 3937, 3937, 10, 2013, 0x0105b38d -0, 118, 118, 1, 518400, 0xfc6c09aa +0, 118, 118, 1, 518400, 0x41890ed6 1, 3952, 3952, 10, 2013, 0x0e5db67e 1, 3962, 3962, 10, 2013, 0xfc9baf97 -0, 119, 119, 1, 518400, 0x561c2ff5 +0, 119, 119, 1, 518400, 0x588534fc 1, 3972, 3972, 10, 2013, 0x8e02a1b1 1, 3982, 3982, 10, 2013, 0x6eecaac8 1, 3992, 3992, 10, 2013, 0xf5558f0c 1, 4002, 4002, 10, 2013, 0x512ba99b -0, 120, 120, 1, 518400, 0xfd62e6bd +0, 120, 120, 1, 518400, 0x2145ebc1 1, 4012, 4012, 10, 2013, 0x932b9932 1, 4022, 4022, 10, 2013, 0xc01ea987 -0, 121, 121, 1, 518400, 0x0427a070 +0, 121, 121, 1, 518400, 0x28bca595 1, 4038, 4038, 10, 2013, 0x10879cf7 1, 4048, 4048, 10, 2013, 0x90679338 1, 4058, 4058, 10, 2013, 0x077d8a9e 1, 4068, 4068, 10, 2013, 0x969fa57c -0, 122, 122, 1, 518400, 0x0bf48fff +0, 122, 122, 1, 518400, 0x77dc951e 1, 4078, 4078, 10, 2013, 0xe049ab07 1, 4088, 4088, 10, 2013, 0xf535b3b3 1, 4098, 4098, 10, 2013, 0xfe76bd37 -0, 123, 123, 1, 518400, 0x354d4700 +0, 123, 123, 1, 518400, 0xe8924c17 1, 4108, 4108, 10, 2013, 0xde79ad8c 1, 4123, 4123, 10, 2013, 0xe89b9c47 1, 4133, 4133, 10, 2013, 0xc570b0f0 -0, 124, 124, 1, 518400, 0x6081c80c +0, 124, 124, 1, 518400, 0xadb4cccc 1, 4143, 4143, 10, 2013, 0xee709cd9 1, 4153, 4153, 10, 2013, 0xcfe5afab 1, 4163, 4163, 10, 2013, 0x98ff8ce4 -0, 125, 125, 1, 518400, 0x9c6c51c6 +0, 125, 125, 1, 518400, 0x1d7b56ac 1, 4173, 4173, 10, 2013, 0x9d19b44c 1, 4183, 4183, 10, 2013, 0x4349917a 1, 4193, 4193, 10, 2013, 0xbf54a59a -0, 126, 126, 1, 518400, 0xaacd34d7 +0, 126, 126, 1, 518400, 0xad5739a4 1, 4208, 4208, 10, 2013, 0xc4a399e0 1, 4218, 4218, 10, 2013, 0x1bf58ff0 1, 4228, 4228, 10, 2013, 0x3518ac56 -0, 127, 127, 1, 518400, 0x566bce8e +0, 127, 127, 1, 518400, 0x2733d35a 1, 4238, 4238, 10, 2013, 0xcd38c1de 1, 4248, 4248, 10, 2013, 0xbe7d9c4d 1, 4258, 4258, 10, 2013, 0xe113a306 1, 4268, 4268, 10, 2013, 0x083197ea -0, 128, 128, 1, 518400, 0xb14f68b3 +0, 128, 128, 1, 518400, 0x78e76da2 1, 4278, 4278, 10, 2013, 0x1929b1eb 1, 4294, 4294, 10, 2013, 0x5d6ea5af 1, 4304, 4304, 10, 2013, 0x05519d53 -0, 129, 129, 1, 518400, 0x00545b29 +0, 129, 129, 1, 518400, 0x6c076013 1, 4314, 4314, 10, 2013, 0x5773b380 1, 4324, 4324, 10, 2013, 0xaa70a8f5 1, 4334, 4334, 10, 2013, 0x990db0ec -0, 130, 130, 1, 518400, 0x984bede5 +0, 130, 130, 1, 518400, 0x7854f2b1 1, 4344, 4344, 10, 2013, 0x91d3a623 1, 4354, 4354, 10, 2013, 0xc91f9824 1, 4364, 4364, 10, 2013, 0x1d058abf -0, 131, 131, 1, 518400, 0x6b9319c6 +0, 131, 131, 1, 518400, 0xd2ae1ecd 1, 4379, 4379, 10, 2013, 0x8de1b8d5 1, 4389, 4389, 10, 2013, 0x7872b06b 1, 4399, 4399, 10, 2013, 0xa084c203 -0, 132, 132, 1, 518400, 0x1582ae8c +0, 132, 132, 1, 518400, 0xf5eab38d 1, 4409, 4409, 10, 2013, 0xff90ae8d 1, 4419, 4419, 10, 2013, 0x61dead8e 1, 4429, 4429, 10, 2013, 0xee76b284 -0, 133, 133, 1, 518400, 0xece339b6 +0, 133, 133, 1, 518400, 0x994d3e9c 1, 4439, 4439, 10, 2013, 0xe888af7f 1, 4449, 4449, 10, 2013, 0x5d57b115 1, 4464, 4464, 10, 2013, 0xcdbfb1d0 -0, 134, 134, 1, 518400, 0x74946b8b +0, 134, 134, 1, 518400, 0x95ab705a 1, 4474, 4474, 10, 2013, 0x2e28a952 1, 4484, 4484, 10, 2013, 0x4795a994 1, 4494, 4494, 10, 2013, 0x7e7ea304 1, 4504, 4504, 10, 2013, 0x9502c1e1 -0, 135, 135, 1, 518400, 0x5dffc0d6 +0, 135, 135, 1, 518400, 0x3c83c5ce 1, 4514, 4514, 10, 2013, 0xf7c78ab2 1, 4524, 4524, 10, 2013, 0x24049816 1, 4534, 4534, 10, 2013, 0x52089dcf -0, 136, 136, 1, 518400, 0x3f85c053 +0, 136, 136, 1, 518400, 0xfa22c508 1, 4550, 4550, 10, 2013, 0x2150a0b1 1, 4560, 4560, 10, 2013, 0x3c2e9b93 1, 4570, 4570, 10, 2013, 0x491f932b -0, 137, 137, 1, 518400, 0x7124125d +0, 137, 137, 1, 518400, 0xddda1712 1, 4580, 4580, 10, 2013, 0x31359cf8 1, 4590, 4590, 10, 2013, 0x1b00ac3f 1, 4600, 4600, 10, 2013, 0x8d7ab3cb -0, 138, 138, 1, 518400, 0x46b736d5 +0, 138, 138, 1, 518400, 0x985a3b93 1, 4610, 4610, 10, 2013, 0xb2c2a4de 1, 4620, 4620, 10, 2013, 0x80a4abf2 1, 4635, 4635, 10, 2013, 0x0701a4ee -0, 139, 139, 1, 518400, 0x7bcac123 +0, 139, 139, 1, 518400, 0xea63c5e7 1, 4645, 4645, 10, 2013, 0xdc1ba5bc 1, 4655, 4655, 10, 2013, 0x6083a8a4 1, 4665, 4665, 10, 2013, 0x6226ad45 -0, 140, 140, 1, 518400, 0x79899382 +0, 140, 140, 1, 518400, 0xef64983d 1, 4675, 4675, 10, 2013, 0x2732a205 1, 4685, 4685, 10, 2013, 0x0f62a0d3 1, 4695, 4695, 10, 2013, 0xc1799249 -0, 141, 141, 1, 518400, 0x4a56acef +0, 141, 141, 1, 518400, 0x747bb193 1, 4705, 4705, 10, 2013, 0xbccfa9c8 1, 4720, 4720, 10, 2013, 0xded096e7 1, 4730, 4730, 10, 2013, 0x7f0daf43 -0, 142, 142, 1, 518400, 0xb0a983e5 +0, 142, 142, 1, 518400, 0xb8748862 1, 4740, 4740, 10, 2013, 0xc47ea682 1, 4750, 4750, 10, 2013, 0x5a72b07a 1, 4760, 4760, 10, 2013, 0x386faa8c 1, 4770, 4770, 10, 2013, 0xf9919a91 -0, 143, 143, 1, 518400, 0xb49255cc +0, 143, 143, 1, 518400, 0xaab55a5f 1, 4780, 4780, 10, 2013, 0x4908897e 1, 4790, 4790, 10, 2013, 0x4882b594 -0, 144, 144, 1, 518400, 0x48808663 +0, 144, 144, 1, 518400, 0x7b468add 1, 4806, 4806, 10, 2013, 0x113e98d1 1, 4816, 4816, 10, 2013, 0x5098b30d 1, 4826, 4826, 10, 2013, 0x0ef7b857 1, 4836, 4836, 10, 2013, 0x216ea176 -0, 145, 145, 1, 518400, 0xf5be828c +0, 145, 145, 1, 518400, 0xf2078707 1, 4846, 4846, 10, 2013, 0xf906944a 1, 4856, 4856, 10, 2013, 0xee9b92fb 1, 4866, 4866, 10, 2013, 0xd6029209 -0, 146, 146, 1, 518400, 0xb6688ea3 +0, 146, 146, 1, 518400, 0x6a2d931e 1, 4876, 4876, 10, 2013, 0x2256a12e 1, 4891, 4891, 10, 2013, 0x89de8e4a 1, 4901, 4901, 10, 2013, 0x0bf0a584 -0, 147, 147, 1, 518400, 0x9eb2bfd1 +0, 147, 147, 1, 518400, 0xbbe3c417 1, 4911, 4911, 10, 2013, 0x6a5ebd58 1, 4921, 4921, 10, 2013, 0x3edd9aa4 1, 4931, 4931, 10, 2013, 0xbd66ac26 -0, 148, 148, 1, 518400, 0xab3bdfb5 +0, 148, 148, 1, 518400, 0x6294e449 1, 4941, 4941, 10, 2013, 0x313896ea 1, 4951, 4951, 10, 2013, 0x6b83a6a0 1, 4961, 4961, 10, 2013, 0x9aafb109 -0, 149, 149, 1, 518400, 0xe65f1d81 +0, 149, 149, 1, 518400, 0xa05721e7 1, 4976, 4976, 10, 2013, 0x5192a85a 1, 4986, 4986, 10, 2013, 0x1f919f79 1, 4996, 4996, 10, 2013, 0xc0799c40 -0, 150, 150, 1, 518400, 0x61ca8d13 +0, 150, 150, 1, 518400, 0x37749183 1, 5006, 5006, 10, 2013, 0x2988bcd8 1, 5016, 5016, 10, 2013, 0x1482913a 1, 5026, 5026, 10, 2013, 0x74da9a94 1, 5036, 5036, 10, 2013, 0x763eb709 -0, 151, 151, 1, 518400, 0xbdd0d82f +0, 151, 151, 1, 518400, 0xf9d9dca0 1, 5046, 5046, 10, 2013, 0x1285b405 1, 5062, 5062, 10, 2013, 0xb6ab9dfc -0, 152, 152, 1, 518400, 0xab1acaad +0, 152, 152, 1, 518400, 0x5f8ccf08 1, 5072, 5072, 10, 2013, 0xe4c8bf19 1, 5082, 5082, 10, 2013, 0xabbbade8 1, 5092, 5092, 10, 2013, 0xf8b69d89 1, 5102, 5102, 10, 2013, 0xce04a866 -0, 153, 153, 1, 518400, 0x1889f31c +0, 153, 153, 1, 518400, 0x7303f77b 1, 5112, 5112, 10, 2013, 0x07528abf 1, 5122, 5122, 10, 2013, 0x74fb98bf 1, 5132, 5132, 10, 2013, 0x579fb1c9 -0, 154, 154, 1, 518400, 0x02484cce +0, 154, 154, 1, 518400, 0x22b0513f 1, 5147, 5147, 10, 2013, 0x7ddea2ed 1, 5157, 5157, 10, 2013, 0x296caa2c 1, 5167, 5167, 10, 2013, 0x346d9c4f -0, 155, 155, 1, 518400, 0x59998165 +0, 155, 155, 1, 518400, 0x330485d2 1, 5177, 5177, 10, 2013, 0x3e1fba15 1, 5187, 5187, 10, 2013, 0x48a2908f 1, 5197, 5197, 10, 2013, 0xc1938d09 -0, 156, 156, 1, 518400, 0x0f3dd671 +0, 156, 156, 1, 518400, 0x7f83daea 1, 5207, 5207, 10, 2013, 0x0e96a060 1, 5217, 5217, 10, 2013, 0x7b6a9e06 1, 5232, 5232, 10, 2013, 0x5b779d28 -0, 157, 157, 1, 518400, 0x378bee63 +0, 157, 157, 1, 518400, 0xee19f2df 1, 5242, 5242, 10, 2013, 0xf600aca1 1, 5252, 5252, 10, 2013, 0x3a6c9e68 1, 5262, 5262, 10, 2013, 0x0c8dc1b0 -0, 158, 158, 1, 518400, 0x0d8e17d2 +0, 158, 158, 1, 518400, 0xb71b1c77 1, 5272, 5272, 10, 2013, 0x26beb245 1, 5282, 5282, 10, 2013, 0x2bc09557 1, 5292, 5292, 10, 2013, 0x27fc8845 1, 5302, 5302, 10, 2013, 0x1025aa47 -0, 159, 159, 1, 518400, 0x61f113bf +0, 159, 159, 1, 518400, 0xbffc1856 1, 5318, 5318, 10, 2013, 0xc2e69baa 1, 5328, 5328, 10, 2013, 0xdb249b92 1, 5338, 5338, 10, 2013, 0x6ccda29e -0, 160, 160, 1, 518400, 0x44ec211a +0, 160, 160, 1, 518400, 0xabc125aa 1, 5348, 5348, 10, 2013, 0xeaf6a1cf 1, 5358, 5358, 10, 2013, 0x509ba397 1, 5368, 5368, 10, 2013, 0xfaf8a2df -0, 161, 161, 1, 518400, 0xcb036306 +0, 161, 161, 1, 518400, 0x5ee467f8 1, 5378, 5378, 10, 2013, 0x41388f28 1, 5388, 5388, 10, 2013, 0xfe5eab39 1, 5403, 5403, 10, 2013, 0xd5ffa066 -0, 162, 162, 1, 518400, 0x7f1dec7d +0, 162, 162, 1, 518400, 0x6c2cf168 1, 5413, 5413, 10, 2013, 0x6813a30a 1, 5423, 5423, 10, 2013, 0x9be89718 1, 5433, 5433, 10, 2013, 0xaec3a27b -0, 163, 163, 1, 518400, 0x8b8c6640 +0, 163, 163, 1, 518400, 0x63996b26 1, 5446, 5446, 10, 2013, 0x579a983e 1, 5456, 5456, 10, 2013, 0x98cea21f 1, 5466, 5466, 10, 2013, 0xca77a58a -0, 164, 164, 1, 518400, 0xea04737c +0, 164, 164, 1, 518400, 0xb34d789a 1, 5476, 5476, 10, 2013, 0xcbc3b1ee 1, 5486, 5486, 10, 2013, 0xf3bb8f07 1, 5496, 5496, 10, 2013, 0x6aeebd92 -0, 165, 165, 1, 518400, 0xe779fe0c +0, 165, 165, 1, 518400, 0xf49c030f 1, 5506, 5506, 10, 2013, 0xe955a449 1, 5516, 5516, 10, 2013, 0x9436aa5b 1, 5531, 5531, 10, 2013, 0x4f0a8f9f -0, 166, 166, 1, 518400, 0xcd78bf13 +0, 166, 166, 1, 518400, 0x092dc41a 1, 5541, 5541, 10, 2013, 0x3551b22d 1, 5551, 5551, 10, 2013, 0x0959a3d4 1, 5561, 5561, 10, 2013, 0x2ed5a11b 1, 5571, 5571, 10, 2013, 0x8f52a5c3 -0, 167, 167, 1, 518400, 0x45b0c048 +0, 167, 167, 1, 518400, 0x4134c577 1, 5581, 5581, 10, 2013, 0x6552978d 1, 5591, 5591, 10, 2013, 0x7dcca0c1 1, 5601, 5601, 10, 2013, 0xbcd4a3c9 -0, 168, 168, 1, 518400, 0x9783dcd8 +0, 168, 168, 1, 518400, 0x261de1ed 1, 5616, 5616, 10, 2013, 0xfe41a8d8 1, 5626, 5626, 10, 2013, 0xc85aae14 1, 5636, 5636, 10, 2013, 0x1185b346 -0, 169, 169, 1, 518400, 0xbcd1514c +0, 169, 169, 1, 518400, 0xcbc8566a 1, 5646, 5646, 10, 2013, 0xf7429a0d 1, 5656, 5656, 10, 2013, 0x48c2a160 1, 5666, 5666, 10, 2013, 0x9d85a85d -0, 170, 170, 1, 518400, 0xf229575f +0, 170, 170, 1, 518400, 0x407a5c76 1, 5676, 5676, 10, 2013, 0xbbe89fe9 1, 5686, 5686, 10, 2013, 0xea429fe2 1, 5702, 5702, 10, 2013, 0x221ca1d4 -0, 171, 171, 1, 518400, 0x576e365e +0, 171, 171, 1, 518400, 0x1ed73bb2 1, 5712, 5712, 10, 2013, 0x394b925b 1, 5722, 5722, 10, 2013, 0x556dc26f 1, 5732, 5732, 10, 2013, 0xce21a5e1 -0, 172, 172, 1, 518400, 0xa2d2d87b +0, 172, 172, 1, 518400, 0x8467ddb5 1, 5742, 5742, 10, 2013, 0xbc87c0a8 1, 5752, 5752, 10, 2013, 0xbac4ac07 1, 5762, 5762, 10, 2013, 0xdeefa4aa 1, 5772, 5772, 10, 2013, 0x1f15b362 -0, 173, 173, 1, 518400, 0x3804d74a +0, 173, 173, 1, 518400, 0x0523dc73 1, 5787, 5787, 10, 2013, 0x6406b7b2 1, 5797, 5797, 10, 2013, 0x8030a03d -0, 174, 174, 1, 518400, 0xf05de372 +0, 174, 174, 1, 518400, 0x81f5e895 1, 5807, 5807, 10, 2013, 0x0373a5b1 1, 5817, 5817, 10, 2013, 0x34ef93da 1, 5827, 5827, 10, 2013, 0x94c198fe 1, 5837, 5837, 10, 2013, 0xfefcabad -0, 175, 175, 1, 518400, 0x21035b4f +0, 175, 175, 1, 518400, 0xfc74608d 1, 5847, 5847, 10, 2013, 0x8755b3ec 1, 5857, 5857, 10, 2013, 0xe436a6fd 1, 5872, 5872, 10, 2013, 0x9cf5a11e -0, 176, 176, 1, 518400, 0x6e97d583 +0, 176, 176, 1, 518400, 0xc4e0dae0 1, 5882, 5882, 10, 2013, 0x03b8a98c 1, 5892, 5892, 10, 2013, 0x6216a138 1, 5902, 5902, 10, 2013, 0xd87b9f12 -0, 177, 177, 1, 518400, 0x197879a9 +0, 177, 177, 1, 518400, 0x98367f5b 1, 5912, 5912, 10, 2013, 0x4ce99653 1, 5922, 5922, 10, 2013, 0x6c2ea9e2 1, 5932, 5932, 10, 2013, 0x918cae4c -0, 178, 178, 1, 518400, 0x787780ee +0, 178, 178, 1, 518400, 0x0f1a869d 1, 5942, 5942, 10, 2013, 0xd19fa5f2 1, 5958, 5958, 10, 2013, 0x0bdda7c6 1, 5968, 5968, 10, 2013, 0x0f9ab0ca -0, 179, 179, 1, 518400, 0x13fcc74d +0, 179, 179, 1, 518400, 0x45b6ccf2 1, 5978, 5978, 10, 2013, 0x410a92b1 1, 5988, 5988, 10, 2013, 0xcfbe9d1c 1, 5998, 5998, 10, 2013, 0x59ed9d15 -0, 180, 180, 1, 518400, 0x9bd1c5da +0, 180, 180, 1, 518400, 0x5f9ccb77 1, 6008, 6008, 10, 2013, 0x4e129e27 1, 6018, 6018, 10, 2013, 0x7bb9ac0a 1, 6028, 6028, 10, 2013, 0x826ca82b -0, 181, 181, 1, 518400, 0x8e21e4ba +0, 181, 181, 1, 518400, 0x5f15ea31 1, 6043, 6043, 10, 2013, 0x9ad5a74b 1, 6053, 6053, 10, 2013, 0x6c5f969a 1, 6063, 6063, 10, 2013, 0x8479a0e5 -0, 182, 182, 1, 518400, 0x5fe59996 +0, 182, 182, 1, 518400, 0x86369f27 1, 6073, 6073, 10, 2013, 0x165298ef 1, 6083, 6083, 10, 2013, 0xdcadb4a1 1, 6093, 6093, 10, 2013, 0xa90e987c 1, 6103, 6103, 10, 2013, 0x1ac5b510 -0, 183, 183, 1, 518400, 0x308af432 +0, 183, 183, 1, 518400, 0x2e27f9fa 1, 6113, 6113, 10, 2013, 0x66728d85 1, 6128, 6128, 10, 2013, 0xe4859fc5 1, 6138, 6138, 10, 2013, 0x9901786e -0, 184, 184, 1, 518400, 0xc05a9eb1 +0, 184, 184, 1, 518400, 0xc029a44d 1, 6148, 6148, 10, 2013, 0x6aebb406 1, 6158, 6158, 10, 2013, 0x7d13a2cc 1, 6168, 6168, 10, 2013, 0x99b7a8cc -0, 185, 185, 1, 518400, 0xb9a02e51 +0, 185, 185, 1, 518400, 0xebee33b0 1, 6178, 6178, 10, 2013, 0x80b8a624 1, 6188, 6188, 10, 2013, 0xbb6aa271 1, 6198, 6198, 10, 2013, 0x17af9e4a -0, 186, 186, 1, 518400, 0xbd9543aa +0, 186, 186, 1, 518400, 0x19e5494f 1, 6214, 6214, 10, 2013, 0xfaf0a8f1 1, 6224, 6224, 10, 2013, 0xd6849b93 1, 6234, 6234, 10, 2013, 0xe9829669 -0, 187, 187, 1, 518400, 0x0a0cb795 +0, 187, 187, 1, 518400, 0xf697bd7c 1, 6244, 6244, 10, 2013, 0x7ec98944 1, 6254, 6254, 10, 2013, 0x2b2099a4 1, 6264, 6264, 10, 2013, 0x1033a82f -0, 188, 188, 1, 518400, 0xe4bb8a0c +0, 188, 188, 1, 518400, 0x82569002 1, 6274, 6274, 10, 2013, 0x5ec88990 1, 6284, 6284, 10, 2013, 0xd2a19b3d 1, 6299, 6299, 10, 2013, 0xa377b268 -0, 189, 189, 1, 518400, 0x226fd11e +0, 189, 189, 1, 518400, 0xfcb6d707 1, 6309, 6309, 10, 2013, 0xfa859901 1, 6319, 6319, 10, 2013, 0x1713955a 1, 6329, 6329, 10, 2013, 0x70aab0da 1, 6339, 6339, 10, 2013, 0xcdaea422 -0, 190, 190, 1, 518400, 0x76e1604d +0, 190, 190, 1, 518400, 0x82a9662b 1, 6349, 6349, 10, 2013, 0x65c3bf80 1, 6359, 6359, 10, 2013, 0x1d75a55f 1, 6369, 6369, 10, 2013, 0xa5bea4de -0, 191, 191, 1, 518400, 0xca06117c +0, 191, 191, 1, 518400, 0x212e16ee 1, 6384, 6384, 10, 2013, 0x184db71c 1, 6394, 6394, 10, 2013, 0x99858ec8 1, 6404, 6404, 10, 2013, 0xb8f2aee5 -0, 192, 192, 1, 518400, 0xeca14952 +0, 192, 192, 1, 518400, 0x2ca34dca 1, 6414, 6414, 10, 2013, 0x4435b2ef 1, 6424, 6424, 10, 2013, 0x8acfa6c7 1, 6434, 6434, 10, 2013, 0x42b4c01f -0, 193, 193, 1, 518400, 0x3106dbee +0, 193, 193, 1, 518400, 0xe9ebe0a5 1, 6444, 6444, 10, 2013, 0x6e308c13 1, 6454, 6454, 10, 2013, 0x8227a0f6 1, 6470, 6470, 10, 2013, 0x6f12a7a2 -0, 194, 194, 1, 518400, 0x57fa6392 +0, 194, 194, 1, 518400, 0x4e6b6917 1, 6480, 6480, 10, 2013, 0x785392be 1, 6490, 6490, 10, 2013, 0x81849c2b 1, 6500, 6500, 10, 2013, 0x5cf2af65 -0, 195, 195, 1, 518400, 0x47651ac8 +0, 195, 195, 1, 518400, 0x7dcf20ab 1, 6510, 6510, 10, 2013, 0x0c6ca6b4 1, 6520, 6520, 10, 2013, 0x412fab9f 1, 6530, 6530, 10, 2013, 0x08e792b4 -0, 196, 196, 1, 518400, 0x3c1ba6a5 +0, 196, 196, 1, 518400, 0xf30fac97 1, 6540, 6540, 10, 2013, 0x407aace3 1, 6555, 6555, 10, 2013, 0xd26bac16 1, 6565, 6565, 10, 2013, 0xac8bb295 -0, 197, 197, 1, 518400, 0xbc3ec05b +0, 197, 197, 1, 518400, 0xcb9fc692 1, 6575, 6575, 10, 2013, 0xddd1949c 1, 6585, 6585, 10, 2013, 0x6b26b868 1, 6595, 6595, 10, 2013, 0x5eaba587 1, 6605, 6605, 10, 2013, 0xef0793b9 -0, 198, 198, 1, 518400, 0xd6ae59da +0, 198, 198, 1, 518400, 0x5d05601e 1, 6615, 6615, 10, 2013, 0xdef19bd6 1, 6625, 6625, 10, 2013, 0xca98a635 -0, 199, 199, 1, 518400, 0xc62f0e63 +0, 199, 199, 1, 518400, 0x456c1417 1, 6640, 6640, 10, 2013, 0x06269a5a 1, 6650, 6650, 10, 2013, 0x32cb9952 1, 6660, 6660, 10, 2013, 0xf01fa95a 1, 6670, 6670, 10, 2013, 0xefab9e55 -0, 200, 200, 1, 518400, 0xae96cc02 +0, 200, 200, 1, 518400, 0x9a0fd1ad 1, 6680, 6680, 10, 2013, 0x55a3b63a 1, 6690, 6690, 10, 2013, 0xcd36a553 1, 6700, 6700, 10, 2013, 0x2ec19877 -0, 201, 201, 1, 518400, 0x2aa0917b +0, 201, 201, 1, 518400, 0x55db9716 1, 6710, 6710, 10, 2013, 0xc18b924c 1, 6726, 6726, 10, 2013, 0xf132b04c 1, 6736, 6736, 10, 2013, 0x7975a44d -0, 202, 202, 1, 518400, 0xf0d13b48 +0, 202, 202, 1, 518400, 0x1f0d40d6 1, 6746, 6746, 10, 2013, 0x2aaf94cb 1, 6756, 6756, 10, 2013, 0x58cfa60f 1, 6766, 6766, 10, 2013, 0x9757a658 -0, 203, 203, 1, 518400, 0x067f56f8 +0, 203, 203, 1, 518400, 0x73695c82 1, 6776, 6776, 10, 2013, 0x67ebc0d5 1, 6786, 6786, 10, 2013, 0x3c50a70e 1, 6796, 6796, 10, 2013, 0x9c5799c6 -0, 204, 204, 1, 518400, 0x1026025c +0, 204, 204, 1, 518400, 0xb0f10812 1, 6811, 6811, 10, 2013, 0x018d85b2 1, 6821, 6821, 10, 2013, 0x5367a956 -0, 205, 205, 1, 518400, 0x11ee7f7f -0, 208, 208, 1, 518400, 0x30a6b398 +0, 205, 205, 1, 518400, 0xdec18505 +0, 208, 208, 1, 518400, 0xb147b947 0, 240, 240, 1, 518400, 0x9d2e3977 diff --git a/tests/ref/fate/filter-overlay_gbrap_gbrap b/tests/ref/fate/filter-overlay_gbrap_gbrap index 49e14c643c3..5048a346ae9 100644 --- a/tests/ref/fate/filter-overlay_gbrap_gbrap +++ b/tests/ref/fate/filter-overlay_gbrap_gbrap @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 128x128 #sar 0: 1/1 -0, 0, 0, 1, 65536, 0xbac99946 +0, 0, 0, 1, 65536, 0x821bcb9b diff --git a/tests/ref/fate/filter-overlay_gbrp_gbrap b/tests/ref/fate/filter-overlay_gbrp_gbrap index 204c9d06251..99524c33552 100644 --- a/tests/ref/fate/filter-overlay_gbrp_gbrap +++ b/tests/ref/fate/filter-overlay_gbrp_gbrap @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 128x128 #sar 0: 1/1 -0, 0, 0, 1, 49152, 0xa905d586 +0, 0, 0, 1, 49152, 0x738d07ea diff --git a/tests/ref/fate/filter-overlay_yuv420_yuva420 b/tests/ref/fate/filter-overlay_yuv420_yuva420 index ee0c82e0625..dffd1356382 100644 --- a/tests/ref/fate/filter-overlay_yuv420_yuva420 +++ b/tests/ref/fate/filter-overlay_yuv420_yuva420 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 128x128 #sar 0: 1/1 -0, 0, 0, 1, 24576, 0x1505f000 +0, 0, 0, 1, 24576, 0xf104fedd diff --git a/tests/ref/fate/filter-overlay_yuv422_yuva422 b/tests/ref/fate/filter-overlay_yuv422_yuva422 index 7d21e1b84a5..de0a9d13cfd 100644 --- a/tests/ref/fate/filter-overlay_yuv422_yuva422 +++ b/tests/ref/fate/filter-overlay_yuv422_yuva422 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 128x128 #sar 0: 1/1 -0, 0, 0, 1, 32768, 0x2d88b114 +0, 0, 0, 1, 32768, 0x180ac096 diff --git a/tests/ref/fate/filter-overlay_yuv444_yuva444 b/tests/ref/fate/filter-overlay_yuv444_yuva444 index e1e5db2d7dd..bc57fb5d546 100644 --- a/tests/ref/fate/filter-overlay_yuv444_yuva444 +++ b/tests/ref/fate/filter-overlay_yuv444_yuva444 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 128x128 #sar 0: 1/1 -0, 0, 0, 1, 49152, 0x92da3b63 +0, 0, 0, 1, 49152, 0x42ec4c43 diff --git a/tests/ref/fate/filter-overlay_yuva420_yuva420 b/tests/ref/fate/filter-overlay_yuva420_yuva420 index a17cc5c2e54..fac08a16e1f 100644 --- a/tests/ref/fate/filter-overlay_yuva420_yuva420 +++ b/tests/ref/fate/filter-overlay_yuva420_yuva420 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 128x128 #sar 0: 1/1 -0, 0, 0, 1, 40960, 0x0a1ab3c0 +0, 0, 0, 1, 40960, 0x5de1c29d diff --git a/tests/ref/fate/filter-overlay_yuva422_yuva422 b/tests/ref/fate/filter-overlay_yuva422_yuva422 index e4d2e3977ec..2e65399d17b 100644 --- a/tests/ref/fate/filter-overlay_yuva422_yuva422 +++ b/tests/ref/fate/filter-overlay_yuva422_yuva422 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 128x128 #sar 0: 1/1 -0, 0, 0, 1, 49152, 0x369974d4 +0, 0, 0, 1, 49152, 0xdb3b8456 diff --git a/tests/ref/fate/filter-overlay_yuva444_yuva444 b/tests/ref/fate/filter-overlay_yuva444_yuva444 index f2d9c564376..794338a6659 100644 --- a/tests/ref/fate/filter-overlay_yuva444_yuva444 +++ b/tests/ref/fate/filter-overlay_yuva444_yuva444 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 128x128 #sar 0: 1/1 -0, 0, 0, 1, 65536, 0xa279ff14 +0, 0, 0, 1, 65536, 0x91d31003 diff --git a/tests/ref/fate/filter-pixdesc-gbrapf32be b/tests/ref/fate/filter-pixdesc-gbrapf32be new file mode 100644 index 00000000000..97d613ad095 --- /dev/null +++ b/tests/ref/fate/filter-pixdesc-gbrapf32be @@ -0,0 +1 @@ +pixdesc-gbrapf32be a4fd00f17d746849f30597c496923107 diff --git a/tests/ref/fate/filter-pixdesc-gbrapf32le b/tests/ref/fate/filter-pixdesc-gbrapf32le new file mode 100644 index 00000000000..ef59306625d --- /dev/null +++ b/tests/ref/fate/filter-pixdesc-gbrapf32le @@ -0,0 +1 @@ +pixdesc-gbrapf32le 26af38a6975e2ce425e9fec477e6b2ba diff --git a/tests/ref/fate/filter-pixdesc-gbrpf32be b/tests/ref/fate/filter-pixdesc-gbrpf32be new file mode 100644 index 00000000000..71033771c19 --- /dev/null +++ b/tests/ref/fate/filter-pixdesc-gbrpf32be @@ -0,0 +1 @@ +pixdesc-gbrpf32be 3ee0b82f5aaea48ca3c01f4294505d73 diff --git a/tests/ref/fate/filter-pixdesc-gbrpf32le b/tests/ref/fate/filter-pixdesc-gbrpf32le new file mode 100644 index 00000000000..17154159999 --- /dev/null +++ b/tests/ref/fate/filter-pixdesc-gbrpf32le @@ -0,0 +1 @@ +pixdesc-gbrpf32le 0e6b20215ac9b475e917c7bb4cbee349 diff --git a/tests/ref/fate/filter-pixdesc-ya16be b/tests/ref/fate/filter-pixdesc-ya16be index 3fadfa355da..124d3aba886 100644 --- a/tests/ref/fate/filter-pixdesc-ya16be +++ b/tests/ref/fate/filter-pixdesc-ya16be @@ -1 +1 @@ -pixdesc-ya16be c5bf539478020302a30f36c5059b7695 +pixdesc-ya16be 86059502198a6d6febb5558e984a30fb diff --git a/tests/ref/fate/filter-pixdesc-ya16le b/tests/ref/fate/filter-pixdesc-ya16le index ae5764c1fe7..f0144be1475 100644 --- a/tests/ref/fate/filter-pixdesc-ya16le +++ b/tests/ref/fate/filter-pixdesc-ya16le @@ -1 +1 @@ -pixdesc-ya16le d238b5905b3ab79f7f00d5ea03ee4b87 +pixdesc-ya16le f19f6f76d395a18b88accc83d333cc50 diff --git a/tests/ref/fate/filter-pixfmts-copy b/tests/ref/fate/filter-pixfmts-copy index 4675b6e8324..45fe1be21aa 100644 --- a/tests/ref/fate/filter-pixfmts-copy +++ b/tests/ref/fate/filter-pixfmts-copy @@ -25,6 +25,8 @@ gbrap12be 0c4c1f8af361571265ca259d5f70f026 gbrap12le 5f1d8c663d4c28863e687192433b34a4 gbrap16be e4edca4361d643794034e5aa9ef290b1 gbrap16le 9a95b389d2bf556179e8f4b27fb550ab +gbrapf32be d908f0950d3735863fe6f0793fa24f76 +gbrapf32le f6eab5a145cffc52c055e07c26d3995f gbrp 5fbc319e30110d19d539f5b274eddb6d gbrp10be 703a17591a2a5c236675c5101c349bcc gbrp10le ee014153f55c011918df5b2394815780 @@ -36,6 +38,8 @@ gbrp16be 7b6764a504c853b09f5c7d0b2223c5a4 gbrp16le fb9323a5bd060282bec7bfd7d38dc230 gbrp9be a4dc6f6f9bb051de2dc348b592ad4282 gbrp9le 699da3a3b324f3fd001a56aee9683384 +gbrpf32be ae33c2d738af01ae66a5d2b08a7a60b7 +gbrpf32le 4e3305c619337beeeacc5e6b2f42c793 gray 188590b1231afd231ea910815aef2b25 gray10be d486558ecd2e27afc17930be861f0e4c gray10le 917d687103b2adcca7132bfc070ca54a @@ -78,8 +82,8 @@ rgba64le b91e1d77f799eb92241a2d2d28437b15 uyvy422 3bcf3c80047592f2211fae3260b1b65d xyz12be a1ef56bf746d71f59669c28e48fc8450 xyz12le 831ff03c1ba4ef19374686f16a064d8c -ya16be 2f2c27f1854ac00c73d13861dcab2705 -ya16le 2c1fbd127c9f0435adc0e9b2ea3f486b +ya16be 37c07787e544f900c87b853253bfc8dd +ya16le e8cab8fad88cba6d285b224d8bf0d4df ya8 dbb99fbcdc204aaa1a7397ff561f1a67 yuv410p 5d4d992a7728431aa4e0700f87fb7fd8 yuv411p 7e1300e89f5bc07939e2c4a6acbdf267 diff --git a/tests/ref/fate/filter-pixfmts-crop b/tests/ref/fate/filter-pixfmts-crop index 4b9f67cf6e9..430e90e195c 100644 --- a/tests/ref/fate/filter-pixfmts-crop +++ b/tests/ref/fate/filter-pixfmts-crop @@ -25,6 +25,8 @@ gbrap12be c9769d18733cdc2664d8b9af09a03f6d gbrap12le 3f80453c1ac6c5d1b2febf3ef141b476 gbrap16be 21c98d0d7e7de2a93f9f095e5bb5c227 gbrap16le ea9a96870c1b742dd9f065c5db568184 +gbrapf32be ec06b3b168dc74048100f29a4412da90 +gbrapf32le 57ee44f9ca41e7b5e6410cdd105442d6 gbrp ec671f573c2105072ab68a1933c58fee gbrp10be 6f0130a41f01e58593d3840446dd94b7 gbrp10le 9c152b7dfb7ad7bc477518d97316d04f @@ -36,6 +38,8 @@ gbrp16be 59e4c27ee33520e23dbceafbec27aa9b gbrp16le 0768a2462783829f63ed0bfd53d01222 gbrp9be 4af43999e5e9742992b6550ea5ad9b23 gbrp9le b4cbfa7878706a14295f09212e41f7fe +gbrpf32be 4f06588a3de6ed0f30436f814eda0909 +gbrpf32le b2a9df783d8c2156c5aafc561989918d gray 0d70b54b4b888ec4dbd89713620ac1ee gray10be 18ed76cab145ab9058cc353fcec6d3c4 gray10le fd83f7489880160783ddb125615b4638 @@ -75,8 +79,8 @@ rgba64be 89910046972ab3c68e2a348302cc8ca9 rgba64le fea8ebfc869b52adf353778f29eac7a7 xyz12be cb4571f9aaa7b59f999ef327276104b7 xyz12le cd6aae8d26b18bdb4b9d068586276d91 -ya16be 029a3b7c523de988e3161484d41ea15c -ya16le 32929a08d11982aec66ea1e665cfba3a +ya16be a3d18014454942a96f15a49947c0c55d +ya16le 3d90169aeab9e9637945cf00ab3e95ae ya8 51a8dd297e35d40b06d3ebe8f4717895 yuv410p 3bb6c7b64f2c46bc5e8b77198ce4ea58 yuv411p 693e4afe96998e6dd91734037d75d887 diff --git a/tests/ref/fate/filter-pixfmts-field b/tests/ref/fate/filter-pixfmts-field index 059347e3e20..b5f7013afdb 100644 --- a/tests/ref/fate/filter-pixfmts-field +++ b/tests/ref/fate/filter-pixfmts-field @@ -25,6 +25,8 @@ gbrap12be e7f471132628b1c034199cc109b84bc2 gbrap12le 886207e5aa379a0312485b94e5fd5edd gbrap16be eaa0158f27ebc40cde9e3d6eef1e2ba1 gbrap16le 6cf68992d4fcac2aa025d1014b669d24 +gbrapf32be 37c627796dee55ca6f4e7ca965460680 +gbrapf32le 3ff02eb8465b921c09182ec5cfda434a gbrp 838025a3062f7f31e99196ce66961ad7 gbrp10be f63c2555ea19fc78b00fd5b3e2b48e8c gbrp10le be64c374ab318235d912372e99a0516a @@ -36,6 +38,8 @@ gbrp16be 020eff1626415ea726c55b23162ce59a gbrp16le 0ab77b498d4a39905515b6e1f0329ed2 gbrp9be 170da3a8644cbea61c3caeadc45354c5 gbrp9le da5d80e6f12cabaa7081bb85d3b7fd30 +gbrpf32be cd5b0edd510652a0bcfd7e36935e3cb0 +gbrpf32le 9d42fc5331376b5307268498a06613ce gray 57fd8e6e00f6be8752726005974cce1b gray10be 437713f3d081238cddb738e106e5a27d gray10le c749b80049b152f4ba3e66a72c0c5acc @@ -78,8 +82,8 @@ rgba64le dfdba4de4a7cac9abf08852666c341d3 uyvy422 1c49e44ab3f060e85fc4a3a9464f045e xyz12be d2fa69ec91d3ed862f2dac3f8e7a3437 xyz12le 02bccd5e0b6824779a1f848b0ea3e3b5 -ya16be c0ce74d2a3da641ea634a3898dda7455 -ya16le 9b098d425e5bc27fa8a8ac8b176d592d +ya16be 40403b5277364777e0671da4d38e01ac +ya16le 54f3295f5326a13d456ac53e973ba398 ya8 28cea4f98ed452bd3da9c752e5e3399c yuv410p a85920d6bd26f51306e2ecbe71d1c554 yuv411p 9106e283d5dbcfba01c611886d58871a diff --git a/tests/ref/fate/filter-pixfmts-fieldorder b/tests/ref/fate/filter-pixfmts-fieldorder index 066b9445134..dfc464ff80a 100644 --- a/tests/ref/fate/filter-pixfmts-fieldorder +++ b/tests/ref/fate/filter-pixfmts-fieldorder @@ -25,6 +25,8 @@ gbrap12be 302b353dff696ec9fd0d85a0cc14802b gbrap12le ae2d6db2c9c825f06d92389de21263d2 gbrap16be 52c10d8046d123dfc4a478276906467c gbrap16le 2317737b8f5140add27d121de8f5ba95 +gbrapf32be 6781751ef9d444d150cb0a1e1cefe141 +gbrapf32le f2ffc9e45dbc9919d516304abb514306 gbrp 506dea2fe492e985a396d1b11ccd8db3 gbrp10be 55bbfe2d472780dcbadf3027778caa0e gbrp10le 13a39077ab1b2c3b49afd3e250b84a77 @@ -36,6 +38,8 @@ gbrp16be f82e4bda468275f51becf70f3880be52 gbrp16le c7813a905f94aabb2bcade79c9b7e39e gbrp9be b8d294d4bc81ceef1fb529e917c02e48 gbrp9le 0d42cc9e222d806c33172781b45cb3e3 +gbrpf32be cef1384ac5c95cf4b3ea2e49133dbef0 +gbrpf32le c053b8bf8314196099b1e2e1d0617b75 gray d96e0f1c73d3f0b9506d691b5cd36c73 gray10be c26c73de96b630f1207ff589b6553ebd gray10le 16e4db1d611ec3fa5c9fd8fbdbf1ffcc @@ -69,8 +73,8 @@ rgba64le b34e6e30621ae579519a2d91a96a0acf uyvy422 75de70e31c435dde878002d3f22b238a xyz12be 15f5cda71de5fef9cec5e75e3833b6bc xyz12le 7be6c8781f38c21a6b8f602f62ca31e6 -ya16be 205d6a21890c1f057c9c20fbbba590e2 -ya16le f35616fdb5d3fbf767a4f11118cf8ad1 +ya16be 0f13e0f52586d172aaa07710fa3e8f31 +ya16le d481d93ea1a1a04d759d9994958983de ya8 055ac5ab5ff8533dd319edc17a398af1 yuv411p e4a040e0e786c4dae07d9d3f90a54905 yuv422p 16ce67249c6ce7ef57a433646ad6dfc1 diff --git a/tests/ref/fate/filter-pixfmts-hflip b/tests/ref/fate/filter-pixfmts-hflip index 100dd708c30..8b8c659fc87 100644 --- a/tests/ref/fate/filter-pixfmts-hflip +++ b/tests/ref/fate/filter-pixfmts-hflip @@ -25,6 +25,8 @@ gbrap12be ffe9aa4cbcc42f71757efe18826764ac gbrap12le 88a85c1b3c5e19e299fdd209b73ac1ba gbrap16be 3117e84b258433a7efb9288bbb8815d4 gbrap16le 3ad08cf8b49d8eb31a1b356ec4b7b88b +gbrapf32be d82e48eb62c1e2d2ce5d614aeda38a99 +gbrapf32le 323259d76d5c5350091704813f22bf57 gbrp 0ecfeca171ba3a1a2ff4e92f572b71cf gbrp10be 774398c2f81757a536c094f16cfc541a gbrp10le e9a6434d691be541f789f850963da181 @@ -36,6 +38,8 @@ gbrp16be 46cf9473646a4b9dbcb05661ade658ec gbrp16le 6ce6093b24d09c0edcd55b2d6fec89a0 gbrp9be 174de037c2a9f2b6fb4d9444ae0ff82f gbrp9le ba7c2631fb2967aa909c66509bd243fe +gbrpf32be a53fc24a298bf419051fb57c63cc4cef +gbrpf32le b44dae0881043398bfd704a944094737 gray 8bd4ece1dbf89b20ee785e0515356e07 gray10be 160dd03e30d33379de92c70ee52c01fd gray10le 6baac1da6be3789409b67cd506afe7da @@ -75,8 +79,8 @@ rgba64be c910444019f4cfbf4d995227af55da8d rgba64le 0c810d8b3a6bca10321788e1cb145340 xyz12be 25f90259ff8a226befdaec3dfe82996e xyz12le 926c0791d59aaff61b2778e8ada3316d -ya16be 632b2e6e8e20c3edcfe99356fa7fca9e -ya16le e2ff5a2fb969c70dcc862937f9224873 +ya16be d5b342355bdd9e3197e01b13b7c6301e +ya16le d58f154cbbbff85af9917cdb34e819a4 ya8 4ad5920716de3d2fbbc49f95adb60345 yuv410p c49fd0c55c41185b1580aac77211992b yuv411p c416371077dce13d31bf1dc706111ae7 diff --git a/tests/ref/fate/filter-pixfmts-il b/tests/ref/fate/filter-pixfmts-il index 979eb0ce3a6..1c7e94643b2 100644 --- a/tests/ref/fate/filter-pixfmts-il +++ b/tests/ref/fate/filter-pixfmts-il @@ -25,6 +25,8 @@ gbrap12be 48609d6b61ff6313939fa2d9c3ebb6d9 gbrap12le e3b5342c8e47820f2de7e2dd61872312 gbrap16be 696c84c8b009c7320cad7f3847bb35da gbrap16le 9bacb81fbbe9cdfd04d71eb55a9719d2 +gbrapf32be 5995aba2bf66254f63d5413cd9860353 +gbrapf32le aab9c11ec483fe28f7156bfeb9f015a3 gbrp dc06edb62e70024a216c8e303b79b328 gbrp10be 321e7f061d8b9b5801221b6cf3c99666 gbrp10le 799ed3afca01076439a0b6785b3dc4bb @@ -36,6 +38,8 @@ gbrp16be c8c95027703c680ed8f8f91c725db40a gbrp16le c95c9d7c2b19826b73ff1811d9fe6bdb gbrp9be f029d87fa642f4261160471ad27fd53f gbrp9le b310d3cf37f7b41d706155993f8f0584 +gbrpf32be 83722ee41b4397e19bb075ab305147b5 +gbrpf32le 82210a8f9e8708968fa13cf8cf64afe4 gray 52ae18648161ac43144f5c9cd2127786 gray10be 8400dec0eefb172849b785d35fc55674 gray10le b7d6e49e8d1291f2b0a57d55e9478ef1 @@ -77,8 +81,8 @@ rgba64le a8a2daae04374a27219bc1c890204007 uyvy422 d6ee3ca43356d08c392382b24b22cda5 xyz12be 7c7d54c55f136cbbc50b18029f3be0b3 xyz12le 090ba6b1170baf2b1358b43b971d33b0 -ya16be bf2cf1e89c9fdb5bc10425db567ba2da -ya16le 4e9c9097fae615b8a5f4c3b237f752f0 +ya16be 7bc720918bc0132e9717acbde89874e0 +ya16le 61203295a8d39601b841de90f2c9797b ya8 a38d6e288f582f1a04310232ed764afc yuv410p dea1ab8843465adf5b8240b2d98fd85b yuv411p 8bf73777a5ff43c126be274245aceff1 diff --git a/tests/ref/fate/filter-pixfmts-null b/tests/ref/fate/filter-pixfmts-null index 4675b6e8324..45fe1be21aa 100644 --- a/tests/ref/fate/filter-pixfmts-null +++ b/tests/ref/fate/filter-pixfmts-null @@ -25,6 +25,8 @@ gbrap12be 0c4c1f8af361571265ca259d5f70f026 gbrap12le 5f1d8c663d4c28863e687192433b34a4 gbrap16be e4edca4361d643794034e5aa9ef290b1 gbrap16le 9a95b389d2bf556179e8f4b27fb550ab +gbrapf32be d908f0950d3735863fe6f0793fa24f76 +gbrapf32le f6eab5a145cffc52c055e07c26d3995f gbrp 5fbc319e30110d19d539f5b274eddb6d gbrp10be 703a17591a2a5c236675c5101c349bcc gbrp10le ee014153f55c011918df5b2394815780 @@ -36,6 +38,8 @@ gbrp16be 7b6764a504c853b09f5c7d0b2223c5a4 gbrp16le fb9323a5bd060282bec7bfd7d38dc230 gbrp9be a4dc6f6f9bb051de2dc348b592ad4282 gbrp9le 699da3a3b324f3fd001a56aee9683384 +gbrpf32be ae33c2d738af01ae66a5d2b08a7a60b7 +gbrpf32le 4e3305c619337beeeacc5e6b2f42c793 gray 188590b1231afd231ea910815aef2b25 gray10be d486558ecd2e27afc17930be861f0e4c gray10le 917d687103b2adcca7132bfc070ca54a @@ -78,8 +82,8 @@ rgba64le b91e1d77f799eb92241a2d2d28437b15 uyvy422 3bcf3c80047592f2211fae3260b1b65d xyz12be a1ef56bf746d71f59669c28e48fc8450 xyz12le 831ff03c1ba4ef19374686f16a064d8c -ya16be 2f2c27f1854ac00c73d13861dcab2705 -ya16le 2c1fbd127c9f0435adc0e9b2ea3f486b +ya16be 37c07787e544f900c87b853253bfc8dd +ya16le e8cab8fad88cba6d285b224d8bf0d4df ya8 dbb99fbcdc204aaa1a7397ff561f1a67 yuv410p 5d4d992a7728431aa4e0700f87fb7fd8 yuv411p 7e1300e89f5bc07939e2c4a6acbdf267 diff --git a/tests/ref/fate/filter-pixfmts-pad b/tests/ref/fate/filter-pixfmts-pad index 41ccec8c29c..56482cf8989 100644 --- a/tests/ref/fate/filter-pixfmts-pad +++ b/tests/ref/fate/filter-pixfmts-pad @@ -29,7 +29,7 @@ rgb0 78d500c8361ab6423a4826a00268c908 rgb24 17f9e2e0c609009acaf2175c42d4a2a5 rgba b157c90191463d34fb3ce77b36c96386 xyz12le 85abf80b77a9236a76ba0b00fcbdea2d -ya16le 17cbe58356d56ff0f0f00280a31e6ca6 +ya16le 940fafa240b9916de5f73cb20a552f24 ya8 5fc0f471207ddf7aa01b07027d56b672 yuv410p cb871dcc1e84a7ef1d21f9237b88cf6e yuv411p aec2c1740de9a62db0d41f4dda9121b0 diff --git a/tests/ref/fate/filter-pixfmts-scale b/tests/ref/fate/filter-pixfmts-scale index 2f38241d879..1e5c7db3d4c 100644 --- a/tests/ref/fate/filter-pixfmts-scale +++ b/tests/ref/fate/filter-pixfmts-scale @@ -25,6 +25,8 @@ gbrap12be 1d9b57766ba9c2192403f43967cb9af0 gbrap12le bb1ba1c157717db3dd612a76d38a018e gbrap16be c72b935a6e57a8e1c37bff08c2db55b1 gbrap16le 13eb0e62b1ac9c1c86c81521eaefab5f +gbrapf32be 42e53d9edccbd9e09c4cd78780ba92f3 +gbrapf32le eebf3973ef94c841f0a1ceb1ed61621d gbrp dc3387f925f972c61aae7eb23cdc19f0 gbrp10be 0277d4c3a8498d75e2783fb81379e481 gbrp10le f3d70f8ab845c3c9b8f7452e4a6e285a @@ -36,6 +38,8 @@ gbrp16be 5fc826cfabebfc1442cb793c4b6303e2 gbrp16le 1b3e0b63d47a3e1b6b20931316883bf2 gbrp9be d9c88968001e1452ff31fbc8d16b18a0 gbrp9le 2ccfed0816bf6bd4bb3a5b7591d9603a +gbrpf32be 4614d32e4417f80e0adcc1bdcf6cde42 +gbrpf32le 1366ee77e5559672260bbe51040e28b2 gray 221201cc7cfc4964eacd8b3e426fd276 gray10be 9452756d0b37f4f5c7cae7635e22d747 gray10le 37fd2e1ec6b66410212d39a342e864df @@ -78,8 +82,8 @@ rgba64le 783d2779adfafe3548bdb671ec0de69e uyvy422 aeb4ba4f9f003ae21f6d18089198244f xyz12be c7ba8345998c0141ddc079cdd29b1a40 xyz12le 95f5d3a0de834cc495c9032a14987cde -ya16be 372195dc947eee1bcb6f733a3544272e -ya16le 3923551514cfa588cf528e6f48e8cb9a +ya16be 20d4842899d61068f5fb6af478bf26a6 +ya16le 6a05895adce85143ae1c1b3470cb4070 ya8 0a9db5bb4b009de9197eede5e9d19e16 yuv410p e8f49b5fb9335b62c074f7f8bb0234fc yuv411p 5af32557c93beb482e26e7af693104c6 diff --git a/tests/ref/fate/filter-pixfmts-transpose b/tests/ref/fate/filter-pixfmts-transpose index b2ab3b72d92..e194c335cfe 100644 --- a/tests/ref/fate/filter-pixfmts-transpose +++ b/tests/ref/fate/filter-pixfmts-transpose @@ -25,6 +25,8 @@ gbrap12be 1518c9a565d1ba1a45dd369acc1aa75e gbrap12le 714fe318af81a46f83655c6e7e13351e gbrap16be 39d488528aacff466aac7539c9b948a8 gbrap16le 5426ac9457289927bfe2ec03038a8780 +gbrapf32be ea02b3912372c8671ff4eacbcbda740a +gbrapf32le 3021d477bdbeba4e2ae7a6bc6cff33e5 gbrp 7b4b6a2f1cdc51455b25515c3ecea944 gbrp10be d7401725699b2ddf954caa16a0878a1e gbrp10le 6036711969eae1979be6358f688bd9c8 @@ -36,6 +38,8 @@ gbrp16be 0d003b88d4f446ae9ba12cab1cbb359a gbrp16le a1c09038fa4636c9843ab8dd2b7601ea gbrp9be df381b4b27be25d172fa556434478807 gbrp9le a5301e978f68b29bfc613b2462ec4888 +gbrpf32be b90d6189e71afd6ec1f379489884cc8e +gbrpf32le 48dee2c9cee8ac6582492fd1c7acb183 gray c5f8bc6636fd15dbc57deb4bba1e7379 gray10be 48b421da79c195fd91dffb8fca79a8a2 gray10le 7774e3296916b896afa46f626334a280 @@ -74,8 +78,8 @@ rgba64be a60041217f4c0cd796d19d3940a12a41 rgba64le ad47197774858858ae7b0c177dffa459 xyz12be 68e5cba640f6e4ef72dff950e88b5342 xyz12le 8b6b6a6db4d7561e80db88ccaecce7a9 -ya16be 41b7ad48693e3ce8b4d3220016ef6b15 -ya16le 8ea70315667011a6ed50b6750f42b142 +ya16be 3e161cb5f225922a80fefdc9cc02a4f9 +ya16le 5b3f6c06850b1678cbfc2c79cc448547 ya8 d4b7a62f80681fa44c977ff3a64f4ce4 yuv410p 4c0143429edd30aa01493447c90132ea yuv420p 2fa5b2201c75034206cc20e2c6134aed diff --git a/tests/ref/fate/filter-pixfmts-vflip b/tests/ref/fate/filter-pixfmts-vflip index e4d58f9f146..6d0f9eecc63 100644 --- a/tests/ref/fate/filter-pixfmts-vflip +++ b/tests/ref/fate/filter-pixfmts-vflip @@ -25,6 +25,8 @@ gbrap12be 16a3d105ba852a9fa23ea5232db51b48 gbrap12le 6ef8a3ac4129ec23c34aec14ac41f249 gbrap16be 70b020b6b9e1896b72f890de3570ffda gbrap16le e0cf341cdbaf1f5c40016f181bc9d7d4 +gbrapf32be e82323abcb665014346a3a34a4b084c3 +gbrapf32le b24471278a899eb2f9cb563632d29b09 gbrp 413b0f6ea51588d4be5f0c76d43d8796 gbrp10be d02bd50db83213667808f5bacefe667c gbrp10le 2d4a5ebc773ffc3d857a6ef24afbe10e @@ -36,6 +38,8 @@ gbrp16be 3fef87fe67bf1fd0e2f5056dc0db2ef4 gbrp16le f3b2b76fe707f77eb1376640759f5168 gbrp9be 99c694dd47d12ae48fc8f47a0c410333 gbrp9le 26e103a4ab99fb3f58667df490997a36 +gbrpf32be 3eaa2d475754c2b4ae3c59dbdb7ccd84 +gbrpf32le 0267e215c3d11ae22414c3e29e665896 gray 41811422d5819ed69389357294384c10 gray10be 52710b3ab3ccf6101d28109f58cd48c4 gray10le 9c432a163f0cfe9ee2a4b72ae8a7c307 @@ -78,8 +82,8 @@ rgba64le 48f45b10503b7dd140329c3dd0d54c98 uyvy422 3a237e8376264e0cfa78f8a3fdadec8a xyz12be 810644e008deb231850d779aaa27cc7e xyz12le 829701db461b43533cf9241e0743bc61 -ya16be 01fa2780505ce1bd187ae7f9dcc5fcc3 -ya16le 492f528782acf22769b0b633187be212 +ya16be 55b1dbbe4d56ed0d22461685ce85520d +ya16le d5bf02471823a16dc523a46cace0101a ya8 4299c6ca3b470a7d8a420e26eb485b1d yuv410p c7adfe96c8e043a6cb9290c39bf8063c yuv411p 3fce29db403a25f81be39e01aaf6ff3a diff --git a/tests/ref/fate/filter-pp7 b/tests/ref/fate/filter-pp7 new file mode 100644 index 00000000000..d8eefe98ac8 --- /dev/null +++ b/tests/ref/fate/filter-pp7 @@ -0,0 +1,10 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 352x288 +#sar 0: 1/1 +0, 1, 1, 1, 152064, 0x4c5d7fe0 +0, 2, 2, 1, 152064, 0xc8857ae1 +0, 3, 3, 1, 152064, 0x3232f092 +0, 4, 4, 1, 152064, 0x5bc481c7 +0, 5, 5, 1, 152064, 0x91fec184 diff --git a/tests/ref/fate/filter-spp b/tests/ref/fate/filter-spp new file mode 100644 index 00000000000..e32159a702a --- /dev/null +++ b/tests/ref/fate/filter-spp @@ -0,0 +1,10 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 352x288 +#sar 0: 1/1 +0, 1, 1, 1, 152064, 0xecd18291 +0, 2, 2, 1, 152064, 0xea34708a +0, 3, 3, 1, 152064, 0xd73debe7 +0, 4, 4, 1, 152064, 0x47f57fbb +0, 5, 5, 1, 152064, 0x508dba95 diff --git a/tests/ref/fate/filter-stereo3d-sbsl-arcd b/tests/ref/fate/filter-stereo3d-sbsl-arcd index b5f11e278ed..20f080b9736 100644 --- a/tests/ref/fate/filter-stereo3d-sbsl-arcd +++ b/tests/ref/fate/filter-stereo3d-sbsl-arcd @@ -3,8 +3,8 @@ #codec_id 0: rawvideo #dimensions 0: 176x288 #sar 0: 0/1 -0, 0, 0, 1, 152064, 0xa0261570 -0, 1, 1, 1, 152064, 0x678403c8 -0, 2, 2, 1, 152064, 0x1087e7b6 -0, 3, 3, 1, 152064, 0xa3909df3 -0, 4, 4, 1, 152064, 0x87e4c4d4 +0, 0, 0, 1, 152064, 0x34b1fb03 +0, 1, 1, 1, 152064, 0x2045eadd +0, 2, 2, 1, 152064, 0x1266cdde +0, 3, 3, 1, 152064, 0xc9f083bd +0, 4, 4, 1, 152064, 0x0701ab16 diff --git a/tests/ref/fate/filter-stereo3d-sbsl-aybd b/tests/ref/fate/filter-stereo3d-sbsl-aybd index 0aa4e8bc2c9..277e66bdcab 100644 --- a/tests/ref/fate/filter-stereo3d-sbsl-aybd +++ b/tests/ref/fate/filter-stereo3d-sbsl-aybd @@ -3,8 +3,8 @@ #codec_id 0: rawvideo #dimensions 0: 176x288 #sar 0: 0/1 -0, 0, 0, 1, 152064, 0x7dc98468 -0, 1, 1, 1, 152064, 0xf72db6c9 -0, 2, 2, 1, 152064, 0x1630f53f -0, 3, 3, 1, 152064, 0xc1765599 -0, 4, 4, 1, 152064, 0x12e35db1 +0, 0, 0, 1, 152064, 0xf576742c +0, 1, 1, 1, 152064, 0x2ce36ae4 +0, 2, 2, 1, 152064, 0x2dc99849 +0, 3, 3, 1, 152064, 0x940413b6 +0, 4, 4, 1, 152064, 0x48a600d3 diff --git a/tests/ref/fate/filter-tremolo b/tests/ref/fate/filter-tremolo deleted file mode 100644 index c6cff52c0e1..00000000000 --- a/tests/ref/fate/filter-tremolo +++ /dev/null @@ -1,26 +0,0 @@ -#tb 0: 1/44100 -#media_type 0: audio -#codec_id 0: pcm_s16le -#sample_rate 0: 44100 -#channel_layout 0: 3 -#channel_layout_name 0: stereo -0, 0, 0, 1024, 4096, 0x5d3be907 -0, 1024, 1024, 1024, 4096, 0xea151fbe -0, 2048, 2048, 1024, 4096, 0xa5bc19f4 -0, 3072, 3072, 1024, 4096, 0x8706ec6d -0, 4096, 4096, 1024, 4096, 0x334ff275 -0, 5120, 5120, 1024, 4096, 0xcd0ff7ad -0, 6144, 6144, 1024, 4096, 0x29a1e9c9 -0, 7168, 7168, 1024, 4096, 0x1d41e77f -0, 8192, 8192, 1024, 4096, 0x99e7fe07 -0, 9216, 9216, 1024, 4096, 0x4bbf09ce -0, 10240, 10240, 1024, 4096, 0x94600236 -0, 11264, 11264, 1024, 4096, 0xc8af0c9e -0, 12288, 12288, 1024, 4096, 0x70eef88f -0, 13312, 13312, 1024, 4096, 0xb222ec47 -0, 14336, 14336, 1024, 4096, 0x1071ee27 -0, 15360, 15360, 1024, 4096, 0x7c390bd2 -0, 16384, 16384, 1024, 4096, 0x68bdf655 -0, 17408, 17408, 1024, 4096, 0x810cfacb -0, 18432, 18432, 1024, 4096, 0x9639e41f -0, 19456, 19456, 1024, 4096, 0xa30be70f diff --git a/tests/ref/fate/filter-untile b/tests/ref/fate/filter-untile new file mode 100644 index 00000000000..42c741acf18 --- /dev/null +++ b/tests/ref/fate/filter-untile @@ -0,0 +1,13 @@ +#tb 0: 1/8 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 160x120 +#sar 0: 1/1 +0, 0, 0, 1, 28800, 0xb3725302 +0, 1, 1, 1, 28800, 0xf9612057 +0, 2, 2, 1, 28800, 0x9b207db0 +0, 3, 3, 1, 28800, 0x1331c2d5 +0, 4, 4, 1, 28800, 0x2edf3ee4 +0, 5, 5, 1, 28800, 0x84105711 +0, 6, 6, 1, 28800, 0xa7a35f25 +0, 7, 7, 1, 28800, 0xa9c49677 diff --git a/tests/ref/fate/filter-vectorscope_color b/tests/ref/fate/filter-vectorscope_color index 57875ab4182..81d97d4fae5 100644 --- a/tests/ref/fate/filter-vectorscope_color +++ b/tests/ref/fate/filter-vectorscope_color @@ -3,6 +3,6 @@ #codec_id 0: rawvideo #dimensions 0: 256x256 #sar 0: 1/1 -0, 0, 0, 1, 196608, 0xf6e3aa30 -0, 1, 1, 1, 196608, 0x5584acf9 -0, 2, 2, 1, 196608, 0xa862775d +0, 0, 0, 1, 196608, 0x7c431d1f +0, 1, 1, 1, 196608, 0xb7e82028 +0, 2, 2, 1, 196608, 0x2feeeb61 diff --git a/tests/ref/fate/filter-vectorscope_color2 b/tests/ref/fate/filter-vectorscope_color2 index 3b2ad90b9c7..adbe9e65bc5 100644 --- a/tests/ref/fate/filter-vectorscope_color2 +++ b/tests/ref/fate/filter-vectorscope_color2 @@ -3,6 +3,6 @@ #codec_id 0: rawvideo #dimensions 0: 256x256 #sar 0: 1/1 -0, 0, 0, 1, 196608, 0x5e62fae5 -0, 1, 1, 1, 196608, 0x4c27fcbf -0, 2, 2, 1, 196608, 0xb7531088 +0, 0, 0, 1, 196608, 0xdad38823 +0, 1, 1, 1, 196608, 0xeb8589bd +0, 2, 2, 1, 196608, 0x31a79c93 diff --git a/tests/ref/fate/filter-vectorscope_color3 b/tests/ref/fate/filter-vectorscope_color3 index 4baecca921e..2b6a6b08fb3 100644 --- a/tests/ref/fate/filter-vectorscope_color3 +++ b/tests/ref/fate/filter-vectorscope_color3 @@ -3,6 +3,6 @@ #codec_id 0: rawvideo #dimensions 0: 256x256 #sar 0: 1/1 -0, 0, 0, 1, 196608, 0x83df8770 -0, 1, 1, 1, 196608, 0xa6a674a7 -0, 2, 2, 1, 196608, 0x11757143 +0, 0, 0, 1, 196608, 0x005f14ae +0, 1, 1, 1, 196608, 0x461301a5 +0, 2, 2, 1, 196608, 0x8bbafd4e diff --git a/tests/ref/fate/filter-vectorscope_color4 b/tests/ref/fate/filter-vectorscope_color4 index 21d6762ff28..fcc21b78574 100644 --- a/tests/ref/fate/filter-vectorscope_color4 +++ b/tests/ref/fate/filter-vectorscope_color4 @@ -3,6 +3,6 @@ #codec_id 0: rawvideo #dimensions 0: 256x256 #sar 0: 1/1 -0, 0, 0, 1, 196608, 0x326953c4 -0, 1, 1, 1, 196608, 0x870e1dcc -0, 2, 2, 1, 196608, 0x87cb8800 +0, 0, 0, 1, 196608, 0xaedae0f3 +0, 1, 1, 1, 196608, 0x267baabb +0, 2, 2, 1, 196608, 0x021f141a diff --git a/tests/ref/fate/filter-vectorscope_gray b/tests/ref/fate/filter-vectorscope_gray index a81fbf8f872..78fb1d70c33 100644 --- a/tests/ref/fate/filter-vectorscope_gray +++ b/tests/ref/fate/filter-vectorscope_gray @@ -3,6 +3,6 @@ #codec_id 0: rawvideo #dimensions 0: 256x256 #sar 0: 1/1 -0, 0, 0, 1, 196608, 0x79ba71e2 -0, 1, 1, 1, 196608, 0x909271e2 -0, 2, 2, 1, 196608, 0x143971e2 +0, 0, 0, 1, 196608, 0xf62bff11 +0, 1, 1, 1, 196608, 0x2ffffed1 +0, 2, 2, 1, 196608, 0x8e7efded diff --git a/tests/ref/fate/filter-vectorscope_xy b/tests/ref/fate/filter-vectorscope_xy index 83b719468ca..6ab64e1f192 100644 --- a/tests/ref/fate/filter-vectorscope_xy +++ b/tests/ref/fate/filter-vectorscope_xy @@ -3,6 +3,6 @@ #codec_id 0: rawvideo #dimensions 0: 256x256 #sar 0: 1/1 -0, 0, 0, 1, 196608, 0xa2899af1 -0, 1, 1, 1, 196608, 0x26409af1 -0, 2, 2, 1, 196608, 0xf5209af1 +0, 0, 0, 1, 196608, 0xd2bfcc40 +0, 1, 1, 1, 196608, 0x2851cb74 +0, 2, 2, 1, 196608, 0x48efcc64 diff --git a/tests/ref/fate/filter-waveform_uv b/tests/ref/fate/filter-waveform_uv index 8cb3bc81f8b..36368204817 100644 --- a/tests/ref/fate/filter-waveform_uv +++ b/tests/ref/fate/filter-waveform_uv @@ -3,53 +3,53 @@ #codec_id 0: rawvideo #dimensions 0: 352x512 #sar 0: 1/1 -0, 0, 0, 1, 540672, 0x8a2521d6 -0, 1, 1, 1, 540672, 0xb9a321d6 -0, 2, 2, 1, 540672, 0x325421d6 -0, 3, 3, 1, 540672, 0xafee21d2 -0, 4, 4, 1, 540672, 0x172121d6 -0, 5, 5, 1, 540672, 0x24d121d6 -0, 6, 6, 1, 540672, 0x7fec21d6 -0, 7, 7, 1, 540672, 0xa8a021d6 -0, 8, 8, 1, 540672, 0x29fd21d6 -0, 9, 9, 1, 540672, 0x6dfe21d6 -0, 10, 10, 1, 540672, 0xe39821d6 -0, 11, 11, 1, 540672, 0x83f521d6 -0, 12, 12, 1, 540672, 0x57aa21d6 -0, 13, 13, 1, 540672, 0x67b221d6 -0, 14, 14, 1, 540672, 0x535821d6 -0, 15, 15, 1, 540672, 0xb8ac21d6 -0, 16, 16, 1, 540672, 0x27f621d6 -0, 17, 17, 1, 540672, 0x775221d6 -0, 18, 18, 1, 540672, 0x8e6621d6 -0, 19, 19, 1, 540672, 0x74c921d6 -0, 20, 20, 1, 540672, 0x04cd21d6 -0, 21, 21, 1, 540672, 0xccd421d6 -0, 22, 22, 1, 540672, 0x317221d6 -0, 23, 23, 1, 540672, 0xd79321d6 -0, 24, 24, 1, 540672, 0xa2ac21d6 -0, 25, 25, 1, 540672, 0x7f0a21d6 -0, 26, 26, 1, 540672, 0x483521d6 -0, 27, 27, 1, 540672, 0xb65721d6 -0, 28, 28, 1, 540672, 0xb77021d6 -0, 29, 29, 1, 540672, 0x9fd521d6 -0, 30, 30, 1, 540672, 0xb72121d6 -0, 31, 31, 1, 540672, 0x540221d6 -0, 32, 32, 1, 540672, 0xa34121d6 -0, 33, 33, 1, 540672, 0xe01421d6 -0, 34, 34, 1, 540672, 0x6fc721d6 -0, 35, 35, 1, 540672, 0x7fa621d6 -0, 36, 36, 1, 540672, 0xc48c21d6 -0, 37, 37, 1, 540672, 0x40f021d6 -0, 38, 38, 1, 540672, 0xdf3f21d6 -0, 39, 39, 1, 540672, 0xb04321d6 -0, 40, 40, 1, 540672, 0x222821d6 -0, 41, 41, 1, 540672, 0x2a5521d6 -0, 42, 42, 1, 540672, 0x6a4621be -0, 43, 43, 1, 540672, 0xed7f21d6 -0, 44, 44, 1, 540672, 0xb16521d6 -0, 45, 45, 1, 540672, 0x9f5621d6 -0, 46, 46, 1, 540672, 0x204321d6 -0, 47, 47, 1, 540672, 0xc26e21d6 -0, 48, 48, 1, 540672, 0x3e8321d6 -0, 49, 49, 1, 540672, 0xaaee21d6 +0, 0, 0, 1, 540672, 0xe33821d6 +0, 1, 1, 1, 540672, 0x12c521d6 +0, 2, 2, 1, 540672, 0x8b6721d6 +0, 3, 3, 1, 540672, 0x6fd321d6 +0, 4, 4, 1, 540672, 0x703421d6 +0, 5, 5, 1, 540672, 0x7de421d6 +0, 6, 6, 1, 540672, 0xd8ff21d6 +0, 7, 7, 1, 540672, 0x01c221d6 +0, 8, 8, 1, 540672, 0x831021d6 +0, 9, 9, 1, 540672, 0xc71121d6 +0, 10, 10, 1, 540672, 0x3cba21d6 +0, 11, 11, 1, 540672, 0xdd0821d6 +0, 12, 12, 1, 540672, 0xb0bd21d6 +0, 13, 13, 1, 540672, 0xc0c521d6 +0, 14, 14, 1, 540672, 0xac6b21d6 +0, 15, 15, 1, 540672, 0x11ce21d6 +0, 16, 16, 1, 540672, 0x810921d6 +0, 17, 17, 1, 540672, 0xd06521d6 +0, 18, 18, 1, 540672, 0xe77921d6 +0, 19, 19, 1, 540672, 0xcddc21d6 +0, 20, 20, 1, 540672, 0x5de021d6 +0, 21, 21, 1, 540672, 0x25f621d6 +0, 22, 22, 1, 540672, 0x8a8521d6 +0, 23, 23, 1, 540672, 0x30b521d6 +0, 24, 24, 1, 540672, 0xfbbf21d6 +0, 25, 25, 1, 540672, 0xd81d21d6 +0, 26, 26, 1, 540672, 0xa14821d6 +0, 27, 27, 1, 540672, 0x0f7921d6 +0, 28, 28, 1, 540672, 0x109221d6 +0, 29, 29, 1, 540672, 0xf8e821d6 +0, 30, 30, 1, 540672, 0x104321d6 +0, 31, 31, 1, 540672, 0xad1521d6 +0, 32, 32, 1, 540672, 0xfc5421d6 +0, 33, 33, 1, 540672, 0x393621d6 +0, 34, 34, 1, 540672, 0xc8da21d6 +0, 35, 35, 1, 540672, 0xd8b921d6 +0, 36, 36, 1, 540672, 0x1dae21d6 +0, 37, 37, 1, 540672, 0x9a0321d6 +0, 38, 38, 1, 540672, 0x386121d6 +0, 39, 39, 1, 540672, 0x096521d6 +0, 40, 40, 1, 540672, 0x7b3b21d6 +0, 41, 41, 1, 540672, 0x836821d6 +0, 42, 42, 1, 540672, 0x97bd21d6 +0, 43, 43, 1, 540672, 0x46a121d6 +0, 44, 44, 1, 540672, 0x0a8721d6 +0, 45, 45, 1, 540672, 0xf86921d6 +0, 46, 46, 1, 540672, 0x795621d6 +0, 47, 47, 1, 540672, 0x1b9021d6 +0, 48, 48, 1, 540672, 0x979621d6 +0, 49, 49, 1, 540672, 0x041021d6 diff --git a/tests/ref/fate/fitsdec-bitpix-32 b/tests/ref/fate/fitsdec-bitpix-32 index 9bce3615550..b3a51401d40 100644 --- a/tests/ref/fate/fitsdec-bitpix-32 +++ b/tests/ref/fate/fitsdec-bitpix-32 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 102x109 #sar 0: 0/1 -0, 0, 0, 1, 22236, 0x34490902 +0, 0, 0, 1, 22236, 0x24634517 diff --git a/tests/ref/fate/fitsdec-bitpix-64 b/tests/ref/fate/fitsdec-bitpix-64 index 9febdd68f4d..e50d5e029c6 100644 --- a/tests/ref/fate/fitsdec-bitpix-64 +++ b/tests/ref/fate/fitsdec-bitpix-64 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 77x173 #sar 0: 0/1 -0, 0, 0, 1, 26642, 0x0ad2a46a +0, 0, 0, 1, 26642, 0xa9eec634 diff --git a/tests/ref/fate/fitsdec-blank_bitpix32 b/tests/ref/fate/fitsdec-blank_bitpix32 index 184fd41c59e..330d6710ca1 100644 --- a/tests/ref/fate/fitsdec-blank_bitpix32 +++ b/tests/ref/fate/fitsdec-blank_bitpix32 @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 256x256 #sar 0: 0/1 -0, 0, 0, 1, 131072, 0x7fb22427 +0, 0, 0, 1, 131072, 0x3ecd0739 diff --git a/tests/ref/fate/fitsdec-ext_data_min_max b/tests/ref/fate/fitsdec-ext_data_min_max index 9009a4efb34..006d8d62504 100644 --- a/tests/ref/fate/fitsdec-ext_data_min_max +++ b/tests/ref/fate/fitsdec-ext_data_min_max @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 512x512 #sar 0: 0/1 -0, 0, 0, 1, 524288, 0xc327ed23 +0, 0, 0, 1, 524288, 0x6567ecb3 diff --git a/tests/ref/fate/fitsdec-gray b/tests/ref/fate/fitsdec-gray index 425b31fc0f1..d0807324526 100644 --- a/tests/ref/fate/fitsdec-gray +++ b/tests/ref/fate/fitsdec-gray @@ -3,4 +3,4 @@ #codec_id 0: rawvideo #dimensions 0: 128x128 #sar 0: 0/1 -0, 0, 0, 1, 16384, 0xd788a2d2 +0, 0, 0, 1, 16384, 0x353dbacd diff --git a/tests/ref/fate/gifenc-bgr4_byte b/tests/ref/fate/gifenc-bgr4_byte index 2cf3b7f93cd..2ec314edf2b 100644 --- a/tests/ref/fate/gifenc-bgr4_byte +++ b/tests/ref/fate/gifenc-bgr4_byte @@ -4,175 +4,175 @@ #dimensions 0: 217x217 #sar 0: 0/1 0, 0, 0, 1, 1297, 0x53e8b1c1 -0, 1, 1, 1, 221, 0x52d24d05, F=0x0 -0, 2, 2, 1, 139, 0xc9e32ab4, F=0x0 -0, 3, 3, 1, 392, 0x9244a858, F=0x0 -0, 4, 4, 1, 389, 0xc5bea3e0, F=0x0 -0, 5, 5, 1, 438, 0xfa2ab3d6, F=0x0 -0, 6, 6, 1, 526, 0x281ed94f, F=0x0 -0, 7, 7, 1, 543, 0xa53ee752, F=0x0 -0, 8, 8, 1, 446, 0x41d0b8ee, F=0x0 -0, 9, 9, 1, 931, 0xe8efa4aa, F=0x0 -0, 10, 10, 1, 702, 0x2d1c4ad3, F=0x0 -0, 11, 11, 1, 1202, 0xf0cc2be3, F=0x0 -0, 12, 12, 1, 1299, 0x189f54cb, F=0x0 -0, 13, 13, 1, 1253, 0xcb883414, F=0x0 -0, 14, 14, 1, 1338, 0xad6a52d2, F=0x0 -0, 15, 15, 1, 1284, 0x14993a0a, F=0x0 -0, 16, 16, 1, 1483, 0x216c991d, F=0x0 -0, 17, 17, 1, 1792, 0x58eb4c1e, F=0x0 -0, 18, 18, 1, 1683, 0x0b49fbbe, F=0x0 -0, 19, 19, 1, 1517, 0x9b57ab38, F=0x0 -0, 20, 20, 1, 1713, 0x23f21b0d, F=0x0 -0, 21, 21, 1, 1753, 0xe1e21eec, F=0x0 -0, 22, 22, 1, 1650, 0xf258037d, F=0x0 -0, 23, 23, 1, 1726, 0x73111ff6, F=0x0 -0, 24, 24, 1, 1908, 0x430b7b09, F=0x0 -0, 25, 25, 1, 1815, 0xa5af5368, F=0x0 -0, 26, 26, 1, 1923, 0x497f847c, F=0x0 -0, 27, 27, 1, 2108, 0x5e2ed19b, F=0x0 -0, 28, 28, 1, 2708, 0x6064f1c3, F=0x0 -0, 29, 29, 1, 2681, 0x7c4ea945, F=0x0 -0, 30, 30, 1, 2903, 0x7cbf4985, F=0x0 -0, 31, 31, 1, 3265, 0x81f7d05c, F=0x0 -0, 32, 32, 1, 3187, 0x6077c273, F=0x0 -0, 33, 33, 1, 3304, 0x3632e867, F=0x0 -0, 34, 34, 1, 3608, 0x7961776c, F=0x0 -0, 35, 35, 1, 3707, 0x515aa11b, F=0x0 -0, 36, 36, 1, 3822, 0x4a03d52c, F=0x0 -0, 37, 37, 1, 3635, 0x67607edf, F=0x0 -0, 38, 38, 1, 2958, 0x18e8618d, F=0x0 -0, 39, 39, 1, 3094, 0x1f1990fa, F=0x0 -0, 40, 40, 1, 3102, 0xde16868b, F=0x0 -0, 41, 41, 1, 3464, 0x7e6f2dba, F=0x0 -0, 42, 42, 1, 4116, 0x91585d9e, F=0x0 -0, 43, 43, 1, 4225, 0x9c785cdc, F=0x0 -0, 44, 44, 1, 3621, 0x0d0a4ebb, F=0x0 -0, 45, 45, 1, 3918, 0xa70ae7ca, F=0x0 -0, 46, 46, 1, 4469, 0xa318e475, F=0x0 -0, 47, 47, 1, 4601, 0x1ae12f09, F=0x0 -0, 48, 48, 1, 4830, 0x57b999bb, F=0x0 -0, 49, 49, 1, 5406, 0x744cc456, F=0x0 -0, 50, 50, 1, 5274, 0x3d6996e2, F=0x0 -0, 51, 51, 1, 5424, 0xc587e525, F=0x0 -0, 52, 52, 1, 5527, 0xd5870857, F=0x0 -0, 53, 53, 1, 5709, 0xbd715dd4, F=0x0 -0, 54, 54, 1, 6168, 0xa5c13fbb, F=0x0 -0, 55, 55, 1, 6241, 0x46485256, F=0x0 -0, 56, 56, 1, 5919, 0xc0bcc64e, F=0x0 -0, 57, 57, 1, 6005, 0x5443fb4d, F=0x0 -0, 58, 58, 1, 5954, 0xe7efef37, F=0x0 -0, 59, 59, 1, 6476, 0x1aadccc4, F=0x0 -0, 60, 60, 1, 6745, 0x6af23ae4, F=0x0 -0, 61, 61, 1, 6283, 0x07049084, F=0x0 -0, 62, 62, 1, 6649, 0x2c1f3be5, F=0x0 -0, 63, 63, 1, 6386, 0xb9848778, F=0x0 -0, 64, 64, 1, 6265, 0xce725997, F=0x0 -0, 65, 65, 1, 6916, 0xdfac7154, F=0x0 -0, 66, 66, 1, 7238, 0x2ace2606, F=0x0 -0, 67, 67, 1, 7564, 0x8cf9a84a, F=0x0 -0, 68, 68, 1, 7421, 0xaa1a71dc, F=0x0 -0, 69, 69, 1, 7484, 0xc67ba899, F=0x0 -0, 70, 70, 1, 7604, 0x2479e569, F=0x0 -0, 71, 71, 1, 7764, 0x0a1906f8, F=0x0 -0, 72, 72, 1, 8023, 0x27206bb7, F=0x0 -0, 73, 73, 1, 8136, 0x7705bf63, F=0x0 -0, 74, 74, 1, 8109, 0xe05cb4da, F=0x0 -0, 75, 75, 1, 7871, 0xce733f35, F=0x0 -0, 76, 76, 1, 7968, 0x27ef92ba, F=0x0 -0, 77, 77, 1, 8246, 0x5fe7ee2b, F=0x0 -0, 78, 78, 1, 8329, 0x809e4b94, F=0x0 -0, 79, 79, 1, 8570, 0x179fc163, F=0x0 -0, 80, 80, 1, 8754, 0xdb870edf, F=0x0 -0, 81, 81, 1, 8586, 0x280ca5f9, F=0x0 -0, 82, 82, 1, 8886, 0x445e5b51, F=0x0 -0, 83, 83, 1, 9085, 0xba37592e, F=0x0 -0, 84, 84, 1, 9318, 0xe970f8b2, F=0x0 -0, 85, 85, 1, 9402, 0x37ee4456, F=0x0 -0, 86, 86, 1, 9169, 0x171196bf, F=0x0 -0, 87, 87, 1, 9470, 0x793837fb, F=0x0 -0, 88, 88, 1, 9658, 0x489294a6, F=0x0 -0, 89, 89, 1, 9709, 0x980c9d78, F=0x0 -0, 90, 90, 1, 9531, 0xc7a83dbb, F=0x0 -0, 91, 91, 1, 9899, 0x658f0fcb, F=0x0 -0, 92, 92, 1, 10013, 0x434c4b1f, F=0x0 -0, 93, 93, 1, 10046, 0x3cdf2e5d, F=0x0 -0, 94, 94, 1, 10094, 0xfeb546c5, F=0x0 -0, 95, 95, 1, 10446, 0xf0d10017, F=0x0 -0, 96, 96, 1, 10591, 0xf90f55af, F=0x0 -0, 97, 97, 1, 10589, 0xd4948126, F=0x0 -0, 98, 98, 1, 10815, 0x5cb2d81b, F=0x0 -0, 99, 99, 1, 11119, 0x25ca4d4b, F=0x0 -0, 100, 100, 1, 11202, 0xae566cab, F=0x0 -0, 101, 101, 1, 11248, 0x5731a277, F=0x0 -0, 102, 102, 1, 11491, 0x1021f4fd, F=0x0 -0, 103, 103, 1, 11688, 0x6cb05cf9, F=0x0 -0, 104, 104, 1, 11793, 0xf036b8cb, F=0x0 -0, 105, 105, 1, 11444, 0x46d90941, F=0x0 -0, 106, 106, 1, 11936, 0x204acadf, F=0x0 -0, 107, 107, 1, 11940, 0x4bddbdf6, F=0x0 -0, 108, 108, 1, 12289, 0x902d708b, F=0x0 -0, 109, 109, 1, 12342, 0xc85680b0, F=0x0 -0, 110, 110, 1, 12460, 0xf3beb2c0, F=0x0 -0, 111, 111, 1, 12703, 0x96493c66, F=0x0 -0, 112, 112, 1, 12676, 0xdc9250b1, F=0x0 -0, 113, 113, 1, 12965, 0x08dba0c4, F=0x0 -0, 114, 114, 1, 13062, 0xac2ce092, F=0x0 -0, 115, 115, 1, 13155, 0xb6552cf5, F=0x0 -0, 116, 116, 1, 13179, 0x03d61a97, F=0x0 -0, 117, 117, 1, 13206, 0x0d8a5a0c, F=0x0 -0, 118, 118, 1, 13219, 0x039862cb, F=0x0 -0, 119, 119, 1, 13218, 0xede6623f, F=0x0 -0, 120, 120, 1, 13475, 0x7672a260, F=0x0 -0, 121, 121, 1, 13673, 0xaf0ddf16, F=0x0 -0, 122, 122, 1, 13700, 0x1ec9ed11, F=0x0 -0, 123, 123, 1, 13829, 0xc6cb2f9a, F=0x0 -0, 124, 124, 1, 13954, 0x3f89936d, F=0x0 -0, 125, 125, 1, 14071, 0x72d2d42d, F=0x0 -0, 126, 126, 1, 14132, 0x6c34cbf9, F=0x0 -0, 127, 127, 1, 14339, 0x4c695263, F=0x0 -0, 128, 128, 1, 14477, 0xfbcda593, F=0x0 -0, 129, 129, 1, 14544, 0xb3d3a37a, F=0x0 -0, 130, 130, 1, 14616, 0xb704d286, F=0x0 -0, 131, 131, 1, 14906, 0xca9226c6, F=0x0 -0, 132, 132, 1, 14986, 0x54e8a122, F=0x0 -0, 133, 133, 1, 15150, 0x40bed744, F=0x0 -0, 134, 134, 1, 15137, 0x6c4ebc1f, F=0x0 -0, 135, 135, 1, 15251, 0xca081273, F=0x0 -0, 136, 136, 1, 15345, 0x4d266373, F=0x0 -0, 137, 137, 1, 15646, 0x3f64a238, F=0x0 -0, 138, 138, 1, 15920, 0xe12020ea, F=0x0 -0, 139, 139, 1, 16049, 0x6bbb437e, F=0x0 -0, 140, 140, 1, 16236, 0x31a6016d, F=0x0 -0, 141, 141, 1, 16270, 0x40f1eb89, F=0x0 -0, 142, 142, 1, 16379, 0xe4d0092a, F=0x0 -0, 143, 143, 1, 16669, 0x6f22a6ca, F=0x0 -0, 144, 144, 1, 16925, 0x564a191d, F=0x0 -0, 145, 145, 1, 17157, 0xf1c7cb79, F=0x0 -0, 146, 146, 1, 17180, 0xc4b3b5d7, F=0x0 -0, 147, 147, 1, 17323, 0xa670a9d8, F=0x0 -0, 148, 148, 1, 17405, 0xf499125a, F=0x0 -0, 149, 149, 1, 17439, 0xd76af40b, F=0x0 -0, 150, 150, 1, 17584, 0x364d53e5, F=0x0 -0, 151, 151, 1, 17772, 0xbc218e98, F=0x0 -0, 152, 152, 1, 17834, 0xcc19808f, F=0x0 -0, 153, 153, 1, 17926, 0x12639cd7, F=0x0 -0, 154, 154, 1, 17831, 0xb0f9c051, F=0x0 -0, 155, 155, 1, 18150, 0x574022e1, F=0x0 -0, 156, 156, 1, 18265, 0x73458364, F=0x0 -0, 157, 157, 1, 18345, 0x2d31c3b1, F=0x0 -0, 158, 158, 1, 18301, 0x9028389e, F=0x0 -0, 159, 159, 1, 18426, 0xe701be30, F=0x0 -0, 160, 160, 1, 18615, 0x1bfd4868, F=0x0 -0, 161, 161, 1, 18924, 0xa269bcfe, F=0x0 -0, 162, 162, 1, 19081, 0xb2f5052e, F=0x0 -0, 163, 163, 1, 19176, 0x0676ff5f, F=0x0 -0, 164, 164, 1, 19218, 0xfbb40323, F=0x0 -0, 165, 165, 1, 19406, 0x714664fe, F=0x0 -0, 166, 166, 1, 19488, 0x1acf9f93, F=0x0 -0, 167, 167, 1, 19667, 0x50bd11c0, F=0x0 -0, 168, 168, 1, 19680, 0xe322168b, F=0x0 -0, 169, 169, 1, 19944, 0x25b763c2, F=0x0 -0, 170, 170, 1, 19983, 0xfd109bea, F=0x0 -0, 171, 171, 1, 20029, 0x565d8efd, F=0x0 -0, 172, 172, 1, 20068, 0xadee793f, F=0x0 +0, 1, 1, 1, 158, 0xe1873465, F=0x0 +0, 2, 2, 1, 143, 0x56992b17, F=0x0 +0, 3, 3, 1, 169, 0x4f0434c7, F=0x0 +0, 4, 4, 1, 254, 0xb4845bf3, F=0x0 +0, 5, 5, 1, 221, 0xa02a4ae2, F=0x0 +0, 6, 6, 1, 176, 0xdcfc3a8b, F=0x0 +0, 7, 7, 1, 189, 0xb45f3f8d, F=0x0 +0, 8, 8, 1, 139, 0xf2622fc0, F=0x0 +0, 9, 9, 1, 160, 0xf77b327d, F=0x0 +0, 10, 10, 1, 149, 0x56b62de9, F=0x0 +0, 11, 11, 1, 190, 0x1eca3f72, F=0x0 +0, 12, 12, 1, 308, 0xfc7373fc, F=0x0 +0, 13, 13, 1, 193, 0x94304232, F=0x0 +0, 14, 14, 1, 191, 0x82e84504, F=0x0 +0, 15, 15, 1, 198, 0xd31944a2, F=0x0 +0, 16, 16, 1, 417, 0x9547ac22, F=0x0 +0, 17, 17, 1, 163, 0xb55537d0, F=0x0 +0, 18, 18, 1, 383, 0x634f9f88, F=0x0 +0, 19, 19, 1, 193, 0xf6d24046, F=0x0 +0, 20, 20, 1, 337, 0x45d6916f, F=0x0 +0, 21, 21, 1, 199, 0xabb34a72, F=0x0 +0, 22, 22, 1, 308, 0x097f7e58, F=0x0 +0, 23, 23, 1, 186, 0x369b41cc, F=0x0 +0, 24, 24, 1, 199, 0xa93c4113, F=0x0 +0, 25, 25, 1, 163, 0x16823904, F=0x0 +0, 26, 26, 1, 302, 0xaf1d78da, F=0x0 +0, 27, 27, 1, 189, 0x3f7a3bde, F=0x0 +0, 28, 28, 1, 157, 0x45e23500, F=0x0 +0, 29, 29, 1, 205, 0x797b4510, F=0x0 +0, 30, 30, 1, 160, 0x20e533d1, F=0x0 +0, 31, 31, 1, 202, 0xd3b748b3, F=0x0 +0, 32, 32, 1, 160, 0x207c371f, F=0x0 +0, 33, 33, 1, 215, 0x079951b6, F=0x0 +0, 34, 34, 1, 422, 0xbb9db080, F=0x0 +0, 35, 35, 1, 184, 0x3a044098, F=0x0 +0, 36, 36, 1, 289, 0xf2757206, F=0x0 +0, 37, 37, 1, 190, 0xc3264203, F=0x0 +0, 38, 38, 1, 195, 0x28544262, F=0x0 +0, 39, 39, 1, 199, 0x32db4bba, F=0x0 +0, 40, 40, 1, 448, 0x64a8bfe8, F=0x0 +0, 41, 41, 1, 170, 0x62a536d7, F=0x0 +0, 42, 42, 1, 279, 0xe1df6ff6, F=0x0 +0, 43, 43, 1, 180, 0x762f3aac, F=0x0 +0, 44, 44, 1, 303, 0x73727c79, F=0x0 +0, 45, 45, 1, 209, 0xf4a8515c, F=0x0 +0, 46, 46, 1, 198, 0xe09f451e, F=0x0 +0, 47, 47, 1, 194, 0x301e4b04, F=0x0 +0, 48, 48, 1, 186, 0x7c66421b, F=0x0 +0, 49, 49, 1, 1200, 0xe55e34e7, F=0x0 +0, 50, 50, 1, 204, 0x25534779, F=0x0 +0, 51, 51, 1, 1066, 0x9792efa8, F=0x0 +0, 52, 52, 1, 187, 0xd82e41f0, F=0x0 +0, 53, 53, 1, 323, 0x0f9d8485, F=0x0 +0, 54, 54, 1, 205, 0x86ef4c75, F=0x0 +0, 55, 55, 1, 213, 0x6e515113, F=0x0 +0, 56, 56, 1, 208, 0x1ac84bed, F=0x0 +0, 57, 57, 1, 737, 0x68a047be, F=0x0 +0, 58, 58, 1, 181, 0x84353a71, F=0x0 +0, 59, 59, 1, 614, 0x3c9a142a, F=0x0 +0, 60, 60, 1, 215, 0xacaf535d, F=0x0 +0, 61, 61, 1, 291, 0x4dab7020, F=0x0 +0, 62, 62, 1, 208, 0x3dd84d1f, F=0x0 +0, 63, 63, 1, 208, 0x8ccb45cc, F=0x0 +0, 64, 64, 1, 203, 0xa905456b, F=0x0 +0, 65, 65, 1, 531, 0x6b32eaf4, F=0x0 +0, 66, 66, 1, 178, 0xd30b3bff, F=0x0 +0, 67, 67, 1, 446, 0x881abe2a, F=0x0 +0, 68, 68, 1, 188, 0xb5a43e6b, F=0x0 +0, 69, 69, 1, 177, 0x9255436c, F=0x0 +0, 70, 70, 1, 112, 0x2990213a, F=0x0 +0, 71, 71, 1, 296, 0x7d6e7183, F=0x0 +0, 72, 72, 1, 153, 0x35053313, F=0x0 +0, 73, 73, 1, 218, 0xcf105015, F=0x0 +0, 74, 74, 1, 202, 0xb4634837, F=0x0 +0, 75, 75, 1, 239, 0x0a4b5b7b, F=0x0 +0, 76, 76, 1, 139, 0x18d92e35, F=0x0 +0, 77, 77, 1, 467, 0x784dc79e, F=0x0 +0, 78, 78, 1, 274, 0x1e9b78ef, F=0x0 +0, 79, 79, 1, 404, 0xb5c6ab9b, F=0x0 +0, 80, 80, 1, 257, 0x7aaa66ce, F=0x0 +0, 81, 81, 1, 419, 0xcb62b6cb, F=0x0 +0, 82, 82, 1, 208, 0xcbba506d, F=0x0 +0, 83, 83, 1, 294, 0x031a76f9, F=0x0 +0, 84, 84, 1, 191, 0x5ba83f77, F=0x0 +0, 85, 85, 1, 181, 0xfc454323, F=0x0 +0, 86, 86, 1, 218, 0xcd7e540e, F=0x0 +0, 87, 87, 1, 423, 0xdcebc01f, F=0x0 +0, 88, 88, 1, 202, 0x160a4ab6, F=0x0 +0, 89, 89, 1, 317, 0xff2b8104, F=0x0 +0, 90, 90, 1, 172, 0x68753e2d, F=0x0 +0, 91, 91, 1, 275, 0x7f766cd1, F=0x0 +0, 92, 92, 1, 214, 0xf37e5043, F=0x0 +0, 93, 93, 1, 182, 0x5e5544f6, F=0x0 +0, 94, 94, 1, 151, 0xf74532b7, F=0x0 +0, 95, 95, 1, 157, 0xe99b33b0, F=0x0 +0, 96, 96, 1, 202, 0x77994327, F=0x0 +0, 97, 97, 1, 162, 0x93f235fc, F=0x0 +0, 98, 98, 1, 186, 0xc25d3f3f, F=0x0 +0, 99, 99, 1, 318, 0xdf547809, F=0x0 +0, 100, 100, 1, 225, 0xf8074f18, F=0x0 +0, 101, 101, 1, 207, 0x12204757, F=0x0 +0, 102, 102, 1, 212, 0xd2ec4c59, F=0x0 +0, 103, 103, 1, 153, 0x0e1c33cd, F=0x0 +0, 104, 104, 1, 196, 0x45d4455d, F=0x0 +0, 105, 105, 1, 146, 0x5dba32b0, F=0x0 +0, 106, 106, 1, 194, 0x13a8490b, F=0x0 +0, 107, 107, 1, 304, 0xcf73832f, F=0x0 +0, 108, 108, 1, 194, 0x68fd43c9, F=0x0 +0, 109, 109, 1, 190, 0xdbc64442, F=0x0 +0, 110, 110, 1, 204, 0x8eb94b27, F=0x0 +0, 111, 111, 1, 136, 0xd0162d03, F=0x0 +0, 112, 112, 1, 171, 0xbb8b3d15, F=0x0 +0, 113, 113, 1, 140, 0x4cb930a9, F=0x0 +0, 114, 114, 1, 158, 0xdebb32d2, F=0x0 +0, 115, 115, 1, 94, 0x16a71c65, F=0x0 +0, 116, 116, 1, 110, 0x358d20bf, F=0x0 +0, 117, 117, 1, 256, 0x49a469b7, F=0x0 +0, 118, 118, 1, 220, 0x08b95486, F=0x0 +0, 119, 119, 1, 221, 0xe0af4f92, F=0x0 +0, 120, 120, 1, 205, 0x7ac444a5, F=0x0 +0, 121, 121, 1, 169, 0x62c039d3, F=0x0 +0, 122, 122, 1, 970, 0xc8f6c8a6, F=0x0 +0, 123, 123, 1, 181, 0x0d9c42ba, F=0x0 +0, 124, 124, 1, 955, 0xdf5dba5e, F=0x0 +0, 125, 125, 1, 158, 0xa01833fd, F=0x0 +0, 126, 126, 1, 344, 0xd17989f3, F=0x0 +0, 127, 127, 1, 627, 0x8c611977, F=0x0 +0, 128, 128, 1, 172, 0x7cf83c63, F=0x0 +0, 129, 129, 1, 494, 0xadccdc2b, F=0x0 +0, 130, 130, 1, 184, 0x4e784407, F=0x0 +0, 131, 131, 1, 352, 0x852f992b, F=0x0 +0, 132, 132, 1, 351, 0x99e78bb8, F=0x0 +0, 133, 133, 1, 170, 0x9f4b3869, F=0x0 +0, 134, 134, 1, 275, 0x29b96b3c, F=0x0 +0, 135, 135, 1, 168, 0x98d0399b, F=0x0 +0, 136, 136, 1, 169, 0x015039aa, F=0x0 +0, 137, 137, 1, 521, 0x3c33db57, F=0x0 +0, 138, 138, 1, 1262, 0x9cf44321, F=0x0 +0, 139, 139, 1, 994, 0x5ea8bcd4, F=0x0 +0, 140, 140, 1, 290, 0xf24f72b0, F=0x0 +0, 141, 141, 1, 188, 0x36cb408f, F=0x0 +0, 142, 142, 1, 164, 0x6c813b02, F=0x0 +0, 143, 143, 1, 212, 0x1dfb463e, F=0x0 +0, 144, 144, 1, 870, 0xd89e94ed, F=0x0 +0, 145, 145, 1, 635, 0x87bf1ae2, F=0x0 +0, 146, 146, 1, 290, 0x34ff78fe, F=0x0 +0, 147, 147, 1, 211, 0x9f755207, F=0x0 +0, 148, 148, 1, 177, 0x3f003c44, F=0x0 +0, 149, 149, 1, 182, 0xe8c23eea, F=0x0 +0, 150, 150, 1, 588, 0xb4b9fc5d, F=0x0 +0, 151, 151, 1, 163, 0x3078356b, F=0x0 +0, 152, 152, 1, 407, 0x4161b245, F=0x0 +0, 153, 153, 1, 204, 0xe2a64478, F=0x0 +0, 154, 154, 1, 290, 0x5cc079af, F=0x0 +0, 155, 155, 1, 308, 0xf5958253, F=0x0 +0, 156, 156, 1, 196, 0xb1cb46f0, F=0x0 +0, 157, 157, 1, 181, 0x0aee4103, F=0x0 +0, 158, 158, 1, 203, 0x36784ee0, F=0x0 +0, 159, 159, 1, 227, 0x23fd5b71, F=0x0 +0, 160, 160, 1, 524, 0x18a6e404, F=0x0 +0, 161, 161, 1, 377, 0x1f5697de, F=0x0 +0, 162, 162, 1, 196, 0x72304538, F=0x0 +0, 163, 163, 1, 250, 0xb8e46580, F=0x0 +0, 164, 164, 1, 214, 0xe9df51b6, F=0x0 +0, 165, 165, 1, 323, 0xacbb9067, F=0x0 +0, 166, 166, 1, 176, 0xa12a410c, F=0x0 +0, 167, 167, 1, 305, 0x21227d7a, F=0x0 +0, 168, 168, 1, 179, 0x3dac422b, F=0x0 +0, 169, 169, 1, 245, 0x948963cd, F=0x0 +0, 170, 170, 1, 181, 0x407140fb, F=0x0 +0, 171, 171, 1, 241, 0xd2a35a7a, F=0x0 +0, 172, 172, 1, 172, 0x9fa83e96, F=0x0 diff --git a/tests/ref/fate/gifenc-bgr8 b/tests/ref/fate/gifenc-bgr8 index 58767a6b045..d4a42b03b28 100644 --- a/tests/ref/fate/gifenc-bgr8 +++ b/tests/ref/fate/gifenc-bgr8 @@ -4,175 +4,175 @@ #dimensions 0: 217x217 #sar 0: 0/1 0, 0, 0, 1, 1341, 0xe4e2af18 -0, 1, 1, 1, 305, 0xefa98bbd, F=0x0 -0, 2, 2, 1, 446, 0x9499cf43, F=0x0 -0, 3, 3, 1, 458, 0x8cb7d0d5, F=0x0 -0, 4, 4, 1, 555, 0x41f2fe63, F=0x0 -0, 5, 5, 1, 622, 0x3651212e, F=0x0 -0, 6, 6, 1, 650, 0x67542087, F=0x0 -0, 7, 7, 1, 668, 0x392934ca, F=0x0 -0, 8, 8, 1, 829, 0x6cd07a33, F=0x0 -0, 9, 9, 1, 1165, 0xb64b2ec9, F=0x0 -0, 10, 10, 1, 187, 0x114a52a4, F=0x0 -0, 11, 11, 1, 1341, 0x6ca57064, F=0x0 -0, 12, 12, 1, 1646, 0xcdd90fae, F=0x0 -0, 13, 13, 1, 1539, 0xbf75e55f, F=0x0 -0, 14, 14, 1, 1728, 0x96b14714, F=0x0 -0, 15, 15, 1, 1918, 0xffd08446, F=0x0 -0, 16, 16, 1, 2132, 0x3d5ae258, F=0x0 -0, 17, 17, 1, 2256, 0x359526d9, F=0x0 -0, 18, 18, 1, 2319, 0x553c382b, F=0x0 -0, 19, 19, 1, 2416, 0x7ac37066, F=0x0 -0, 20, 20, 1, 2609, 0x552bc27f, F=0x0 -0, 21, 21, 1, 2695, 0xbb0c020e, F=0x0 -0, 22, 22, 1, 2792, 0x46670f7d, F=0x0 -0, 23, 23, 1, 2892, 0x5674481e, F=0x0 -0, 24, 24, 1, 2990, 0x015a7bfd, F=0x0 -0, 25, 25, 1, 3109, 0xc73e9d14, F=0x0 -0, 26, 26, 1, 3261, 0x8a77f522, F=0x0 -0, 27, 27, 1, 3337, 0xf6f92558, F=0x0 -0, 28, 28, 1, 3580, 0x23408bc8, F=0x0 -0, 29, 29, 1, 3815, 0x781dfb1b, F=0x0 -0, 30, 30, 1, 2758, 0xf5cd1d5b, F=0x0 -0, 31, 31, 1, 4039, 0x7909712e, F=0x0 -0, 32, 32, 1, 3033, 0x39089d44, F=0x0 -0, 33, 33, 1, 4303, 0x667b0c60, F=0x0 -0, 34, 34, 1, 2052, 0x9edfba63, F=0x0 -0, 35, 35, 1, 3220, 0x5a56f015, F=0x0 -0, 36, 36, 1, 2300, 0x1719645c, F=0x0 -0, 37, 37, 1, 3641, 0x71c49a57, F=0x0 -0, 38, 38, 1, 3560, 0x377575b8, F=0x0 -0, 39, 39, 1, 3698, 0x165adce0, F=0x0 -0, 40, 40, 1, 1567, 0x6ecbf672, F=0x0 -0, 41, 41, 1, 962, 0x21eccba1, F=0x0 -0, 42, 42, 1, 281, 0x7a5a7ac3, F=0x0 -0, 43, 43, 1, 938, 0x659bb1ec, F=0x0 -0, 44, 44, 1, 279, 0x71e181bc, F=0x0 -0, 45, 45, 1, 204, 0xe0765316, F=0x0 -0, 46, 46, 1, 4307, 0xdbdd0e7d, F=0x0 -0, 47, 47, 1, 4903, 0xd8d24194, F=0x0 -0, 48, 48, 1, 4936, 0x6e9f1aff, F=0x0 -0, 49, 49, 1, 4949, 0xb3115902, F=0x0 -0, 50, 50, 1, 4162, 0xee2cad5b, F=0x0 -0, 51, 51, 1, 4686, 0xd583c178, F=0x0 -0, 52, 52, 1, 4749, 0x8c93b6a9, F=0x0 -0, 53, 53, 1, 4990, 0x12f957fd, F=0x0 -0, 54, 54, 1, 5187, 0xf3bcc7c9, F=0x0 -0, 55, 55, 1, 5054, 0xa27684fb, F=0x0 -0, 56, 56, 1, 5148, 0xe76cbad7, F=0x0 -0, 57, 57, 1, 4309, 0x79f7f067, F=0x0 -0, 58, 58, 1, 5087, 0xa2e29f29, F=0x0 -0, 59, 59, 1, 5292, 0xd158e4db, F=0x0 -0, 60, 60, 1, 5434, 0xe0be16f5, F=0x0 -0, 61, 61, 1, 4653, 0x0a3d8a5e, F=0x0 -0, 62, 62, 1, 5271, 0x4412d0a5, F=0x0 -0, 63, 63, 1, 5229, 0x3e06c4f2, F=0x0 -0, 64, 64, 1, 5225, 0x9bc39cfa, F=0x0 -0, 65, 65, 1, 5403, 0x798b009d, F=0x0 -0, 66, 66, 1, 5228, 0x14f2dded, F=0x0 -0, 67, 67, 1, 5712, 0x8724debe, F=0x0 -0, 68, 68, 1, 5644, 0x49d3a383, F=0x0 -0, 69, 69, 1, 5826, 0xde72e0ba, F=0x0 -0, 70, 70, 1, 5771, 0x62efd718, F=0x0 -0, 71, 71, 1, 6124, 0xb2a68c70, F=0x0 -0, 72, 72, 1, 6077, 0xb48b693f, F=0x0 -0, 73, 73, 1, 5804, 0xb700db6c, F=0x0 -0, 74, 74, 1, 6007, 0x02953898, F=0x0 -0, 75, 75, 1, 6228, 0x87a7b197, F=0x0 -0, 76, 76, 1, 6382, 0x49e7e65b, F=0x0 -0, 77, 77, 1, 6473, 0x3b9b6c3d, F=0x0 -0, 78, 78, 1, 7027, 0x2a4e1c17, F=0x0 -0, 79, 79, 1, 7263, 0x2e48c2e7, F=0x0 -0, 80, 80, 1, 8205, 0x013b701a, F=0x0 -0, 81, 81, 1, 8366, 0xcca97b10, F=0x0 -0, 82, 82, 1, 7716, 0x3b088fb3, F=0x0 -0, 83, 83, 1, 7420, 0xefdd1187, F=0x0 -0, 84, 84, 1, 7549, 0x1731227c, F=0x0 -0, 85, 85, 1, 7956, 0x8186ceb3, F=0x0 -0, 86, 86, 1, 8416, 0x23add53c, F=0x0 -0, 87, 87, 1, 8064, 0x09c616c4, F=0x0 -0, 88, 88, 1, 7409, 0x9d98af03, F=0x0 -0, 89, 89, 1, 7502, 0x0b81ebf3, F=0x0 -0, 90, 90, 1, 7814, 0x2f0d75cb, F=0x0 -0, 91, 91, 1, 7776, 0x45d6800f, F=0x0 -0, 92, 92, 1, 7757, 0x777f98b6, F=0x0 -0, 93, 93, 1, 8055, 0x4eea1634, F=0x0 -0, 94, 94, 1, 7626, 0xfb3931fd, F=0x0 -0, 95, 95, 1, 7987, 0x22a1d052, F=0x0 -0, 96, 96, 1, 12070, 0x3aa2924d, F=0x0 -0, 97, 97, 1, 12325, 0xda6cd811, F=0x0 -0, 98, 98, 1, 12225, 0xd478e671, F=0x0 -0, 99, 99, 1, 11235, 0xc6c09240, F=0x0 -0, 100, 100, 1, 11116, 0x95050c25, F=0x0 -0, 101, 101, 1, 11374, 0x14a68e3d, F=0x0 -0, 102, 102, 1, 11904, 0xb14436f2, F=0x0 -0, 103, 103, 1, 11487, 0xa3358311, F=0x0 -0, 104, 104, 1, 13403, 0xccf33a00, F=0x0 -0, 105, 105, 1, 12921, 0xbf7e4759, F=0x0 -0, 106, 106, 1, 13872, 0x7dace890, F=0x0 -0, 107, 107, 1, 13559, 0xb6c868f9, F=0x0 -0, 108, 108, 1, 14049, 0xa5d569b0, F=0x0 -0, 109, 109, 1, 14152, 0x8c9c31fb, F=0x0 -0, 110, 110, 1, 14285, 0x2ffe3bc3, F=0x0 -0, 111, 111, 1, 14432, 0x27abe196, F=0x0 -0, 112, 112, 1, 14697, 0x20d1aedd, F=0x0 -0, 113, 113, 1, 14606, 0xbcbe586d, F=0x0 -0, 114, 114, 1, 15221, 0x515f8224, F=0x0 -0, 115, 115, 1, 15433, 0x68089081, F=0x0 -0, 116, 116, 1, 15603, 0x8ff4e6fd, F=0x0 -0, 117, 117, 1, 15606, 0xc8527e30, F=0x0 -0, 118, 118, 1, 15871, 0x056ddca3, F=0x0 -0, 119, 119, 1, 15725, 0xc1871caf, F=0x0 -0, 120, 120, 1, 16086, 0x063e257b, F=0x0 -0, 121, 121, 1, 16233, 0xf683823b, F=0x0 -0, 122, 122, 1, 16143, 0x576df609, F=0x0 -0, 123, 123, 1, 16669, 0x3d02346b, F=0x0 -0, 124, 124, 1, 16627, 0xe35236cc, F=0x0 -0, 125, 125, 1, 16837, 0x389c996c, F=0x0 -0, 126, 126, 1, 16952, 0xc833af9f, F=0x0 -0, 127, 127, 1, 17127, 0xbf124531, F=0x0 -0, 128, 128, 1, 17158, 0x7abbfe59, F=0x0 -0, 129, 129, 1, 17329, 0x8102cc38, F=0x0 -0, 130, 130, 1, 17403, 0xa9468cad, F=0x0 -0, 131, 131, 1, 17674, 0x46d8038c, F=0x0 -0, 132, 132, 1, 17738, 0x5ee865ce, F=0x0 -0, 133, 133, 1, 17942, 0xc2449873, F=0x0 -0, 134, 134, 1, 17952, 0x3aafedbc, F=0x0 -0, 135, 135, 1, 18246, 0xdd930054, F=0x0 -0, 136, 136, 1, 18399, 0x68a59331, F=0x0 -0, 137, 137, 1, 18551, 0x1301c157, F=0x0 -0, 138, 138, 1, 18947, 0xbed8a9b4, F=0x0 -0, 139, 139, 1, 19153, 0xe338fea2, F=0x0 -0, 140, 140, 1, 19128, 0xbf05820e, F=0x0 -0, 141, 141, 1, 19138, 0x73b5cd49, F=0x0 -0, 142, 142, 1, 19502, 0xd8d68603, F=0x0 -0, 143, 143, 1, 19542, 0x84b47563, F=0x0 -0, 144, 144, 1, 19755, 0x7194b230, F=0x0 -0, 145, 145, 1, 20122, 0x83633f5e, F=0x0 -0, 146, 146, 1, 20265, 0x7365b06b, F=0x0 -0, 147, 147, 1, 20378, 0x1aaf1a98, F=0x0 -0, 148, 148, 1, 20300, 0x1da6d336, F=0x0 -0, 149, 149, 1, 20499, 0x72d54d30, F=0x0 -0, 150, 150, 1, 20655, 0x6996b124, F=0x0 -0, 151, 151, 1, 20674, 0xa883abd2, F=0x0 -0, 152, 152, 1, 21015, 0x96cf4018, F=0x0 -0, 153, 153, 1, 21066, 0x307e4479, F=0x0 -0, 154, 154, 1, 21161, 0xd45a2c38, F=0x0 -0, 155, 155, 1, 21086, 0xcf0e597d, F=0x0 -0, 156, 156, 1, 21466, 0xcc4032fe, F=0x0 -0, 157, 157, 1, 21677, 0x755ccb7b, F=0x0 -0, 158, 158, 1, 21589, 0x5d74fb47, F=0x0 -0, 159, 159, 1, 21662, 0x0c459189, F=0x0 -0, 160, 160, 1, 21995, 0x43d46eb3, F=0x0 -0, 161, 161, 1, 22213, 0x68455c92, F=0x0 -0, 162, 162, 1, 22483, 0xdc83c15a, F=0x0 -0, 163, 163, 1, 22498, 0xfae1251e, F=0x0 -0, 164, 164, 1, 22468, 0x7944b1d4, F=0x0 -0, 165, 165, 1, 22869, 0xff8c10ac, F=0x0 -0, 166, 166, 1, 22754, 0xd8183b84, F=0x0 -0, 167, 167, 1, 23173, 0x6c3c48d5, F=0x0 -0, 168, 168, 1, 23281, 0xc32ca477, F=0x0 -0, 169, 169, 1, 23219, 0x4dc26193, F=0x0 -0, 170, 170, 1, 23656, 0xb85ee15f, F=0x0 -0, 171, 171, 1, 23683, 0x26ba4915, F=0x0 -0, 172, 172, 1, 23882, 0xf57285de, F=0x0 +0, 1, 1, 1, 236, 0x332769fd, F=0x0 +0, 2, 2, 1, 186, 0x770d5061, F=0x0 +0, 3, 3, 1, 208, 0x55784c8f, F=0x0 +0, 4, 4, 1, 282, 0x98e8825d, F=0x0 +0, 5, 5, 1, 209, 0x4cc15280, F=0x0 +0, 6, 6, 1, 225, 0xf8785d6c, F=0x0 +0, 7, 7, 1, 204, 0x322754b4, F=0x0 +0, 8, 8, 1, 181, 0x9f4f4d10, F=0x0 +0, 9, 9, 1, 200, 0x64a453a1, F=0x0 +0, 10, 10, 1, 184, 0x79c344d3, F=0x0 +0, 11, 11, 1, 191, 0x7097487b, F=0x0 +0, 12, 12, 1, 290, 0x1f998186, F=0x0 +0, 13, 13, 1, 153, 0xfe4c3a7c, F=0x0 +0, 14, 14, 1, 175, 0xfdf442e0, F=0x0 +0, 15, 15, 1, 187, 0x393845f2, F=0x0 +0, 16, 16, 1, 418, 0xe94bc757, F=0x0 +0, 17, 17, 1, 200, 0x8dab52be, F=0x0 +0, 18, 18, 1, 347, 0xae379838, F=0x0 +0, 19, 19, 1, 176, 0x868742d2, F=0x0 +0, 20, 20, 1, 294, 0xb1fb8365, F=0x0 +0, 21, 21, 1, 166, 0x06e44260, F=0x0 +0, 22, 22, 1, 306, 0x29e983b7, F=0x0 +0, 23, 23, 1, 180, 0xfa4a44eb, F=0x0 +0, 24, 24, 1, 207, 0x99de52c8, F=0x0 +0, 25, 25, 1, 204, 0x73944c35, F=0x0 +0, 26, 26, 1, 259, 0xa0637375, F=0x0 +0, 27, 27, 1, 181, 0x130f4b9a, F=0x0 +0, 28, 28, 1, 152, 0x4be93cae, F=0x0 +0, 29, 29, 1, 194, 0x81e34e1d, F=0x0 +0, 30, 30, 1, 159, 0x5913380b, F=0x0 +0, 31, 31, 1, 162, 0xf0683bb1, F=0x0 +0, 32, 32, 1, 178, 0xce32498c, F=0x0 +0, 33, 33, 1, 187, 0x5d61509f, F=0x0 +0, 34, 34, 1, 336, 0xe5569440, F=0x0 +0, 35, 35, 1, 184, 0x3fa349ea, F=0x0 +0, 36, 36, 1, 227, 0x7cad5f66, F=0x0 +0, 37, 37, 1, 182, 0xf23a4522, F=0x0 +0, 38, 38, 1, 162, 0x2a053dcc, F=0x0 +0, 39, 39, 1, 187, 0xc18a4686, F=0x0 +0, 40, 40, 1, 503, 0xa5a7e669, F=0x0 +0, 41, 41, 1, 167, 0xa28a43e8, F=0x0 +0, 42, 42, 1, 289, 0xf1968090, F=0x0 +0, 43, 43, 1, 194, 0xedbb4eb7, F=0x0 +0, 44, 44, 1, 285, 0x82e7818b, F=0x0 +0, 45, 45, 1, 199, 0x5e725190, F=0x0 +0, 46, 46, 1, 197, 0x0c6a4fb9, F=0x0 +0, 47, 47, 1, 203, 0x05684b96, F=0x0 +0, 48, 48, 1, 208, 0xd33457ad, F=0x0 +0, 49, 49, 1, 1198, 0x029f5f1b, F=0x0 +0, 50, 50, 1, 175, 0xf4e94c40, F=0x0 +0, 51, 51, 1, 740, 0x6e096787, F=0x0 +0, 52, 52, 1, 180, 0xf34f45be, F=0x0 +0, 53, 53, 1, 238, 0xfbed6adb, F=0x0 +0, 54, 54, 1, 198, 0x6f3a5344, F=0x0 +0, 55, 55, 1, 196, 0x8bbb4b02, F=0x0 +0, 56, 56, 1, 224, 0x92c55d92, F=0x0 +0, 57, 57, 1, 765, 0xd16e6d65, F=0x0 +0, 58, 58, 1, 149, 0x97aa38d2, F=0x0 +0, 59, 59, 1, 479, 0x1030d73b, F=0x0 +0, 60, 60, 1, 179, 0x441e493a, F=0x0 +0, 61, 61, 1, 217, 0x16c259b7, F=0x0 +0, 62, 62, 1, 190, 0x5b2349ca, F=0x0 +0, 63, 63, 1, 155, 0x422f39ff, F=0x0 +0, 64, 64, 1, 167, 0x28444898, F=0x0 +0, 65, 65, 1, 649, 0x57b331e2, F=0x0 +0, 66, 66, 1, 196, 0x99a1574f, F=0x0 +0, 67, 67, 1, 427, 0xfd45d548, F=0x0 +0, 68, 68, 1, 175, 0x90cc42ce, F=0x0 +0, 69, 69, 1, 284, 0x49398208, F=0x0 +0, 70, 70, 1, 131, 0x18e42fc2, F=0x0 +0, 71, 71, 1, 256, 0xac896bbe, F=0x0 +0, 72, 72, 1, 186, 0x522d4974, F=0x0 +0, 73, 73, 1, 190, 0x5e064e04, F=0x0 +0, 74, 74, 1, 216, 0x863f53fa, F=0x0 +0, 75, 75, 1, 154, 0x17f1383f, F=0x0 +0, 76, 76, 1, 113, 0x20f827ee, F=0x0 +0, 77, 77, 1, 402, 0xce2caf45, F=0x0 +0, 78, 78, 1, 198, 0x0bc851ae, F=0x0 +0, 79, 79, 1, 466, 0xb31ad387, F=0x0 +0, 80, 80, 1, 322, 0x20018e02, F=0x0 +0, 81, 81, 1, 387, 0x5038b1b8, F=0x0 +0, 82, 82, 1, 158, 0xc6ac3feb, F=0x0 +0, 83, 83, 1, 278, 0x59f17c03, F=0x0 +0, 84, 84, 1, 190, 0xd7665022, F=0x0 +0, 85, 85, 1, 175, 0x1a6e4225, F=0x0 +0, 86, 86, 1, 206, 0xc3f44e3a, F=0x0 +0, 87, 87, 1, 379, 0xb1e6b77e, F=0x0 +0, 88, 88, 1, 202, 0x17975145, F=0x0 +0, 89, 89, 1, 225, 0x96985fd2, F=0x0 +0, 90, 90, 1, 175, 0x71b1497a, F=0x0 +0, 91, 91, 1, 235, 0x2bdc5faa, F=0x0 +0, 92, 92, 1, 185, 0x68124958, F=0x0 +0, 93, 93, 1, 182, 0x120c4c63, F=0x0 +0, 94, 94, 1, 114, 0xdd9b2b02, F=0x0 +0, 95, 95, 1, 176, 0x0d5b4b46, F=0x0 +0, 96, 96, 1, 214, 0xfb5e5b47, F=0x0 +0, 97, 97, 1, 182, 0xf4c54860, F=0x0 +0, 98, 98, 1, 195, 0x6e075188, F=0x0 +0, 99, 99, 1, 303, 0x4c158465, F=0x0 +0, 100, 100, 1, 191, 0x12005055, F=0x0 +0, 101, 101, 1, 177, 0x01ed4929, F=0x0 +0, 102, 102, 1, 172, 0x86984280, F=0x0 +0, 103, 103, 1, 179, 0xb854481c, F=0x0 +0, 104, 104, 1, 188, 0xeacd47ed, F=0x0 +0, 105, 105, 1, 177, 0x36be4889, F=0x0 +0, 106, 106, 1, 215, 0x6aa65c99, F=0x0 +0, 107, 107, 1, 339, 0x1af496b2, F=0x0 +0, 108, 108, 1, 189, 0x941045fa, F=0x0 +0, 109, 109, 1, 209, 0x37445651, F=0x0 +0, 110, 110, 1, 205, 0x9cf85222, F=0x0 +0, 111, 111, 1, 185, 0x8b964cb5, F=0x0 +0, 112, 112, 1, 211, 0xd03d59ce, F=0x0 +0, 113, 113, 1, 178, 0xe40e4c08, F=0x0 +0, 114, 114, 1, 195, 0x971d4d92, F=0x0 +0, 115, 115, 1, 116, 0x5f9a2d3f, F=0x0 +0, 116, 116, 1, 116, 0x5c4e2d06, F=0x0 +0, 117, 117, 1, 427, 0x6128c7da, F=0x0 +0, 118, 118, 1, 182, 0x8a9a4a85, F=0x0 +0, 119, 119, 1, 267, 0x94e16d98, F=0x0 +0, 120, 120, 1, 191, 0x8cd6499b, F=0x0 +0, 121, 121, 1, 195, 0x2955524a, F=0x0 +0, 122, 122, 1, 755, 0x21115eef, F=0x0 +0, 123, 123, 1, 179, 0x89ff45fd, F=0x0 +0, 124, 124, 1, 522, 0x1b1cf19d, F=0x0 +0, 125, 125, 1, 171, 0x48034194, F=0x0 +0, 126, 126, 1, 379, 0x3914a793, F=0x0 +0, 127, 127, 1, 539, 0x7155fc34, F=0x0 +0, 128, 128, 1, 199, 0xb8674f8a, F=0x0 +0, 129, 129, 1, 458, 0x0a87ce97, F=0x0 +0, 130, 130, 1, 177, 0xac704b44, F=0x0 +0, 131, 131, 1, 299, 0x49318aa9, F=0x0 +0, 132, 132, 1, 333, 0xa188949d, F=0x0 +0, 133, 133, 1, 179, 0x2474436d, F=0x0 +0, 134, 134, 1, 263, 0x84e871f8, F=0x0 +0, 135, 135, 1, 177, 0xf7904597, F=0x0 +0, 136, 136, 1, 184, 0xb053486d, F=0x0 +0, 137, 137, 1, 433, 0xd3c0c5a1, F=0x0 +0, 138, 138, 1, 1138, 0xd5af3462, F=0x0 +0, 139, 139, 1, 863, 0xdce99f4f, F=0x0 +0, 140, 140, 1, 328, 0x608a949d, F=0x0 +0, 141, 141, 1, 222, 0xecb75a19, F=0x0 +0, 142, 142, 1, 171, 0x6cf94548, F=0x0 +0, 143, 143, 1, 222, 0xcf8d5778, F=0x0 +0, 144, 144, 1, 738, 0x9cf358a2, F=0x0 +0, 145, 145, 1, 444, 0x3798cdf6, F=0x0 +0, 146, 146, 1, 267, 0x8ca87717, F=0x0 +0, 147, 147, 1, 187, 0x4d734c80, F=0x0 +0, 148, 148, 1, 186, 0x9def4fb6, F=0x0 +0, 149, 149, 1, 196, 0x49214f94, F=0x0 +0, 150, 150, 1, 589, 0x02e2142f, F=0x0 +0, 151, 151, 1, 188, 0x285c4dad, F=0x0 +0, 152, 152, 1, 339, 0x8a0fa092, F=0x0 +0, 153, 153, 1, 179, 0x775543d4, F=0x0 +0, 154, 154, 1, 294, 0x0c8885eb, F=0x0 +0, 155, 155, 1, 291, 0xd78084b1, F=0x0 +0, 156, 156, 1, 144, 0xd7323963, F=0x0 +0, 157, 157, 1, 182, 0x97194ede, F=0x0 +0, 158, 158, 1, 195, 0x410f50a6, F=0x0 +0, 159, 159, 1, 268, 0xb878789a, F=0x0 +0, 160, 160, 1, 526, 0x6e13fb3e, F=0x0 +0, 161, 161, 1, 372, 0xf1fca999, F=0x0 +0, 162, 162, 1, 171, 0x86033fb7, F=0x0 +0, 163, 163, 1, 344, 0x8db8a374, F=0x0 +0, 164, 164, 1, 159, 0xa3463fb6, F=0x0 +0, 165, 165, 1, 391, 0x6ab1aa7d, F=0x0 +0, 166, 166, 1, 180, 0x55d04d01, F=0x0 +0, 167, 167, 1, 303, 0xac779365, F=0x0 +0, 168, 168, 1, 180, 0x54e94840, F=0x0 +0, 169, 169, 1, 269, 0x56e57720, F=0x0 +0, 170, 170, 1, 199, 0x510b5589, F=0x0 +0, 171, 171, 1, 287, 0xd70d8529, F=0x0 +0, 172, 172, 1, 217, 0x04a0649e, F=0x0 diff --git a/tests/ref/fate/gifenc-pal8 b/tests/ref/fate/gifenc-pal8 index 3ed39357f6d..1a074b8fd8b 100644 --- a/tests/ref/fate/gifenc-pal8 +++ b/tests/ref/fate/gifenc-pal8 @@ -4,175 +4,175 @@ #dimensions 0: 217x217 #sar 0: 0/1 0, 0, 0, 1, 2109, 0x39642b3d -0, 1, 1, 1, 305, 0xefa98bbd, F=0x0 -0, 2, 2, 1, 446, 0x9499cf43, F=0x0 -0, 3, 3, 1, 458, 0x8cb7d0d5, F=0x0 -0, 4, 4, 1, 555, 0x41f2fe63, F=0x0 -0, 5, 5, 1, 622, 0x3651212e, F=0x0 -0, 6, 6, 1, 650, 0x67542087, F=0x0 -0, 7, 7, 1, 668, 0x392934ca, F=0x0 -0, 8, 8, 1, 829, 0x6cd07a33, F=0x0 -0, 9, 9, 1, 1165, 0xb64b2ec9, F=0x0 -0, 10, 10, 1, 187, 0x114a52a4, F=0x0 -0, 11, 11, 1, 1341, 0x6ca57064, F=0x0 -0, 12, 12, 1, 1646, 0xcdd90fae, F=0x0 -0, 13, 13, 1, 1539, 0xbf75e55f, F=0x0 -0, 14, 14, 1, 1728, 0x96b14714, F=0x0 -0, 15, 15, 1, 1918, 0xffd08446, F=0x0 -0, 16, 16, 1, 2132, 0x3d5ae258, F=0x0 -0, 17, 17, 1, 2256, 0x359526d9, F=0x0 -0, 18, 18, 1, 2319, 0x553c382b, F=0x0 -0, 19, 19, 1, 2416, 0x7ac37066, F=0x0 -0, 20, 20, 1, 2609, 0x552bc27f, F=0x0 -0, 21, 21, 1, 2695, 0xbb0c020e, F=0x0 -0, 22, 22, 1, 2792, 0x46670f7d, F=0x0 -0, 23, 23, 1, 2892, 0x5674481e, F=0x0 -0, 24, 24, 1, 2990, 0x015a7bfd, F=0x0 -0, 25, 25, 1, 3109, 0xc73e9d14, F=0x0 -0, 26, 26, 1, 3261, 0x8a77f522, F=0x0 -0, 27, 27, 1, 3337, 0xf6f92558, F=0x0 -0, 28, 28, 1, 3580, 0x23408bc8, F=0x0 -0, 29, 29, 1, 3815, 0x781dfb1b, F=0x0 -0, 30, 30, 1, 2758, 0xf5cd1d5b, F=0x0 -0, 31, 31, 1, 4039, 0x7909712e, F=0x0 -0, 32, 32, 1, 3033, 0x39089d44, F=0x0 -0, 33, 33, 1, 4303, 0x667b0c60, F=0x0 -0, 34, 34, 1, 2052, 0x9edfba63, F=0x0 -0, 35, 35, 1, 3220, 0x5a56f015, F=0x0 -0, 36, 36, 1, 2300, 0x1719645c, F=0x0 -0, 37, 37, 1, 3641, 0x71c49a57, F=0x0 -0, 38, 38, 1, 3560, 0x377575b8, F=0x0 -0, 39, 39, 1, 3698, 0x165adce0, F=0x0 -0, 40, 40, 1, 1567, 0x6ecbf672, F=0x0 -0, 41, 41, 1, 962, 0x21eccba1, F=0x0 -0, 42, 42, 1, 281, 0x7a5a7ac3, F=0x0 -0, 43, 43, 1, 938, 0x659bb1ec, F=0x0 -0, 44, 44, 1, 279, 0x71e181bc, F=0x0 -0, 45, 45, 1, 204, 0xe0765316, F=0x0 -0, 46, 46, 1, 4307, 0xdbdd0e7d, F=0x0 -0, 47, 47, 1, 4903, 0xd8d24194, F=0x0 -0, 48, 48, 1, 4936, 0x6e9f1aff, F=0x0 -0, 49, 49, 1, 4949, 0xb3115902, F=0x0 -0, 50, 50, 1, 4162, 0xee2cad5b, F=0x0 -0, 51, 51, 1, 4686, 0xd583c178, F=0x0 -0, 52, 52, 1, 4749, 0x8c93b6a9, F=0x0 -0, 53, 53, 1, 4990, 0x12f957fd, F=0x0 -0, 54, 54, 1, 5187, 0xf3bcc7c9, F=0x0 -0, 55, 55, 1, 5054, 0xa27684fb, F=0x0 -0, 56, 56, 1, 5148, 0xe76cbad7, F=0x0 -0, 57, 57, 1, 4309, 0x79f7f067, F=0x0 -0, 58, 58, 1, 5087, 0xa2e29f29, F=0x0 -0, 59, 59, 1, 5292, 0xd158e4db, F=0x0 -0, 60, 60, 1, 5434, 0xe0be16f5, F=0x0 -0, 61, 61, 1, 4653, 0x0a3d8a5e, F=0x0 -0, 62, 62, 1, 5271, 0x4412d0a5, F=0x0 -0, 63, 63, 1, 5229, 0x3e06c4f2, F=0x0 -0, 64, 64, 1, 5225, 0x9bc39cfa, F=0x0 -0, 65, 65, 1, 5403, 0x798b009d, F=0x0 -0, 66, 66, 1, 5228, 0x14f2dded, F=0x0 -0, 67, 67, 1, 5712, 0x8724debe, F=0x0 -0, 68, 68, 1, 5644, 0x49d3a383, F=0x0 -0, 69, 69, 1, 5826, 0xde72e0ba, F=0x0 -0, 70, 70, 1, 5771, 0x62efd718, F=0x0 -0, 71, 71, 1, 6124, 0xb2a68c70, F=0x0 -0, 72, 72, 1, 6077, 0xb48b693f, F=0x0 -0, 73, 73, 1, 5804, 0xb700db6c, F=0x0 -0, 74, 74, 1, 6007, 0x02953898, F=0x0 -0, 75, 75, 1, 6228, 0x87a7b197, F=0x0 -0, 76, 76, 1, 6382, 0x49e7e65b, F=0x0 -0, 77, 77, 1, 6473, 0x3b9b6c3d, F=0x0 -0, 78, 78, 1, 7027, 0x2a4e1c17, F=0x0 -0, 79, 79, 1, 7263, 0x2e48c2e7, F=0x0 -0, 80, 80, 1, 8205, 0x013b701a, F=0x0 -0, 81, 81, 1, 8366, 0xcca97b10, F=0x0 -0, 82, 82, 1, 7716, 0x3b088fb3, F=0x0 -0, 83, 83, 1, 7420, 0xefdd1187, F=0x0 -0, 84, 84, 1, 7549, 0x1731227c, F=0x0 -0, 85, 85, 1, 7956, 0x8186ceb3, F=0x0 -0, 86, 86, 1, 8416, 0x23add53c, F=0x0 -0, 87, 87, 1, 8064, 0x09c616c4, F=0x0 -0, 88, 88, 1, 7409, 0x9d98af03, F=0x0 -0, 89, 89, 1, 7502, 0x0b81ebf3, F=0x0 -0, 90, 90, 1, 7814, 0x2f0d75cb, F=0x0 -0, 91, 91, 1, 7776, 0x45d6800f, F=0x0 -0, 92, 92, 1, 7757, 0x777f98b6, F=0x0 -0, 93, 93, 1, 8055, 0x4eea1634, F=0x0 -0, 94, 94, 1, 7626, 0xfb3931fd, F=0x0 -0, 95, 95, 1, 7987, 0x22a1d052, F=0x0 -0, 96, 96, 1, 12070, 0x3aa2924d, F=0x0 -0, 97, 97, 1, 12325, 0xda6cd811, F=0x0 -0, 98, 98, 1, 12225, 0xd478e671, F=0x0 -0, 99, 99, 1, 11235, 0xc6c09240, F=0x0 -0, 100, 100, 1, 11116, 0x95050c25, F=0x0 -0, 101, 101, 1, 11374, 0x14a68e3d, F=0x0 -0, 102, 102, 1, 11904, 0xb14436f2, F=0x0 -0, 103, 103, 1, 11487, 0xa3358311, F=0x0 -0, 104, 104, 1, 13403, 0xccf33a00, F=0x0 -0, 105, 105, 1, 12921, 0xbf7e4759, F=0x0 -0, 106, 106, 1, 13872, 0x7dace890, F=0x0 -0, 107, 107, 1, 13559, 0xb6c868f9, F=0x0 -0, 108, 108, 1, 14049, 0xa5d569b0, F=0x0 -0, 109, 109, 1, 14152, 0x8c9c31fb, F=0x0 -0, 110, 110, 1, 14285, 0x2ffe3bc3, F=0x0 -0, 111, 111, 1, 14432, 0x27abe196, F=0x0 -0, 112, 112, 1, 14697, 0x20d1aedd, F=0x0 -0, 113, 113, 1, 14606, 0xbcbe586d, F=0x0 -0, 114, 114, 1, 15221, 0x515f8224, F=0x0 -0, 115, 115, 1, 15433, 0x68089081, F=0x0 -0, 116, 116, 1, 15603, 0x8ff4e6fd, F=0x0 -0, 117, 117, 1, 15606, 0xc8527e30, F=0x0 -0, 118, 118, 1, 15871, 0x056ddca3, F=0x0 -0, 119, 119, 1, 15725, 0xc1871caf, F=0x0 -0, 120, 120, 1, 16086, 0x063e257b, F=0x0 -0, 121, 121, 1, 16233, 0xf683823b, F=0x0 -0, 122, 122, 1, 16143, 0x576df609, F=0x0 -0, 123, 123, 1, 16669, 0x3d02346b, F=0x0 -0, 124, 124, 1, 16627, 0xe35236cc, F=0x0 -0, 125, 125, 1, 16837, 0x389c996c, F=0x0 -0, 126, 126, 1, 16952, 0xc833af9f, F=0x0 -0, 127, 127, 1, 17127, 0xbf124531, F=0x0 -0, 128, 128, 1, 17158, 0x7abbfe59, F=0x0 -0, 129, 129, 1, 17329, 0x8102cc38, F=0x0 -0, 130, 130, 1, 17403, 0xa9468cad, F=0x0 -0, 131, 131, 1, 17674, 0x46d8038c, F=0x0 -0, 132, 132, 1, 17738, 0x5ee865ce, F=0x0 -0, 133, 133, 1, 17942, 0xc2449873, F=0x0 -0, 134, 134, 1, 17952, 0x3aafedbc, F=0x0 -0, 135, 135, 1, 18246, 0xdd930054, F=0x0 -0, 136, 136, 1, 18399, 0x68a59331, F=0x0 -0, 137, 137, 1, 18551, 0x1301c157, F=0x0 -0, 138, 138, 1, 18947, 0xbed8a9b4, F=0x0 -0, 139, 139, 1, 19153, 0xe338fea2, F=0x0 -0, 140, 140, 1, 19128, 0xbf05820e, F=0x0 -0, 141, 141, 1, 19138, 0x73b5cd49, F=0x0 -0, 142, 142, 1, 19502, 0xd8d68603, F=0x0 -0, 143, 143, 1, 19542, 0x84b47563, F=0x0 -0, 144, 144, 1, 19755, 0x7194b230, F=0x0 -0, 145, 145, 1, 20122, 0x83633f5e, F=0x0 -0, 146, 146, 1, 20265, 0x7365b06b, F=0x0 -0, 147, 147, 1, 20378, 0x1aaf1a98, F=0x0 -0, 148, 148, 1, 20300, 0x1da6d336, F=0x0 -0, 149, 149, 1, 20499, 0x72d54d30, F=0x0 -0, 150, 150, 1, 20655, 0x6996b124, F=0x0 -0, 151, 151, 1, 20674, 0xa883abd2, F=0x0 -0, 152, 152, 1, 21015, 0x96cf4018, F=0x0 -0, 153, 153, 1, 21066, 0x307e4479, F=0x0 -0, 154, 154, 1, 21161, 0xd45a2c38, F=0x0 -0, 155, 155, 1, 21086, 0xcf0e597d, F=0x0 -0, 156, 156, 1, 21466, 0xcc4032fe, F=0x0 -0, 157, 157, 1, 21677, 0x755ccb7b, F=0x0 -0, 158, 158, 1, 21589, 0x5d74fb47, F=0x0 -0, 159, 159, 1, 21662, 0x0c459189, F=0x0 -0, 160, 160, 1, 21995, 0x43d46eb3, F=0x0 -0, 161, 161, 1, 22213, 0x68455c92, F=0x0 -0, 162, 162, 1, 22483, 0xdc83c15a, F=0x0 -0, 163, 163, 1, 22498, 0xfae1251e, F=0x0 -0, 164, 164, 1, 22468, 0x7944b1d4, F=0x0 -0, 165, 165, 1, 22869, 0xff8c10ac, F=0x0 -0, 166, 166, 1, 22754, 0xd8183b84, F=0x0 -0, 167, 167, 1, 23173, 0x6c3c48d5, F=0x0 -0, 168, 168, 1, 23281, 0xc32ca477, F=0x0 -0, 169, 169, 1, 23219, 0x4dc26193, F=0x0 -0, 170, 170, 1, 23656, 0xb85ee15f, F=0x0 -0, 171, 171, 1, 23683, 0x26ba4915, F=0x0 -0, 172, 172, 1, 23882, 0xf57285de, F=0x0 +0, 1, 1, 1, 236, 0x332769fd, F=0x0 +0, 2, 2, 1, 186, 0x770d5061, F=0x0 +0, 3, 3, 1, 208, 0x55784c8f, F=0x0 +0, 4, 4, 1, 282, 0x98e8825d, F=0x0 +0, 5, 5, 1, 209, 0x4cc15280, F=0x0 +0, 6, 6, 1, 225, 0xf8785d6c, F=0x0 +0, 7, 7, 1, 204, 0x322754b4, F=0x0 +0, 8, 8, 1, 181, 0x9f4f4d10, F=0x0 +0, 9, 9, 1, 200, 0x64a453a1, F=0x0 +0, 10, 10, 1, 184, 0x79c344d3, F=0x0 +0, 11, 11, 1, 191, 0x7097487b, F=0x0 +0, 12, 12, 1, 290, 0x1f998186, F=0x0 +0, 13, 13, 1, 153, 0xfe4c3a7c, F=0x0 +0, 14, 14, 1, 175, 0xfdf442e0, F=0x0 +0, 15, 15, 1, 187, 0x393845f2, F=0x0 +0, 16, 16, 1, 418, 0xe94bc757, F=0x0 +0, 17, 17, 1, 200, 0x8dab52be, F=0x0 +0, 18, 18, 1, 347, 0xae379838, F=0x0 +0, 19, 19, 1, 176, 0x868742d2, F=0x0 +0, 20, 20, 1, 294, 0xb1fb8365, F=0x0 +0, 21, 21, 1, 166, 0x06e44260, F=0x0 +0, 22, 22, 1, 306, 0x29e983b7, F=0x0 +0, 23, 23, 1, 180, 0xfa4a44eb, F=0x0 +0, 24, 24, 1, 207, 0x99de52c8, F=0x0 +0, 25, 25, 1, 204, 0x73944c35, F=0x0 +0, 26, 26, 1, 259, 0xa0637375, F=0x0 +0, 27, 27, 1, 181, 0x130f4b9a, F=0x0 +0, 28, 28, 1, 152, 0x4be93cae, F=0x0 +0, 29, 29, 1, 194, 0x81e34e1d, F=0x0 +0, 30, 30, 1, 159, 0x5913380b, F=0x0 +0, 31, 31, 1, 162, 0xf0683bb1, F=0x0 +0, 32, 32, 1, 178, 0xce32498c, F=0x0 +0, 33, 33, 1, 187, 0x5d61509f, F=0x0 +0, 34, 34, 1, 336, 0xe5569440, F=0x0 +0, 35, 35, 1, 184, 0x3fa349ea, F=0x0 +0, 36, 36, 1, 227, 0x7cad5f66, F=0x0 +0, 37, 37, 1, 182, 0xf23a4522, F=0x0 +0, 38, 38, 1, 162, 0x2a053dcc, F=0x0 +0, 39, 39, 1, 187, 0xc18a4686, F=0x0 +0, 40, 40, 1, 503, 0xa5a7e669, F=0x0 +0, 41, 41, 1, 167, 0xa28a43e8, F=0x0 +0, 42, 42, 1, 289, 0xf1968090, F=0x0 +0, 43, 43, 1, 194, 0xedbb4eb7, F=0x0 +0, 44, 44, 1, 285, 0x82e7818b, F=0x0 +0, 45, 45, 1, 199, 0x5e725190, F=0x0 +0, 46, 46, 1, 197, 0x0c6a4fb9, F=0x0 +0, 47, 47, 1, 203, 0x05684b96, F=0x0 +0, 48, 48, 1, 208, 0xd33457ad, F=0x0 +0, 49, 49, 1, 1198, 0x029f5f1b, F=0x0 +0, 50, 50, 1, 175, 0xf4e94c40, F=0x0 +0, 51, 51, 1, 740, 0x6e096787, F=0x0 +0, 52, 52, 1, 180, 0xf34f45be, F=0x0 +0, 53, 53, 1, 238, 0xfbed6adb, F=0x0 +0, 54, 54, 1, 198, 0x6f3a5344, F=0x0 +0, 55, 55, 1, 196, 0x8bbb4b02, F=0x0 +0, 56, 56, 1, 224, 0x92c55d92, F=0x0 +0, 57, 57, 1, 765, 0xd16e6d65, F=0x0 +0, 58, 58, 1, 149, 0x97aa38d2, F=0x0 +0, 59, 59, 1, 479, 0x1030d73b, F=0x0 +0, 60, 60, 1, 179, 0x441e493a, F=0x0 +0, 61, 61, 1, 217, 0x16c259b7, F=0x0 +0, 62, 62, 1, 190, 0x5b2349ca, F=0x0 +0, 63, 63, 1, 155, 0x422f39ff, F=0x0 +0, 64, 64, 1, 167, 0x28444898, F=0x0 +0, 65, 65, 1, 649, 0x57b331e2, F=0x0 +0, 66, 66, 1, 196, 0x99a1574f, F=0x0 +0, 67, 67, 1, 427, 0xfd45d548, F=0x0 +0, 68, 68, 1, 175, 0x90cc42ce, F=0x0 +0, 69, 69, 1, 284, 0x49398208, F=0x0 +0, 70, 70, 1, 131, 0x18e42fc2, F=0x0 +0, 71, 71, 1, 256, 0xac896bbe, F=0x0 +0, 72, 72, 1, 186, 0x522d4974, F=0x0 +0, 73, 73, 1, 190, 0x5e064e04, F=0x0 +0, 74, 74, 1, 216, 0x863f53fa, F=0x0 +0, 75, 75, 1, 154, 0x17f1383f, F=0x0 +0, 76, 76, 1, 113, 0x20f827ee, F=0x0 +0, 77, 77, 1, 402, 0xce2caf45, F=0x0 +0, 78, 78, 1, 198, 0x0bc851ae, F=0x0 +0, 79, 79, 1, 466, 0xb31ad387, F=0x0 +0, 80, 80, 1, 322, 0x20018e02, F=0x0 +0, 81, 81, 1, 387, 0x5038b1b8, F=0x0 +0, 82, 82, 1, 158, 0xc6ac3feb, F=0x0 +0, 83, 83, 1, 278, 0x59f17c03, F=0x0 +0, 84, 84, 1, 190, 0xd7665022, F=0x0 +0, 85, 85, 1, 175, 0x1a6e4225, F=0x0 +0, 86, 86, 1, 206, 0xc3f44e3a, F=0x0 +0, 87, 87, 1, 379, 0xb1e6b77e, F=0x0 +0, 88, 88, 1, 202, 0x17975145, F=0x0 +0, 89, 89, 1, 225, 0x96985fd2, F=0x0 +0, 90, 90, 1, 175, 0x71b1497a, F=0x0 +0, 91, 91, 1, 235, 0x2bdc5faa, F=0x0 +0, 92, 92, 1, 185, 0x68124958, F=0x0 +0, 93, 93, 1, 182, 0x120c4c63, F=0x0 +0, 94, 94, 1, 114, 0xdd9b2b02, F=0x0 +0, 95, 95, 1, 176, 0x0d5b4b46, F=0x0 +0, 96, 96, 1, 214, 0xfb5e5b47, F=0x0 +0, 97, 97, 1, 182, 0xf4c54860, F=0x0 +0, 98, 98, 1, 195, 0x6e075188, F=0x0 +0, 99, 99, 1, 303, 0x4c158465, F=0x0 +0, 100, 100, 1, 191, 0x12005055, F=0x0 +0, 101, 101, 1, 177, 0x01ed4929, F=0x0 +0, 102, 102, 1, 172, 0x86984280, F=0x0 +0, 103, 103, 1, 179, 0xb854481c, F=0x0 +0, 104, 104, 1, 188, 0xeacd47ed, F=0x0 +0, 105, 105, 1, 177, 0x36be4889, F=0x0 +0, 106, 106, 1, 215, 0x6aa65c99, F=0x0 +0, 107, 107, 1, 339, 0x1af496b2, F=0x0 +0, 108, 108, 1, 189, 0x941045fa, F=0x0 +0, 109, 109, 1, 209, 0x37445651, F=0x0 +0, 110, 110, 1, 205, 0x9cf85222, F=0x0 +0, 111, 111, 1, 185, 0x8b964cb5, F=0x0 +0, 112, 112, 1, 211, 0xd03d59ce, F=0x0 +0, 113, 113, 1, 178, 0xe40e4c08, F=0x0 +0, 114, 114, 1, 195, 0x971d4d92, F=0x0 +0, 115, 115, 1, 116, 0x5f9a2d3f, F=0x0 +0, 116, 116, 1, 116, 0x5c4e2d06, F=0x0 +0, 117, 117, 1, 427, 0x6128c7da, F=0x0 +0, 118, 118, 1, 182, 0x8a9a4a85, F=0x0 +0, 119, 119, 1, 267, 0x94e16d98, F=0x0 +0, 120, 120, 1, 191, 0x8cd6499b, F=0x0 +0, 121, 121, 1, 195, 0x2955524a, F=0x0 +0, 122, 122, 1, 755, 0x21115eef, F=0x0 +0, 123, 123, 1, 179, 0x89ff45fd, F=0x0 +0, 124, 124, 1, 522, 0x1b1cf19d, F=0x0 +0, 125, 125, 1, 171, 0x48034194, F=0x0 +0, 126, 126, 1, 379, 0x3914a793, F=0x0 +0, 127, 127, 1, 539, 0x7155fc34, F=0x0 +0, 128, 128, 1, 199, 0xb8674f8a, F=0x0 +0, 129, 129, 1, 458, 0x0a87ce97, F=0x0 +0, 130, 130, 1, 177, 0xac704b44, F=0x0 +0, 131, 131, 1, 299, 0x49318aa9, F=0x0 +0, 132, 132, 1, 333, 0xa188949d, F=0x0 +0, 133, 133, 1, 179, 0x2474436d, F=0x0 +0, 134, 134, 1, 263, 0x84e871f8, F=0x0 +0, 135, 135, 1, 177, 0xf7904597, F=0x0 +0, 136, 136, 1, 184, 0xb053486d, F=0x0 +0, 137, 137, 1, 433, 0xd3c0c5a1, F=0x0 +0, 138, 138, 1, 1138, 0xd5af3462, F=0x0 +0, 139, 139, 1, 863, 0xdce99f4f, F=0x0 +0, 140, 140, 1, 328, 0x608a949d, F=0x0 +0, 141, 141, 1, 222, 0xecb75a19, F=0x0 +0, 142, 142, 1, 171, 0x6cf94548, F=0x0 +0, 143, 143, 1, 222, 0xcf8d5778, F=0x0 +0, 144, 144, 1, 738, 0x9cf358a2, F=0x0 +0, 145, 145, 1, 444, 0x3798cdf6, F=0x0 +0, 146, 146, 1, 267, 0x8ca87717, F=0x0 +0, 147, 147, 1, 187, 0x4d734c80, F=0x0 +0, 148, 148, 1, 186, 0x9def4fb6, F=0x0 +0, 149, 149, 1, 196, 0x49214f94, F=0x0 +0, 150, 150, 1, 589, 0x02e2142f, F=0x0 +0, 151, 151, 1, 188, 0x285c4dad, F=0x0 +0, 152, 152, 1, 339, 0x8a0fa092, F=0x0 +0, 153, 153, 1, 179, 0x775543d4, F=0x0 +0, 154, 154, 1, 294, 0x0c8885eb, F=0x0 +0, 155, 155, 1, 291, 0xd78084b1, F=0x0 +0, 156, 156, 1, 144, 0xd7323963, F=0x0 +0, 157, 157, 1, 182, 0x97194ede, F=0x0 +0, 158, 158, 1, 195, 0x410f50a6, F=0x0 +0, 159, 159, 1, 268, 0xb878789a, F=0x0 +0, 160, 160, 1, 526, 0x6e13fb3e, F=0x0 +0, 161, 161, 1, 372, 0xf1fca999, F=0x0 +0, 162, 162, 1, 171, 0x86033fb7, F=0x0 +0, 163, 163, 1, 344, 0x8db8a374, F=0x0 +0, 164, 164, 1, 159, 0xa3463fb6, F=0x0 +0, 165, 165, 1, 391, 0x6ab1aa7d, F=0x0 +0, 166, 166, 1, 180, 0x55d04d01, F=0x0 +0, 167, 167, 1, 303, 0xac779365, F=0x0 +0, 168, 168, 1, 180, 0x54e94840, F=0x0 +0, 169, 169, 1, 269, 0x56e57720, F=0x0 +0, 170, 170, 1, 199, 0x510b5589, F=0x0 +0, 171, 171, 1, 287, 0xd70d8529, F=0x0 +0, 172, 172, 1, 217, 0x04a0649e, F=0x0 diff --git a/tests/ref/fate/gifenc-rgb4_byte b/tests/ref/fate/gifenc-rgb4_byte index c99c983f87b..25e9fc7dcf2 100644 --- a/tests/ref/fate/gifenc-rgb4_byte +++ b/tests/ref/fate/gifenc-rgb4_byte @@ -4,175 +4,175 @@ #dimensions 0: 217x217 #sar 0: 0/1 0, 0, 0, 1, 1297, 0x5618fe71 -0, 1, 1, 1, 221, 0x230c4e66, F=0x0 -0, 2, 2, 1, 139, 0xf87a2b65, F=0x0 -0, 3, 3, 1, 392, 0x7794a5e6, F=0x0 -0, 4, 4, 1, 389, 0xf856a3f1, F=0x0 -0, 5, 5, 1, 438, 0x0f95b4fb, F=0x0 -0, 6, 6, 1, 526, 0x7885d860, F=0x0 -0, 7, 7, 1, 543, 0x859fe7df, F=0x0 -0, 8, 8, 1, 446, 0xb34dba74, F=0x0 -0, 9, 9, 1, 931, 0x1c2fa48d, F=0x0 -0, 10, 10, 1, 702, 0x1ce74b4d, F=0x0 -0, 11, 11, 1, 1202, 0x2f232db8, F=0x0 -0, 12, 12, 1, 1299, 0x904e56af, F=0x0 -0, 13, 13, 1, 1253, 0x20803653, F=0x0 -0, 14, 14, 1, 1338, 0x24e052f6, F=0x0 -0, 15, 15, 1, 1284, 0x590a3a42, F=0x0 -0, 16, 16, 1, 1483, 0x93e09883, F=0x0 -0, 17, 17, 1, 1792, 0x70984bdf, F=0x0 -0, 18, 18, 1, 1683, 0x23b9fc30, F=0x0 -0, 19, 19, 1, 1517, 0x0fc1acf6, F=0x0 -0, 20, 20, 1, 1713, 0x6b4c1861, F=0x0 -0, 21, 21, 1, 1753, 0x896b20c1, F=0x0 -0, 22, 22, 1, 1650, 0x2f26027f, F=0x0 -0, 23, 23, 1, 1726, 0x9f731df0, F=0x0 -0, 24, 24, 1, 1908, 0xebd67b41, F=0x0 -0, 25, 25, 1, 1815, 0xf2685278, F=0x0 -0, 26, 26, 1, 1923, 0xbe18821e, F=0x0 -0, 27, 27, 1, 2108, 0x1318d306, F=0x0 -0, 28, 28, 1, 2708, 0x5e28f23c, F=0x0 -0, 29, 29, 1, 2681, 0xb96da855, F=0x0 -0, 30, 30, 1, 2903, 0x61cf4983, F=0x0 -0, 31, 31, 1, 3265, 0x95a1d25d, F=0x0 -0, 32, 32, 1, 3187, 0x6a67c299, F=0x0 -0, 33, 33, 1, 3304, 0x1c5ce63e, F=0x0 -0, 34, 34, 1, 3608, 0x2ed2770f, F=0x0 -0, 35, 35, 1, 3707, 0xa075a17b, F=0x0 -0, 36, 36, 1, 3822, 0xc842d6b6, F=0x0 -0, 37, 37, 1, 3635, 0x814680c7, F=0x0 -0, 38, 38, 1, 2958, 0x64de62cf, F=0x0 -0, 39, 39, 1, 3094, 0x2c338e84, F=0x0 -0, 40, 40, 1, 3102, 0xec5e8757, F=0x0 -0, 41, 41, 1, 3464, 0xea4f2bce, F=0x0 -0, 42, 42, 1, 4116, 0xded05ff3, F=0x0 -0, 43, 43, 1, 4225, 0x21825a2c, F=0x0 -0, 44, 44, 1, 3621, 0xa3125130, F=0x0 -0, 45, 45, 1, 3918, 0x35e0e876, F=0x0 -0, 46, 46, 1, 4469, 0xa408e1f7, F=0x0 -0, 47, 47, 1, 4601, 0x6f7d3081, F=0x0 -0, 48, 48, 1, 4830, 0x023e9cab, F=0x0 -0, 49, 49, 1, 5406, 0xf67ac12c, F=0x0 -0, 50, 50, 1, 5274, 0xf14e9768, F=0x0 -0, 51, 51, 1, 5424, 0x2af8e2a7, F=0x0 -0, 52, 52, 1, 5527, 0x80f70710, F=0x0 -0, 53, 53, 1, 5709, 0x89975c2f, F=0x0 -0, 54, 54, 1, 6168, 0xf5a542af, F=0x0 -0, 55, 55, 1, 6241, 0x091651ad, F=0x0 -0, 56, 56, 1, 5919, 0x32a7c5a2, F=0x0 -0, 57, 57, 1, 6005, 0x88adf6be, F=0x0 -0, 58, 58, 1, 5954, 0x3f92f18d, F=0x0 -0, 59, 59, 1, 6476, 0x9d90d099, F=0x0 -0, 60, 60, 1, 6745, 0xdf4f3cb1, F=0x0 -0, 61, 61, 1, 6283, 0x6e7e8f63, F=0x0 -0, 62, 62, 1, 6649, 0x5c1f366c, F=0x0 -0, 63, 63, 1, 6386, 0xa09389c3, F=0x0 -0, 64, 64, 1, 6265, 0xe5b85888, F=0x0 -0, 65, 65, 1, 6916, 0x445e734f, F=0x0 -0, 66, 66, 1, 7238, 0x30a52626, F=0x0 -0, 67, 67, 1, 7564, 0x1ddaa7cc, F=0x0 -0, 68, 68, 1, 7421, 0x118e7850, F=0x0 -0, 69, 69, 1, 7484, 0x5459acd8, F=0x0 -0, 70, 70, 1, 7604, 0x66f9e6a5, F=0x0 -0, 71, 71, 1, 7764, 0x862c0630, F=0x0 -0, 72, 72, 1, 8023, 0x06e16db8, F=0x0 -0, 73, 73, 1, 8136, 0xce28c084, F=0x0 -0, 74, 74, 1, 8109, 0x61c4aef6, F=0x0 -0, 75, 75, 1, 7871, 0x631d4073, F=0x0 -0, 76, 76, 1, 7968, 0x3b7692f0, F=0x0 -0, 77, 77, 1, 8246, 0x531bebc2, F=0x0 -0, 78, 78, 1, 8329, 0xe1fd48c9, F=0x0 -0, 79, 79, 1, 8570, 0x996fc156, F=0x0 -0, 80, 80, 1, 8754, 0x99fb0c5a, F=0x0 -0, 81, 81, 1, 8586, 0xf3eba620, F=0x0 -0, 82, 82, 1, 8886, 0xa08258a9, F=0x0 -0, 83, 83, 1, 9085, 0x229359d3, F=0x0 -0, 84, 84, 1, 9318, 0x3baaf9b9, F=0x0 -0, 85, 85, 1, 9402, 0xe27a4804, F=0x0 -0, 86, 86, 1, 9169, 0x23fd9a49, F=0x0 -0, 87, 87, 1, 9470, 0xe389379c, F=0x0 -0, 88, 88, 1, 9658, 0x6a409434, F=0x0 -0, 89, 89, 1, 9709, 0x42969ccd, F=0x0 -0, 90, 90, 1, 9531, 0x473139f3, F=0x0 -0, 91, 91, 1, 9899, 0xb85e0cd0, F=0x0 -0, 92, 92, 1, 10013, 0xda4d4db8, F=0x0 -0, 93, 93, 1, 10046, 0x0cc12baa, F=0x0 -0, 94, 94, 1, 10094, 0x22b6451c, F=0x0 -0, 95, 95, 1, 10446, 0x03a604aa, F=0x0 -0, 96, 96, 1, 10591, 0x3c8d599a, F=0x0 -0, 97, 97, 1, 10589, 0x3936826f, F=0x0 -0, 98, 98, 1, 10815, 0xb795dc11, F=0x0 -0, 99, 99, 1, 11119, 0x506c5187, F=0x0 -0, 100, 100, 1, 11202, 0x92856baf, F=0x0 -0, 101, 101, 1, 11248, 0xdf29a1db, F=0x0 -0, 102, 102, 1, 11491, 0x656df632, F=0x0 -0, 103, 103, 1, 11688, 0xeb175aa9, F=0x0 -0, 104, 104, 1, 11793, 0x99ccb713, F=0x0 -0, 105, 105, 1, 11444, 0x602c0c0d, F=0x0 -0, 106, 106, 1, 11936, 0xe89cc9bd, F=0x0 -0, 107, 107, 1, 11940, 0xc668bf2f, F=0x0 -0, 108, 108, 1, 12289, 0x9bd5780d, F=0x0 -0, 109, 109, 1, 12342, 0xd9c680fb, F=0x0 -0, 110, 110, 1, 12460, 0xb539b50f, F=0x0 -0, 111, 111, 1, 12703, 0x07473fac, F=0x0 -0, 112, 112, 1, 12676, 0x743052ae, F=0x0 -0, 113, 113, 1, 12965, 0xf6aaa045, F=0x0 -0, 114, 114, 1, 13062, 0xea13e513, F=0x0 -0, 115, 115, 1, 13155, 0x82312fa0, F=0x0 -0, 116, 116, 1, 13179, 0xd59c1dda, F=0x0 -0, 117, 117, 1, 13206, 0xde825850, F=0x0 -0, 118, 118, 1, 13219, 0x0b375f69, F=0x0 -0, 119, 119, 1, 13218, 0x14036301, F=0x0 -0, 120, 120, 1, 13475, 0x259da599, F=0x0 -0, 121, 121, 1, 13673, 0x9b43dd2a, F=0x0 -0, 122, 122, 1, 13700, 0x190bebf1, F=0x0 -0, 123, 123, 1, 13829, 0x9f802c28, F=0x0 -0, 124, 124, 1, 13954, 0x5236926c, F=0x0 -0, 125, 125, 1, 14071, 0xf5c3d36a, F=0x0 -0, 126, 126, 1, 14132, 0xbf35cdb9, F=0x0 -0, 127, 127, 1, 14339, 0x9cf65120, F=0x0 -0, 128, 128, 1, 14477, 0x3a9eabb7, F=0x0 -0, 129, 129, 1, 14544, 0xed09a2d3, F=0x0 -0, 130, 130, 1, 14616, 0xa138d2a0, F=0x0 -0, 131, 131, 1, 14906, 0x13bd28eb, F=0x0 -0, 132, 132, 1, 14986, 0x5454a127, F=0x0 -0, 133, 133, 1, 15150, 0x585cdb9b, F=0x0 -0, 134, 134, 1, 15137, 0x191bbaae, F=0x0 -0, 135, 135, 1, 15251, 0xa8461431, F=0x0 -0, 136, 136, 1, 15345, 0x2015670e, F=0x0 -0, 137, 137, 1, 15646, 0x772da02d, F=0x0 -0, 138, 138, 1, 15920, 0xfabc27f3, F=0x0 -0, 139, 139, 1, 16049, 0x2a66498f, F=0x0 -0, 140, 140, 1, 16236, 0x29750328, F=0x0 -0, 141, 141, 1, 16270, 0x4528e71c, F=0x0 -0, 142, 142, 1, 16379, 0x9a450fab, F=0x0 -0, 143, 143, 1, 16669, 0x31ffaac8, F=0x0 -0, 144, 144, 1, 16925, 0x6c4318ac, F=0x0 -0, 145, 145, 1, 17157, 0xf4b1cf0c, F=0x0 -0, 146, 146, 1, 17180, 0xd1bab36c, F=0x0 -0, 147, 147, 1, 17323, 0x419eaee3, F=0x0 -0, 148, 148, 1, 17405, 0xdf3f16ee, F=0x0 -0, 149, 149, 1, 17439, 0x7153f9f8, F=0x0 -0, 150, 150, 1, 17584, 0x1612557d, F=0x0 -0, 151, 151, 1, 17772, 0xffa78d73, F=0x0 -0, 152, 152, 1, 17834, 0x3e33866a, F=0x0 -0, 153, 153, 1, 17926, 0xdfc39bcd, F=0x0 -0, 154, 154, 1, 17831, 0x5f8ebc12, F=0x0 -0, 155, 155, 1, 18150, 0xc25e1f7f, F=0x0 -0, 156, 156, 1, 18265, 0x9b688807, F=0x0 -0, 157, 157, 1, 18345, 0xdc6abd59, F=0x0 -0, 158, 158, 1, 18301, 0x14d43a0e, F=0x0 -0, 159, 159, 1, 18426, 0x3b2cbcde, F=0x0 -0, 160, 160, 1, 18615, 0x6fa5492e, F=0x0 -0, 161, 161, 1, 18924, 0xa370be9b, F=0x0 -0, 162, 162, 1, 19081, 0xe019041b, F=0x0 -0, 163, 163, 1, 19176, 0x1acc040f, F=0x0 -0, 164, 164, 1, 19218, 0x27080108, F=0x0 -0, 165, 165, 1, 19406, 0xae676585, F=0x0 -0, 166, 166, 1, 19488, 0x9619a232, F=0x0 -0, 167, 167, 1, 19667, 0x88e71365, F=0x0 -0, 168, 168, 1, 19680, 0xd20d1a77, F=0x0 -0, 169, 169, 1, 19944, 0xc3d0647e, F=0x0 -0, 170, 170, 1, 19983, 0xbca79c59, F=0x0 -0, 171, 171, 1, 20029, 0x2c238ced, F=0x0 -0, 172, 172, 1, 20068, 0xaad778bc, F=0x0 +0, 1, 1, 1, 158, 0xfa673468, F=0x0 +0, 2, 2, 1, 143, 0x61cf2b35, F=0x0 +0, 3, 3, 1, 169, 0x152a369a, F=0x0 +0, 4, 4, 1, 254, 0x22935c08, F=0x0 +0, 5, 5, 1, 221, 0x9972496f, F=0x0 +0, 6, 6, 1, 176, 0xf6af3ab2, F=0x0 +0, 7, 7, 1, 189, 0xcdc23f20, F=0x0 +0, 8, 8, 1, 139, 0x081e3020, F=0x0 +0, 9, 9, 1, 160, 0x9153335f, F=0x0 +0, 10, 10, 1, 149, 0x8fa12e7d, F=0x0 +0, 11, 11, 1, 190, 0x43ef3de1, F=0x0 +0, 12, 12, 1, 308, 0x95c77160, F=0x0 +0, 13, 13, 1, 193, 0xf9084196, F=0x0 +0, 14, 14, 1, 191, 0x07784447, F=0x0 +0, 15, 15, 1, 198, 0x7d72420a, F=0x0 +0, 16, 16, 1, 417, 0xbf40acdc, F=0x0 +0, 17, 17, 1, 163, 0xf7df3842, F=0x0 +0, 18, 18, 1, 383, 0xadcf9e8e, F=0x0 +0, 19, 19, 1, 193, 0xba544052, F=0x0 +0, 20, 20, 1, 337, 0x1510922f, F=0x0 +0, 21, 21, 1, 199, 0x11d64936, F=0x0 +0, 22, 22, 1, 308, 0x79597dbc, F=0x0 +0, 23, 23, 1, 186, 0x25d04175, F=0x0 +0, 24, 24, 1, 199, 0x8b65402f, F=0x0 +0, 25, 25, 1, 163, 0x128e38b6, F=0x0 +0, 26, 26, 1, 302, 0xacc979b2, F=0x0 +0, 27, 27, 1, 189, 0x491f3c5c, F=0x0 +0, 28, 28, 1, 157, 0x50783600, F=0x0 +0, 29, 29, 1, 205, 0xad9045b3, F=0x0 +0, 30, 30, 1, 160, 0xcbf332f8, F=0x0 +0, 31, 31, 1, 202, 0xc72c48bf, F=0x0 +0, 32, 32, 1, 160, 0xe6c436dd, F=0x0 +0, 33, 33, 1, 215, 0x4f705072, F=0x0 +0, 34, 34, 1, 422, 0xed27b0b0, F=0x0 +0, 35, 35, 1, 184, 0x6ba8415a, F=0x0 +0, 36, 36, 1, 289, 0x0b5f73a3, F=0x0 +0, 37, 37, 1, 190, 0xea5b4316, F=0x0 +0, 38, 38, 1, 195, 0xd9f2441d, F=0x0 +0, 39, 39, 1, 199, 0x47e34e05, F=0x0 +0, 40, 40, 1, 448, 0xec8bbf56, F=0x0 +0, 41, 41, 1, 170, 0x42fb3755, F=0x0 +0, 42, 42, 1, 279, 0x87cc6f54, F=0x0 +0, 43, 43, 1, 180, 0x085f398a, F=0x0 +0, 44, 44, 1, 303, 0x46fe7c55, F=0x0 +0, 45, 45, 1, 209, 0xc60b5116, F=0x0 +0, 46, 46, 1, 198, 0x795f4655, F=0x0 +0, 47, 47, 1, 194, 0xeb1b4abe, F=0x0 +0, 48, 48, 1, 186, 0x97b44251, F=0x0 +0, 49, 49, 1, 1200, 0xeb5a36ba, F=0x0 +0, 50, 50, 1, 204, 0x00bc4594, F=0x0 +0, 51, 51, 1, 1066, 0xdc39eee0, F=0x0 +0, 52, 52, 1, 187, 0x344b4304, F=0x0 +0, 53, 53, 1, 323, 0x899f8522, F=0x0 +0, 54, 54, 1, 205, 0x48af49da, F=0x0 +0, 55, 55, 1, 213, 0xf2534ff5, F=0x0 +0, 56, 56, 1, 208, 0x8fba4b25, F=0x0 +0, 57, 57, 1, 737, 0x1612477c, F=0x0 +0, 58, 58, 1, 181, 0x903b38ad, F=0x0 +0, 59, 59, 1, 614, 0xb154157d, F=0x0 +0, 60, 60, 1, 215, 0x4e82532e, F=0x0 +0, 61, 61, 1, 291, 0x69476efd, F=0x0 +0, 62, 62, 1, 208, 0x471d4ea1, F=0x0 +0, 63, 63, 1, 208, 0xa2b04628, F=0x0 +0, 64, 64, 1, 203, 0x96d646b2, F=0x0 +0, 65, 65, 1, 531, 0xd085ea85, F=0x0 +0, 66, 66, 1, 178, 0x51663ad1, F=0x0 +0, 67, 67, 1, 446, 0x00adbe2d, F=0x0 +0, 68, 68, 1, 188, 0x7ccb3dda, F=0x0 +0, 69, 69, 1, 177, 0xd881441a, F=0x0 +0, 70, 70, 1, 112, 0x44cc2135, F=0x0 +0, 71, 71, 1, 296, 0x717d7172, F=0x0 +0, 72, 72, 1, 153, 0xbd403424, F=0x0 +0, 73, 73, 1, 218, 0x1d9a4f26, F=0x0 +0, 74, 74, 1, 202, 0x6ee647eb, F=0x0 +0, 75, 75, 1, 239, 0x71245ad2, F=0x0 +0, 76, 76, 1, 139, 0xbbdd2d23, F=0x0 +0, 77, 77, 1, 467, 0x6e1cc838, F=0x0 +0, 78, 78, 1, 274, 0xea1079c5, F=0x0 +0, 79, 79, 1, 404, 0xbf5caaa0, F=0x0 +0, 80, 80, 1, 257, 0xac4865e5, F=0x0 +0, 81, 81, 1, 419, 0xa45ab5fc, F=0x0 +0, 82, 82, 1, 208, 0xb5dd4fc3, F=0x0 +0, 83, 83, 1, 294, 0xcf5176ee, F=0x0 +0, 84, 84, 1, 191, 0xed7e3e98, F=0x0 +0, 85, 85, 1, 181, 0x9105450e, F=0x0 +0, 86, 86, 1, 218, 0x445c54ae, F=0x0 +0, 87, 87, 1, 423, 0x5dc9bec5, F=0x0 +0, 88, 88, 1, 202, 0xdfde4a35, F=0x0 +0, 89, 89, 1, 317, 0x9a918033, F=0x0 +0, 90, 90, 1, 172, 0x081e3d8c, F=0x0 +0, 91, 91, 1, 275, 0x37536b50, F=0x0 +0, 92, 92, 1, 214, 0xecfc4e8f, F=0x0 +0, 93, 93, 1, 182, 0x85bf44e5, F=0x0 +0, 94, 94, 1, 151, 0x4c8230ae, F=0x0 +0, 95, 95, 1, 157, 0xa02a32eb, F=0x0 +0, 96, 96, 1, 202, 0xb24a4355, F=0x0 +0, 97, 97, 1, 162, 0x632d3576, F=0x0 +0, 98, 98, 1, 186, 0x6f9c407c, F=0x0 +0, 99, 99, 1, 318, 0xade078ca, F=0x0 +0, 100, 100, 1, 225, 0xd2754eee, F=0x0 +0, 101, 101, 1, 207, 0xdedb4552, F=0x0 +0, 102, 102, 1, 212, 0x25384cd1, F=0x0 +0, 103, 103, 1, 153, 0x489434d3, F=0x0 +0, 104, 104, 1, 196, 0x659145f9, F=0x0 +0, 105, 105, 1, 146, 0x8b223366, F=0x0 +0, 106, 106, 1, 194, 0x0bc14890, F=0x0 +0, 107, 107, 1, 304, 0xdece8235, F=0x0 +0, 108, 108, 1, 194, 0x7737464d, F=0x0 +0, 109, 109, 1, 190, 0x945e43a4, F=0x0 +0, 110, 110, 1, 204, 0x8de14af3, F=0x0 +0, 111, 111, 1, 136, 0xd0862cb1, F=0x0 +0, 112, 112, 1, 171, 0xff6f3da8, F=0x0 +0, 113, 113, 1, 140, 0x3475307b, F=0x0 +0, 114, 114, 1, 158, 0x8f31321e, F=0x0 +0, 115, 115, 1, 94, 0xf6691c01, F=0x0 +0, 116, 116, 1, 110, 0x66e52218, F=0x0 +0, 117, 117, 1, 256, 0x4b086864, F=0x0 +0, 118, 118, 1, 220, 0x6fb75337, F=0x0 +0, 119, 119, 1, 221, 0x69a94e72, F=0x0 +0, 120, 120, 1, 205, 0xbae94509, F=0x0 +0, 121, 121, 1, 169, 0xdd3c388b, F=0x0 +0, 122, 122, 1, 970, 0x9771c82f, F=0x0 +0, 123, 123, 1, 181, 0xec794298, F=0x0 +0, 124, 124, 1, 955, 0x23edba10, F=0x0 +0, 125, 125, 1, 158, 0x3ad83314, F=0x0 +0, 126, 126, 1, 344, 0x29aa8843, F=0x0 +0, 127, 127, 1, 627, 0x6d3f18dc, F=0x0 +0, 128, 128, 1, 172, 0x985d3cc5, F=0x0 +0, 129, 129, 1, 494, 0x591cdcc3, F=0x0 +0, 130, 130, 1, 184, 0x83ca42c1, F=0x0 +0, 131, 131, 1, 352, 0x68169925, F=0x0 +0, 132, 132, 1, 351, 0x06998cfa, F=0x0 +0, 133, 133, 1, 170, 0xaec83809, F=0x0 +0, 134, 134, 1, 275, 0xd1ea6a74, F=0x0 +0, 135, 135, 1, 168, 0xe9b93a2e, F=0x0 +0, 136, 136, 1, 169, 0xa27f3870, F=0x0 +0, 137, 137, 1, 521, 0xf195dc2e, F=0x0 +0, 138, 138, 1, 1262, 0x52b34497, F=0x0 +0, 139, 139, 1, 994, 0x9d72bc25, F=0x0 +0, 140, 140, 1, 290, 0xb2c17360, F=0x0 +0, 141, 141, 1, 188, 0x2c30402b, F=0x0 +0, 142, 142, 1, 164, 0x8bb13b7a, F=0x0 +0, 143, 143, 1, 212, 0x69af44ca, F=0x0 +0, 144, 144, 1, 870, 0xebc09472, F=0x0 +0, 145, 145, 1, 635, 0x1f781aee, F=0x0 +0, 146, 146, 1, 290, 0x01a5786c, F=0x0 +0, 147, 147, 1, 211, 0xec4d5052, F=0x0 +0, 148, 148, 1, 177, 0x56db3c7b, F=0x0 +0, 149, 149, 1, 182, 0x312c3f58, F=0x0 +0, 150, 150, 1, 588, 0x9924fcca, F=0x0 +0, 151, 151, 1, 163, 0xe23a370e, F=0x0 +0, 152, 152, 1, 407, 0x525ab1a8, F=0x0 +0, 153, 153, 1, 204, 0x0b84449d, F=0x0 +0, 154, 154, 1, 290, 0xa97e7886, F=0x0 +0, 155, 155, 1, 308, 0xac988116, F=0x0 +0, 156, 156, 1, 196, 0x6d6c47fa, F=0x0 +0, 157, 157, 1, 181, 0x7a413f71, F=0x0 +0, 158, 158, 1, 203, 0xb03b4fa8, F=0x0 +0, 159, 159, 1, 227, 0xb3d55aeb, F=0x0 +0, 160, 160, 1, 524, 0xa731e285, F=0x0 +0, 161, 161, 1, 377, 0x4ac097de, F=0x0 +0, 162, 162, 1, 196, 0x78264511, F=0x0 +0, 163, 163, 1, 250, 0x6d4f65a1, F=0x0 +0, 164, 164, 1, 214, 0x31534ffe, F=0x0 +0, 165, 165, 1, 323, 0xd4d78fa2, F=0x0 +0, 166, 166, 1, 176, 0x642e40ab, F=0x0 +0, 167, 167, 1, 305, 0x63db7e18, F=0x0 +0, 168, 168, 1, 179, 0x4c4042da, F=0x0 +0, 169, 169, 1, 245, 0x645264fb, F=0x0 +0, 170, 170, 1, 181, 0x0a3e403e, F=0x0 +0, 171, 171, 1, 241, 0x592e57b1, F=0x0 +0, 172, 172, 1, 172, 0xf5cb3f7f, F=0x0 diff --git a/tests/ref/fate/gifenc-rgb8 b/tests/ref/fate/gifenc-rgb8 index c5ff394d993..dd1648e7761 100644 --- a/tests/ref/fate/gifenc-rgb8 +++ b/tests/ref/fate/gifenc-rgb8 @@ -4,175 +4,175 @@ #dimensions 0: 217x217 #sar 0: 0/1 0, 0, 0, 1, 1341, 0xaa85adb1 -0, 1, 1, 1, 305, 0xa970896f, F=0x0 -0, 2, 2, 1, 446, 0x4a20d47a, F=0x0 -0, 3, 3, 1, 458, 0x32ded2af, F=0x0 -0, 4, 4, 1, 555, 0x622205e4, F=0x0 -0, 5, 5, 1, 622, 0xde06214f, F=0x0 -0, 6, 6, 1, 650, 0x8f482007, F=0x0 -0, 7, 7, 1, 668, 0xd3df3a59, F=0x0 -0, 8, 8, 1, 829, 0x812f80d4, F=0x0 -0, 9, 9, 1, 1165, 0x8e402ff5, F=0x0 -0, 10, 10, 1, 187, 0xdda45544, F=0x0 -0, 11, 11, 1, 1341, 0x2f2d71cf, F=0x0 -0, 12, 12, 1, 1646, 0xfe910a65, F=0x0 -0, 13, 13, 1, 1539, 0xbc77e727, F=0x0 -0, 14, 14, 1, 1728, 0x9bd9486b, F=0x0 -0, 15, 15, 1, 1918, 0xcf948795, F=0x0 -0, 16, 16, 1, 2132, 0x4618dbf8, F=0x0 -0, 17, 17, 1, 2256, 0xcdce2bb0, F=0x0 -0, 18, 18, 1, 2319, 0x4f1b36d7, F=0x0 -0, 19, 19, 1, 2416, 0xcba76df1, F=0x0 -0, 20, 20, 1, 2609, 0xb2eec38f, F=0x0 -0, 21, 21, 1, 2695, 0x29d40439, F=0x0 -0, 22, 22, 1, 2792, 0x663f0f16, F=0x0 -0, 23, 23, 1, 2892, 0xd6924cc1, F=0x0 -0, 24, 24, 1, 2990, 0x8bed794d, F=0x0 -0, 25, 25, 1, 3109, 0x48789f36, F=0x0 -0, 26, 26, 1, 3261, 0x3eaff7a0, F=0x0 -0, 27, 27, 1, 3337, 0x3bcd1c99, F=0x0 -0, 28, 28, 1, 3580, 0xcf0c8b7a, F=0x0 -0, 29, 29, 1, 3815, 0x53d40009, F=0x0 -0, 30, 30, 1, 2758, 0x73fd20c6, F=0x0 -0, 31, 31, 1, 4039, 0xab517451, F=0x0 -0, 32, 32, 1, 3033, 0x66d29fbc, F=0x0 -0, 33, 33, 1, 4303, 0x30e50ba8, F=0x0 -0, 34, 34, 1, 2052, 0x4843c19a, F=0x0 -0, 35, 35, 1, 3220, 0x73f7f1b7, F=0x0 -0, 36, 36, 1, 2300, 0xca376466, F=0x0 -0, 37, 37, 1, 3641, 0x1e3a9bcb, F=0x0 -0, 38, 38, 1, 3560, 0x014776da, F=0x0 -0, 39, 39, 1, 3698, 0xd3bcde35, F=0x0 -0, 40, 40, 1, 1567, 0x6c8bf458, F=0x0 -0, 41, 41, 1, 962, 0x0389caed, F=0x0 -0, 42, 42, 1, 281, 0x575f7959, F=0x0 -0, 43, 43, 1, 938, 0x2d7aaf67, F=0x0 -0, 44, 44, 1, 279, 0xd39d81d7, F=0x0 -0, 45, 45, 1, 204, 0x90a9544a, F=0x0 -0, 46, 46, 1, 4307, 0xcf9d0eae, F=0x0 -0, 47, 47, 1, 4903, 0xe9743ace, F=0x0 -0, 48, 48, 1, 4936, 0x3cf21510, F=0x0 -0, 49, 49, 1, 4949, 0xcbff5648, F=0x0 -0, 50, 50, 1, 4162, 0x94a9b9ce, F=0x0 -0, 51, 51, 1, 4686, 0x5098c461, F=0x0 -0, 52, 52, 1, 4749, 0x7304bc6d, F=0x0 -0, 53, 53, 1, 4990, 0x9d025a9f, F=0x0 -0, 54, 54, 1, 5187, 0x0566c4c9, F=0x0 -0, 55, 55, 1, 5054, 0x574f81f5, F=0x0 -0, 56, 56, 1, 5148, 0xe7bac30f, F=0x0 -0, 57, 57, 1, 4309, 0x7844f361, F=0x0 -0, 58, 58, 1, 5087, 0xacf4a406, F=0x0 -0, 59, 59, 1, 5292, 0x9e7be109, F=0x0 -0, 60, 60, 1, 5434, 0x85541f7f, F=0x0 -0, 61, 61, 1, 4653, 0xf5118b09, F=0x0 -0, 62, 62, 1, 5271, 0x54f3d911, F=0x0 -0, 63, 63, 1, 5229, 0xc520cb6b, F=0x0 -0, 64, 64, 1, 5225, 0x2a449e23, F=0x0 -0, 65, 65, 1, 5403, 0x4a2502f3, F=0x0 -0, 66, 66, 1, 5228, 0x8002e47d, F=0x0 -0, 67, 67, 1, 5712, 0x9c9ed686, F=0x0 -0, 68, 68, 1, 5644, 0xfdf5a30d, F=0x0 -0, 69, 69, 1, 5826, 0xb142ece5, F=0x0 -0, 70, 70, 1, 5771, 0xd22ac8b9, F=0x0 -0, 71, 71, 1, 6124, 0x5e4e8f58, F=0x0 -0, 72, 72, 1, 6077, 0x94ab6503, F=0x0 -0, 73, 73, 1, 5804, 0x5c56e07a, F=0x0 -0, 74, 74, 1, 6007, 0x3e453829, F=0x0 -0, 75, 75, 1, 6228, 0xa4e0b278, F=0x0 -0, 76, 76, 1, 6382, 0xd488ebcd, F=0x0 -0, 77, 77, 1, 6473, 0xb1bb78b2, F=0x0 -0, 78, 78, 1, 7027, 0x98ce29cb, F=0x0 -0, 79, 79, 1, 7263, 0xecadb896, F=0x0 -0, 80, 80, 1, 8205, 0x36cb6ba1, F=0x0 -0, 81, 81, 1, 8366, 0xdbbe8308, F=0x0 -0, 82, 82, 1, 7716, 0xd6959765, F=0x0 -0, 83, 83, 1, 7420, 0x60fd1316, F=0x0 -0, 84, 84, 1, 7549, 0x668a1ae9, F=0x0 -0, 85, 85, 1, 7956, 0xe146cb65, F=0x0 -0, 86, 86, 1, 8416, 0x47c3d590, F=0x0 -0, 87, 87, 1, 8064, 0x9a0e249f, F=0x0 -0, 88, 88, 1, 7409, 0x9e5eb771, F=0x0 -0, 89, 89, 1, 7502, 0x1906fad6, F=0x0 -0, 90, 90, 1, 7814, 0x8e117365, F=0x0 -0, 91, 91, 1, 7776, 0xd0077a82, F=0x0 -0, 92, 92, 1, 7757, 0x74a49d3a, F=0x0 -0, 93, 93, 1, 8055, 0x70ef25d2, F=0x0 -0, 94, 94, 1, 7626, 0x4a003537, F=0x0 -0, 95, 95, 1, 7987, 0x1c14c607, F=0x0 -0, 96, 96, 1, 12070, 0x4a729eba, F=0x0 -0, 97, 97, 1, 12325, 0xbff1dd34, F=0x0 -0, 98, 98, 1, 12225, 0xb1ffee58, F=0x0 -0, 99, 99, 1, 11235, 0xea998fe1, F=0x0 -0, 100, 100, 1, 11116, 0x855407b4, F=0x0 -0, 101, 101, 1, 11374, 0xaa019a67, F=0x0 -0, 102, 102, 1, 11904, 0xf8384969, F=0x0 -0, 103, 103, 1, 11487, 0xce63834d, F=0x0 -0, 104, 104, 1, 13403, 0x36444d9a, F=0x0 -0, 105, 105, 1, 12921, 0x86af447b, F=0x0 -0, 106, 106, 1, 13872, 0x104af758, F=0x0 -0, 107, 107, 1, 13559, 0x37d07d0f, F=0x0 -0, 108, 108, 1, 14049, 0xa47572e1, F=0x0 -0, 109, 109, 1, 14152, 0x217c3e45, F=0x0 -0, 110, 110, 1, 14285, 0xf37e5a5d, F=0x0 -0, 111, 111, 1, 14432, 0xd841f100, F=0x0 -0, 112, 112, 1, 14697, 0xf25cd254, F=0x0 -0, 113, 113, 1, 14606, 0x4f3069dc, F=0x0 -0, 114, 114, 1, 15221, 0x222f9680, F=0x0 -0, 115, 115, 1, 15433, 0x3e43af1c, F=0x0 -0, 116, 116, 1, 15603, 0x038a0ae1, F=0x0 -0, 117, 117, 1, 15606, 0x9040acec, F=0x0 -0, 118, 118, 1, 15871, 0xd63f0294, F=0x0 -0, 119, 119, 1, 15725, 0xe95d42ec, F=0x0 -0, 120, 120, 1, 16086, 0x27223bb4, F=0x0 -0, 121, 121, 1, 16233, 0xebfca656, F=0x0 -0, 122, 122, 1, 16143, 0x1a7717ba, F=0x0 -0, 123, 123, 1, 16669, 0x56926ea6, F=0x0 -0, 124, 124, 1, 16627, 0xe0ac5c7e, F=0x0 -0, 125, 125, 1, 16837, 0x67b5c267, F=0x0 -0, 126, 126, 1, 16952, 0x2d9bd2b7, F=0x0 -0, 127, 127, 1, 17127, 0xcab06c88, F=0x0 -0, 128, 128, 1, 17158, 0x10be3e59, F=0x0 -0, 129, 129, 1, 17329, 0x53d2ef82, F=0x0 -0, 130, 130, 1, 17403, 0x37a5ca9c, F=0x0 -0, 131, 131, 1, 17674, 0x0e34434b, F=0x0 -0, 132, 132, 1, 17738, 0xc1ef86db, F=0x0 -0, 133, 133, 1, 17942, 0xbf37caa5, F=0x0 -0, 134, 134, 1, 17952, 0x256e0aee, F=0x0 -0, 135, 135, 1, 18246, 0xd818335f, F=0x0 -0, 136, 136, 1, 18399, 0x6b84c5b2, F=0x0 -0, 137, 137, 1, 18551, 0x5e02f360, F=0x0 -0, 138, 138, 1, 18947, 0x5cdbdb83, F=0x0 -0, 139, 139, 1, 19153, 0x97f52a72, F=0x0 -0, 140, 140, 1, 19128, 0x537baaeb, F=0x0 -0, 141, 141, 1, 19138, 0xa629eee4, F=0x0 -0, 142, 142, 1, 19502, 0x4b8da41b, F=0x0 -0, 143, 143, 1, 19542, 0x21a5a87a, F=0x0 -0, 144, 144, 1, 19755, 0xc5d0c4a1, F=0x0 -0, 145, 145, 1, 20122, 0x913a698c, F=0x0 -0, 146, 146, 1, 20265, 0xfdbcdb4e, F=0x0 -0, 147, 147, 1, 20378, 0x173f3952, F=0x0 -0, 148, 148, 1, 20300, 0xff58f32f, F=0x0 -0, 149, 149, 1, 20499, 0x89246e44, F=0x0 -0, 150, 150, 1, 20655, 0xff78de6d, F=0x0 -0, 151, 151, 1, 20674, 0x9ccbc9cc, F=0x0 -0, 152, 152, 1, 21015, 0x289450a5, F=0x0 -0, 153, 153, 1, 21066, 0x006d6bc5, F=0x0 -0, 154, 154, 1, 21161, 0x306b4b03, F=0x0 -0, 155, 155, 1, 21086, 0x5c4f8181, F=0x0 -0, 156, 156, 1, 21466, 0x5be353c4, F=0x0 -0, 157, 157, 1, 21677, 0xbc05e406, F=0x0 -0, 158, 158, 1, 21589, 0xa69322d7, F=0x0 -0, 159, 159, 1, 21662, 0x64e7ac40, F=0x0 -0, 160, 160, 1, 21995, 0xbfa18826, F=0x0 -0, 161, 161, 1, 22213, 0xb9fa745d, F=0x0 -0, 162, 162, 1, 22483, 0x3070e144, F=0x0 -0, 163, 163, 1, 22498, 0x1feb3894, F=0x0 -0, 164, 164, 1, 22468, 0xb34dbd26, F=0x0 -0, 165, 165, 1, 22869, 0x67c63638, F=0x0 -0, 166, 166, 1, 22754, 0xaa2d573d, F=0x0 -0, 167, 167, 1, 23173, 0x9f7767cb, F=0x0 -0, 168, 168, 1, 23281, 0x3f319c01, F=0x0 -0, 169, 169, 1, 23219, 0xb706632d, F=0x0 -0, 170, 170, 1, 23656, 0x7e2ff143, F=0x0 -0, 171, 171, 1, 23683, 0x729d61e1, F=0x0 -0, 172, 172, 1, 23882, 0x8fb999ed, F=0x0 +0, 1, 1, 1, 236, 0xa46f676e, F=0x0 +0, 2, 2, 1, 186, 0xd99b4ec2, F=0x0 +0, 3, 3, 1, 208, 0xb9be5007, F=0x0 +0, 4, 4, 1, 282, 0xe43d8422, F=0x0 +0, 5, 5, 1, 209, 0xda215145, F=0x0 +0, 6, 6, 1, 225, 0xc6375b19, F=0x0 +0, 7, 7, 1, 204, 0x467a54c1, F=0x0 +0, 8, 8, 1, 181, 0x1c0e4dae, F=0x0 +0, 9, 9, 1, 200, 0xc55e53c1, F=0x0 +0, 10, 10, 1, 184, 0x87644454, F=0x0 +0, 11, 11, 1, 191, 0x3847484a, F=0x0 +0, 12, 12, 1, 290, 0x9ce37f1f, F=0x0 +0, 13, 13, 1, 153, 0x7ab03afc, F=0x0 +0, 14, 14, 1, 175, 0x64bc4621, F=0x0 +0, 15, 15, 1, 187, 0x9284451d, F=0x0 +0, 16, 16, 1, 418, 0x7b18c8a5, F=0x0 +0, 17, 17, 1, 200, 0xf8e5527f, F=0x0 +0, 18, 18, 1, 347, 0xbc6494eb, F=0x0 +0, 19, 19, 1, 176, 0x8d1842e5, F=0x0 +0, 20, 20, 1, 294, 0x52fe8678, F=0x0 +0, 21, 21, 1, 166, 0x614142a1, F=0x0 +0, 22, 22, 1, 306, 0xafe8850d, F=0x0 +0, 23, 23, 1, 180, 0xa2cc44a0, F=0x0 +0, 24, 24, 1, 207, 0x91e25233, F=0x0 +0, 25, 25, 1, 204, 0x0f174cb7, F=0x0 +0, 26, 26, 1, 259, 0x629071c3, F=0x0 +0, 27, 27, 1, 181, 0x950e4a15, F=0x0 +0, 28, 28, 1, 152, 0x77023d8a, F=0x0 +0, 29, 29, 1, 194, 0x890b4d08, F=0x0 +0, 30, 30, 1, 159, 0xaa5a397d, F=0x0 +0, 31, 31, 1, 162, 0x8ae73c95, F=0x0 +0, 32, 32, 1, 178, 0x7fba4974, F=0x0 +0, 33, 33, 1, 187, 0x41fd52cc, F=0x0 +0, 34, 34, 1, 336, 0xd8139332, F=0x0 +0, 35, 35, 1, 184, 0x61b3484a, F=0x0 +0, 36, 36, 1, 227, 0x8be2607e, F=0x0 +0, 37, 37, 1, 182, 0x9bc84478, F=0x0 +0, 38, 38, 1, 162, 0x33423e3c, F=0x0 +0, 39, 39, 1, 187, 0x62fd4805, F=0x0 +0, 40, 40, 1, 503, 0xfc0ce5c8, F=0x0 +0, 41, 41, 1, 167, 0x40bd40d7, F=0x0 +0, 42, 42, 1, 289, 0xe5ad805c, F=0x0 +0, 43, 43, 1, 194, 0xc0174c24, F=0x0 +0, 44, 44, 1, 285, 0x48f38060, F=0x0 +0, 45, 45, 1, 199, 0x62bd52dc, F=0x0 +0, 46, 46, 1, 197, 0xcc1f4d1d, F=0x0 +0, 47, 47, 1, 203, 0xa0cb4c90, F=0x0 +0, 48, 48, 1, 208, 0x460155cd, F=0x0 +0, 49, 49, 1, 1198, 0x0b795bb1, F=0x0 +0, 50, 50, 1, 175, 0x36cc4a76, F=0x0 +0, 51, 51, 1, 740, 0xeb6f68b9, F=0x0 +0, 52, 52, 1, 180, 0xa22544ba, F=0x0 +0, 53, 53, 1, 238, 0x08ff6b12, F=0x0 +0, 54, 54, 1, 198, 0x3d4d50dd, F=0x0 +0, 55, 55, 1, 196, 0xbef74ccd, F=0x0 +0, 56, 56, 1, 224, 0x47c05ea9, F=0x0 +0, 57, 57, 1, 765, 0xb89e6f01, F=0x0 +0, 58, 58, 1, 149, 0x26bb3969, F=0x0 +0, 59, 59, 1, 479, 0xcdd6d6bf, F=0x0 +0, 60, 60, 1, 179, 0x537948eb, F=0x0 +0, 61, 61, 1, 217, 0x6b9b5b06, F=0x0 +0, 62, 62, 1, 190, 0x56b54a3a, F=0x0 +0, 63, 63, 1, 155, 0x487439ef, F=0x0 +0, 64, 64, 1, 167, 0x1bb947ba, F=0x0 +0, 65, 65, 1, 649, 0xb44a3058, F=0x0 +0, 66, 66, 1, 196, 0x66ae5688, F=0x0 +0, 67, 67, 1, 427, 0x8b0bd38d, F=0x0 +0, 68, 68, 1, 175, 0xbb9d4294, F=0x0 +0, 69, 69, 1, 284, 0x9f768221, F=0x0 +0, 70, 70, 1, 131, 0x612d2e14, F=0x0 +0, 71, 71, 1, 256, 0x701a69b4, F=0x0 +0, 72, 72, 1, 186, 0xf7114a80, F=0x0 +0, 73, 73, 1, 190, 0x53144c4d, F=0x0 +0, 74, 74, 1, 216, 0xc67b53e5, F=0x0 +0, 75, 75, 1, 154, 0xc04e39ba, F=0x0 +0, 76, 76, 1, 113, 0x4800284b, F=0x0 +0, 77, 77, 1, 402, 0xee43b0dd, F=0x0 +0, 78, 78, 1, 198, 0x55e451cc, F=0x0 +0, 79, 79, 1, 466, 0xb3c8cff3, F=0x0 +0, 80, 80, 1, 322, 0x5ade8d02, F=0x0 +0, 81, 81, 1, 387, 0x72edb2f7, F=0x0 +0, 82, 82, 1, 158, 0x12a5402c, F=0x0 +0, 83, 83, 1, 278, 0xd6727a4a, F=0x0 +0, 84, 84, 1, 190, 0x58205087, F=0x0 +0, 85, 85, 1, 175, 0x9a58432a, F=0x0 +0, 86, 86, 1, 206, 0x85954c9e, F=0x0 +0, 87, 87, 1, 379, 0x4f1db56a, F=0x0 +0, 88, 88, 1, 202, 0x19b05154, F=0x0 +0, 89, 89, 1, 225, 0xf4166005, F=0x0 +0, 90, 90, 1, 175, 0x44414761, F=0x0 +0, 91, 91, 1, 235, 0xe01c603f, F=0x0 +0, 92, 92, 1, 185, 0xfab74905, F=0x0 +0, 93, 93, 1, 182, 0x9c5f4ae7, F=0x0 +0, 94, 94, 1, 114, 0xe0542abd, F=0x0 +0, 95, 95, 1, 176, 0x6e274c2e, F=0x0 +0, 96, 96, 1, 214, 0x44725ac3, F=0x0 +0, 97, 97, 1, 182, 0x54e9482e, F=0x0 +0, 98, 98, 1, 195, 0x6231525e, F=0x0 +0, 99, 99, 1, 303, 0xf63c84a1, F=0x0 +0, 100, 100, 1, 191, 0x4aa84e6f, F=0x0 +0, 101, 101, 1, 177, 0xfd82496d, F=0x0 +0, 102, 102, 1, 172, 0x13d041cb, F=0x0 +0, 103, 103, 1, 179, 0x89f54866, F=0x0 +0, 104, 104, 1, 188, 0xf39d4d8d, F=0x0 +0, 105, 105, 1, 177, 0x4d0e4882, F=0x0 +0, 106, 106, 1, 215, 0xca7b5a6f, F=0x0 +0, 107, 107, 1, 339, 0x1dd598fe, F=0x0 +0, 108, 108, 1, 189, 0xe2d94497, F=0x0 +0, 109, 109, 1, 209, 0x682356be, F=0x0 +0, 110, 110, 1, 205, 0xa5af51b7, F=0x0 +0, 111, 111, 1, 185, 0xa1224e22, F=0x0 +0, 112, 112, 1, 211, 0x09a95a00, F=0x0 +0, 113, 113, 1, 178, 0x31ab47cc, F=0x0 +0, 114, 114, 1, 195, 0x960f4c9c, F=0x0 +0, 115, 115, 1, 116, 0x8f8b2da3, F=0x0 +0, 116, 116, 1, 116, 0xf30c2b3d, F=0x0 +0, 117, 117, 1, 427, 0xa3dcc81b, F=0x0 +0, 118, 118, 1, 182, 0xce37489b, F=0x0 +0, 119, 119, 1, 267, 0x516c6f3e, F=0x0 +0, 120, 120, 1, 191, 0xff5c4af7, F=0x0 +0, 121, 121, 1, 195, 0x49cd5178, F=0x0 +0, 122, 122, 1, 755, 0xb9b4608d, F=0x0 +0, 123, 123, 1, 179, 0x6d0c4600, F=0x0 +0, 124, 124, 1, 522, 0xffe5f236, F=0x0 +0, 125, 125, 1, 171, 0xea4c40b8, F=0x0 +0, 126, 126, 1, 379, 0xbf2fa98b, F=0x0 +0, 127, 127, 1, 539, 0xace2f7b5, F=0x0 +0, 128, 128, 1, 199, 0xe0534e3d, F=0x0 +0, 129, 129, 1, 458, 0x2b54d13e, F=0x0 +0, 130, 130, 1, 177, 0x6ff04b91, F=0x0 +0, 131, 131, 1, 299, 0x51d1893e, F=0x0 +0, 132, 132, 1, 333, 0x5cba941f, F=0x0 +0, 133, 133, 1, 179, 0x2381453b, F=0x0 +0, 134, 134, 1, 263, 0xac907176, F=0x0 +0, 135, 135, 1, 177, 0xb87546d9, F=0x0 +0, 136, 136, 1, 184, 0x761c4765, F=0x0 +0, 137, 137, 1, 433, 0x55f0c2d6, F=0x0 +0, 138, 138, 1, 1138, 0x3b7137a0, F=0x0 +0, 139, 139, 1, 863, 0x5afd9dae, F=0x0 +0, 140, 140, 1, 328, 0x4537973f, F=0x0 +0, 141, 141, 1, 222, 0xadb859e4, F=0x0 +0, 142, 142, 1, 171, 0x7ce844b6, F=0x0 +0, 143, 143, 1, 222, 0xa5815a1f, F=0x0 +0, 144, 144, 1, 738, 0x3fe75c6a, F=0x0 +0, 145, 145, 1, 444, 0x320fd03e, F=0x0 +0, 146, 146, 1, 267, 0x288273f3, F=0x0 +0, 147, 147, 1, 187, 0x07594c67, F=0x0 +0, 148, 148, 1, 186, 0xbd1c50de, F=0x0 +0, 149, 149, 1, 196, 0xf3e14fdb, F=0x0 +0, 150, 150, 1, 589, 0x127314a0, F=0x0 +0, 151, 151, 1, 188, 0x22d74d85, F=0x0 +0, 152, 152, 1, 339, 0xcadaa1b6, F=0x0 +0, 153, 153, 1, 179, 0x6a1843a7, F=0x0 +0, 154, 154, 1, 294, 0xe04184fa, F=0x0 +0, 155, 155, 1, 291, 0x4b018587, F=0x0 +0, 156, 156, 1, 144, 0x470f3737, F=0x0 +0, 157, 157, 1, 182, 0x3b994dcd, F=0x0 +0, 158, 158, 1, 195, 0x7f884fd3, F=0x0 +0, 159, 159, 1, 268, 0xb6097b3b, F=0x0 +0, 160, 160, 1, 526, 0x40e0fd27, F=0x0 +0, 161, 161, 1, 372, 0x9dffaa6d, F=0x0 +0, 162, 162, 1, 171, 0xc06841eb, F=0x0 +0, 163, 163, 1, 344, 0xc47da473, F=0x0 +0, 164, 164, 1, 159, 0xacf64002, F=0x0 +0, 165, 165, 1, 391, 0x520cad43, F=0x0 +0, 166, 166, 1, 180, 0x05c04cac, F=0x0 +0, 167, 167, 1, 303, 0x748493c3, F=0x0 +0, 168, 168, 1, 180, 0x3fc54928, F=0x0 +0, 169, 169, 1, 269, 0xcd227967, F=0x0 +0, 170, 170, 1, 199, 0x3a3053e8, F=0x0 +0, 171, 171, 1, 287, 0x3c37840b, F=0x0 +0, 172, 172, 1, 217, 0xabd063fc, F=0x0 diff --git a/tests/ref/fate/h264-attachment-631 b/tests/ref/fate/h264-attachment-631 index ebb5eb4fcd8..5ac45e70a40 100644 --- a/tests/ref/fate/h264-attachment-631 +++ b/tests/ref/fate/h264-attachment-631 @@ -3,6 +3,154 @@ #codec_id 0: rawvideo #dimensions 0: 720x480 #sar 0: 8/9 +0, 6, 6, 1, 518400, 0xd2068698 +0, 10, 10, 1, 518400, 0x2ee4865f +0, 14, 14, 1, 518400, 0x2a01b188 +0, 18, 18, 1, 518400, 0xa4bc9572 +0, 22, 22, 1, 518400, 0x4e882f72 +0, 26, 26, 1, 518400, 0xf79cfc9c +0, 30, 30, 1, 518400, 0x93afec23 +0, 34, 34, 1, 518400, 0xadf210e6 +0, 38, 38, 1, 518400, 0xb0bdd1f1 +0, 42, 42, 1, 518400, 0x4bbb4a24 +0, 46, 46, 1, 518400, 0x49db06c8 +0, 50, 50, 1, 518400, 0xf9781bfb +0, 54, 54, 1, 518400, 0xd3a373bc +0, 58, 58, 1, 518400, 0xccfb31c5 +0, 62, 62, 1, 518400, 0x276423a7 +0, 66, 66, 1, 518400, 0xb3729230 +0, 70, 70, 1, 518400, 0xeaf4586d +0, 74, 74, 1, 518400, 0x9e629b29 +0, 78, 78, 1, 518400, 0x921d6e58 +0, 82, 82, 1, 518400, 0xc988f527 +0, 86, 86, 1, 518400, 0x4e1fed4b +0, 90, 90, 1, 518400, 0xe3819724 +0, 94, 94, 1, 518400, 0xc07602ba +0, 98, 98, 1, 518400, 0xc6b1e8d0 +0, 102, 102, 1, 518400, 0x12d94755 +0, 106, 106, 1, 518400, 0x257a5264 +0, 110, 110, 1, 518400, 0x4f985461 +0, 114, 114, 1, 518400, 0x77577244 +0, 118, 118, 1, 518400, 0x81a59edf +0, 122, 122, 1, 518400, 0x9f33c0fa +0, 126, 126, 1, 518400, 0xa89cbb3f +0, 130, 130, 1, 518400, 0x6b1bcc1c +0, 134, 134, 1, 518400, 0x520acb74 +0, 138, 138, 1, 518400, 0x006dda91 +0, 142, 142, 1, 518400, 0x7377f96f +0, 146, 146, 1, 518400, 0x0b713224 +0, 150, 150, 1, 518400, 0x98943e53 +0, 154, 154, 1, 518400, 0x59f967e2 +0, 158, 158, 1, 518400, 0x976a2461 +0, 162, 162, 1, 518400, 0xcb4d3872 +0, 166, 166, 1, 518400, 0x0a174f59 +0, 170, 170, 1, 518400, 0x7cbe6c4f +0, 174, 174, 1, 518400, 0x475cbce4 +0, 178, 178, 1, 518400, 0x2c281bb9 +0, 182, 182, 1, 518400, 0xadee7826 +0, 186, 186, 1, 518400, 0x936059a6 +0, 190, 190, 1, 518400, 0xba09ae20 +0, 194, 194, 1, 518400, 0x355e94d7 +0, 198, 198, 1, 518400, 0xafc3a0b3 +0, 202, 202, 1, 518400, 0x55bd78af +0, 206, 206, 1, 518400, 0x9678c886 +0, 210, 210, 1, 518400, 0x4d69a62a +0, 214, 214, 1, 518400, 0x406e617c +0, 218, 218, 1, 518400, 0x7031ebdb +0, 222, 222, 1, 518400, 0xf862127d +0, 226, 226, 1, 518400, 0x619b8a53 +0, 230, 230, 1, 518400, 0x0fde6b72 +0, 234, 234, 1, 518400, 0xd137deff +0, 238, 238, 1, 518400, 0x9ae6ac2e +0, 242, 242, 1, 518400, 0x6cefb571 +0, 246, 246, 1, 518400, 0x7694dda2 +0, 250, 250, 1, 518400, 0x2253f6a2 +0, 254, 254, 1, 518400, 0x770db468 +0, 258, 258, 1, 518400, 0xf4c815a5 +0, 262, 262, 1, 518400, 0x0a0f38b6 +0, 266, 266, 1, 518400, 0x17490907 +0, 270, 270, 1, 518400, 0xbc362ed6 +0, 274, 274, 1, 518400, 0x24de1b5c +0, 278, 278, 1, 518400, 0x55d20b2a +0, 282, 282, 1, 518400, 0xca1af9b1 +0, 286, 286, 1, 518400, 0x7e7b7473 +0, 290, 290, 1, 518400, 0xed30dd23 +0, 294, 294, 1, 518400, 0xb694c58f +0, 298, 298, 1, 518400, 0x8270deb7 +0, 302, 302, 1, 518400, 0x91b3b4f7 +0, 306, 306, 1, 518400, 0x37fcb63c +0, 310, 310, 1, 518400, 0x7ebcafca +0, 314, 314, 1, 518400, 0x8508b6da +0, 318, 318, 1, 518400, 0xe7e0b15e +0, 322, 322, 1, 518400, 0x9618fa0e +0, 326, 326, 1, 518400, 0xd4c3b20c +0, 330, 330, 1, 518400, 0x1aad03d1 +0, 334, 334, 1, 518400, 0xb5c18e20 +0, 338, 338, 1, 518400, 0x70144034 +0, 342, 342, 1, 518400, 0x937ee203 +0, 346, 346, 1, 518400, 0x680d72ad +0, 350, 350, 1, 518400, 0x8c9647b1 +0, 354, 354, 1, 518400, 0x65fce70a +0, 358, 358, 1, 518400, 0xa3d785dd +0, 362, 362, 1, 518400, 0xaf1a54c2 +0, 366, 366, 1, 518400, 0x301c6f4c +0, 370, 370, 1, 518400, 0x0255b5ac +0, 374, 374, 1, 518400, 0x967da8de +0, 378, 378, 1, 518400, 0x1f7e6c8c +0, 382, 382, 1, 518400, 0xb41badbf +0, 386, 386, 1, 518400, 0xca853613 +0, 390, 390, 1, 518400, 0x9f8696cb +0, 394, 394, 1, 518400, 0x55ec8427 +0, 398, 398, 1, 518400, 0x08779f91 +0, 402, 402, 1, 518400, 0x171fbc34 +0, 406, 406, 1, 518400, 0x5e9c6ddd +0, 410, 410, 1, 518400, 0xd9a55786 +0, 414, 414, 1, 518400, 0xdb509948 +0, 418, 418, 1, 518400, 0x2a326178 +0, 422, 422, 1, 518400, 0x4842c411 +0, 426, 426, 1, 518400, 0x35399db4 +0, 430, 430, 1, 518400, 0xa182b9aa +0, 434, 434, 1, 518400, 0xb6df772d +0, 438, 438, 1, 518400, 0xfe61b651 +0, 442, 442, 1, 518400, 0x031cb305 +0, 446, 446, 1, 518400, 0xde553506 +0, 450, 450, 1, 518400, 0x24ab8557 +0, 454, 454, 1, 518400, 0xadf5e251 +0, 458, 458, 1, 518400, 0xb3a3c6c5 +0, 462, 462, 1, 518400, 0x9cedc6ac +0, 466, 466, 1, 518400, 0x6ddf9b26 +0, 470, 470, 1, 518400, 0x3bfaf200 +0, 474, 474, 1, 518400, 0x0337d6f1 +0, 478, 478, 1, 518400, 0x71367bc7 +0, 482, 482, 1, 518400, 0x9e1876b8 +0, 486, 486, 1, 518400, 0x37b89366 +0, 490, 490, 1, 518400, 0x6e349056 +0, 494, 494, 1, 518400, 0x718a9543 +0, 498, 498, 1, 518400, 0x48e46e57 +0, 502, 502, 1, 518400, 0xb2ae494c +0, 506, 506, 1, 518400, 0x0ec937dc +0, 510, 510, 1, 518400, 0xb1e88149 +0, 514, 514, 1, 518400, 0xedbba51d +0, 518, 518, 1, 518400, 0x8955d114 +0, 522, 522, 1, 518400, 0x951e8716 +0, 526, 526, 1, 518400, 0x119064de +0, 530, 530, 1, 518400, 0xc06bd99a +0, 534, 534, 1, 518400, 0xdfccd738 +0, 538, 538, 1, 518400, 0x6c2de0a5 +0, 542, 542, 1, 518400, 0x11c1fdf7 +0, 546, 546, 1, 518400, 0xdcd26a62 +0, 550, 550, 1, 518400, 0x0ff63f3d +0, 554, 554, 1, 518400, 0x6443382a +0, 558, 558, 1, 518400, 0x28ce5ce3 +0, 562, 562, 1, 518400, 0xe0d47fbd +0, 566, 566, 1, 518400, 0xfdc0beed +0, 570, 570, 1, 518400, 0x9adeddc4 +0, 574, 574, 1, 518400, 0x8e5669fc +0, 578, 578, 1, 518400, 0xf0beb8ae +0, 582, 582, 1, 518400, 0xbdd68806 +0, 586, 586, 1, 518400, 0xe3c6ae23 +0, 590, 590, 1, 518400, 0xeba952c1 +0, 594, 594, 1, 518400, 0x734ff153 0, 598, 598, 1, 518400, 0xc3c0f1cf 0, 603, 603, 1, 518400, 0x21a5df80 0, 607, 607, 1, 518400, 0x5b8e115b diff --git a/tests/ref/fate/h264-bsf-mp4toannexb b/tests/ref/fate/h264-bsf-mp4toannexb index 7cd086a268c..2049f39701e 100644 --- a/tests/ref/fate/h264-bsf-mp4toannexb +++ b/tests/ref/fate/h264-bsf-mp4toannexb @@ -1 +1 @@ -f340e7ca9a46d437af4e96f6c8de221c +5f04c27cc6ee8625fe2405fb0f7da9a3 diff --git a/tests/ref/fate/h264-encparams b/tests/ref/fate/h264-encparams new file mode 100644 index 00000000000..8dd5e86b74e --- /dev/null +++ b/tests/ref/fate/h264-encparams @@ -0,0 +1,404 @@ +frame 0 +AVVideoEncParams 1 +qp 28 +delta_qp[1][0] 3 +delta_qp[1][1] 3 +delta_qp[2][0] -2 +delta_qp[2][1] -2 +nb_blocks 396 +block 0 0:0 16x16 0 +block 1 16:0 16x16 0 +block 2 32:0 16x16 0 +block 3 48:0 16x16 0 +block 4 64:0 16x16 0 +block 5 80:0 16x16 0 +block 6 96:0 16x16 0 +block 7 112:0 16x16 0 +block 8 128:0 16x16 0 +block 9 144:0 16x16 0 +block 10 160:0 16x16 0 +block 11 176:0 16x16 0 +block 12 192:0 16x16 0 +block 13 208:0 16x16 0 +block 14 224:0 16x16 0 +block 15 240:0 16x16 0 +block 16 256:0 16x16 0 +block 17 272:0 16x16 0 +block 18 288:0 16x16 0 +block 19 304:0 16x16 0 +block 20 320:0 16x16 0 +block 21 336:0 16x16 0 +block 22 0:16 16x16 0 +block 23 16:16 16x16 0 +block 24 32:16 16x16 0 +block 25 48:16 16x16 0 +block 26 64:16 16x16 0 +block 27 80:16 16x16 0 +block 28 96:16 16x16 0 +block 29 112:16 16x16 0 +block 30 128:16 16x16 0 +block 31 144:16 16x16 0 +block 32 160:16 16x16 0 +block 33 176:16 16x16 0 +block 34 192:16 16x16 0 +block 35 208:16 16x16 0 +block 36 224:16 16x16 0 +block 37 240:16 16x16 0 +block 38 256:16 16x16 0 +block 39 272:16 16x16 0 +block 40 288:16 16x16 0 +block 41 304:16 16x16 0 +block 42 320:16 16x16 0 +block 43 336:16 16x16 0 +block 44 0:32 16x16 0 +block 45 16:32 16x16 0 +block 46 32:32 16x16 0 +block 47 48:32 16x16 0 +block 48 64:32 16x16 0 +block 49 80:32 16x16 0 +block 50 96:32 16x16 0 +block 51 112:32 16x16 0 +block 52 128:32 16x16 0 +block 53 144:32 16x16 0 +block 54 160:32 16x16 0 +block 55 176:32 16x16 0 +block 56 192:32 16x16 0 +block 57 208:32 16x16 0 +block 58 224:32 16x16 0 +block 59 240:32 16x16 0 +block 60 256:32 16x16 0 +block 61 272:32 16x16 0 +block 62 288:32 16x16 0 +block 63 304:32 16x16 0 +block 64 320:32 16x16 0 +block 65 336:32 16x16 0 +block 66 0:48 16x16 0 +block 67 16:48 16x16 0 +block 68 32:48 16x16 0 +block 69 48:48 16x16 0 +block 70 64:48 16x16 0 +block 71 80:48 16x16 0 +block 72 96:48 16x16 0 +block 73 112:48 16x16 0 +block 74 128:48 16x16 0 +block 75 144:48 16x16 0 +block 76 160:48 16x16 0 +block 77 176:48 16x16 0 +block 78 192:48 16x16 0 +block 79 208:48 16x16 0 +block 80 224:48 16x16 0 +block 81 240:48 16x16 0 +block 82 256:48 16x16 0 +block 83 272:48 16x16 0 +block 84 288:48 16x16 0 +block 85 304:48 16x16 0 +block 86 320:48 16x16 0 +block 87 336:48 16x16 0 +block 88 0:64 16x16 0 +block 89 16:64 16x16 0 +block 90 32:64 16x16 0 +block 91 48:64 16x16 0 +block 92 64:64 16x16 0 +block 93 80:64 16x16 0 +block 94 96:64 16x16 0 +block 95 112:64 16x16 0 +block 96 128:64 16x16 0 +block 97 144:64 16x16 0 +block 98 160:64 16x16 0 +block 99 176:64 16x16 0 +block 100 192:64 16x16 0 +block 101 208:64 16x16 0 +block 102 224:64 16x16 0 +block 103 240:64 16x16 0 +block 104 256:64 16x16 0 +block 105 272:64 16x16 0 +block 106 288:64 16x16 0 +block 107 304:64 16x16 0 +block 108 320:64 16x16 0 +block 109 336:64 16x16 0 +block 110 0:80 16x16 0 +block 111 16:80 16x16 0 +block 112 32:80 16x16 0 +block 113 48:80 16x16 0 +block 114 64:80 16x16 0 +block 115 80:80 16x16 0 +block 116 96:80 16x16 0 +block 117 112:80 16x16 0 +block 118 128:80 16x16 0 +block 119 144:80 16x16 0 +block 120 160:80 16x16 0 +block 121 176:80 16x16 0 +block 122 192:80 16x16 0 +block 123 208:80 16x16 0 +block 124 224:80 16x16 0 +block 125 240:80 16x16 0 +block 126 256:80 16x16 0 +block 127 272:80 16x16 0 +block 128 288:80 16x16 0 +block 129 304:80 16x16 0 +block 130 320:80 16x16 0 +block 131 336:80 16x16 0 +block 132 0:96 16x16 0 +block 133 16:96 16x16 0 +block 134 32:96 16x16 0 +block 135 48:96 16x16 0 +block 136 64:96 16x16 0 +block 137 80:96 16x16 0 +block 138 96:96 16x16 0 +block 139 112:96 16x16 0 +block 140 128:96 16x16 0 +block 141 144:96 16x16 0 +block 142 160:96 16x16 0 +block 143 176:96 16x16 0 +block 144 192:96 16x16 0 +block 145 208:96 16x16 0 +block 146 224:96 16x16 0 +block 147 240:96 16x16 0 +block 148 256:96 16x16 0 +block 149 272:96 16x16 0 +block 150 288:96 16x16 0 +block 151 304:96 16x16 0 +block 152 320:96 16x16 0 +block 153 336:96 16x16 0 +block 154 0:112 16x16 0 +block 155 16:112 16x16 0 +block 156 32:112 16x16 0 +block 157 48:112 16x16 0 +block 158 64:112 16x16 0 +block 159 80:112 16x16 0 +block 160 96:112 16x16 0 +block 161 112:112 16x16 0 +block 162 128:112 16x16 0 +block 163 144:112 16x16 0 +block 164 160:112 16x16 0 +block 165 176:112 16x16 0 +block 166 192:112 16x16 0 +block 167 208:112 16x16 0 +block 168 224:112 16x16 0 +block 169 240:112 16x16 0 +block 170 256:112 16x16 0 +block 171 272:112 16x16 0 +block 172 288:112 16x16 0 +block 173 304:112 16x16 0 +block 174 320:112 16x16 0 +block 175 336:112 16x16 0 +block 176 0:128 16x16 0 +block 177 16:128 16x16 0 +block 178 32:128 16x16 0 +block 179 48:128 16x16 0 +block 180 64:128 16x16 0 +block 181 80:128 16x16 0 +block 182 96:128 16x16 0 +block 183 112:128 16x16 0 +block 184 128:128 16x16 0 +block 185 144:128 16x16 0 +block 186 160:128 16x16 0 +block 187 176:128 16x16 0 +block 188 192:128 16x16 0 +block 189 208:128 16x16 0 +block 190 224:128 16x16 0 +block 191 240:128 16x16 0 +block 192 256:128 16x16 0 +block 193 272:128 16x16 0 +block 194 288:128 16x16 0 +block 195 304:128 16x16 0 +block 196 320:128 16x16 0 +block 197 336:128 16x16 0 +block 198 0:144 16x16 0 +block 199 16:144 16x16 0 +block 200 32:144 16x16 0 +block 201 48:144 16x16 0 +block 202 64:144 16x16 0 +block 203 80:144 16x16 0 +block 204 96:144 16x16 0 +block 205 112:144 16x16 0 +block 206 128:144 16x16 0 +block 207 144:144 16x16 0 +block 208 160:144 16x16 0 +block 209 176:144 16x16 0 +block 210 192:144 16x16 0 +block 211 208:144 16x16 0 +block 212 224:144 16x16 0 +block 213 240:144 16x16 0 +block 214 256:144 16x16 0 +block 215 272:144 16x16 0 +block 216 288:144 16x16 0 +block 217 304:144 16x16 0 +block 218 320:144 16x16 0 +block 219 336:144 16x16 0 +block 220 0:160 16x16 0 +block 221 16:160 16x16 0 +block 222 32:160 16x16 0 +block 223 48:160 16x16 0 +block 224 64:160 16x16 0 +block 225 80:160 16x16 0 +block 226 96:160 16x16 0 +block 227 112:160 16x16 0 +block 228 128:160 16x16 0 +block 229 144:160 16x16 0 +block 230 160:160 16x16 0 +block 231 176:160 16x16 0 +block 232 192:160 16x16 0 +block 233 208:160 16x16 0 +block 234 224:160 16x16 0 +block 235 240:160 16x16 0 +block 236 256:160 16x16 0 +block 237 272:160 16x16 0 +block 238 288:160 16x16 0 +block 239 304:160 16x16 0 +block 240 320:160 16x16 0 +block 241 336:160 16x16 0 +block 242 0:176 16x16 0 +block 243 16:176 16x16 0 +block 244 32:176 16x16 0 +block 245 48:176 16x16 0 +block 246 64:176 16x16 0 +block 247 80:176 16x16 0 +block 248 96:176 16x16 0 +block 249 112:176 16x16 0 +block 250 128:176 16x16 0 +block 251 144:176 16x16 0 +block 252 160:176 16x16 0 +block 253 176:176 16x16 0 +block 254 192:176 16x16 0 +block 255 208:176 16x16 0 +block 256 224:176 16x16 0 +block 257 240:176 16x16 0 +block 258 256:176 16x16 0 +block 259 272:176 16x16 0 +block 260 288:176 16x16 0 +block 261 304:176 16x16 0 +block 262 320:176 16x16 0 +block 263 336:176 16x16 0 +block 264 0:192 16x16 0 +block 265 16:192 16x16 0 +block 266 32:192 16x16 0 +block 267 48:192 16x16 0 +block 268 64:192 16x16 0 +block 269 80:192 16x16 0 +block 270 96:192 16x16 0 +block 271 112:192 16x16 0 +block 272 128:192 16x16 0 +block 273 144:192 16x16 0 +block 274 160:192 16x16 0 +block 275 176:192 16x16 0 +block 276 192:192 16x16 0 +block 277 208:192 16x16 0 +block 278 224:192 16x16 0 +block 279 240:192 16x16 0 +block 280 256:192 16x16 0 +block 281 272:192 16x16 0 +block 282 288:192 16x16 0 +block 283 304:192 16x16 0 +block 284 320:192 16x16 0 +block 285 336:192 16x16 0 +block 286 0:208 16x16 0 +block 287 16:208 16x16 0 +block 288 32:208 16x16 0 +block 289 48:208 16x16 0 +block 290 64:208 16x16 0 +block 291 80:208 16x16 0 +block 292 96:208 16x16 0 +block 293 112:208 16x16 0 +block 294 128:208 16x16 0 +block 295 144:208 16x16 0 +block 296 160:208 16x16 0 +block 297 176:208 16x16 0 +block 298 192:208 16x16 0 +block 299 208:208 16x16 0 +block 300 224:208 16x16 0 +block 301 240:208 16x16 0 +block 302 256:208 16x16 0 +block 303 272:208 16x16 0 +block 304 288:208 16x16 0 +block 305 304:208 16x16 0 +block 306 320:208 16x16 0 +block 307 336:208 16x16 0 +block 308 0:224 16x16 0 +block 309 16:224 16x16 0 +block 310 32:224 16x16 0 +block 311 48:224 16x16 0 +block 312 64:224 16x16 0 +block 313 80:224 16x16 0 +block 314 96:224 16x16 0 +block 315 112:224 16x16 0 +block 316 128:224 16x16 0 +block 317 144:224 16x16 0 +block 318 160:224 16x16 0 +block 319 176:224 16x16 0 +block 320 192:224 16x16 0 +block 321 208:224 16x16 0 +block 322 224:224 16x16 0 +block 323 240:224 16x16 0 +block 324 256:224 16x16 0 +block 325 272:224 16x16 0 +block 326 288:224 16x16 0 +block 327 304:224 16x16 0 +block 328 320:224 16x16 0 +block 329 336:224 16x16 0 +block 330 0:240 16x16 0 +block 331 16:240 16x16 0 +block 332 32:240 16x16 0 +block 333 48:240 16x16 0 +block 334 64:240 16x16 0 +block 335 80:240 16x16 0 +block 336 96:240 16x16 0 +block 337 112:240 16x16 0 +block 338 128:240 16x16 0 +block 339 144:240 16x16 0 +block 340 160:240 16x16 0 +block 341 176:240 16x16 0 +block 342 192:240 16x16 0 +block 343 208:240 16x16 0 +block 344 224:240 16x16 0 +block 345 240:240 16x16 0 +block 346 256:240 16x16 0 +block 347 272:240 16x16 0 +block 348 288:240 16x16 0 +block 349 304:240 16x16 0 +block 350 320:240 16x16 0 +block 351 336:240 16x16 0 +block 352 0:256 16x16 0 +block 353 16:256 16x16 0 +block 354 32:256 16x16 0 +block 355 48:256 16x16 0 +block 356 64:256 16x16 0 +block 357 80:256 16x16 0 +block 358 96:256 16x16 0 +block 359 112:256 16x16 0 +block 360 128:256 16x16 0 +block 361 144:256 16x16 0 +block 362 160:256 16x16 0 +block 363 176:256 16x16 0 +block 364 192:256 16x16 0 +block 365 208:256 16x16 0 +block 366 224:256 16x16 0 +block 367 240:256 16x16 0 +block 368 256:256 16x16 0 +block 369 272:256 16x16 0 +block 370 288:256 16x16 0 +block 371 304:256 16x16 0 +block 372 320:256 16x16 0 +block 373 336:256 16x16 0 +block 374 0:272 16x16 0 +block 375 16:272 16x16 0 +block 376 32:272 16x16 0 +block 377 48:272 16x16 0 +block 378 64:272 16x16 0 +block 379 80:272 16x16 0 +block 380 96:272 16x16 0 +block 381 112:272 16x16 0 +block 382 128:272 16x16 0 +block 383 144:272 16x16 0 +block 384 160:272 16x16 0 +block 385 176:272 16x16 0 +block 386 192:272 16x16 0 +block 387 208:272 16x16 0 +block 388 224:272 16x16 0 +block 389 240:272 16x16 0 +block 390 256:272 16x16 0 +block 391 272:272 16x16 0 +block 392 288:272 16x16 0 +block 393 304:272 16x16 0 +block 394 320:272 16x16 0 +block 395 336:272 16x16 0 diff --git a/tests/ref/fate/h264_mp4toannexb_ticket2991 b/tests/ref/fate/h264_mp4toannexb_ticket2991 index 3245ef442c6..76bdf3cae79 100644 --- a/tests/ref/fate/h264_mp4toannexb_ticket2991 +++ b/tests/ref/fate/h264_mp4toannexb_ticket2991 @@ -1,12 +1,12 @@ -dba672c154b41414cf26aae967c27eef *tests/data/fate/h264_mp4toannexb_ticket2991.h264 -1985823 tests/data/fate/h264_mp4toannexb_ticket2991.h264 -#extradata 0: 48, 0x47ae0d55 +05d66e60ab22ee004720e0051af0fe74 *tests/data/fate/h264_mp4toannexb_ticket2991.h264 +1985815 tests/data/fate/h264_mp4toannexb_ticket2991.h264 +#extradata 0: 47, 0x3a590d55 #tb 0: 1/1200000 #media_type 0: video #codec_id 0: h264 #dimensions 0: 1280x720 #sar 0: 3/4 -0, 0, 0, 48000, 37127, 0xc125184c +0, 0, 0, 48000, 37126, 0xb020184c 0, 48000, 48000, 40040, 6920, 0x8512361a, F=0x0 0, 88040, 88040, 40040, 7550, 0x1bc56ed4, F=0x0 0, 128081, 128081, 40040, 8752, 0xb8c6f0a1, F=0x0 @@ -21,7 +21,7 @@ dba672c154b41414cf26aae967c27eef *tests/data/fate/h264_mp4toannexb_ticket2991.h2 0, 488444, 488444, 40040, 11234, 0x83cbd9fd, F=0x0 0, 528485, 528485, 40040, 17616, 0xfdf95104, F=0x0 0, 568525, 568525, 40040, 10689, 0x9633d32b, F=0x0 -0, 608566, 608566, 40040, 45292, 0x66dd2cf6 +0, 608566, 608566, 40040, 45291, 0x543c2cf6 0, 648606, 648606, 40040, 20837, 0x051abfab, F=0x0 0, 688646, 688646, 40040, 21418, 0xe2a59d70, F=0x0 0, 728687, 728687, 40040, 15643, 0x15cf2cec, F=0x0 @@ -36,7 +36,7 @@ dba672c154b41414cf26aae967c27eef *tests/data/fate/h264_mp4toannexb_ticket2991.h2 0, 1089050, 1089050, 40040, 13130, 0xcbb6bb8e, F=0x0 0, 1129091, 1129091, 40040, 16180, 0x5d188a7a, F=0x0 0, 1169131, 1169131, 40040, 14961, 0x9ff2f463, F=0x0 -0, 1209172, 1209172, 40040, 54297, 0xf98d30ed +0, 1209172, 1209172, 40040, 54296, 0xe6ec30ed 0, 1249212, 1249212, 40040, 11500, 0x8c4852c9, F=0x0 0, 1289252, 1289252, 40040, 12065, 0xfb7954c3, F=0x0 0, 1329293, 1329293, 40040, 12532, 0xf0a935d3, F=0x0 @@ -51,7 +51,7 @@ dba672c154b41414cf26aae967c27eef *tests/data/fate/h264_mp4toannexb_ticket2991.h2 0, 1689656, 1689656, 40040, 13250, 0xfed0deb8, F=0x0 0, 1729697, 1729697, 40040, 13360, 0xbf92d476, F=0x0 0, 1769737, 1769737, 40040, 11749, 0x3041eaf1, F=0x0 -0, 1809778, 1809778, 40040, 23998, 0xee87d5c4 +0, 1809778, 1809778, 40040, 23997, 0xdbe6d5c4 0, 1849818, 1849818, 40040, 16065, 0xe8f715b7, F=0x0 0, 1889858, 1889858, 40040, 16441, 0x0a4e060f, F=0x0 0, 1929899, 1929899, 40040, 17395, 0xa8edecc2, F=0x0 @@ -66,7 +66,7 @@ dba672c154b41414cf26aae967c27eef *tests/data/fate/h264_mp4toannexb_ticket2991.h2 0, 2290262, 2290262, 40040, 13748, 0xed26aeb4, F=0x0 0, 2330303, 2330303, 40040, 15092, 0x3c983538, F=0x0 0, 2370343, 2370343, 40040, 14636, 0x9b278a6c, F=0x0 -0, 2410384, 2410384, 40040, 29135, 0x0a34be18 +0, 2410384, 2410384, 40040, 29134, 0xf784be18 0, 2450424, 2450424, 40040, 10232, 0x5408e15b, F=0x0 0, 2490464, 2490464, 40040, 9769, 0xc93cb7f9, F=0x0 0, 2530505, 2530505, 40040, 14454, 0x45230dbe, F=0x0 @@ -81,7 +81,7 @@ dba672c154b41414cf26aae967c27eef *tests/data/fate/h264_mp4toannexb_ticket2991.h2 0, 2890868, 2890868, 40040, 14801, 0x40bae016, F=0x0 0, 2930909, 2930909, 40040, 17303, 0x9ce1fd31, F=0x0 0, 2970949, 2970949, 40040, 17678, 0x9bd66141, F=0x0 -0, 3010990, 3010990, 40040, 48673, 0x44b6ce46 +0, 3010990, 3010990, 40040, 48672, 0x3215ce46 0, 3051030, 3051030, 40040, 11894, 0x12e1fece, F=0x0 0, 3091070, 3091070, 40040, 16514, 0xc57aed05, F=0x0 0, 3131111, 3131111, 40040, 13044, 0x61914fa0, F=0x0 @@ -96,7 +96,7 @@ dba672c154b41414cf26aae967c27eef *tests/data/fate/h264_mp4toannexb_ticket2991.h2 0, 3491474, 3491474, 40040, 12208, 0x81a587c0, F=0x0 0, 3531515, 3531515, 40040, 14709, 0x5dffbe04, F=0x0 0, 3571555, 3571555, 40040, 14390, 0xbfd1e041, F=0x0 -0, 3611596, 3611596, 40040, 37237, 0xfa9a24b1 +0, 3611596, 3611596, 40040, 37236, 0xe7f924b1 0, 3651636, 3651636, 40040, 14056, 0x24714c7c, F=0x0 0, 3691676, 3691676, 40040, 19438, 0x0c50dcd5, F=0x0 0, 3731717, 3731717, 40040, 21728, 0x7eea4a11, F=0x0 @@ -111,7 +111,7 @@ dba672c154b41414cf26aae967c27eef *tests/data/fate/h264_mp4toannexb_ticket2991.h2 0, 4092080, 4092080, 40040, 16878, 0x98efbae2, F=0x0 0, 4132121, 4132121, 40040, 14685, 0x1bf78d65, F=0x0 0, 4172161, 4172161, 40040, 13127, 0x0b91881d, F=0x0 -0, 4212202, 4212202, 40040, 29391, 0x0955ed6b +0, 4212202, 4212202, 40040, 29390, 0xf6a5ed6b 0, 4252242, 4252242, 40040, 12576, 0xe9845ded, F=0x0 0, 4292282, 4292282, 40040, 12599, 0x96a79ab8, F=0x0 0, 4332323, 4332323, 40040, 16134, 0xb4c36d3f, F=0x0 diff --git a/tests/ref/fate/h264_mp4toannexb_ticket5927 b/tests/ref/fate/h264_mp4toannexb_ticket5927 index 006ea398fd6..95e35c4d802 100644 --- a/tests/ref/fate/h264_mp4toannexb_ticket5927 +++ b/tests/ref/fate/h264_mp4toannexb_ticket5927 @@ -1,12 +1,12 @@ -562487bfea635cdadbc23d390322b589 *tests/data/fate/h264_mp4toannexb_ticket5927.h264 -595585 tests/data/fate/h264_mp4toannexb_ticket5927.h264 -#extradata 0: 34, 0x8df608f8 +a3b02fd09392e01619cebc959d4d9ff2 *tests/data/fate/h264_mp4toannexb_ticket5927.h264 +595583 tests/data/fate/h264_mp4toannexb_ticket5927.h264 +#extradata 0: 33, 0x84fe08f8 #tb 0: 1/1200000 #media_type 0: video #codec_id 0: h264 #dimensions 0: 1920x1080 #sar 0: 0/1 -0, -48000, -9223372036854775808, 48000, 247994, 0x2e1e21ea +0, -48000, -9223372036854775808, 48000, 247993, 0x1ce821ea 0, 0, -9223372036854775808, 48000, 43354, 0xa05dca6f, F=0x0 0, 48000, -9223372036854775808, 48000, 11423, 0x5e8086dd, F=0x0 0, 96000, -9223372036854775808, 48000, 50798, 0x145fbe4f, F=0x0 @@ -18,4 +18,4 @@ 0, 384000, -9223372036854775808, 48000, 54483, 0xefead99f, F=0x0 0, 432000, -9223372036854775808, 48000, 13705, 0x23cd27e8, F=0x0 0, 480000, -9223372036854775808, 48000, 22308, 0x4093b5af, F=0x0 -0, 528000, -9223372036854775808, 48000, 6370, 0x96c12aa1 +0, 528000, -9223372036854775808, 48000, 6369, 0x858b2aa1 diff --git a/tests/ref/fate/h264_mp4toannexb_ticket5927_2 b/tests/ref/fate/h264_mp4toannexb_ticket5927_2 index 51432b15353..8db6a7e54a7 100644 --- a/tests/ref/fate/h264_mp4toannexb_ticket5927_2 +++ b/tests/ref/fate/h264_mp4toannexb_ticket5927_2 @@ -1,12 +1,12 @@ -562487bfea635cdadbc23d390322b589 *tests/data/fate/h264_mp4toannexb_ticket5927_2.h264 -595585 tests/data/fate/h264_mp4toannexb_ticket5927_2.h264 -#extradata 0: 34, 0x8df608f8 +a3b02fd09392e01619cebc959d4d9ff2 *tests/data/fate/h264_mp4toannexb_ticket5927_2.h264 +595583 tests/data/fate/h264_mp4toannexb_ticket5927_2.h264 +#extradata 0: 33, 0x84fe08f8 #tb 0: 1/1200000 #media_type 0: video #codec_id 0: h264 #dimensions 0: 1920x1080 #sar 0: 0/1 -0, -48000, -9223372036854775808, 48000, 247994, 0x2e1e21ea +0, -48000, -9223372036854775808, 48000, 247993, 0x1ce821ea 0, 0, -9223372036854775808, 48000, 43354, 0xa05dca6f, F=0x0 0, 48000, -9223372036854775808, 48000, 11423, 0x5e8086dd, F=0x0 0, 96000, -9223372036854775808, 48000, 50798, 0x145fbe4f, F=0x0 @@ -18,4 +18,4 @@ 0, 384000, -9223372036854775808, 48000, 54483, 0xefead99f, F=0x0 0, 432000, -9223372036854775808, 48000, 13705, 0x23cd27e8, F=0x0 0, 480000, -9223372036854775808, 48000, 22308, 0x4093b5af, F=0x0 -0, 528000, -9223372036854775808, 48000, 6370, 0x96c12aa1 +0, 528000, -9223372036854775808, 48000, 6369, 0x858b2aa1 diff --git a/tests/ref/fate/hapqa-extract-nosnappy-to-hapalphaonly-mov b/tests/ref/fate/hapqa-extract-nosnappy-to-hapalphaonly-mov index eacb1f1910c..61af08aa238 100644 --- a/tests/ref/fate/hapqa-extract-nosnappy-to-hapalphaonly-mov +++ b/tests/ref/fate/hapqa-extract-nosnappy-to-hapalphaonly-mov @@ -26,6 +26,7 @@ width=127 height=71 coded_width=128 coded_height=72 +closed_captions=0 has_b_frames=0 sample_aspect_ratio=1:1 display_aspect_ratio=127:71 diff --git a/tests/ref/fate/hapqa-extract-nosnappy-to-hapq-mov b/tests/ref/fate/hapqa-extract-nosnappy-to-hapq-mov index ff96888f412..1fb31ec7f03 100644 --- a/tests/ref/fate/hapqa-extract-nosnappy-to-hapq-mov +++ b/tests/ref/fate/hapqa-extract-nosnappy-to-hapq-mov @@ -26,6 +26,7 @@ width=127 height=71 coded_width=128 coded_height=72 +closed_captions=0 has_b_frames=0 sample_aspect_ratio=1:1 display_aspect_ratio=127:71 diff --git a/tests/ref/fate/hapqa-extract-snappy1-to-hapalphaonly b/tests/ref/fate/hapqa-extract-snappy1-to-hapalphaonly index 9ab123f09d1..7edd5fa65bb 100644 --- a/tests/ref/fate/hapqa-extract-snappy1-to-hapalphaonly +++ b/tests/ref/fate/hapqa-extract-snappy1-to-hapalphaonly @@ -3,4 +3,4 @@ #codec_id 0: hap #dimensions 0: 127x71 #sar 0: 1/1 -0, 0, 0, 1, 3044, 0xcaf6ddd0 +0, 0, 0, 1, 3044, 0xcaf6ddd0, F=0x11 diff --git a/tests/ref/fate/hapqa-extract-snappy1-to-hapq b/tests/ref/fate/hapqa-extract-snappy1-to-hapq index f658b1c0b49..1340f77bf53 100644 --- a/tests/ref/fate/hapqa-extract-snappy1-to-hapq +++ b/tests/ref/fate/hapqa-extract-snappy1-to-hapq @@ -3,4 +3,4 @@ #codec_id 0: hap #dimensions 0: 127x71 #sar 0: 1/1 -0, 0, 0, 1, 8217, 0x04271f0f +0, 0, 0, 1, 8217, 0x04271f0f, F=0x11 diff --git a/tests/ref/fate/hapqa-extract-snappy16-to-hapalphaonly b/tests/ref/fate/hapqa-extract-snappy16-to-hapalphaonly index 1bd920699ae..e6adc004d9b 100644 --- a/tests/ref/fate/hapqa-extract-snappy16-to-hapalphaonly +++ b/tests/ref/fate/hapqa-extract-snappy16-to-hapalphaonly @@ -3,4 +3,4 @@ #codec_id 0: hap #dimensions 0: 127x71 #sar 0: 1/1 -0, 0, 0, 1, 3513, 0x69c7014f +0, 0, 0, 1, 3513, 0x69c7014f, F=0x11 diff --git a/tests/ref/fate/hapqa-extract-snappy16-to-hapq b/tests/ref/fate/hapqa-extract-snappy16-to-hapq index 8334d53d616..f356301501d 100644 --- a/tests/ref/fate/hapqa-extract-snappy16-to-hapq +++ b/tests/ref/fate/hapqa-extract-snappy16-to-hapq @@ -3,4 +3,4 @@ #codec_id 0: hap #dimensions 0: 127x71 #sar 0: 1/1 -0, 0, 0, 1, 8726, 0xf889691c +0, 0, 0, 1, 8726, 0xf889691c, F=0x11 diff --git a/tests/ref/fate/hevc-cabac-tudepth b/tests/ref/fate/hevc-cabac-tudepth new file mode 100644 index 00000000000..ad874c3ddeb --- /dev/null +++ b/tests/ref/fate/hevc-cabac-tudepth @@ -0,0 +1,6 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 64x64 +#sar 0: 0/1 +0, 0, 0, 1, 12288, 0x0127a0d9 diff --git a/tests/ref/fate/hls-segment-single b/tests/ref/fate/hls-segment-single new file mode 100644 index 00000000000..ee3c7b2c62a --- /dev/null +++ b/tests/ref/fate/hls-segment-single @@ -0,0 +1,772 @@ +#tb 0: 1/44100 +#media_type 0: audio +#codec_id 0: pcm_s16le +#sample_rate 0: 44100 +#channel_layout 0: 4 +#channel_layout_name 0: mono +0, 0, 0, 1152, 2304, 0x907cb7fa +0, 1152, 1152, 1152, 2304, 0xb8dc7525 +0, 2304, 2304, 1152, 2304, 0x3e7d6905 +0, 3456, 3456, 1152, 2304, 0xef47877b +0, 4608, 4608, 1152, 2304, 0xfe916b7e +0, 5760, 5760, 1152, 2304, 0xe3d08cde +0, 6912, 6912, 1152, 2304, 0xff7f86cf +0, 8064, 8064, 1152, 2304, 0x843e6f95 +0, 9216, 9216, 1152, 2304, 0x81577c26 +0, 10368, 10368, 1152, 2304, 0x04a085d5 +0, 11520, 11520, 1152, 2304, 0x1c5a76f5 +0, 12672, 12672, 1152, 2304, 0x4ee78623 +0, 13824, 13824, 1152, 2304, 0x8ec861dc +0, 14976, 14976, 1152, 2304, 0x0ca179d8 +0, 16128, 16128, 1152, 2304, 0xc6da750f +0, 17280, 17280, 1152, 2304, 0xf6bf79b5 +0, 18432, 18432, 1152, 2304, 0x97b88a43 +0, 19584, 19584, 1152, 2304, 0xf13c7b9c +0, 20736, 20736, 1152, 2304, 0xdfba83af +0, 21888, 21888, 1152, 2304, 0xc9467d4b +0, 23040, 23040, 1152, 2304, 0xbbb58e2b +0, 24192, 24192, 1152, 2304, 0x3a1078ea +0, 25344, 25344, 1152, 2304, 0xe9587a5c +0, 26496, 26496, 1152, 2304, 0xef5a8039 +0, 27648, 27648, 1152, 2304, 0x9d5f782f +0, 28800, 28800, 1152, 2304, 0x1a548291 +0, 29952, 29952, 1152, 2304, 0x07517701 +0, 31104, 31104, 1152, 2304, 0x78127d6e +0, 32256, 32256, 1152, 2304, 0x62e2788a +0, 33408, 33408, 1152, 2304, 0x29397ad9 +0, 34560, 34560, 1152, 2304, 0x45da82d6 +0, 35712, 35712, 1152, 2304, 0x8ed66e51 +0, 36864, 36864, 1152, 2304, 0x660775cd +0, 38016, 38016, 1152, 2304, 0x802c767a +0, 39168, 39168, 1152, 2304, 0xcc055840 +0, 40320, 40320, 1152, 2304, 0x701b7eaf +0, 41472, 41472, 1152, 2304, 0x8290749f +0, 42624, 42624, 1152, 2304, 0x2c7b7d30 +0, 43776, 43776, 1152, 2304, 0xe4f17743 +0, 44928, 44928, 1152, 2304, 0x0e747d6e +0, 46080, 46080, 1152, 2304, 0xbe7775a0 +0, 47232, 47232, 1152, 2304, 0xcf797673 +0, 48384, 48384, 1152, 2304, 0x29cb7800 +0, 49536, 49536, 1152, 2304, 0xfc947890 +0, 50688, 50688, 1152, 2304, 0x62757fc6 +0, 51840, 51840, 1152, 2304, 0x098876d0 +0, 52992, 52992, 1152, 2304, 0xa9567ee2 +0, 54144, 54144, 1152, 2304, 0xe3bb9173 +0, 55296, 55296, 1152, 2304, 0xcc2d6dee +0, 56448, 56448, 1152, 2304, 0xe94591ab +0, 57600, 57600, 1152, 2304, 0x5c7588de +0, 58752, 58752, 1152, 2304, 0xfd83643c +0, 59904, 59904, 1152, 2304, 0x528177f1 +0, 61056, 61056, 1152, 2304, 0x65d08474 +0, 62208, 62208, 1152, 2304, 0x738d765b +0, 63360, 63360, 1152, 2304, 0xdd3d810e +0, 64512, 64512, 1152, 2304, 0xef4f90d3 +0, 65664, 65664, 1152, 2304, 0x61e28d43 +0, 66816, 66816, 1152, 2304, 0x9a11796b +0, 67968, 67968, 1152, 2304, 0x96c97dcd +0, 69120, 69120, 1152, 2304, 0xa8fe8621 +0, 70272, 70272, 1152, 2304, 0x499b7d38 +0, 71424, 71424, 1152, 2304, 0xfcb078a9 +0, 72576, 72576, 1152, 2304, 0x40d78651 +0, 73728, 73728, 1152, 2304, 0xa4af7234 +0, 74880, 74880, 1152, 2304, 0x6831870a +0, 76032, 76032, 1152, 2304, 0x030e7b9d +0, 77184, 77184, 1152, 2304, 0x445a75b6 +0, 78336, 78336, 1152, 2304, 0x09857389 +0, 79488, 79488, 1152, 2304, 0x0d018866 +0, 80640, 80640, 1152, 2304, 0x2afe810a +0, 81792, 81792, 1152, 2304, 0x0bcf7c43 +0, 82944, 82944, 1152, 2304, 0x13737c12 +0, 84096, 84096, 1152, 2304, 0x716c7bba +0, 85248, 85248, 1152, 2304, 0xb801823b +0, 86400, 86400, 1152, 2304, 0x0fd573ee +0, 87552, 87552, 1152, 2304, 0xe1ab879c +0, 88704, 88704, 1152, 2304, 0x49e6764f +0, 89856, 89856, 1152, 2304, 0xd5f26ddc +0, 91008, 91008, 1152, 2304, 0x076775ff +0, 92160, 92160, 1152, 2304, 0xfbb86fce +0, 93312, 93312, 1152, 2304, 0x20c56858 +0, 94464, 94464, 1152, 2304, 0x043e6891 +0, 95616, 95616, 1152, 2304, 0x59648729 +0, 96768, 96768, 1152, 2304, 0xd4907a63 +0, 97920, 97920, 1152, 2304, 0xd0208a4c +0, 99072, 99072, 1152, 2304, 0xce968383 +0, 100224, 100224, 1152, 2304, 0x3cfc7cd1 +0, 101376, 101376, 1152, 2304, 0x628a7bf5 +0, 102528, 102528, 1152, 2304, 0x9cfe8a4f +0, 103680, 103680, 1152, 2304, 0xdf6f7c6d +0, 104832, 104832, 1152, 2304, 0x6cf6882a +0, 105984, 105984, 1152, 2304, 0x099773a3 +0, 107136, 107136, 1152, 2304, 0x4a1c7649 +0, 108288, 108288, 1152, 2304, 0x31ea71cb +0, 109440, 109440, 1152, 2304, 0xed127ed9 +0, 110592, 110592, 1152, 2304, 0x5b156954 +0, 111744, 111744, 1152, 2304, 0xdd638532 +0, 112896, 112896, 1152, 2304, 0xf1a271f2 +0, 114048, 114048, 1152, 2304, 0x779184d7 +0, 115200, 115200, 1152, 2304, 0x49a88aa8 +0, 116352, 116352, 1152, 2304, 0xa11b7c90 +0, 117504, 117504, 1152, 2304, 0xbf488274 +0, 118656, 118656, 1152, 2304, 0x002f79a8 +0, 119808, 119808, 1152, 2304, 0x0ed97e2f +0, 120960, 120960, 1152, 2304, 0x7845878f +0, 122112, 122112, 1152, 2304, 0x46d777dc +0, 123264, 123264, 1152, 2304, 0x8d0179e3 +0, 124416, 124416, 1152, 2304, 0x38917f9f +0, 125568, 125568, 1152, 2304, 0x449876e7 +0, 126720, 126720, 1152, 2304, 0x001a8769 +0, 127872, 127872, 1152, 2304, 0x06c1826b +0, 129024, 129024, 1152, 2304, 0x41b68047 +0, 130176, 130176, 1152, 2304, 0xeb9782c6 +0, 131328, 131328, 1152, 2304, 0x7cd9719c +0, 132480, 132480, 1152, 2304, 0x3a4a767c +0, 133632, 133632, 1152, 2304, 0x7f887e81 +0, 134784, 134784, 1152, 2304, 0xf75d714b +0, 135936, 135936, 1152, 2304, 0x33b57e9f +0, 137088, 137088, 1152, 2304, 0xc732749e +0, 138240, 138240, 1152, 2304, 0x386f7e1a +0, 139392, 139392, 1152, 2304, 0x6b9c767d +0, 140544, 140544, 1152, 2304, 0x701c83e5 +0, 141696, 141696, 1152, 2304, 0xb92571e1 +0, 142848, 142848, 1152, 2304, 0x833a84bc +0, 144000, 144000, 1152, 2304, 0x1b6984e0 +0, 145152, 145152, 1152, 2304, 0x1b2474ba +0, 146304, 146304, 1152, 2304, 0xc22775a6 +0, 147456, 147456, 1152, 2304, 0x3e8f7972 +0, 148608, 148608, 1152, 2304, 0x17a28a65 +0, 149760, 149760, 1152, 2304, 0x9b6178a4 +0, 150912, 150912, 1152, 2304, 0x5d707873 +0, 152064, 152064, 1152, 2304, 0x68e2645a +0, 153216, 153216, 1152, 2304, 0x1e377d28 +0, 154368, 154368, 1152, 2304, 0x54b384be +0, 155520, 155520, 1152, 2304, 0x0617808c +0, 156672, 156672, 1152, 2304, 0xbc2b8a6c +0, 157824, 157824, 1152, 2304, 0x7ced7180 +0, 158976, 158976, 1152, 2304, 0xf22180ab +0, 160128, 160128, 1152, 2304, 0xf13682c9 +0, 161280, 161280, 1152, 2304, 0x7eff87fd +0, 162432, 162432, 1152, 2304, 0x5a0b5cec +0, 163584, 163584, 1152, 2304, 0x57c18906 +0, 164736, 164736, 1152, 2304, 0xb55a6a16 +0, 165888, 165888, 1152, 2304, 0xf2608371 +0, 167040, 167040, 1152, 2304, 0x36df7576 +0, 168192, 168192, 1152, 2304, 0xdb106fb4 +0, 169344, 169344, 1152, 2304, 0x7e4f85d0 +0, 170496, 170496, 1152, 2304, 0xe3ee78ab +0, 171648, 171648, 1152, 2304, 0xd36b7dc7 +0, 172800, 172800, 1152, 2304, 0xadab7c5c +0, 173952, 173952, 1152, 2304, 0x70786f26 +0, 175104, 175104, 1152, 2304, 0xcd5d717e +0, 176256, 176256, 1152, 2304, 0xc1a96f9a +0, 177408, 177408, 1152, 2304, 0xad777887 +0, 178560, 178560, 1152, 2304, 0x98277c16 +0, 179712, 179712, 1152, 2304, 0x868882c5 +0, 180864, 180864, 1152, 2304, 0xc48092b9 +0, 182016, 182016, 1152, 2304, 0x230069da +0, 183168, 183168, 1152, 2304, 0x14147ad6 +0, 184320, 184320, 1152, 2304, 0xc9007172 +0, 185472, 185472, 1152, 2304, 0x85d67bcc +0, 186624, 186624, 1152, 2304, 0x22418bab +0, 187776, 187776, 1152, 2304, 0xe53c8b71 +0, 188928, 188928, 1152, 2304, 0x5a1a9053 +0, 190080, 190080, 1152, 2304, 0x9cd179af +0, 191232, 191232, 1152, 2304, 0xbb3c7d72 +0, 192384, 192384, 1152, 2304, 0x477a8677 +0, 193536, 193536, 1152, 2304, 0xe3337834 +0, 194688, 194688, 1152, 2304, 0x1cb56d77 +0, 195840, 195840, 1152, 2304, 0xe89d6dac +0, 196992, 196992, 1152, 2304, 0xd468827e +0, 198144, 198144, 1152, 2304, 0xebc46b87 +0, 199296, 199296, 1152, 2304, 0x5fbb78d2 +0, 200448, 200448, 1152, 2304, 0xa1b483d6 +0, 201600, 201600, 1152, 2304, 0x6fec7cab +0, 202752, 202752, 1152, 2304, 0xd86d6f6c +0, 203904, 203904, 1152, 2304, 0x8c2c7d51 +0, 205056, 205056, 1152, 2304, 0xe8377cd7 +0, 206208, 206208, 1152, 2304, 0xb57071b4 +0, 207360, 207360, 1152, 2304, 0xc35c71fd +0, 208512, 208512, 1152, 2304, 0x789079e9 +0, 209664, 209664, 1152, 2304, 0x413b710e +0, 210816, 210816, 1152, 2304, 0x82678332 +0, 211968, 211968, 1152, 2304, 0xe1576e75 +0, 213120, 213120, 1152, 2304, 0x7c0b7ad6 +0, 214272, 214272, 1152, 2304, 0xc6b6786d +0, 215424, 215424, 1152, 2304, 0x736f7b89 +0, 216576, 216576, 1152, 2304, 0x0ded72f1 +0, 217728, 217728, 1152, 2304, 0xcb877a3c +0, 218880, 218880, 1152, 2304, 0x7c497d40 +0, 220032, 220032, 1152, 2304, 0xaefc798c +0, 221184, 221184, 1152, 2304, 0x4cce748c +0, 222336, 222336, 1152, 2304, 0xaa187fbe +0, 223488, 223488, 1152, 2304, 0x1aa77db9 +0, 224640, 224640, 1152, 2304, 0x9e0074b8 +0, 225792, 225792, 1152, 2304, 0x74ee822b +0, 226944, 226944, 1152, 2304, 0x975c6ff6 +0, 228096, 228096, 1152, 2304, 0xe1847bb4 +0, 229248, 229248, 1152, 2304, 0xe0828777 +0, 230400, 230400, 1152, 2304, 0xf4027205 +0, 231552, 231552, 1152, 2304, 0x535e7a20 +0, 232704, 232704, 1152, 2304, 0x5bd88404 +0, 233856, 233856, 1152, 2304, 0xf29478b1 +0, 235008, 235008, 1152, 2304, 0x9b7c7d88 +0, 236160, 236160, 1152, 2304, 0xaeb07335 +0, 237312, 237312, 1152, 2304, 0xbef06e08 +0, 238464, 238464, 1152, 2304, 0x795f7b8c +0, 239616, 239616, 1152, 2304, 0x435a674d +0, 240768, 240768, 1152, 2304, 0xd8ee7a09 +0, 241920, 241920, 1152, 2304, 0x9059812e +0, 243072, 243072, 1152, 2304, 0x7481744a +0, 244224, 244224, 1152, 2304, 0xdff27475 +0, 245376, 245376, 1152, 2304, 0xb17783ab +0, 246528, 246528, 1152, 2304, 0x42e9706b +0, 247680, 247680, 1152, 2304, 0x9f0d86b4 +0, 248832, 248832, 1152, 2304, 0x2963955f +0, 249984, 249984, 1152, 2304, 0x059a6957 +0, 251136, 251136, 1152, 2304, 0x85948206 +0, 252288, 252288, 1152, 2304, 0x185e8400 +0, 253440, 253440, 1152, 2304, 0xe98e70df +0, 254592, 254592, 1152, 2304, 0x69057b27 +0, 255744, 255744, 1152, 2304, 0x49e26f21 +0, 256896, 256896, 1152, 2304, 0xb0867da5 +0, 258048, 258048, 1152, 2304, 0x785980ff +0, 259200, 259200, 1152, 2304, 0xf4b774be +0, 260352, 260352, 1152, 2304, 0x63897e8c +0, 261504, 261504, 1152, 2304, 0x248b89af +0, 262656, 262656, 1152, 2304, 0xd3627c4a +0, 263808, 263808, 1152, 2304, 0x5a4d9349 +0, 264960, 264960, 1152, 2304, 0xe2ce7c4c +0, 266112, 266112, 1152, 2304, 0x321f6c0b +0, 267264, 267264, 1152, 2304, 0x51ac74e0 +0, 268416, 268416, 1152, 2304, 0x8efa91ba +0, 269568, 269568, 1152, 2304, 0x8b4b784c +0, 270720, 270720, 1152, 2304, 0xe9e4879e +0, 271872, 271872, 1152, 2304, 0x8dc28081 +0, 273024, 273024, 1152, 2304, 0x44b477b0 +0, 274176, 274176, 1152, 2304, 0xf7b67084 +0, 275328, 275328, 1152, 2304, 0x4b198c17 +0, 276480, 276480, 1152, 2304, 0x9c947194 +0, 277632, 277632, 1152, 2304, 0x6eaa7f15 +0, 278784, 278784, 1152, 2304, 0x119f7c1d +0, 279936, 279936, 1152, 2304, 0x157b7f43 +0, 281088, 281088, 1152, 2304, 0xcd2e7acc +0, 282240, 282240, 1152, 2304, 0x97597247 +0, 283392, 283392, 1152, 2304, 0x7ba06acb +0, 284544, 284544, 1152, 2304, 0x233c7995 +0, 285696, 285696, 1152, 2304, 0x08e28587 +0, 286848, 286848, 1152, 2304, 0x92be84b5 +0, 288000, 288000, 1152, 2304, 0xbb857d43 +0, 289152, 289152, 1152, 2304, 0x168e7c74 +0, 290304, 290304, 1152, 2304, 0xac5465d9 +0, 291456, 291456, 1152, 2304, 0x18f58831 +0, 292608, 292608, 1152, 2304, 0x19b48196 +0, 293760, 293760, 1152, 2304, 0x20297653 +0, 294912, 294912, 1152, 2304, 0x93397a82 +0, 296064, 296064, 1152, 2304, 0x65ea7deb +0, 297216, 297216, 1152, 2304, 0xd7316e20 +0, 298368, 298368, 1152, 2304, 0x94107f2b +0, 299520, 299520, 1152, 2304, 0xec3b7dc6 +0, 300672, 300672, 1152, 2304, 0x2d3783aa +0, 301824, 301824, 1152, 2304, 0x07e47340 +0, 302976, 302976, 1152, 2304, 0xbc117893 +0, 304128, 304128, 1152, 2304, 0x8bd97851 +0, 305280, 305280, 1152, 2304, 0xc27376a9 +0, 306432, 306432, 1152, 2304, 0x30d88c83 +0, 307584, 307584, 1152, 2304, 0x19c2704c +0, 308736, 308736, 1152, 2304, 0x093b7b6e +0, 309888, 309888, 1152, 2304, 0x221a7349 +0, 311040, 311040, 1152, 2304, 0xa4fd82cd +0, 312192, 312192, 1152, 2304, 0x762e6bc9 +0, 313344, 313344, 1152, 2304, 0x270075d4 +0, 314496, 314496, 1152, 2304, 0xa5f27b90 +0, 315648, 315648, 1152, 2304, 0xf72e7edc +0, 316800, 316800, 1152, 2304, 0x42178486 +0, 317952, 317952, 1152, 2304, 0x5f7978e8 +0, 319104, 319104, 1152, 2304, 0x5d7c6703 +0, 320256, 320256, 1152, 2304, 0x2c4483d5 +0, 321408, 321408, 1152, 2304, 0x31bd951d +0, 322560, 322560, 1152, 2304, 0x99487af0 +0, 323712, 323712, 1152, 2304, 0x0bd27ee7 +0, 324864, 324864, 1152, 2304, 0xc3e07ac4 +0, 326016, 326016, 1152, 2304, 0x98a16ba7 +0, 327168, 327168, 1152, 2304, 0xd7a5747b +0, 328320, 328320, 1152, 2304, 0x96fb811c +0, 329472, 329472, 1152, 2304, 0x7cee8109 +0, 330624, 330624, 1152, 2304, 0x52b18ba2 +0, 331776, 331776, 1152, 2304, 0x33be8861 +0, 332928, 332928, 1152, 2304, 0xf41282a0 +0, 334080, 334080, 1152, 2304, 0xb4268993 +0, 335232, 335232, 1152, 2304, 0x52126a1c +0, 336384, 336384, 1152, 2304, 0x050b6f7a +0, 337536, 337536, 1152, 2304, 0x67a26fc3 +0, 338688, 338688, 1152, 2304, 0x966c7cf2 +0, 339840, 339840, 1152, 2304, 0x22097750 +0, 340992, 340992, 1152, 2304, 0xfbb0796c +0, 342144, 342144, 1152, 2304, 0xbd508964 +0, 343296, 343296, 1152, 2304, 0xc24478d8 +0, 344448, 344448, 1152, 2304, 0x3913769d +0, 345600, 345600, 1152, 2304, 0x8aab872f +0, 346752, 346752, 1152, 2304, 0x7cb4822f +0, 347904, 347904, 1152, 2304, 0xea318144 +0, 349056, 349056, 1152, 2304, 0xaf0f86d2 +0, 350208, 350208, 1152, 2304, 0x24f27598 +0, 351360, 351360, 1152, 2304, 0xd76f6d40 +0, 352512, 352512, 1152, 2304, 0x085071a7 +0, 353664, 353664, 1152, 2304, 0x1d11704c +0, 354816, 354816, 1152, 2304, 0x21517cbd +0, 355968, 355968, 1152, 2304, 0xcdca8d32 +0, 357120, 357120, 1152, 2304, 0x71c18433 +0, 358272, 358272, 1152, 2304, 0xd39d7d81 +0, 359424, 359424, 1152, 2304, 0x7a0d7a43 +0, 360576, 360576, 1152, 2304, 0x007c8884 +0, 361728, 361728, 1152, 2304, 0x403282d0 +0, 362880, 362880, 1152, 2304, 0xe3737214 +0, 364032, 364032, 1152, 2304, 0xaf906f47 +0, 365184, 365184, 1152, 2304, 0x54f57b3b +0, 366336, 366336, 1152, 2304, 0x29be7791 +0, 367488, 367488, 1152, 2304, 0xe3c663d5 +0, 368640, 368640, 1152, 2304, 0xd7258238 +0, 369792, 369792, 1152, 2304, 0x3719820d +0, 370944, 370944, 1152, 2304, 0xbe04814f +0, 372096, 372096, 1152, 2304, 0x556c815e +0, 373248, 373248, 1152, 2304, 0xb2447e10 +0, 374400, 374400, 1152, 2304, 0x7c16867c +0, 375552, 375552, 1152, 2304, 0x6a7b78ed +0, 376704, 376704, 1152, 2304, 0x5d307b81 +0, 377856, 377856, 1152, 2304, 0xaab680d3 +0, 379008, 379008, 1152, 2304, 0xb5d37a23 +0, 380160, 380160, 1152, 2304, 0x7f7d6f76 +0, 381312, 381312, 1152, 2304, 0x317a8296 +0, 382464, 382464, 1152, 2304, 0x8a987b3d +0, 383616, 383616, 1152, 2304, 0x4f317a27 +0, 384768, 384768, 1152, 2304, 0xfc65852f +0, 385920, 385920, 1152, 2304, 0x40527719 +0, 387072, 387072, 1152, 2304, 0x84988e13 +0, 388224, 388224, 1152, 2304, 0x318b6ddc +0, 389376, 389376, 1152, 2304, 0x94cf7939 +0, 390528, 390528, 1152, 2304, 0x6f22819d +0, 391680, 391680, 1152, 2304, 0xa7dd80a9 +0, 392832, 392832, 1152, 2304, 0x1c7968fa +0, 393984, 393984, 1152, 2304, 0xd9937bae +0, 395136, 395136, 1152, 2304, 0xf7137cf9 +0, 396288, 396288, 1152, 2304, 0xeadb84b5 +0, 397440, 397440, 1152, 2304, 0x9a2390ac +0, 398592, 398592, 1152, 2304, 0xdb6a73f6 +0, 399744, 399744, 1152, 2304, 0x69e07507 +0, 400896, 400896, 1152, 2304, 0xbc8478b2 +0, 402048, 402048, 1152, 2304, 0x32cf8638 +0, 403200, 403200, 1152, 2304, 0x2b8d755a +0, 404352, 404352, 1152, 2304, 0x52e05bd2 +0, 405504, 405504, 1152, 2304, 0x2aed8c49 +0, 406656, 406656, 1152, 2304, 0x587a896e +0, 407808, 407808, 1152, 2304, 0x6dd87dee +0, 408960, 408960, 1152, 2304, 0xd2858338 +0, 410112, 410112, 1152, 2304, 0xd90f7842 +0, 411264, 411264, 1152, 2304, 0xd6fb6d4a +0, 412416, 412416, 1152, 2304, 0x85498aea +0, 413568, 413568, 1152, 2304, 0x18597790 +0, 414720, 414720, 1152, 2304, 0x3cd78fea +0, 415872, 415872, 1152, 2304, 0x94377fbc +0, 417024, 417024, 1152, 2304, 0xf9db73f5 +0, 418176, 418176, 1152, 2304, 0x14fb6fca +0, 419328, 419328, 1152, 2304, 0xe9d17d69 +0, 420480, 420480, 1152, 2304, 0xdeb57286 +0, 421632, 421632, 1152, 2304, 0xa5d37e17 +0, 422784, 422784, 1152, 2304, 0xcf6882fb +0, 423936, 423936, 1152, 2304, 0x31758066 +0, 425088, 425088, 1152, 2304, 0x6b4d8175 +0, 426240, 426240, 1152, 2304, 0x2a3d7f8e +0, 427392, 427392, 1152, 2304, 0xc066743b +0, 428544, 428544, 1152, 2304, 0xcab88146 +0, 429696, 429696, 1152, 2304, 0x2b4c6e13 +0, 430848, 430848, 1152, 2304, 0x00b36b6f +0, 432000, 432000, 1152, 2304, 0x664a88d3 +0, 433152, 433152, 1152, 2304, 0x18a66f76 +0, 434304, 434304, 1152, 2304, 0x4f828a8b +0, 435456, 435456, 1152, 2304, 0x9cc7728e +0, 436608, 436608, 1152, 2304, 0xbe357936 +0, 437760, 437760, 1152, 2304, 0x19878f8d +0, 438912, 438912, 1152, 2304, 0x227b7c71 +0, 440064, 440064, 1152, 2304, 0xf7c879ec +0, 441216, 441216, 1152, 2304, 0x0bca7b50 +0, 442368, 442368, 1152, 2304, 0xe4398304 +0, 443520, 443520, 1152, 2304, 0xf5da75c7 +0, 444672, 444672, 1152, 2304, 0x9f9070a3 +0, 445824, 445824, 1152, 2304, 0x789076f6 +0, 446976, 446976, 1152, 2304, 0x362977cc +0, 448128, 448128, 1152, 2304, 0x4d0a8928 +0, 449280, 449280, 1152, 2304, 0x1bb767ec +0, 450432, 450432, 1152, 2304, 0xbe727fa5 +0, 451584, 451584, 1152, 2304, 0x27f38347 +0, 452736, 452736, 1152, 2304, 0x0a3c8783 +0, 453888, 453888, 1152, 2304, 0x8249639c +0, 455040, 455040, 1152, 2304, 0x3b076f69 +0, 456192, 456192, 1152, 2304, 0xd9597ee3 +0, 457344, 457344, 1152, 2304, 0x026e7fad +0, 458496, 458496, 1152, 2304, 0xbd7a6de4 +0, 459648, 459648, 1152, 2304, 0x7d718a4d +0, 460800, 460800, 1152, 2304, 0x1f5e83b4 +0, 461952, 461952, 1152, 2304, 0x597d7755 +0, 463104, 463104, 1152, 2304, 0x3fb080bd +0, 464256, 464256, 1152, 2304, 0xdcbd7b16 +0, 465408, 465408, 1152, 2304, 0x5c48865f +0, 466560, 466560, 1152, 2304, 0xcda37ae8 +0, 467712, 467712, 1152, 2304, 0x4810796d +0, 468864, 468864, 1152, 2304, 0x34317fd0 +0, 470016, 470016, 1152, 2304, 0x5c0e7456 +0, 471168, 471168, 1152, 2304, 0x44d78040 +0, 472320, 472320, 1152, 2304, 0x88587882 +0, 473472, 473472, 1152, 2304, 0x77687d5e +0, 474624, 474624, 1152, 2304, 0x116d68bb +0, 475776, 475776, 1152, 2304, 0x3e5b6f60 +0, 476928, 476928, 1152, 2304, 0x64ea783c +0, 478080, 478080, 1152, 2304, 0x23547f7d +0, 479232, 479232, 1152, 2304, 0x2eee7892 +0, 480384, 480384, 1152, 2304, 0xfb837cba +0, 481536, 481536, 1152, 2304, 0x86518209 +0, 482688, 482688, 1152, 2304, 0x672f7bba +0, 483840, 483840, 1152, 2304, 0x6ab583fb +0, 484992, 484992, 1152, 2304, 0xc1297428 +0, 486144, 486144, 1152, 2304, 0x164e7717 +0, 487296, 487296, 1152, 2304, 0xb754976d +0, 488448, 488448, 1152, 2304, 0xb99d81b2 +0, 489600, 489600, 1152, 2304, 0x79046fd6 +0, 490752, 490752, 1152, 2304, 0x9a3f8426 +0, 491904, 491904, 1152, 2304, 0x896371f5 +0, 493056, 493056, 1152, 2304, 0x63d1799f +0, 494208, 494208, 1152, 2304, 0x4842844e +0, 495360, 495360, 1152, 2304, 0x850e8372 +0, 496512, 496512, 1152, 2304, 0x85d07022 +0, 497664, 497664, 1152, 2304, 0x9e6683d1 +0, 498816, 498816, 1152, 2304, 0x301b8981 +0, 499968, 499968, 1152, 2304, 0x1f1e98c5 +0, 501120, 501120, 1152, 2304, 0xc8797b03 +0, 502272, 502272, 1152, 2304, 0xf9d189f5 +0, 503424, 503424, 1152, 2304, 0x0e0d88be +0, 504576, 504576, 1152, 2304, 0x6c1d7843 +0, 505728, 505728, 1152, 2304, 0xd13b8a38 +0, 506880, 506880, 1152, 2304, 0x9b8f773c +0, 508032, 508032, 1152, 2304, 0x9acd7309 +0, 509184, 509184, 1152, 2304, 0x5e7d7d15 +0, 510336, 510336, 1152, 2304, 0xf09d7640 +0, 511488, 511488, 1152, 2304, 0xaebb718f +0, 512640, 512640, 1152, 2304, 0x0c8570f4 +0, 513792, 513792, 1152, 2304, 0x3c93862c +0, 514944, 514944, 1152, 2304, 0xcee46696 +0, 516096, 516096, 1152, 2304, 0x01ba7e20 +0, 517248, 517248, 1152, 2304, 0x7fcb7a09 +0, 518400, 518400, 1152, 2304, 0xddf18c14 +0, 519552, 519552, 1152, 2304, 0xd2e97eeb +0, 520704, 520704, 1152, 2304, 0x514d8719 +0, 521856, 521856, 1152, 2304, 0xe89279c9 +0, 523008, 523008, 1152, 2304, 0x806d6a95 +0, 524160, 524160, 1152, 2304, 0xbc7a8a60 +0, 525312, 525312, 1152, 2304, 0x302f8fa8 +0, 526464, 526464, 1152, 2304, 0xb136784c +0, 527616, 527616, 1152, 2304, 0x9b0f6aab +0, 528768, 528768, 1152, 2304, 0xd8e27582 +0, 529920, 529920, 1152, 2304, 0xdaaf78b1 +0, 531072, 531072, 1152, 2304, 0x65967f5f +0, 532224, 532224, 1152, 2304, 0x6f917aa4 +0, 533376, 533376, 1152, 2304, 0x7f607444 +0, 534528, 534528, 1152, 2304, 0xfd316f2c +0, 535680, 535680, 1152, 2304, 0x776e83c7 +0, 536832, 536832, 1152, 2304, 0xb9c17e16 +0, 537984, 537984, 1152, 2304, 0xdf287de8 +0, 539136, 539136, 1152, 2304, 0xf33d96a3 +0, 540288, 540288, 1152, 2304, 0xad216e5b +0, 541440, 541440, 1152, 2304, 0x4a328342 +0, 542592, 542592, 1152, 2304, 0xcf3f8079 +0, 543744, 543744, 1152, 2304, 0xb46f77b0 +0, 544896, 544896, 1152, 2304, 0x3199713d +0, 546048, 546048, 1152, 2304, 0x5e667a0d +0, 547200, 547200, 1152, 2304, 0xa3047ae3 +0, 548352, 548352, 1152, 2304, 0x9edf8594 +0, 549504, 549504, 1152, 2304, 0xd16382d5 +0, 550656, 550656, 1152, 2304, 0x6e838328 +0, 551808, 551808, 1152, 2304, 0xa1f697c1 +0, 552960, 552960, 1152, 2304, 0xefcc749c +0, 554112, 554112, 1152, 2304, 0x1f94839e +0, 555264, 555264, 1152, 2304, 0x429e7c5b +0, 556416, 556416, 1152, 2304, 0x9b59711c +0, 557568, 557568, 1152, 2304, 0xdac27323 +0, 558720, 558720, 1152, 2304, 0xa4856d2b +0, 559872, 559872, 1152, 2304, 0x07a37498 +0, 561024, 561024, 1152, 2304, 0xe1ce7512 +0, 562176, 562176, 1152, 2304, 0x15e182c3 +0, 563328, 563328, 1152, 2304, 0x0fa46b8c +0, 564480, 564480, 1152, 2304, 0xbdf07bfd +0, 565632, 565632, 1152, 2304, 0xe0238b2a +0, 566784, 566784, 1152, 2304, 0xab537267 +0, 567936, 567936, 1152, 2304, 0xd46b75f3 +0, 569088, 569088, 1152, 2304, 0xec73794b +0, 570240, 570240, 1152, 2304, 0x680580c1 +0, 571392, 571392, 1152, 2304, 0x1ace8f6c +0, 572544, 572544, 1152, 2304, 0x19d583ac +0, 573696, 573696, 1152, 2304, 0x4b6b8105 +0, 574848, 574848, 1152, 2304, 0x392a78b2 +0, 576000, 576000, 1152, 2304, 0xd3916dad +0, 577152, 577152, 1152, 2304, 0x569c7a75 +0, 578304, 578304, 1152, 2304, 0xf5ac814b +0, 579456, 579456, 1152, 2304, 0x18d77e98 +0, 580608, 580608, 1152, 2304, 0x007074ce +0, 581760, 581760, 1152, 2304, 0x0fe38373 +0, 582912, 582912, 1152, 2304, 0x5a967920 +0, 584064, 584064, 1152, 2304, 0x22167501 +0, 585216, 585216, 1152, 2304, 0xf0828cab +0, 586368, 586368, 1152, 2304, 0xaeec71b7 +0, 587520, 587520, 1152, 2304, 0xc47b62ff +0, 588672, 588672, 1152, 2304, 0xab688478 +0, 589824, 589824, 1152, 2304, 0xf35e7bd2 +0, 590976, 590976, 1152, 2304, 0x9cff763e +0, 592128, 592128, 1152, 2304, 0x59568dc8 +0, 593280, 593280, 1152, 2304, 0x51a278ac +0, 594432, 594432, 1152, 2304, 0xc08a6e68 +0, 595584, 595584, 1152, 2304, 0xd3067ef4 +0, 596736, 596736, 1152, 2304, 0x54767c49 +0, 597888, 597888, 1152, 2304, 0xf8ff8386 +0, 599040, 599040, 1152, 2304, 0xef267f63 +0, 600192, 600192, 1152, 2304, 0xe2537cd9 +0, 601344, 601344, 1152, 2304, 0x77a57680 +0, 602496, 602496, 1152, 2304, 0x325c74ad +0, 603648, 603648, 1152, 2304, 0xd7fe87c4 +0, 604800, 604800, 1152, 2304, 0x2e756310 +0, 605952, 605952, 1152, 2304, 0x6a81796b +0, 607104, 607104, 1152, 2304, 0x2f057daf +0, 608256, 608256, 1152, 2304, 0xcd9f7c9d +0, 609408, 609408, 1152, 2304, 0xc91560a0 +0, 610560, 610560, 1152, 2304, 0x962a91eb +0, 611712, 611712, 1152, 2304, 0xa0ff7416 +0, 612864, 612864, 1152, 2304, 0xcb5c7dff +0, 614016, 614016, 1152, 2304, 0xd3527041 +0, 615168, 615168, 1152, 2304, 0xc89d77c2 +0, 616320, 616320, 1152, 2304, 0xe1ce7ccf +0, 617472, 617472, 1152, 2304, 0xe3417c4c +0, 618624, 618624, 1152, 2304, 0x3f1a7166 +0, 619776, 619776, 1152, 2304, 0xcdcc7e23 +0, 620928, 620928, 1152, 2304, 0x4e727e97 +0, 622080, 622080, 1152, 2304, 0x53427ff1 +0, 623232, 623232, 1152, 2304, 0x173f6ca9 +0, 624384, 624384, 1152, 2304, 0x962887ec +0, 625536, 625536, 1152, 2304, 0xcbec67f4 +0, 626688, 626688, 1152, 2304, 0x7a2c943d +0, 627840, 627840, 1152, 2304, 0x8b877570 +0, 628992, 628992, 1152, 2304, 0xcf337323 +0, 630144, 630144, 1152, 2304, 0x8c8682a4 +0, 631296, 631296, 1152, 2304, 0x94c3753c +0, 632448, 632448, 1152, 2304, 0x86898d79 +0, 633600, 633600, 1152, 2304, 0xdf667312 +0, 634752, 634752, 1152, 2304, 0x062f8ba8 +0, 635904, 635904, 1152, 2304, 0xa2c36f08 +0, 637056, 637056, 1152, 2304, 0x5bca7358 +0, 638208, 638208, 1152, 2304, 0x5648804d +0, 639360, 639360, 1152, 2304, 0xefac87c8 +0, 640512, 640512, 1152, 2304, 0x66bf7dcf +0, 641664, 641664, 1152, 2304, 0x62ad73bc +0, 642816, 642816, 1152, 2304, 0x72fe630c +0, 643968, 643968, 1152, 2304, 0xeebe87da +0, 645120, 645120, 1152, 2304, 0x11c870cf +0, 646272, 646272, 1152, 2304, 0x18fb7c27 +0, 647424, 647424, 1152, 2304, 0x39047145 +0, 648576, 648576, 1152, 2304, 0xdcf07032 +0, 649728, 649728, 1152, 2304, 0x61027c50 +0, 650880, 650880, 1152, 2304, 0x6e2e89de +0, 652032, 652032, 1152, 2304, 0xc50c6d1d +0, 653184, 653184, 1152, 2304, 0xeed587ee +0, 654336, 654336, 1152, 2304, 0xe38269c7 +0, 655488, 655488, 1152, 2304, 0xcf66806e +0, 656640, 656640, 1152, 2304, 0x2d3b7c1b +0, 657792, 657792, 1152, 2304, 0xa4127d48 +0, 658944, 658944, 1152, 2304, 0x480b8325 +0, 660096, 660096, 1152, 2304, 0xc1527221 +0, 661248, 661248, 1152, 2304, 0x94c1769a +0, 662400, 662400, 1152, 2304, 0xcfb37271 +0, 663552, 663552, 1152, 2304, 0x946d7a96 +0, 664704, 664704, 1152, 2304, 0xdfc18e50 +0, 665856, 665856, 1152, 2304, 0x10c48393 +0, 667008, 667008, 1152, 2304, 0x58556b10 +0, 668160, 668160, 1152, 2304, 0x997b7993 +0, 669312, 669312, 1152, 2304, 0x4a787992 +0, 670464, 670464, 1152, 2304, 0x11406c20 +0, 671616, 671616, 1152, 2304, 0x04a4874a +0, 672768, 672768, 1152, 2304, 0xf3077164 +0, 673920, 673920, 1152, 2304, 0x08ac80e3 +0, 675072, 675072, 1152, 2304, 0x268370d0 +0, 676224, 676224, 1152, 2304, 0x1d137778 +0, 677376, 677376, 1152, 2304, 0xfa148e97 +0, 678528, 678528, 1152, 2304, 0xec50717c +0, 679680, 679680, 1152, 2304, 0xcbf46b75 +0, 680832, 680832, 1152, 2304, 0xd4168038 +0, 681984, 681984, 1152, 2304, 0xdd9577f0 +0, 683136, 683136, 1152, 2304, 0xc7077685 +0, 684288, 684288, 1152, 2304, 0x34d25e91 +0, 685440, 685440, 1152, 2304, 0x96537e6d +0, 686592, 686592, 1152, 2304, 0xb12e7940 +0, 687744, 687744, 1152, 2304, 0x861d64c0 +0, 688896, 688896, 1152, 2304, 0xa2bc64ed +0, 690048, 690048, 1152, 2304, 0x0c5f8261 +0, 691200, 691200, 1152, 2304, 0x540584ff +0, 692352, 692352, 1152, 2304, 0xe8328b09 +0, 693504, 693504, 1152, 2304, 0x1e777079 +0, 694656, 694656, 1152, 2304, 0x453483b4 +0, 695808, 695808, 1152, 2304, 0x1cab7a1e +0, 696960, 696960, 1152, 2304, 0xcb37856d +0, 698112, 698112, 1152, 2304, 0x5a4883ed +0, 699264, 699264, 1152, 2304, 0xd1f27cbf +0, 700416, 700416, 1152, 2304, 0x0d377a4d +0, 701568, 701568, 1152, 2304, 0x264e76df +0, 702720, 702720, 1152, 2304, 0x2a68771e +0, 703872, 703872, 1152, 2304, 0xcb317a31 +0, 705024, 705024, 1152, 2304, 0xfc5d7a27 +0, 706176, 706176, 1152, 2304, 0x6e067d96 +0, 707328, 707328, 1152, 2304, 0x0c538560 +0, 708480, 708480, 1152, 2304, 0xfbad717a +0, 709632, 709632, 1152, 2304, 0xf9fc7608 +0, 710784, 710784, 1152, 2304, 0xb1817c8f +0, 711936, 711936, 1152, 2304, 0x57c37f82 +0, 713088, 713088, 1152, 2304, 0x8cac8356 +0, 714240, 714240, 1152, 2304, 0x97108186 +0, 715392, 715392, 1152, 2304, 0x095d81bb +0, 716544, 716544, 1152, 2304, 0x475f6b2b +0, 717696, 717696, 1152, 2304, 0xdf7c8cc5 +0, 718848, 718848, 1152, 2304, 0x979c77be +0, 720000, 720000, 1152, 2304, 0x56a7844b +0, 721152, 721152, 1152, 2304, 0x7ee46b21 +0, 722304, 722304, 1152, 2304, 0x05b67220 +0, 723456, 723456, 1152, 2304, 0x25787252 +0, 724608, 724608, 1152, 2304, 0x8ad278ad +0, 725760, 725760, 1152, 2304, 0x67bd722e +0, 726912, 726912, 1152, 2304, 0x204f77be +0, 728064, 728064, 1152, 2304, 0x82d27ae2 +0, 729216, 729216, 1152, 2304, 0x23fa82e4 +0, 730368, 730368, 1152, 2304, 0xa9cf8159 +0, 731520, 731520, 1152, 2304, 0x13f08749 +0, 732672, 732672, 1152, 2304, 0xf84f71b5 +0, 733824, 733824, 1152, 2304, 0x1cb777c8 +0, 734976, 734976, 1152, 2304, 0x11236722 +0, 736128, 736128, 1152, 2304, 0x10197cac +0, 737280, 737280, 1152, 2304, 0xbd417e65 +0, 738432, 738432, 1152, 2304, 0x9a1c7d05 +0, 739584, 739584, 1152, 2304, 0x4c3a85de +0, 740736, 740736, 1152, 2304, 0x03816eb7 +0, 741888, 741888, 1152, 2304, 0x80186e6c +0, 743040, 743040, 1152, 2304, 0x5c097928 +0, 744192, 744192, 1152, 2304, 0x94aa823d +0, 745344, 745344, 1152, 2304, 0xa1c27f04 +0, 746496, 746496, 1152, 2304, 0x6ddb74a9 +0, 747648, 747648, 1152, 2304, 0x5ea67901 +0, 748800, 748800, 1152, 2304, 0xd710742d +0, 749952, 749952, 1152, 2304, 0xf8c27add +0, 751104, 751104, 1152, 2304, 0xf1717011 +0, 752256, 752256, 1152, 2304, 0xb59072d0 +0, 753408, 753408, 1152, 2304, 0xc8dc84a7 +0, 754560, 754560, 1152, 2304, 0x33116737 +0, 755712, 755712, 1152, 2304, 0x86216bdd +0, 756864, 756864, 1152, 2304, 0xa2f87866 +0, 758016, 758016, 1152, 2304, 0x5d77771e +0, 759168, 759168, 1152, 2304, 0x5d8c77fd +0, 760320, 760320, 1152, 2304, 0x23cc89cb +0, 761472, 761472, 1152, 2304, 0x334e7407 +0, 762624, 762624, 1152, 2304, 0x01c976ff +0, 763776, 763776, 1152, 2304, 0x3a3b7b15 +0, 764928, 764928, 1152, 2304, 0xfa427de9 +0, 766080, 766080, 1152, 2304, 0xbeaa7c91 +0, 767232, 767232, 1152, 2304, 0xd6988b31 +0, 768384, 768384, 1152, 2304, 0x4db47f80 +0, 769536, 769536, 1152, 2304, 0xea687d9e +0, 770688, 770688, 1152, 2304, 0x1a6281ce +0, 771840, 771840, 1152, 2304, 0xe1958003 +0, 772992, 772992, 1152, 2304, 0xb4ae7c5e +0, 774144, 774144, 1152, 2304, 0x28827c8a +0, 775296, 775296, 1152, 2304, 0x1fb88b25 +0, 776448, 776448, 1152, 2304, 0x588d71e8 +0, 777600, 777600, 1152, 2304, 0x68227c34 +0, 778752, 778752, 1152, 2304, 0xee4d73e8 +0, 779904, 779904, 1152, 2304, 0x69287c6d +0, 781056, 781056, 1152, 2304, 0xbb04926a +0, 782208, 782208, 1152, 2304, 0x89456cec +0, 783360, 783360, 1152, 2304, 0xabe18992 +0, 784512, 784512, 1152, 2304, 0x50cc7f6c +0, 785664, 785664, 1152, 2304, 0x6d7270be +0, 786816, 786816, 1152, 2304, 0x664c6fef +0, 787968, 787968, 1152, 2304, 0x7f7982f3 +0, 789120, 789120, 1152, 2304, 0x6ca170e9 +0, 790272, 790272, 1152, 2304, 0x36437d5b +0, 791424, 791424, 1152, 2304, 0xfd2380e8 +0, 792576, 792576, 1152, 2304, 0x2e3c6e9f +0, 793728, 793728, 1152, 2304, 0xc8427f3f +0, 794880, 794880, 1152, 2304, 0x962a79ad +0, 796032, 796032, 1152, 2304, 0xc9597c8b +0, 797184, 797184, 1152, 2304, 0x899580bb +0, 798336, 798336, 1152, 2304, 0x2d179dff +0, 799488, 799488, 1152, 2304, 0x4ac1707c +0, 800640, 800640, 1152, 2304, 0x32ea7e95 +0, 801792, 801792, 1152, 2304, 0x265e9a2d +0, 802944, 802944, 1152, 2304, 0x1c6484d0 +0, 804096, 804096, 1152, 2304, 0x39ae6884 +0, 805248, 805248, 1152, 2304, 0x82ed7bc5 +0, 806400, 806400, 1152, 2304, 0x556b7b3c +0, 807552, 807552, 1152, 2304, 0xb7f778dd +0, 808704, 808704, 1152, 2304, 0x74447d55 +0, 809856, 809856, 1152, 2304, 0x0c66861e +0, 811008, 811008, 1152, 2304, 0x15ba7932 +0, 812160, 812160, 1152, 2304, 0xb19170fc +0, 813312, 813312, 1152, 2304, 0x19d37551 +0, 814464, 814464, 1152, 2304, 0xdc529142 +0, 815616, 815616, 1152, 2304, 0xf2637e77 +0, 816768, 816768, 1152, 2304, 0xd065944b +0, 817920, 817920, 1152, 2304, 0x22878123 +0, 819072, 819072, 1152, 2304, 0xc21a8bf7 +0, 820224, 820224, 1152, 2304, 0x2e3582dc +0, 821376, 821376, 1152, 2304, 0xd42f7987 +0, 822528, 822528, 1152, 2304, 0x69b88236 +0, 823680, 823680, 1152, 2304, 0x7c988f90 +0, 824832, 824832, 1152, 2304, 0x2cd66ded +0, 825984, 825984, 1152, 2304, 0x3e65828b +0, 827136, 827136, 1152, 2304, 0x7e9871c9 +0, 828288, 828288, 1152, 2304, 0xf1f2806b +0, 829440, 829440, 1152, 2304, 0xf5087c7b +0, 830592, 830592, 1152, 2304, 0x62b98097 +0, 831744, 831744, 1152, 2304, 0xec457c43 +0, 832896, 832896, 1152, 2304, 0x87af87a6 +0, 834048, 834048, 1152, 2304, 0x97cc757d +0, 835200, 835200, 1152, 2304, 0x08ca76bd +0, 836352, 836352, 1152, 2304, 0x14ae7cbd +0, 837504, 837504, 1152, 2304, 0x1f79709a +0, 838656, 838656, 1152, 2304, 0x17948207 +0, 839808, 839808, 1152, 2304, 0x16ee7228 +0, 840960, 840960, 1152, 2304, 0x76cc82d7 +0, 842112, 842112, 1152, 2304, 0x8f327a8e +0, 843264, 843264, 1152, 2304, 0x14ee7756 +0, 844416, 844416, 1152, 2304, 0x15996d2f +0, 845568, 845568, 1152, 2304, 0x4c707d5c +0, 846720, 846720, 1152, 2304, 0x268c6fee +0, 847872, 847872, 1152, 2304, 0x6d838c76 +0, 849024, 849024, 1152, 2304, 0xafa17e64 +0, 850176, 850176, 1152, 2304, 0xb6546e66 +0, 851328, 851328, 1152, 2304, 0x945d8b9f +0, 852480, 852480, 1152, 2304, 0x5bfb7446 +0, 853632, 853632, 1152, 2304, 0xae6086f9 +0, 854784, 854784, 1152, 2304, 0xa01380cd +0, 855936, 855936, 1152, 2304, 0x06f0828f +0, 857088, 857088, 1152, 2304, 0x0ae07176 +0, 858240, 858240, 1152, 2304, 0x66f07522 +0, 859392, 859392, 1152, 2304, 0x44018106 +0, 860544, 860544, 1152, 2304, 0x8cd283da +0, 861696, 861696, 1152, 2304, 0x14257f45 +0, 862848, 862848, 1152, 2304, 0x04979537 +0, 864000, 864000, 1152, 2304, 0x8b5f797c +0, 865152, 865152, 1152, 2304, 0x12d67493 +0, 866304, 866304, 1152, 2304, 0xc8886a25 +0, 867456, 867456, 1152, 2304, 0x614b803a +0, 868608, 868608, 1152, 2304, 0x75667d35 +0, 869760, 869760, 1152, 2304, 0xe42c7b00 +0, 870912, 870912, 1152, 2304, 0x37787927 +0, 872064, 872064, 1152, 2304, 0x85db8409 +0, 873216, 873216, 1152, 2304, 0x823b822c +0, 874368, 874368, 1152, 2304, 0xa1658479 +0, 875520, 875520, 1152, 2304, 0xdbe58ff7 +0, 876672, 876672, 1152, 2304, 0x725175e2 +0, 877824, 877824, 1152, 2304, 0xb2ae7741 +0, 878976, 878976, 1152, 2304, 0x4de169e4 +0, 880128, 880128, 1152, 2304, 0x3cb18530 +0, 881280, 881280, 1152, 2304, 0x5a0c5e7b diff --git a/tests/ref/fate/lossless-truehd-5.1 b/tests/ref/fate/lossless-truehd-5.1 deleted file mode 100644 index 373b9179486..00000000000 --- a/tests/ref/fate/lossless-truehd-5.1 +++ /dev/null @@ -1 +0,0 @@ -95d8aac39dd9f0d7fb83dc7b6f88df35 diff --git a/tests/ref/fate/lossless-truehd-5.1-downmix-2.0 b/tests/ref/fate/lossless-truehd-5.1-downmix-2.0 deleted file mode 100644 index f4afbc19b8b..00000000000 --- a/tests/ref/fate/lossless-truehd-5.1-downmix-2.0 +++ /dev/null @@ -1 +0,0 @@ -a269aee0051d4400c9117136f08c9767 diff --git a/tests/ref/fate/matroska-flac-channel-mapping b/tests/ref/fate/matroska-flac-channel-mapping new file mode 100644 index 00000000000..6d901600e5c --- /dev/null +++ b/tests/ref/fate/matroska-flac-channel-mapping @@ -0,0 +1,38 @@ +#extradata 0: 34, 0x7a7909e5 +#extradata 1: 34, 0x7a7909e5 +#tb 0: 651/31250000 +#media_type 0: audio +#codec_id 0: flac +#sample_rate 0: 48000 +#channel_layout 0: 3f +#channel_layout_name 0: 5.1 +#tb 1: 651/31250000 +#media_type 1: audio +#codec_id 1: flac +#sample_rate 1: 48000 +#channel_layout 1: 60f +#channel_layout_name 1: 5.1(side) +0, 0, 0, 4096, 26, 0x50f80431 +1, 0, 0, 4096, 26, 0x50f80431 +0, 4096, 4096, 4096, 26, 0x50d30416 +1, 4096, 4096, 4096, 26, 0x50d30416 +0, 8192, 8192, 4096, 26, 0x51ee046f +1, 8192, 8192, 4096, 26, 0x51ee046f +0, 12288, 12288, 4096, 26, 0x521d0458 +1, 12288, 12288, 4096, 26, 0x521d0458 +0, 16385, 16385, 4096, 26, 0x531c043d +1, 16385, 16385, 4096, 26, 0x531c043d +0, 20481, 20481, 4096, 26, 0x53cf0442 +1, 20481, 20481, 4096, 26, 0x53cf0442 +0, 24577, 24577, 4096, 26, 0x536a0473 +1, 24577, 24577, 4096, 26, 0x536a0473 +0, 28673, 28673, 4096, 26, 0x5321046c +1, 28673, 28673, 4096, 26, 0x5321046c +0, 32770, 32770, 4096, 26, 0x51b00449 +1, 32770, 32770, 4096, 26, 0x51b00449 +0, 36866, 36866, 4096, 26, 0x518b042e +1, 36866, 36866, 4096, 26, 0x518b042e +0, 40962, 40962, 4096, 26, 0x50f60447 +1, 40962, 40962, 4096, 26, 0x50f60447 +0, 45059, 45059, 3072, 28, 0x72540504 +1, 45059, 45059, 3072, 28, 0x72540504 diff --git a/tests/ref/fate/matroska-flac-extradata-update b/tests/ref/fate/matroska-flac-extradata-update new file mode 100644 index 00000000000..fb17bfea0b9 --- /dev/null +++ b/tests/ref/fate/matroska-flac-extradata-update @@ -0,0 +1,53 @@ +3c721898cf2cf3e2e6c43ad58952bd2d *tests/data/fate/matroska-flac-extradata-update.matroska +2032 tests/data/fate/matroska-flac-extradata-update.matroska +#extradata 0: 34, 0x7acb09e7 +#extradata 1: 34, 0x7acb09e7 +#extradata 2: 34, 0x443402dd +#tb 0: 1/1000 +#media_type 0: audio +#codec_id 0: flac +#sample_rate 0: 48000 +#channel_layout 0: 3f +#channel_layout_name 0: 5.1 +#tb 1: 1/1000 +#media_type 1: audio +#codec_id 1: flac +#sample_rate 1: 48000 +#channel_layout 1: 60f +#channel_layout_name 1: 5.1(side) +#tb 2: 1/1000 +#media_type 2: audio +#codec_id 2: flac +#sample_rate 2: 48000 +#channel_layout 2: 3f +#channel_layout_name 2: 5.1 +0, 0, 0, 96, 26, 0x4e160341 +1, 0, 0, 96, 26, 0x4e160341 +2, 0, 0, 96, 26, 0x4e160341 +0, 96, 96, 96, 26, 0x4e17035c +1, 96, 96, 96, 26, 0x4e17035c +2, 96, 96, 96, 26, 0x4e17035c +0, 192, 192, 96, 26, 0x4de40383 +1, 192, 192, 96, 26, 0x4de40383 +2, 192, 192, 96, 26, 0x4de40383 +0, 288, 288, 96, 26, 0x4e3903a2 +1, 288, 288, 96, 26, 0x4e3903a2 +2, 288, 288, 96, 26, 0x4e3903a2 +0, 384, 384, 96, 26, 0x4f9a03d5 +1, 384, 384, 96, 26, 0x4f9a03d5 +2, 384, 384, 96, 26, 0x4f9a03d5 +0, 480, 480, 96, 26, 0x501303e0 +1, 480, 480, 96, 26, 0x501303e0 +2, 480, 480, 96, 26, 0x501303e0 +0, 576, 576, 96, 26, 0x5160042f +1, 576, 576, 96, 26, 0x5160042f +2, 576, 576, 96, 26, 0x5160042f +0, 672, 672, 96, 26, 0x50dd042e +1, 672, 672, 96, 26, 0x50dd042e +2, 672, 672, 96, 26, 0x50dd042e +0, 768, 768, 96, 26, 0x53de0499 +1, 768, 768, 96, 26, 0x53de0499 +0, 864, 864, 96, 26, 0x53df04b4 +1, 864, 864, 96, 26, 0x53df04b4 +0, 960, 960, 42, 26, 0x5740044b +1, 960, 960, 42, 26, 0x5740044b diff --git a/tests/ref/fate/matroska-lzo-decompression b/tests/ref/fate/matroska-lzo-decompression new file mode 100644 index 00000000000..241d5adf442 --- /dev/null +++ b/tests/ref/fate/matroska-lzo-decompression @@ -0,0 +1,10 @@ +#tb 0: 11337/500000000 +#media_type 0: audio +#codec_id 0: pcm_s16le +#sample_rate 0: 44100 +#channel_layout 0: 3 +#channel_layout_name 0: stereo +0, 0, 0, 4096, 16384, 0x00000000 +0, 4096, 4096, 4096, 16384, 0xad7eebf4 +0, 8192, 8192, 4096, 16384, 0x1d1ff9d9 +0, 12288, 12288, 4097, 16384, 0xf1d9e2e2 diff --git a/tests/ref/fate/matroska-prores-header-insertion-bz2 b/tests/ref/fate/matroska-prores-header-insertion-bz2 new file mode 100644 index 00000000000..63a59f9b8d3 --- /dev/null +++ b/tests/ref/fate/matroska-prores-header-insertion-bz2 @@ -0,0 +1,16 @@ +#extradata 0: 4, 0x0402019c +#extradata 1: 4, 0x0402019c +#tb 0: 1/1000 +#media_type 0: video +#codec_id 0: prores +#dimensions 0: 720x480 +#sar 0: 186/157 +#tb 1: 1/1000 +#media_type 1: video +#codec_id 1: prores +#dimensions 1: 720x480 +#sar 1: 186/157 +0, 0, 0, 0, 4304, 0x3625b1fc +1, 0, 0, 0, 4304, 0x3625b1fc +0, 42, 42, 0, 4304, 0x3625b1fc +1, 42, 42, 0, 4304, 0x3625b1fc diff --git a/tests/ref/fate/matroska-prores-zlib b/tests/ref/fate/matroska-prores-zlib new file mode 100644 index 00000000000..d88015cd1a5 --- /dev/null +++ b/tests/ref/fate/matroska-prores-zlib @@ -0,0 +1,8 @@ +#extradata 0: 4, 0x040801a2 +#tb 0: 1/1000 +#media_type 0: video +#codec_id 0: prores +#dimensions 0: 1920x1080 +#sar 0: 1/1 +0, 0, 0, 33, 82240, 0x5271abe6 +0, 33, 33, 33, 82240, 0x5271abe6 diff --git a/tests/ref/fate/matroska-wavpack-missing-codecprivate b/tests/ref/fate/matroska-wavpack-missing-codecprivate new file mode 100644 index 00000000000..4645a86ff6c --- /dev/null +++ b/tests/ref/fate/matroska-wavpack-missing-codecprivate @@ -0,0 +1,9 @@ +#extradata 0: 2, 0x00240014 +#tb 0: 11337/500000000 +#media_type 0: audio +#codec_id 0: wavpack +#sample_rate 0: 44100 +#channel_layout 0: 3 +#channel_layout_name 0: stereo +0, 0, 0, 22051, 14778, 0x02819286 +0, 22051, 22051, 22052, 14756, 0x21976243 diff --git a/tests/ref/fate/matroska-xiph-lacing b/tests/ref/fate/matroska-xiph-lacing new file mode 100644 index 00000000000..c74cf9900d7 --- /dev/null +++ b/tests/ref/fate/matroska-xiph-lacing @@ -0,0 +1,91 @@ +#extradata 0: 3766, 0xde3392f7 +#tb 0: 11337/500000000 +#media_type 0: audio +#codec_id 0: vorbis +#sample_rate 0: 44100 +#channel_layout 0: 3 +#channel_layout_name 0: stereo +0, 0, 0, 128, 84, 0x1beb2cc9 +0, 128, 128, 576, 533, 0xa5d425e9 +0, 704, 704, 1024, 505, 0x4b970e01 +0, 1728, 1728, 1024, 503, 0x8f4b0607 +0, 2752, 2752, 1024, 507, 0x546e0bc6 +0, 3776, 3776, 1024, 507, 0xe006103f +0, 4800, 4800, 1024, 502, 0xc7770a66 +0, 5824, 5824, 1024, 500, 0x89690d08 +0, 6785, 6785, 1024, 501, 0x47c50720 +0, 7809, 7809, 1024, 490, 0x4b5efd2d +0, 8833, 8833, 1024, 510, 0xa5110fdf +0, 9857, 9857, 576, 80, 0x6d1129bd +0, 10433, 10433, 128, 84, 0x18272cc1 +0, 10561, 10561, 128, 79, 0x7720262e +0, 10689, 10689, 128, 83, 0x5d1c2d22 +0, 10817, 10817, 128, 84, 0xbbdc2c28 +0, 10945, 10945, 128, 154, 0x2c41556c +0, 11073, 11073, 128, 148, 0xdd135006 +0, 11201, 11201, 128, 151, 0xf4b253b4 +0, 11329, 11329, 128, 91, 0x258a34b8 +0, 11457, 11457, 128, 79, 0x654923dc +0, 11585, 11585, 128, 88, 0xb3853007 +0, 11713, 11713, 128, 84, 0x33a72c00 +0, 11841, 11841, 128, 86, 0x38802c2f +0, 11969, 11969, 128, 155, 0x923253ce +0, 12097, 12097, 128, 138, 0x0d3a408d +0, 12225, 12225, 128, 140, 0x95ca470e +0, 12353, 12353, 576, 481, 0x6d6de7fd +0, 12929, 12929, 1024, 474, 0x74bee6fb +0, 13953, 13953, 1024, 446, 0x777be048 +0, 14977, 14977, 1024, 469, 0x9b49e7bd +0, 16001, 16001, 1024, 458, 0xb728df34 +0, 17025, 17025, 1024, 460, 0x1519e267 +0, 18049, 18049, 1024, 467, 0x391ff1ab +0, 19073, 19073, 1024, 464, 0x7b4ff249 +0, 20097, 20097, 576, 144, 0x9daa47e4 +0, 20673, 20673, 128, 151, 0xc8b24f08 +0, 20801, 20801, 128, 134, 0xfda444ec +0, 20929, 20929, 576, 428, 0x2ad8d2de +0, 21505, 21505, 576, 80, 0xcdf12536 +0, 22082, 22082, 128, 143, 0x935a4d4e +0, 22210, 22210, 128, 140, 0xb99a4ee5 +0, 22338, 22338, 128, 141, 0xf36954b9 +0, 22466, 22466, 128, 151, 0x13b9505c +0, 22594, 22594, 128, 142, 0xd8734a5f +0, 22722, 22722, 576, 465, 0x9e6ceade +0, 23298, 23298, 1024, 480, 0x6e98f308 +0, 24322, 24322, 1024, 465, 0xeecdeac4 +0, 25346, 25346, 1024, 464, 0x2a0cec3c +0, 26370, 26370, 1024, 487, 0xc66d0123 +0, 27394, 27394, 1024, 505, 0x0d1f0d85 +0, 28418, 28418, 1024, 479, 0x8d3af09e +0, 29442, 29442, 1024, 485, 0x4927fa6a +0, 30466, 30466, 1024, 483, 0x95c102a7 +0, 31490, 31490, 1024, 467, 0x7646ec5b +0, 32514, 32514, 1024, 492, 0x967505e9 +0, 33539, 33539, 1024, 491, 0x06670254 +0, 34563, 34563, 576, 76, 0x994e25d8 +0, 35139, 35139, 128, 84, 0xc8ba2a76 +0, 35267, 35267, 128, 90, 0x0008339a +0, 35395, 35395, 128, 84, 0x3d722790 +0, 35523, 35523, 128, 76, 0x3d5c23bf +0, 35651, 35651, 128, 82, 0x4cba28a1 +0, 35779, 35779, 128, 157, 0xf79058e2 +0, 35907, 35907, 128, 141, 0x72a24a6c +0, 36035, 36035, 576, 498, 0x5a200efb +0, 36611, 36611, 1024, 489, 0x135dfd19 +0, 37635, 37635, 1024, 509, 0x98580e17 +0, 38659, 38659, 1024, 508, 0x05851706 +0, 39683, 39683, 576, 89, 0xc7872e24 +0, 40259, 40259, 128, 87, 0x956e2e17 +0, 40387, 40387, 128, 91, 0x382e2fd0 +0, 40515, 40515, 128, 90, 0x6a6830f1 +0, 40643, 40643, 128, 88, 0xa50432da +0, 40771, 40771, 128, 85, 0xac5f30b2 +0, 40899, 40899, 128, 155, 0x4fa559da +0, 41027, 41027, 128, 154, 0x62535bad +0, 41155, 41155, 128, 155, 0x056c5a66 +0, 41283, 41283, 576, 499, 0xa19af50c +0, 41859, 41859, 1024, 495, 0xdd9c008b +0, 42883, 42883, 576, 83, 0x85722403 +0, 43459, 43459, 128, 155, 0x43ad5339 +0, 43587, 43587, 128, 144, 0x70804dd2 +0, 43715, 43715, 576, 480, 0x8b6101c7 diff --git a/tests/ref/fate/matroska-zlib-decompression b/tests/ref/fate/matroska-zlib-decompression new file mode 100644 index 00000000000..0ea732346a3 --- /dev/null +++ b/tests/ref/fate/matroska-zlib-decompression @@ -0,0 +1,5 @@ +#extradata 0: 348, 0x5f625922 +#tb 0: 1/1000 +#media_type 0: subtitle +#codec_id 0: dvd_subtitle +0, 1000, 1000, 1991, 191, 0x52f74934 diff --git a/tests/ref/fate/mjpeg-ticket3229 b/tests/ref/fate/mjpeg-ticket3229 new file mode 100644 index 00000000000..fc5a8dd7ff5 --- /dev/null +++ b/tests/ref/fate/mjpeg-ticket3229 @@ -0,0 +1,8 @@ +#tb 0: 1/30 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 468x312 +#sar 0: 0/1 +0, 0, 0, 1, 292032, 0x3af3a5f7 +0, 6, 6, 1, 292032, 0xe97fb504 +0, 8, 8, 1, 292032, 0xd448db04 diff --git a/tests/ref/fate/mov-zombie b/tests/ref/fate/mov-zombie index f45fa596373..445f9212844 100644 --- a/tests/ref/fate/mov-zombie +++ b/tests/ref/fate/mov-zombie @@ -129,5 +129,5 @@ packet|codec_type=video|stream_index=0|pts=188623|pts_time=2.095811|dts=188622|d frame|media_type=video|stream_index=0|key_frame=0|pkt_pts=188623|pkt_pts_time=2.095811|pkt_dts=188622|pkt_dts_time=2.095800|best_effort_timestamp=188623|best_effort_timestamp_time=2.095811|pkt_duration=3003|pkt_duration_time=0.033367|pkt_pos=100846|pkt_size=974|width=160|height=240|pix_fmt=yuv420p|sample_aspect_ratio=2:1|pict_type=B|coded_picture_number=64|display_picture_number=0|interlaced_frame=0|top_field_first=0|repeat_pict=0|color_range=tv|color_space=smpte170m|color_primaries=smpte170m|color_transfer=bt709|chroma_location=topleft packet|codec_type=video|stream_index=0|pts=197632|pts_time=2.195911|dts=191625|dts_time=2.129167|duration=3003|duration_time=0.033367|convergence_duration=N/A|convergence_duration_time=N/A|size=580|pos=101820|flags=__ frame|media_type=video|stream_index=0|key_frame=0|pkt_pts=191626|pkt_pts_time=2.129178|pkt_dts=N/A|pkt_dts_time=N/A|best_effort_timestamp=191626|best_effort_timestamp_time=2.129178|pkt_duration=3003|pkt_duration_time=0.033367|pkt_pos=99180|pkt_size=1666|width=160|height=240|pix_fmt=yuv420p|sample_aspect_ratio=2:1|pict_type=P|coded_picture_number=63|display_picture_number=0|interlaced_frame=0|top_field_first=0|repeat_pict=0|color_range=tv|color_space=smpte170m|color_primaries=smpte170m|color_transfer=bt709|chroma_location=topleft -stream|index=0|codec_name=h264|profile=77|codec_type=video|codec_time_base=212521/12744000|codec_tag_string=avc1|codec_tag=0x31637661|width=160|height=240|coded_width=160|coded_height=240|has_b_frames=1|sample_aspect_ratio=2:1|display_aspect_ratio=4:3|pix_fmt=yuv420p|level=12|color_range=tv|color_space=smpte170m|color_transfer=bt709|color_primaries=smpte170m|chroma_location=topleft|field_order=unknown|timecode=N/A|refs=2|is_avc=true|nal_length_size=4|id=N/A|r_frame_rate=30000/1001|avg_frame_rate=6372000/212521|time_base=1/90000|start_pts=0|start_time=0.000000|duration_ts=2125200|duration=23.613333|bit_rate=333874|max_bit_rate=N/A|bits_per_raw_sample=8|nb_frames=708|nb_read_frames=65|nb_read_packets=66|disposition:default=1|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|tag:rotate=0|tag:creation_time=2008-05-12T20:59:27.000000Z|tag:language=eng|tag:handler_name=Apple Video Media Handler|tag:encoder=H.264 +stream|index=0|codec_name=h264|profile=77|codec_type=video|codec_time_base=212521/12744000|codec_tag_string=avc1|codec_tag=0x31637661|width=160|height=240|coded_width=160|coded_height=240|closed_captions=0|has_b_frames=1|sample_aspect_ratio=2:1|display_aspect_ratio=4:3|pix_fmt=yuv420p|level=12|color_range=tv|color_space=smpte170m|color_transfer=bt709|color_primaries=smpte170m|chroma_location=topleft|field_order=unknown|timecode=N/A|refs=2|is_avc=true|nal_length_size=4|id=N/A|r_frame_rate=30000/1001|avg_frame_rate=6372000/212521|time_base=1/90000|start_pts=0|start_time=0.000000|duration_ts=2125200|duration=23.613333|bit_rate=333874|max_bit_rate=N/A|bits_per_raw_sample=8|nb_frames=708|nb_read_frames=65|nb_read_packets=66|disposition:default=1|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|tag:rotate=0|tag:creation_time=2008-05-12T20:59:27.000000Z|tag:language=eng|tag:handler_name=Apple Video Media Handler|tag:encoder=H.264 side_data|side_data_type=Display Matrix|displaymatrix=\n00000000: 131072 0 0\n00000001: 0 65536 0\n00000002: 0 0 1073741824\n|rotation=0 diff --git a/tests/ref/fate/movenc b/tests/ref/fate/movenc index 5e8f324ea37..fb39b98165f 100644 --- a/tests/ref/fate/movenc +++ b/tests/ref/fate/movenc @@ -2,127 +2,127 @@ write_data len 36, time nopts, type header atom ftyp write_data len 2389, time nopts, type header atom - write_data len 788, time 1000000, type sync atom moof write_data len 110, time nopts, type trailer atom - -17a37691eba8b858cf15e60aa9a7dbf7 3323 non-empty-moov +5f401347fc3c771b819e2449d69d4861 3323 non-empty-moov write_data len 36, time nopts, type header atom ftyp write_data len 2721, time nopts, type header atom - write_data len 908, time 966667, type sync atom moof write_data len 110, time nopts, type trailer atom - -0026ffe059c06c592021f972bf2c5e79 3775 non-empty-moov-elst +4267feee527adf8cd4f7b36ac0fc0872 3775 non-empty-moov-elst write_data len 36, time nopts, type header atom ftyp write_data len 2629, time nopts, type header atom - write_data len 908, time 1000000, type sync atom moof write_data len 110, time nopts, type trailer atom - -c184e168ac1e5bb3d9c70e580ab6179c 3683 non-empty-moov-no-elst -write_data len 20, time nopts, type header atom ftyp +44077b9ad45f3e16fafe4e5ada54e9b0 3683 non-empty-moov-no-elst +write_data len 24, time nopts, type header atom ftyp write_data len 1171, time nopts, type header atom - write_data len 728, time 0, type sync atom moof write_data len 828, time nopts, type unknown atom - -write_data len 728, time 1046439, type sync atom moof +write_data len 728, time 999999, type sync atom moof write_data len 812, time nopts, type unknown atom - write_data len 148, time nopts, type trailer atom - -49bf122c4c732a344ef68b58acd19be5 4435 ismv +92ce825ff40505ec8676191705adb7e7 4439 ismv write_data len 36, time nopts, type header atom ftyp write_data len 1123, time nopts, type header atom - write_data len 796, time 0, type sync atom moof write_data len 788, time 1000000, type sync atom moof write_data len 148, time nopts, type trailer atom - -ed8506ebfce4c41732205ae26a4759fd 2891 empty-moov +08f4b3ad3a3ea224b2ee731476b9056b 2891 empty-moov write_data len 36, time nopts, type header atom ftyp write_data len 1123, time nopts, type header atom - write_data len 1068, time 0, type sync atom moof write_data len 908, time 1000000, type sync atom moof write_data len 148, time nopts, type trailer atom - -1844ee6d19fd1e6daf2655632cf26310 3283 empty-moov-no-elst +d7a2dcb43eb0f95f92669f55fc7adeba 3283 empty-moov-no-elst write_data len 36, time nopts, type header atom ftyp write_data len 1123, time nopts, type header atom - write_data len 900, time -33333, type sync atom moof write_data len 908, time 966667, type sync atom moof write_data len 148, time nopts, type trailer atom - -139b27dbe2a80c2dc088d0c755f26033 3115 empty-moov-no-elst-no-adjust +ea70ca697306976879be408431c27aee 3115 empty-moov-no-elst-no-adjust write_data len 1159, time nopts, type header atom ftyp write_data len 796, time 0, type sync atom moof write_data len 788, time 1000000, type sync atom moof write_data len 148, time nopts, type trailer atom - -ed8506ebfce4c41732205ae26a4759fd 2891 delay-moov +08f4b3ad3a3ea224b2ee731476b9056b 2891 delay-moov write_data len 1231, time nopts, type header atom ftyp write_data len 916, time -33333, type sync atom moof write_data len 908, time 966667, type sync atom moof write_data len 148, time nopts, type trailer atom - -3ece148745cd64b4428530a4d1080a2d 3203 delay-moov-elst +314cc3b6296f4ee583b328a34be50b2f 3203 delay-moov-elst write_data len 1195, time nopts, type header atom ftyp write_data len 836, time 0, type sync atom moof write_data len 67, time nopts, type trailer atom - -9562946a369e6fb570fb2fd7aa2fe728 2098 delay-moov-empty-track +95d6f59a7354b0cfe7ce49927baada4e 2098 delay-moov-empty-track write_data len 1195, time nopts, type header atom ftyp write_data len 360, time 0, type sync atom moof write_data len 360, time 1000000, type sync atom moof write_data len 86, time nopts, type trailer atom - -4c7832b81836331c6c37155dc31d95be 2001 delay-moov-empty-track-flush +8805d72a27b340ea229c16edde78f974 2001 delay-moov-empty-track-flush write_data len 36, time nopts, type header atom ftyp write_data len 1123, time nopts, type header atom - -b7e3c768b9094ebe2fda44979a7f8985 1159 empty-moov-header +351ae2c8b6d35d98b4848c309cce6704 1159 empty-moov-header write_data len 796, time 0, type sync atom moof write_data len 788, time 1000000, type sync atom moof -a0165f4a26a409212b0946e981bdefb9 1584 empty-moov-content +289ee982188d66988a374a462b0b5376 1584 empty-moov-content write_data len 148, time nopts, type trailer atom - write_data len 1159, time nopts, type header atom ftyp -b7e3c768b9094ebe2fda44979a7f8985 1159 delay-moov-header +351ae2c8b6d35d98b4848c309cce6704 1159 delay-moov-header write_data len 796, time 0, type sync atom moof write_data len 788, time 1000000, type sync atom moof -a0165f4a26a409212b0946e981bdefb9 1584 delay-moov-content +289ee982188d66988a374a462b0b5376 1584 delay-moov-content write_data len 148, time nopts, type trailer atom - -write_data len 24, time nopts, type header atom - +write_data len 28, time nopts, type header atom - write_data len 1123, time nopts, type header atom - write_data len 884, time 0, type sync atom sidx write_data len 876, time 1000000, type sync atom sidx -272a474cfd2a68cc5f05b426b14a2b7d 876 empty-moov-second-frag +c0307f99a2a362205b7e3d65b1066f86 876 empty-moov-second-frag write_data len 148, time nopts, type trailer atom - -write_data len 24, time nopts, type header atom - +write_data len 28, time nopts, type header atom - write_data len 1123, time nopts, type header atom - write_data len 876, time 1000000, type sync atom sidx -272a474cfd2a68cc5f05b426b14a2b7d 876 empty-moov-second-frag-discont +c0307f99a2a362205b7e3d65b1066f86 876 empty-moov-second-frag-discont write_data len 110, time nopts, type trailer atom - -write_data len 1219, time nopts, type header atom - +write_data len 1223, time nopts, type header atom - write_data len 876, time 1000000, type sync atom sidx -272a474cfd2a68cc5f05b426b14a2b7d 876 delay-moov-second-frag-discont +c0307f99a2a362205b7e3d65b1066f86 876 delay-moov-second-frag-discont write_data len 110, time nopts, type trailer atom - -write_data len 1219, time nopts, type header atom ftyp -6ec3698bcc86013e0016e3d47d230363 1219 delay-moov-elst-init +write_data len 1223, time nopts, type header atom ftyp +b3811928793ed0749927eb2f7958421c 1223 delay-moov-elst-init write_data len 988, time -33333, type sync atom sidx write_data len 996, time 966667, type sync atom sidx -fcae8f40e015b59aabc8d4a99a759ca1 996 delay-moov-elst-second-frag +0df125407c7e81978ce722e0ae4f6f84 996 delay-moov-elst-second-frag write_data len 148, time nopts, type trailer atom - -write_data len 1219, time nopts, type header atom ftyp -6ec3698bcc86013e0016e3d47d230363 1219 delay-moov-elst-init-discont +write_data len 1223, time nopts, type header atom ftyp +b3811928793ed0749927eb2f7958421c 1223 delay-moov-elst-init-discont write_data len 996, time 966667, type sync atom sidx -fcae8f40e015b59aabc8d4a99a759ca1 996 delay-moov-elst-second-frag-discont +0df125407c7e81978ce722e0ae4f6f84 996 delay-moov-elst-second-frag-discont write_data len 110, time nopts, type trailer atom - -write_data len 1219, time nopts, type header atom ftyp -c3681590a292cb9ca19a5a982e530166 1219 delay-moov-elst-signal-init +write_data len 1223, time nopts, type header atom ftyp +041ac8efc35a0d023c26d05eedb20403 1223 delay-moov-elst-signal-init write_data len 1004, time -33333, type sync atom sidx write_data len 996, time 966667, type sync atom sidx -aa5462cc0d2144f72154d9c309edb57d 996 delay-moov-elst-signal-second-frag +5a583d89318827d2569eecbeaa18c238 996 delay-moov-elst-signal-second-frag write_data len 148, time nopts, type trailer atom - -write_data len 1219, time nopts, type header atom ftyp -c3681590a292cb9ca19a5a982e530166 1219 delay-moov-elst-signal-init-discont +write_data len 1223, time nopts, type header atom ftyp +041ac8efc35a0d023c26d05eedb20403 1223 delay-moov-elst-signal-init-discont write_data len 996, time 966667, type sync atom sidx -aa5462cc0d2144f72154d9c309edb57d 996 delay-moov-elst-signal-second-frag-discont +5a583d89318827d2569eecbeaa18c238 996 delay-moov-elst-signal-second-frag-discont write_data len 110, time nopts, type trailer atom - -write_data len 1243, time nopts, type header atom ftyp -dac14c8795d5cbd91ae770c6e2880c62 1243 delay-moov-elst-signal-init-discont-largets +write_data len 1247, time nopts, type header atom ftyp +80511a51d1ac9cde62337eed7176ae03 1247 delay-moov-elst-signal-init-discont-largets write_data len 996, time 279621233333, type sync atom sidx -41cac4c3df656a87bb38363fdcd745e6 996 delay-moov-elst-signal-second-frag-discont-largets +dc695d65e8a0cdafee28acd8a5ccf81a 996 delay-moov-elst-signal-second-frag-discont-largets write_data len 110, time nopts, type trailer atom - -write_data len 1219, time nopts, type header atom ftyp +write_data len 1223, time nopts, type header atom ftyp write_data len 2572, time -333333, type sync atom sidx write_data len 996, time 5166667, type sync atom sidx write_data len 148, time nopts, type trailer atom - -f12d4a0e054abcc508cc0d28cb320e57 4935 vfr -write_data len 1219, time nopts, type header atom ftyp +d37a7eda807912b9ed05ccfe003a9e4f 4939 vfr +write_data len 1223, time nopts, type header atom ftyp write_data len 2572, time -333333, type sync atom sidx write_data len 996, time 5166667, type sync atom sidx write_data len 148, time nopts, type trailer atom - -f12d4a0e054abcc508cc0d28cb320e57 4935 vfr-noduration +d37a7eda807912b9ed05ccfe003a9e4f 4939 vfr-noduration write_data len 1231, time nopts, type header atom ftyp write_data len 1500, time -333333, type sync atom moof write_data len 1500, time nopts, type unknown atom - @@ -131,7 +131,7 @@ write_data len 1500, time 9666667, type sync atom moof write_data len 1500, time nopts, type unknown atom - write_data len 1004, time nopts, type unknown atom - write_data len 148, time nopts, type trailer atom - -3c2c3f98c8a047f0ecefff07570fd457 9299 large_frag +08b6401dc81912e5264245b7233c4ab3 9299 large_frag write_data len 1231, time nopts, type header atom ftyp write_data len 684, time -33333, type sync atom moof write_data len 504, time 800000, type boundary atom moof @@ -139,15 +139,15 @@ write_data len 420, time 1266667, type boundary atom moof write_data len 668, time 1566667, type sync atom moof write_data len 440, time 2233333, type boundary atom moof write_data len 262, time nopts, type trailer atom - -edd19deae2b70afcf2cd744b89b7013d 4209 vfr-noduration-interleave +a5d087611a9229ba91eb0964cf2f17d9 4209 vfr-noduration-interleave write_data len 1231, time nopts, type header atom ftyp write_data len 916, time 0, type sync atom moof write_data len 908, time 1000000, type sync atom moof write_data len 148, time nopts, type trailer atom - -781dbfd228f36903178e29faa727d78b 3203 delay-moov-elst-neg-cts +d81c3a0ce5940a2db74c99ad435e0560 3203 delay-moov-elst-neg-cts write_data len 36, time nopts, type header atom ftyp write_data len 1123, time nopts, type header atom - -write_data len 1188, time 0, type sync atom moof -write_data len 908, time 1033333, type sync atom moof +write_data len 900, time 0, type sync atom moof +write_data len 908, time 1000000, type sync atom moof write_data len 148, time nopts, type trailer atom - -7630fdf358e02c79e88f312f82a260b7 3403 empty-moov-neg-cts +3be575022e446855bca1e45b7942cc0c 3115 empty-moov-neg-cts diff --git a/tests/ref/fate/mpeg4-als-conformance-09 b/tests/ref/fate/mpeg4-als-conformance-09 new file mode 100644 index 00000000000..b26276da916 --- /dev/null +++ b/tests/ref/fate/mpeg4-als-conformance-09 @@ -0,0 +1 @@ +CRC=0xbf3e7189 diff --git a/tests/ref/fate/mpegts-probe-latm b/tests/ref/fate/mpegts-probe-latm index 13aea2bcfdc..6fa9b623f31 100644 --- a/tests/ref/fate/mpegts-probe-latm +++ b/tests/ref/fate/mpegts-probe-latm @@ -12,3 +12,6 @@ codec_name=hevc [STREAM] codec_name=aac_latm [/STREAM] +[STREAM] +codec_name=epg +[/STREAM] diff --git a/tests/ref/fate/mxf-probe-d10 b/tests/ref/fate/mxf-probe-d10 index ab564467b5b..b59fe4b5971 100644 --- a/tests/ref/fate/mxf-probe-d10 +++ b/tests/ref/fate/mxf-probe-d10 @@ -10,6 +10,7 @@ width=720 height=608 coded_width=0 coded_height=0 +closed_captions=0 has_b_frames=0 sample_aspect_ratio=152:135 display_aspect_ratio=4:3 @@ -50,6 +51,9 @@ DISPOSITION:clean_effects=0 DISPOSITION:attached_pic=0 DISPOSITION:timed_thumbnails=0 TAG:file_package_umid=0x060A2B340101010501010D1313000000AE86B200913105800000080046A54011 +[SIDE_DATA] +side_data_type=CPB properties +[/SIDE_DATA] [/STREAM] [STREAM] index=1 diff --git a/tests/ref/fate/mxf-probe-dnxhd b/tests/ref/fate/mxf-probe-dnxhd index 39968ad987a..5d385e8a066 100644 --- a/tests/ref/fate/mxf-probe-dnxhd +++ b/tests/ref/fate/mxf-probe-dnxhd @@ -118,6 +118,7 @@ width=640 height=480 coded_width=640 coded_height=480 +closed_captions=0 has_b_frames=0 sample_aspect_ratio=1:1 display_aspect_ratio=4:3 diff --git a/tests/ref/fate/mxf-probe-dv25 b/tests/ref/fate/mxf-probe-dv25 index 00028b08c50..d0fb308c192 100644 --- a/tests/ref/fate/mxf-probe-dv25 +++ b/tests/ref/fate/mxf-probe-dv25 @@ -10,6 +10,7 @@ width=720 height=576 coded_width=720 coded_height=576 +closed_captions=0 has_b_frames=0 sample_aspect_ratio=16:15 display_aspect_ratio=4:3 diff --git a/tests/ref/fate/mxf-user-comments b/tests/ref/fate/mxf-user-comments index 4b734a0f851..e91b23baa5f 100644 --- a/tests/ref/fate/mxf-user-comments +++ b/tests/ref/fate/mxf-user-comments @@ -1 +1 @@ -683bacb0105e5bc5bbf46aa430c644d1 +c6469c0ae2aaee602eacbc009080ae8e diff --git a/tests/ref/fate/oggopus-demux b/tests/ref/fate/oggopus-demux index 91927607000..580758c0dc8 100644 --- a/tests/ref/fate/oggopus-demux +++ b/tests/ref/fate/oggopus-demux @@ -5,7 +5,7 @@ #sample_rate 0: 48000 #channel_layout 0: 3 #channel_layout_name 0: stereo -0, -356, -356, 960, 402, 0x89b1c40f +0, -356, -356, 960, 402, 0x89b1c40f, S=1, 10, 0x03f10065 0, 604, 604, 960, 216, 0x7bf97146 0, 1564, 1564, 960, 215, 0x6cb86d8b 0, 2524, 2524, 960, 218, 0x9cfd691c diff --git a/tests/ref/fate/opt b/tests/ref/fate/opt index 6a7dbfa7970..46ea0652f8b 100644 --- a/tests/ref/fate/opt +++ b/tests/ref/fate/opt @@ -18,31 +18,33 @@ num64=1 flt=0.333333 dbl=0.333333 TestContext AVOptions: - -num E........ set num (from 0 to 100) (default 0) - -toggle E........ set toggle (from 0 to 1) (default 1) - -rational E........ set rational (from 0 to 10) (default 1/1) - -string E........ set string (default "default") - -escape E........ set escape str (default "\=,") - -flags E........ set flags (default cool) - cool E........ set cool flag - lame E........ set lame flag - mu E........ set mu flag - -size E........ set size (default "200x300") - -pix_fmt E........ set pixfmt (default 0bgr) - -sample_fmt E........ set samplefmt (default s16) - -video_rate E........ set videorate (default "25") - -duration E........ set duration (default 0.001) - -color E........ set color (default "pink") - -cl E........ set channel layout (default 0x137) - -bin E........ set binary value - -bin1 E........ set binary value - -bin2 E........ set binary value - -num64 E........ set num 64bit (from 0 to 100) (default 1) - -flt E........ set float (from 0 to 100) (default 0.333333) - -dbl E........ set double (from 0 to 100) (default 0.333333) - -bool1 E........ set boolean value (default auto) - -bool2 E........ set boolean value (default true) - -bool3 E........ set boolean value (default false) + -num E......... set num (from 0 to 100) (default 0) + -toggle E......... set toggle (from 0 to 1) (default 1) + -rational E......... set rational (from 0 to 10) (default 1/1) + -string E......... set string (default "default") + -escape E......... set escape str (default "\=,") + -flags E......... set flags (default cool) + cool E......... set cool flag + lame E......... set lame flag + mu E......... set mu flag + -size E......... set size (default "200x300") + -pix_fmt E......... set pixfmt (default 0bgr) + -sample_fmt E......... set samplefmt (default s16) + -video_rate E......... set videorate (default "25") + -duration E......... set duration (default 0.001) + -color E......... set color (default "pink") + -cl E......... set channel layout (default 0x137) + -bin E......... set binary value + -bin1 E......... set binary value + -bin2 E......... set binary value + -num64 E......... set num 64bit (from 0 to 100) (default 1) + -flt E......... set float (from 0 to 100) (default 0.333333) + -dbl E......... set double (from 0 to 100) (default 0.333333) + -bool1 E......... set boolean value (default auto) + -bool2 E......... set boolean value (default true) + -bool3 E......... set boolean value (default false) + -dict1 E......... set dictionary value + -dict2 E......... set dictionary value (default "happy=':-)'") Testing av_opt_is_set_to_default() name: num default:1 error: @@ -70,6 +72,8 @@ name: dbl default:0 error: name: bool1 default:0 error: name: bool2 default:0 error: name: bool3 default:1 error: +name: dict1 default:1 error: +name: dict2 default:0 error: name: num default:1 error: name: toggle default:1 error: name: rational default:1 error: @@ -95,9 +99,37 @@ name: dbl default:1 error: name: bool1 default:1 error: name: bool2 default:1 error: name: bool3 default:1 error: +name: dict1 default:1 error: +name: dict2 default:1 error: + +Testing av_opt_get/av_opt_set() +name: num get: 0 set: OK get: 0 OK +name: toggle get: 1 set: OK get: 1 OK +name: rational get: 1/1 set: OK get: 1/1 OK +name: string get: default set: OK get: default OK +name: escape get: \=, set: OK get: \=, OK +name: flags get: 0x00000001 set: OK get: 0x00000001 OK +name: size get: 200x300 set: OK get: 200x300 OK +name: pix_fmt get: 0bgr set: OK get: 0bgr OK +name: sample_fmt get: s16 set: OK get: s16 OK +name: video_rate get: 25/1 set: OK get: 25/1 OK +name: duration get: 0.001 set: OK get: 0.001 OK +name: color get: 0xffc0cbff set: OK get: 0xffc0cbff OK +name: cl get: 0x137 set: OK get: 0x137 OK +name: bin get: 62696E00 set: OK get: 62696E00 OK +name: bin1 get: set: OK get: OK +name: bin2 get: set: OK get: OK +name: num64 get: 1 set: OK get: 1 OK +name: flt get: 0.333333 set: OK get: 0.333333 OK +name: dbl get: 0.333333 set: OK get: 0.333333 OK +name: bool1 get: auto set: OK get: auto OK +name: bool2 get: true set: OK get: true OK +name: bool3 get: false set: OK get: false OK +name: dict1 get: set: OK get: OK +name: dict2 get: happy=\:-) set: OK get: happy=\:-) OK Test av_opt_serialize() -num=0,toggle=1,rational=1/1,string=default,escape=\\\=\,,flags=0x00000001,size=200x300,pix_fmt=0bgr,sample_fmt=s16,video_rate=25/1,duration=0.001,color=0xffc0cbff,cl=0x137,bin=62696E00,bin1=,bin2=,num64=1,flt=0.333333,dbl=0.333333,bool1=auto,bool2=true,bool3=false +num=0,toggle=1,rational=1/1,string=default,escape=\\\=\,,flags=0x00000001,size=200x300,pix_fmt=0bgr,sample_fmt=s16,video_rate=25/1,duration=0.001,color=0xffc0cbff,cl=0x137,bin=62696E00,bin1=,bin2=,num64=1,flt=0.333333,dbl=0.333333,bool1=auto,bool2=true,bool3=false,dict1=,dict2=happy\=\\:-) Setting entry with key 'num' to value '0' Setting entry with key 'toggle' to value '1' Setting entry with key 'rational' to value '1/1' @@ -120,7 +152,9 @@ Setting entry with key 'dbl' to value '0.333333' Setting entry with key 'bool1' to value 'auto' Setting entry with key 'bool2' to value 'true' Setting entry with key 'bool3' to value 'false' -num=0,toggle=1,rational=1/1,string=default,escape=\\\=\,,flags=0x00000001,size=200x300,pix_fmt=0bgr,sample_fmt=s16,video_rate=25/1,duration=0.001,color=0xffc0cbff,cl=0x137,bin=62696E00,bin1=,bin2=,num64=1,flt=0.333333,dbl=0.333333,bool1=auto,bool2=true,bool3=false +Setting entry with key 'dict1' to value '' +Setting entry with key 'dict2' to value 'happy=\:-)' +num=0,toggle=1,rational=1/1,string=default,escape=\\\=\,,flags=0x00000001,size=200x300,pix_fmt=0bgr,sample_fmt=s16,video_rate=25/1,duration=0.001,color=0xffc0cbff,cl=0x137,bin=62696E00,bin1=,bin2=,num64=1,flt=0.333333,dbl=0.333333,bool1=auto,bool2=true,bool3=false,dict1=,dict2=happy\=\\:-) Testing av_set_options_string() Setting options string '' @@ -341,6 +375,9 @@ OK 'bool1=true' Setting options string 'bool2=auto' Setting entry with key 'bool2' to value 'auto' OK 'bool2=auto' +Setting options string 'dict1='happy=\:-):sad=\:-('' +Setting entry with key 'dict1' to value 'happy=\:-):sad=\:-(' +OK 'dict1='happy=\:-):sad=\:-('' Testing av_opt_set_from_string() Setting options string '' diff --git a/tests/ref/fate/qtrle-8bit b/tests/ref/fate/qtrle-8bit index 27bb8aad711..8da113d83eb 100644 --- a/tests/ref/fate/qtrle-8bit +++ b/tests/ref/fate/qtrle-8bit @@ -4,60 +4,169 @@ #dimensions 0: 640x480 #sar 0: 0/1 0, 0, 0, 1, 921600, 0x1492e3ed +0, 1, 1, 1, 921600, 0x1492e3ed +0, 2, 2, 1, 921600, 0x1492e3ed 0, 3, 3, 1, 921600, 0x23ef4fc7 +0, 4, 4, 1, 921600, 0x23ef4fc7 0, 5, 5, 1, 921600, 0xe406d4be +0, 6, 6, 1, 921600, 0xe406d4be +0, 7, 7, 1, 921600, 0xe406d4be 0, 8, 8, 1, 921600, 0x62b8b5a1 +0, 9, 9, 1, 921600, 0x62b8b5a1 0, 10, 10, 1, 921600, 0x7d8ba674 +0, 11, 11, 1, 921600, 0x7d8ba674 +0, 12, 12, 1, 921600, 0x7d8ba674 0, 13, 13, 1, 921600, 0xfe666be7 +0, 14, 14, 1, 921600, 0xfe666be7 0, 15, 15, 1, 921600, 0x721baec0 +0, 16, 16, 1, 921600, 0x721baec0 +0, 17, 17, 1, 921600, 0x721baec0 0, 18, 18, 1, 921600, 0xc237180a +0, 19, 19, 1, 921600, 0xc237180a 0, 20, 20, 1, 921600, 0xf03a7482 +0, 21, 21, 1, 921600, 0xf03a7482 +0, 22, 22, 1, 921600, 0xf03a7482 0, 23, 23, 1, 921600, 0x5612a391 +0, 24, 24, 1, 921600, 0x5612a391 0, 25, 25, 1, 921600, 0x9dbcc46a +0, 26, 26, 1, 921600, 0x9dbcc46a +0, 27, 27, 1, 921600, 0x9dbcc46a 0, 28, 28, 1, 921600, 0xa128a5d5 +0, 29, 29, 1, 921600, 0xa128a5d5 0, 30, 30, 1, 921600, 0x63e0025c +0, 31, 31, 1, 921600, 0x63e0025c +0, 32, 32, 1, 921600, 0x63e0025c 0, 33, 33, 1, 921600, 0x262359ed +0, 34, 34, 1, 921600, 0x262359ed 0, 35, 35, 1, 921600, 0x343688e8 +0, 36, 36, 1, 921600, 0x343688e8 +0, 37, 37, 1, 921600, 0x343688e8 +0, 38, 38, 1, 921600, 0x343688e8 +0, 39, 39, 1, 921600, 0x343688e8 +0, 40, 40, 1, 921600, 0x343688e8 +0, 41, 41, 1, 921600, 0x343688e8 +0, 42, 42, 1, 921600, 0x343688e8 +0, 43, 43, 1, 921600, 0x343688e8 +0, 44, 44, 1, 921600, 0x343688e8 0, 45, 45, 1, 921600, 0xe4b29d57 +0, 46, 46, 1, 921600, 0xe4b29d57 +0, 47, 47, 1, 921600, 0xe4b29d57 0, 48, 48, 1, 921600, 0x198e8a4a +0, 49, 49, 1, 921600, 0x198e8a4a 0, 50, 50, 1, 921600, 0x0cad8dc9 +0, 51, 51, 1, 921600, 0x0cad8dc9 +0, 52, 52, 1, 921600, 0x0cad8dc9 0, 53, 53, 1, 921600, 0x1f74cf3d +0, 54, 54, 1, 921600, 0x1f74cf3d 0, 55, 55, 1, 921600, 0xec5b5449 +0, 56, 56, 1, 921600, 0xec5b5449 +0, 57, 57, 1, 921600, 0xec5b5449 0, 58, 58, 1, 921600, 0x39829711 +0, 59, 59, 1, 921600, 0x39829711 0, 60, 60, 1, 921600, 0x6de5b9c6 +0, 61, 61, 1, 921600, 0x6de5b9c6 +0, 62, 62, 1, 921600, 0x6de5b9c6 0, 63, 63, 1, 921600, 0x47b0e9d4 +0, 64, 64, 1, 921600, 0x47b0e9d4 0, 65, 65, 1, 921600, 0x756452b8 +0, 66, 66, 1, 921600, 0x756452b8 +0, 67, 67, 1, 921600, 0x756452b8 0, 68, 68, 1, 921600, 0x6fce3478 +0, 69, 69, 1, 921600, 0x6fce3478 0, 70, 70, 1, 921600, 0x372397cd +0, 71, 71, 1, 921600, 0x372397cd +0, 72, 72, 1, 921600, 0x372397cd 0, 73, 73, 1, 921600, 0xe3999ba1 +0, 74, 74, 1, 921600, 0xe3999ba1 0, 75, 75, 1, 921600, 0x6ba26b43 +0, 76, 76, 1, 921600, 0x6ba26b43 +0, 77, 77, 1, 921600, 0x6ba26b43 0, 78, 78, 1, 921600, 0x4e9ee49e +0, 79, 79, 1, 921600, 0x4e9ee49e 0, 80, 80, 1, 921600, 0xdb5fd6e7 +0, 81, 81, 1, 921600, 0xdb5fd6e7 +0, 82, 82, 1, 921600, 0xdb5fd6e7 0, 83, 83, 1, 921600, 0x8f2254a5 +0, 84, 84, 1, 921600, 0x8f2254a5 +0, 85, 85, 1, 921600, 0x8f2254a5 +0, 86, 86, 1, 921600, 0x8f2254a5 +0, 87, 87, 1, 921600, 0x8f2254a5 +0, 88, 88, 1, 921600, 0x8f2254a5 +0, 89, 89, 1, 921600, 0x8f2254a5 +0, 90, 90, 1, 921600, 0x8f2254a5 +0, 91, 91, 1, 921600, 0x8f2254a5 +0, 92, 92, 1, 921600, 0x8f2254a5 0, 93, 93, 1, 921600, 0x57e95c32 +0, 94, 94, 1, 921600, 0x57e95c32 0, 95, 95, 1, 921600, 0x41627a9b +0, 96, 96, 1, 921600, 0x41627a9b +0, 97, 97, 1, 921600, 0x41627a9b 0, 98, 98, 1, 921600, 0x7412dcee +0, 99, 99, 1, 921600, 0x7412dcee 0, 100, 100, 1, 921600, 0xaebe10ed +0, 101, 101, 1, 921600, 0xaebe10ed +0, 102, 102, 1, 921600, 0xaebe10ed 0, 103, 103, 1, 921600, 0x411a91f6 +0, 104, 104, 1, 921600, 0x411a91f6 0, 105, 105, 1, 921600, 0xb059df3f +0, 106, 106, 1, 921600, 0xb059df3f +0, 107, 107, 1, 921600, 0xb059df3f 0, 108, 108, 1, 921600, 0x4d6f5a77 +0, 109, 109, 1, 921600, 0x4d6f5a77 0, 110, 110, 1, 921600, 0xbbf06df4 +0, 111, 111, 1, 921600, 0xbbf06df4 +0, 112, 112, 1, 921600, 0xbbf06df4 0, 113, 113, 1, 921600, 0xe27f7bf6 +0, 114, 114, 1, 921600, 0xe27f7bf6 0, 115, 115, 1, 921600, 0xd7e8360e +0, 116, 116, 1, 921600, 0xd7e8360e +0, 117, 117, 1, 921600, 0xd7e8360e 0, 118, 118, 1, 921600, 0x1dd4c344 +0, 119, 119, 1, 921600, 0x1dd4c344 0, 120, 120, 1, 921600, 0x7995a7ce +0, 121, 121, 1, 921600, 0x7995a7ce +0, 122, 122, 1, 921600, 0x7995a7ce 0, 123, 123, 1, 921600, 0x2ef3c566 +0, 124, 124, 1, 921600, 0x2ef3c566 0, 125, 125, 1, 921600, 0xf296736e +0, 126, 126, 1, 921600, 0xf296736e +0, 127, 127, 1, 921600, 0xf296736e +0, 128, 128, 1, 921600, 0xf296736e +0, 129, 129, 1, 921600, 0xf296736e +0, 130, 130, 1, 921600, 0xf296736e +0, 131, 131, 1, 921600, 0xf296736e +0, 132, 132, 1, 921600, 0xf296736e +0, 133, 133, 1, 921600, 0xf296736e +0, 134, 134, 1, 921600, 0xf296736e 0, 135, 135, 1, 921600, 0x1a488311 +0, 136, 136, 1, 921600, 0x1a488311 +0, 137, 137, 1, 921600, 0x1a488311 0, 138, 138, 1, 921600, 0x9e28011b +0, 139, 139, 1, 921600, 0x9e28011b 0, 140, 140, 1, 921600, 0x84d1ea80 +0, 141, 141, 1, 921600, 0x84d1ea80 +0, 142, 142, 1, 921600, 0x84d1ea80 0, 143, 143, 1, 921600, 0x9ed41052 +0, 144, 144, 1, 921600, 0x9ed41052 0, 145, 145, 1, 921600, 0xd4db7206 +0, 146, 146, 1, 921600, 0xd4db7206 +0, 147, 147, 1, 921600, 0xd4db7206 0, 148, 148, 1, 921600, 0x55f695a9 +0, 149, 149, 1, 921600, 0x55f695a9 0, 150, 150, 1, 921600, 0x9d8c667f +0, 151, 151, 1, 921600, 0x9d8c667f +0, 152, 152, 1, 921600, 0x9d8c667f 0, 153, 153, 1, 921600, 0x9b6037ec +0, 154, 154, 1, 921600, 0x9b6037ec 0, 155, 155, 1, 921600, 0x57c5e835 +0, 156, 156, 1, 921600, 0x57c5e835 +0, 157, 157, 1, 921600, 0x57c5e835 0, 158, 158, 1, 921600, 0x476dad89 +0, 159, 159, 1, 921600, 0x476dad89 0, 160, 160, 1, 921600, 0xcfd6ad2b +0, 161, 161, 1, 921600, 0xcfd6ad2b +0, 162, 162, 1, 921600, 0xcfd6ad2b 0, 163, 163, 1, 921600, 0x3b372379 +0, 164, 164, 1, 921600, 0x3b372379 0, 165, 165, 1, 921600, 0x36f245f5 +0, 166, 166, 1, 921600, 0x36f245f5 diff --git a/tests/ref/fate/rgb24-mkv b/tests/ref/fate/rgb24-mkv index b9db53f5921..34d028cbfdd 100644 --- a/tests/ref/fate/rgb24-mkv +++ b/tests/ref/fate/rgb24-mkv @@ -1,5 +1,5 @@ -ffe2c21083d56764aaae7945fd146326 *tests/data/fate/rgb24-mkv.matroska -58305 tests/data/fate/rgb24-mkv.matroska +fdc02d700dbe99315a9f0d928a9b935e *tests/data/fate/rgb24-mkv.matroska +58213 tests/data/fate/rgb24-mkv.matroska #tb 0: 1/10 #media_type 0: video #codec_id 0: rawvideo diff --git a/tests/ref/fate/segment-mp4-to-ts b/tests/ref/fate/segment-mp4-to-ts index b5accb60f72..847c1a297db 100644 --- a/tests/ref/fate/segment-mp4-to-ts +++ b/tests/ref/fate/segment-mp4-to-ts @@ -1,10 +1,10 @@ -#extradata 0: 51, 0x5d140df9 +#extradata 0: 50, 0x4f1b0df9 #tb 0: 1/90000 #media_type 0: video #codec_id 0: h264 #dimensions 0: 640x360 #sar 0: 1/1 -0, -7200, 0, 0, 22631, 0x9cec9541, S=1, 1, 0x00e000e0 +0, -7200, 0, 0, 22630, 0x9b109541, S=1, 1, 0x00e000e0 0, -3600, 14400, 0, 4021, 0xbf7cdb02, F=0x0, S=1, 1, 0x00e000e0 0, 0, 7200, 0, 1096, 0x4f162690, F=0x0, S=1, 1, 0x00e000e0 0, 3600, 3600, 0, 687, 0x00394b95, F=0x0, S=1, 1, 0x00e000e0 diff --git a/tests/ref/fate/source b/tests/ref/fate/source index ad1e5b95d6d..c64bc052417 100644 --- a/tests/ref/fate/source +++ b/tests/ref/fate/source @@ -1,6 +1,4 @@ Files without standard license headers: -compat/avisynth/windowsPorts/basicDataTypeConversions.h -compat/avisynth/windowsPorts/windows2linux.h libavcodec/file_open.c libavcodec/ilbcdata.h libavcodec/ilbcdec.c @@ -9,6 +7,7 @@ libavcodec/log2_tab.c libavcodec/reverse.c libavdevice/file_open.c libavdevice/reverse.c +libavfilter/af_arnndn.c libavfilter/log2_tab.c libavformat/file_open.c libavformat/golomb_tab.c @@ -18,13 +17,6 @@ libswscale/log2_tab.c tools/uncoded_frame.c tools/yuvcmp.c Headers without standard inclusion guards: -compat/avisynth/avisynth_c.h -compat/avisynth/avs/capi.h -compat/avisynth/avs/config.h -compat/avisynth/avs/types.h -compat/avisynth/avxsynth_c.h -compat/avisynth/windowsPorts/basicDataTypeConversions.h -compat/avisynth/windowsPorts/windows2linux.h compat/djgpp/math.h compat/float/float.h compat/float/limits.h diff --git a/tests/ref/fate/sub-aqtitle b/tests/ref/fate/sub-aqtitle index 87253c9a2d2..af0c06d7c28 100644 --- a/tests/ref/fate/sub-aqtitle +++ b/tests/ref/fate/sub-aqtitle @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-cc b/tests/ref/fate/sub-cc index 4cc02d1d174..2b30a35be0a 100644 --- a/tests/ref/fate/sub-cc +++ b/tests/ref/fate/sub-cc @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-cc-realtime b/tests/ref/fate/sub-cc-realtime index be800a4d296..5a95ff5cb70 100644 --- a/tests/ref/fate/sub-cc-realtime +++ b/tests/ref/fate/sub-cc-realtime @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-cc-scte20 b/tests/ref/fate/sub-cc-scte20 index 71fc92bfc5d..be28084887a 100644 --- a/tests/ref/fate/sub-cc-scte20 +++ b/tests/ref/fate/sub-cc-scte20 @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-charenc b/tests/ref/fate/sub-charenc index a056cd10926..4efacb073d1 100644 --- a/tests/ref/fate/sub-charenc +++ b/tests/ref/fate/sub-charenc @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-jacosub b/tests/ref/fate/sub-jacosub index 5f282cdcf64..b574dda54df 100644 --- a/tests/ref/fate/sub-jacosub +++ b/tests/ref/fate/sub-jacosub @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-microdvd b/tests/ref/fate/sub-microdvd index d2170bc2a41..4ddb254c698 100644 --- a/tests/ref/fate/sub-microdvd +++ b/tests/ref/fate/sub-microdvd @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-microdvd-remux b/tests/ref/fate/sub-microdvd-remux index a71da99031f..92ff233f56b 100644 Binary files a/tests/ref/fate/sub-microdvd-remux and b/tests/ref/fate/sub-microdvd-remux differ diff --git a/tests/ref/fate/sub-movtext b/tests/ref/fate/sub-movtext index 94ed22d318e..6e2d2e35dbd 100644 --- a/tests/ref/fate/sub-movtext +++ b/tests/ref/fate/sub-movtext @@ -3,10 +3,11 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding -Style: Default,Serif,18,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,2,10,10,10,0 +Style: Default,Serif,18,&Hffffff,&Hffffff,&Hff000000,&Hff000000,0,0,0,0,100,100,0,0,1,1,0,2,10,10,10,0 [Events] Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text diff --git a/tests/ref/fate/sub-mpl2 b/tests/ref/fate/sub-mpl2 index 72fc0fccd78..f78cf684953 100644 --- a/tests/ref/fate/sub-mpl2 +++ b/tests/ref/fate/sub-mpl2 @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-mpsub b/tests/ref/fate/sub-mpsub index 890ceb08a25..1a8e757585a 100644 --- a/tests/ref/fate/sub-mpsub +++ b/tests/ref/fate/sub-mpsub @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-mpsub-frames b/tests/ref/fate/sub-mpsub-frames index 64528ec70ea..abd52ad2772 100644 --- a/tests/ref/fate/sub-mpsub-frames +++ b/tests/ref/fate/sub-mpsub-frames @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding @@ -10,5 +11,5 @@ Style: Default,Arial,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,2,10 [Events] Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text -Dialogue: 0,0:00:01.00,0:00:02.48,Default,,0,0,0,,Start at 1sec,\Nlast 1.5 seconds -Dialogue: 0,0:00:02.52,0:00:11.52,Default,,0,0,0,,One frame later,\Nduring 9 seconds +Dialogue: 0,0:00:01.00,0:00:02.50,Default,,0,0,0,,Start at 1sec,\Nlast 1.5 seconds +Dialogue: 0,0:00:02.54,0:00:11.54,Default,,0,0,0,,One frame later,\Nduring 9 seconds diff --git a/tests/ref/fate/sub-pjs b/tests/ref/fate/sub-pjs index 799c62b2a3e..378190a3eb0 100644 --- a/tests/ref/fate/sub-pjs +++ b/tests/ref/fate/sub-pjs @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-realtext b/tests/ref/fate/sub-realtext index cd9aa5a294a..04b1664f896 100644 --- a/tests/ref/fate/sub-realtext +++ b/tests/ref/fate/sub-realtext @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-sami b/tests/ref/fate/sub-sami index 3a013908d5c..dbd1cc310d6 100644 --- a/tests/ref/fate/sub-sami +++ b/tests/ref/fate/sub-sami @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-sami2 b/tests/ref/fate/sub-sami2 index 64656f06cbd..dbec842d2b0 100644 --- a/tests/ref/fate/sub-sami2 +++ b/tests/ref/fate/sub-sami2 @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-scc b/tests/ref/fate/sub-scc index 5c3e1880afc..62cbf6fa4a4 100644 --- a/tests/ref/fate/sub-scc +++ b/tests/ref/fate/sub-scc @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-srt b/tests/ref/fate/sub-srt index fd602909299..b4eed235ce0 100644 --- a/tests/ref/fate/sub-srt +++ b/tests/ref/fate/sub-srt @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-srt-badsyntax b/tests/ref/fate/sub-srt-badsyntax index 1561d3f2f25..61f472a84b9 100644 --- a/tests/ref/fate/sub-srt-badsyntax +++ b/tests/ref/fate/sub-srt-badsyntax @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-stl b/tests/ref/fate/sub-stl index cde33cd7cd9..0f326c21730 100644 --- a/tests/ref/fate/sub-stl +++ b/tests/ref/fate/sub-stl @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-subviewer b/tests/ref/fate/sub-subviewer index 19944f6416b..c68a6442be2 100644 --- a/tests/ref/fate/sub-subviewer +++ b/tests/ref/fate/sub-subviewer @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-subviewer1 b/tests/ref/fate/sub-subviewer1 index a75406b8579..e88729ad5ec 100644 --- a/tests/ref/fate/sub-subviewer1 +++ b/tests/ref/fate/sub-subviewer1 @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-vplayer b/tests/ref/fate/sub-vplayer index 6e804f6c22e..d83db9e09fc 100644 --- a/tests/ref/fate/sub-vplayer +++ b/tests/ref/fate/sub-vplayer @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-webvtt b/tests/ref/fate/sub-webvtt index 8c63a90279b..dea535b19b2 100644 --- a/tests/ref/fate/sub-webvtt +++ b/tests/ref/fate/sub-webvtt @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub-webvtt2 b/tests/ref/fate/sub-webvtt2 index 9f7827d66fe..357b8178ea1 100644 --- a/tests/ref/fate/sub-webvtt2 +++ b/tests/ref/fate/sub-webvtt2 @@ -3,6 +3,7 @@ ScriptType: v4.00+ PlayResX: 384 PlayResY: 288 +ScaledBorderAndShadow: yes [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding diff --git a/tests/ref/fate/sub2video b/tests/ref/fate/sub2video index 4e034a5e91d..80abe9c9053 100644 --- a/tests/ref/fate/sub2video +++ b/tests/ref/fate/sub2video @@ -10,7 +10,7 @@ 0, 0, 0, 1, 518400, 0x83c27b82 0, 1, 1, 1, 518400, 0x4051c7f9 0, 2, 2, 1, 518400, 0xfb00e17e -1, 499000, 499000, 4960000, 1015, 0x19e092d2, F=0x0 +1, 499000, 499000, 4960000, 1015, 0x19e092d2 0, 3, 3, 1, 518400, 0x192abb74 0, 4, 4, 1, 518400, 0x4669a88b 0, 5, 5, 1, 518400, 0xaababe00 @@ -58,129 +58,129 @@ 0, 47, 47, 1, 518400, 0xde69683f 0, 48, 48, 1, 518400, 0x7df08fba 0, 49, 49, 1, 518400, 0xbab197ea -1, 15355000, 15355000, 4733000, 2094, 0x3c171425, F=0x0 +1, 15355000, 15355000, 4733000, 2094, 0x3c171425 0, 77, 77, 1, 518400, 0x902285d9 0, 100, 100, 1, 518400, 0xbab197ea -1, 48797000, 48797000, 2560000, 2480, 0x7c0edf21, F=0x0 +1, 48797000, 48797000, 2560000, 2480, 0x7c0edf21 0, 244, 244, 1, 518400, 0x7a11c812 0, 257, 257, 1, 518400, 0xbab197ea -1, 51433000, 51433000, 2366000, 3059, 0xc95b8a05, F=0x0 +1, 51433000, 51433000, 2366000, 3059, 0xc95b8a05 0, 258, 258, 1, 518400, 0x34cdddee 0, 269, 269, 1, 518400, 0xbab197ea -1, 53910000, 53910000, 2696000, 2095, 0x61bb15ed, F=0x0 +1, 53910000, 53910000, 2696000, 2095, 0x61bb15ed 0, 270, 270, 1, 518400, 0x4db4ce51 0, 283, 283, 1, 518400, 0xbab197ea -1, 56663000, 56663000, 1262000, 1013, 0xc9ae89b7, F=0x0 +1, 56663000, 56663000, 1262000, 1013, 0xc9ae89b7 0, 284, 284, 1, 518400, 0xe6bc0ea9 0, 290, 290, 1, 518400, 0xbab197ea -1, 58014000, 58014000, 1661000, 969, 0xe01878f0, F=0x0 +1, 58014000, 58014000, 1661000, 969, 0xe01878f0 0, 291, 291, 1, 518400, 0xa8643af7 0, 298, 298, 1, 518400, 0xbab197ea -1, 67724000, 67724000, 1365000, 844, 0xe7db4fc1, F=0x0 +1, 67724000, 67724000, 1365000, 844, 0xe7db4fc1 0, 339, 339, 1, 518400, 0xb1885c67 0, 345, 345, 1, 518400, 0xbab197ea -1, 69175000, 69175000, 1558000, 802, 0xf48531ba, F=0x0 +1, 69175000, 69175000, 1558000, 802, 0xf48531ba 0, 346, 346, 1, 518400, 0x378e3fd0 0, 354, 354, 1, 518400, 0xbab197ea -1, 70819000, 70819000, 1865000, 1709, 0xb4d5a1bd, F=0x0 +1, 70819000, 70819000, 1865000, 1709, 0xb4d5a1bd 0, 355, 355, 1, 518400, 0xa3782469 0, 363, 363, 1, 518400, 0xbab197ea -1, 72762000, 72762000, 1968000, 2438, 0x99d7bc82, F=0x0 +1, 72762000, 72762000, 1968000, 2438, 0x99d7bc82 0, 364, 364, 1, 518400, 0xba23a0d5 0, 374, 374, 1, 518400, 0xbab197ea -1, 74806000, 74806000, 1831000, 2116, 0x96514097, F=0x0 +1, 74806000, 74806000, 1831000, 2116, 0x96514097 0, 375, 375, 1, 518400, 0x129de2f8 0, 383, 383, 1, 518400, 0xbab197ea -1, 76716000, 76716000, 1262000, 1822, 0xefccc72e, F=0x0 +1, 76716000, 76716000, 1262000, 1822, 0xefccc72e 0, 384, 384, 1, 518400, 0x19772f0f 0, 390, 390, 1, 518400, 0xbab197ea -1, 78051000, 78051000, 1524000, 987, 0x7b927a27, F=0x0 +1, 78051000, 78051000, 1524000, 987, 0x7b927a27 0, 391, 391, 1, 518400, 0x56f54e73 0, 398, 398, 1, 518400, 0xbab197ea -1, 79644000, 79644000, 2662000, 2956, 0x190778f7, F=0x0 +1, 79644000, 79644000, 2662000, 2956, 0x190778f7 0, 399, 399, 1, 518400, 0x300b5247 -1, 82380000, 82380000, 2764000, 3094, 0xc021b7d3, F=0x0 +1, 82380000, 82380000, 2764000, 3094, 0xc021b7d3 0, 412, 412, 1, 518400, 0xbab197ea 0, 413, 413, 1, 518400, 0x6fd028fa 0, 426, 426, 1, 518400, 0xbab197ea -1, 85225000, 85225000, 2366000, 2585, 0x74d0048f, F=0x0 +1, 85225000, 85225000, 2366000, 2585, 0x74d0048f 0, 427, 427, 1, 518400, 0x01f80e9d 0, 438, 438, 1, 518400, 0xbab197ea -1, 87652000, 87652000, 1831000, 634, 0x8832fda1, F=0x0 +1, 87652000, 87652000, 1831000, 634, 0x8832fda1 0, 439, 439, 1, 518400, 0xb48d90c0 0, 447, 447, 1, 518400, 0xbab197ea -1, 91531000, 91531000, 2332000, 2080, 0x97a1146f, F=0x0 +1, 91531000, 91531000, 2332000, 2080, 0x97a1146f 0, 458, 458, 1, 518400, 0xcb5a0173 0, 469, 469, 1, 518400, 0xbab197ea -1, 95510000, 95510000, 3299000, 2964, 0x8b8f6684, F=0x0 +1, 95510000, 95510000, 3299000, 2964, 0x8b8f6684 0, 478, 478, 1, 518400, 0xb8a323e4 0, 494, 494, 1, 518400, 0xbab197ea -1, 98872000, 98872000, 2161000, 1875, 0x9002ef71, F=0x0 +1, 98872000, 98872000, 2161000, 1875, 0x9002ef71 0, 495, 495, 1, 518400, 0xc43518ba 0, 505, 505, 1, 518400, 0xbab197ea -1, 101124000, 101124000, 4096000, 3872, 0x20c6ed9c, F=0x0 +1, 101124000, 101124000, 4096000, 3872, 0x20c6ed9c 0, 506, 506, 1, 518400, 0x04e38692 0, 526, 526, 1, 518400, 0xbab197ea -1, 105303000, 105303000, 2730000, 3094, 0xf203a663, F=0x0 +1, 105303000, 105303000, 2730000, 3094, 0xf203a663 0, 527, 527, 1, 518400, 0x856b0ee5 0, 540, 540, 1, 518400, 0xbab197ea -1, 108106000, 108106000, 2059000, 2404, 0x41a7b429, F=0x0 +1, 108106000, 108106000, 2059000, 2404, 0x41a7b429 0, 541, 541, 1, 518400, 0x3e5beee2 0, 551, 551, 1, 518400, 0xbab197ea -1, 141556000, 141556000, 1661000, 1088, 0xde20aa20, F=0x0 +1, 141556000, 141556000, 1661000, 1088, 0xde20aa20 0, 708, 708, 1, 518400, 0xb8bc1365 0, 716, 716, 1, 518400, 0xbab197ea 0, 817, 817, 1, 518400, 0x83efa32d -1, 163445000, 163445000, 1331000, 339, 0x8bd186ef, F=0x0 +1, 163445000, 163445000, 1331000, 339, 0x8bd186ef 0, 824, 824, 1, 518400, 0xbab197ea 0, 840, 840, 1, 518400, 0x03ea0e90 -1, 168049000, 168049000, 1900000, 1312, 0x0bf20e8d, F=0x0 +1, 168049000, 168049000, 1900000, 1312, 0x0bf20e8d 0, 850, 850, 1, 518400, 0xbab197ea -1, 170035000, 170035000, 1524000, 1279, 0xb6c2dafe, F=0x0 +1, 170035000, 170035000, 1524000, 1279, 0xb6c2dafe 0, 851, 851, 1, 518400, 0x8780239e 0, 858, 858, 1, 518400, 0xbab197ea 0, 861, 861, 1, 518400, 0x6eb72347 -1, 172203000, 172203000, 1695000, 1826, 0x9a1ac769, F=0x0 +1, 172203000, 172203000, 1695000, 1826, 0x9a1ac769 0, 869, 869, 1, 518400, 0xbab197ea -1, 173947000, 173947000, 1934000, 1474, 0xa9b03cdc, F=0x0 +1, 173947000, 173947000, 1934000, 1474, 0xa9b03cdc 0, 870, 870, 1, 518400, 0x9c4a3a3d 0, 879, 879, 1, 518400, 0xbab197ea -1, 175957000, 175957000, 1763000, 1019, 0x20409355, F=0x0 +1, 175957000, 175957000, 1763000, 1019, 0x20409355 0, 880, 880, 1, 518400, 0xc9ebfa89 0, 889, 889, 1, 518400, 0xbab197ea 0, 946, 946, 1, 518400, 0xbaf801ef -1, 189295000, 189295000, 1968000, 1596, 0x408c726e, F=0x0 +1, 189295000, 189295000, 1968000, 1596, 0x408c726e 0, 956, 956, 1, 518400, 0xbab197ea -1, 191356000, 191356000, 1228000, 1517, 0xae8c5c2b, F=0x0 +1, 191356000, 191356000, 1228000, 1517, 0xae8c5c2b 0, 957, 957, 1, 518400, 0x59f4e72f 0, 963, 963, 1, 518400, 0xbab197ea -1, 192640000, 192640000, 1763000, 2506, 0xa458d6d4, F=0x0 +1, 192640000, 192640000, 1763000, 2506, 0xa458d6d4 0, 964, 964, 1, 518400, 0x9d5b9d69 0, 972, 972, 1, 518400, 0xbab197ea -1, 195193000, 195193000, 1092000, 1074, 0x397ba9a8, F=0x0 +1, 195193000, 195193000, 1092000, 1074, 0x397ba9a8 0, 976, 976, 1, 518400, 0x923d1ce7 0, 981, 981, 1, 518400, 0xbab197ea -1, 196361000, 196361000, 1524000, 1715, 0x695ca41e, F=0x0 +1, 196361000, 196361000, 1524000, 1715, 0x695ca41e 0, 982, 982, 1, 518400, 0x6e652cd2 0, 989, 989, 1, 518400, 0xbab197ea -1, 197946000, 197946000, 1160000, 789, 0xc63a189e, F=0x0 +1, 197946000, 197946000, 1160000, 789, 0xc63a189e 0, 990, 990, 1, 518400, 0x25113966 0, 996, 996, 1, 518400, 0xbab197ea -1, 199230000, 199230000, 1627000, 1846, 0xeea8c599, F=0x0 +1, 199230000, 199230000, 1627000, 1846, 0xeea8c599 0, 997, 997, 1, 518400, 0x2dc83609 0, 1004, 1004, 1, 518400, 0xbab197ea -1, 200924000, 200924000, 1763000, 922, 0xd4a87222, F=0x0 +1, 200924000, 200924000, 1763000, 922, 0xd4a87222 0, 1005, 1005, 1, 518400, 0x90483bc6 0, 1013, 1013, 1, 518400, 0xbab197ea 0, 1053, 1053, 1, 518400, 0x3de86ab7 -1, 210600000, 210600000, 1831000, 665, 0x55580135, F=0x0 +1, 210600000, 210600000, 1831000, 665, 0x55580135 0, 1062, 1062, 1, 518400, 0xbab197ea -1, 214771000, 214771000, 1558000, 1216, 0x50d1f6c5, F=0x0 +1, 214771000, 214771000, 1558000, 1216, 0x50d1f6c5 0, 1074, 1074, 1, 518400, 0x8c320e68 0, 1082, 1082, 1, 518400, 0xbab197ea 0, 1128, 1128, 1, 518400, 0x81e977b2 -1, 225640000, 225640000, 2127000, 2133, 0x670c11a5, F=0x0 +1, 225640000, 225640000, 2127000, 2133, 0x670c11a5 0, 1139, 1139, 1, 518400, 0xbab197ea -1, 227834000, 227834000, 1262000, 1264, 0xc1d9fc57, F=0x0 +1, 227834000, 227834000, 1262000, 1264, 0xc1d9fc57 0, 1140, 1140, 1, 518400, 0xb046dd30 0, 1145, 1145, 1, 518400, 0xbab197ea diff --git a/tests/ref/fate/sub2video_basic b/tests/ref/fate/sub2video_basic new file mode 100644 index 00000000000..5f72e292c9c --- /dev/null +++ b/tests/ref/fate/sub2video_basic @@ -0,0 +1,95 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 720x480 +#sar 0: 0/1 +0, 3312, 3312, 1, 1382400, 0x00000000 +0, 3312, 3312, 1, 1382400, 0x8c93c2ba +0, 3436, 3436, 1, 1382400, 0x00000000 +0, 3684, 3684, 1, 1382400, 0xb02e32ca +0, 3802, 3802, 1, 1382400, 0x00000000 +0, 4520, 4520, 1, 1382400, 0x83b71116 +0, 4584, 4584, 1, 1382400, 0x00000000 +0, 4586, 4586, 1, 1382400, 0x85547fd1 +0, 4645, 4645, 1, 1382400, 0x00000000 +0, 4648, 4648, 1, 1382400, 0x00000000 +0, 4648, 4648, 1, 1382400, 0xb6a8f181 +0, 4715, 4715, 1, 1382400, 0x00000000 +0, 4717, 4717, 1, 1382400, 0xb64d1a2c +0, 4748, 4748, 1, 1382400, 0x00000000 +0, 4750, 4750, 1, 1382400, 0x7b37ecf3 +0, 4792, 4792, 1, 1382400, 0x00000000 +0, 4993, 4993, 1, 1382400, 0xdc025bd1 +0, 5027, 5027, 1, 1382400, 0x00000000 +0, 5029, 5029, 1, 1382400, 0x688b294d +0, 5068, 5068, 1, 1382400, 0x00000000 +0, 5070, 5070, 1, 1382400, 0xa2b33d1b +0, 5117, 5117, 1, 1382400, 0x00000000 +0, 5119, 5119, 1, 1382400, 0xb3e525e3 +0, 5168, 5168, 1, 1382400, 0x00000000 +0, 5170, 5170, 1, 1382400, 0xaa8fbdd7 +0, 5216, 5216, 1, 1382400, 0x00000000 +0, 5218, 5218, 1, 1382400, 0x7b7f26dd +0, 5249, 5249, 1, 1382400, 0x00000000 +0, 5251, 5251, 1, 1382400, 0x15e2f836 +0, 5289, 5289, 1, 1382400, 0x00000000 +0, 5291, 5291, 1, 1382400, 0x0fee9b0c +0, 5358, 5358, 1, 1382400, 0x00000000 +0, 5360, 5360, 1, 1382400, 0x89d62791 +0, 5429, 5429, 1, 1382400, 0x00000000 +0, 5431, 5431, 1, 1382400, 0xa6a9fd74 +0, 5490, 5490, 1, 1382400, 0x00000000 +0, 5491, 5491, 1, 1382400, 0x7896178d +0, 5537, 5537, 1, 1382400, 0x00000000 +0, 5588, 5588, 1, 1382400, 0x01751a52 +0, 5647, 5647, 1, 1382400, 0x00000000 +0, 5688, 5688, 1, 1382400, 0xa3959c6f +0, 5770, 5770, 1, 1382400, 0x00000000 +0, 5772, 5772, 1, 1382400, 0x3d3ea47b +0, 5826, 5826, 1, 1382400, 0x00000000 +0, 5828, 5828, 1, 1382400, 0x593f8b24 +0, 5931, 5931, 1, 1382400, 0x00000000 +0, 5933, 5933, 1, 1382400, 0x171f05ba +0, 6001, 6001, 1, 1382400, 0x00000000 +0, 6003, 6003, 1, 1382400, 0xb014cdf1 +0, 6054, 6054, 1, 1382400, 0x00000000 +0, 6839, 6839, 1, 1382400, 0xd918e667 +0, 6880, 6880, 1, 1382400, 0x00000000 +0, 7386, 7386, 1, 1382400, 0xc9406331 +0, 7419, 7419, 1, 1382400, 0x00000000 +0, 7501, 7501, 1, 1382400, 0xaf08b10d +0, 7549, 7549, 1, 1382400, 0x00000000 +0, 7551, 7551, 1, 1382400, 0x00000000 +0, 7551, 7551, 1, 1382400, 0x853a9d93 +0, 7589, 7589, 1, 1382400, 0x00000000 +0, 7605, 7605, 1, 1382400, 0x7491a87d +0, 7647, 7647, 1, 1382400, 0x00000000 +0, 7649, 7649, 1, 1382400, 0xf7383c58 +0, 7697, 7697, 1, 1382400, 0x00000000 +0, 7699, 7699, 1, 1382400, 0xe66be411 +0, 7743, 7743, 1, 1382400, 0x00000000 +0, 8032, 8032, 1, 1382400, 0xd6850362 +0, 8082, 8082, 1, 1382400, 0x00000000 +0, 8084, 8084, 1, 1382400, 0x3e1ed109 +0, 8115, 8115, 1, 1382400, 0x00000000 +0, 8116, 8116, 1, 1382400, 0x39c1b7bd +0, 8160, 8160, 1, 1382400, 0x00000000 +0, 8180, 8180, 1, 1382400, 0x35b85f2e +0, 8207, 8207, 1, 1382400, 0x00000000 +0, 8209, 8209, 1, 1382400, 0x00000000 +0, 8209, 8209, 1, 1382400, 0x83f103e5 +0, 8247, 8247, 1, 1382400, 0x00000000 +0, 8249, 8249, 1, 1382400, 0xbc1ca9b3 +0, 8278, 8278, 1, 1382400, 0x00000000 +0, 8281, 8281, 1, 1382400, 0x94d4a51e +0, 8321, 8321, 1, 1382400, 0x00000000 +0, 8323, 8323, 1, 1382400, 0xf88cdfde +0, 8367, 8367, 1, 1382400, 0x00000000 +0, 8565, 8565, 1, 1382400, 0xdd51423b +0, 8611, 8611, 1, 1382400, 0x00000000 +0, 8669, 8669, 1, 1382400, 0x08259fa4 +0, 8708, 8708, 1, 1382400, 0x00000000 +0, 8941, 8941, 1, 1382400, 0x1663fa34 +0, 8994, 8994, 1, 1382400, 0x00000000 +0, 8996, 8996, 1, 1382400, 0xda2ceb55 +0, 9027, 9027, 1, 1382400, 0x00000000 diff --git a/tests/ref/fate/sub2video_time_limited b/tests/ref/fate/sub2video_time_limited new file mode 100644 index 00000000000..9fb6fb06f92 --- /dev/null +++ b/tests/ref/fate/sub2video_time_limited @@ -0,0 +1,8 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 1920x1080 +#sar 0: 0/1 +0, 2, 2, 1, 8294400, 0x00000000 +0, 2, 2, 1, 8294400, 0xa87c518f +0, 10, 10, 1, 8294400, 0xa87c518f diff --git a/tests/ref/fate/sws-pixdesc-query b/tests/ref/fate/sws-pixdesc-query index e23492293e9..bc9a0d874d3 100644 --- a/tests/ref/fate/sws-pixdesc-query +++ b/tests/ref/fate/sws-pixdesc-query @@ -59,6 +59,8 @@ isNBPS: p010le xyz12be xyz12le + y210be + y210le yuv420p10be yuv420p10le yuv420p12be @@ -140,6 +142,7 @@ isBE: rgb565be rgba64be xyz12be + y210be ya16be yuv420p10be yuv420p12be @@ -188,6 +191,8 @@ isYUV: uyyvyy411 xyz12be xyz12le + y210be + y210le ya16be ya16le ya8 @@ -686,6 +691,8 @@ Packed: uyyvyy411 xyz12be xyz12le + y210be + y210le ya16be ya16le ya8 diff --git a/tests/ref/fate/ts-demux b/tests/ref/fate/ts-demux index eb13ecc6840..cdf34d6af0c 100644 --- a/tests/ref/fate/ts-demux +++ b/tests/ref/fate/ts-demux @@ -15,7 +15,7 @@ 1, 5760, 5760, 2880, 1536, 0xbab5129c 1, 8640, 8640, 2880, 1536, 0x602f034b, S=1, 1, 0x00bd00bd 1, 11520, 11520, 2880, 906, 0x69cdcbcd -0, 32037, 36541, 1501, 114336, 0x37a215a8, S=1, 1, 0x00e000e0 +0, 32037, 36541, 1501, 114336, 0x37a215a8, S=2, 1, 0x00e000e0, 24, 0x663d0b52 0, 33538, 33538, 1501, 12560, 0xb559a3d4, F=0x0, S=1, 1, 0x00e000e0 0, 35040, 35040, 1501, 12704, 0x2614adf4, F=0x0, S=1, 1, 0x00e000e0 0, 36541, 41046, 1501, 51976, 0x9ff1dbfe, F=0x0, S=1, 1, 0x00e000e0 diff --git a/tests/ref/fate/url b/tests/ref/fate/url index 1a6051ee0fd..533ba2cb1e1 100644 --- a/tests/ref/fate/url +++ b/tests/ref/fate/url @@ -1,13 +1,30 @@ -baz -/foo/baz -/baz -/baz -http://server/foo/baz -http://server/foo/baz -http://server/baz -http://server/baz -http://server/baz -https://other/url -http://server/baz -http://server/foo/bar?someparam -http://other/url +Testing ff_make_absolute_url: + (null) baz => baz + /foo/bar baz => /foo/baz + /foo/bar ../baz => /baz + /foo/bar /baz => /baz + /foo/bar ../../../baz => /baz + http://server/foo/ baz => http://server/foo/baz + http://server/foo/bar baz => http://server/foo/baz + http://server/foo/ ../baz => http://server/baz + http://server/foo/bar/123 ../../baz => http://server/baz + http://server/foo/bar/123 /baz => http://server/baz + http://server/foo/bar/123 https://other/url => https://other/url + http://server/foo/bar?param=value/with/slashes /baz => http://server/baz + http://server/foo/bar?param&otherparam ?someparam => http://server/foo/bar?someparam + http://server/foo/bar //other/url => http://other/url + http://server/foo/bar ../../../../../other/url => http://server/other/url + http://server/foo/bar /../../../../../other/url => http://server/other/url + http://server/foo/bar /test/../../../../../other/url => http://server/other/url + http://server/foo/bar /test/../../test/../../../other/url => http://server/other/url + +Testing av_url_split: +/foo/bar => -1 /foo/bar +http://server/foo/ => http server -1 /foo/ +http://example.com/foo/bar => http example.com -1 /foo/bar +http://user:pass@localhost:8080/foo/bar/123 => http user:pass localhost 8080 /foo/bar/123 +http://server/foo/bar?param=value/with/slashes => http server -1 /foo/bar?param=value/with/slashes +https://1l-lh.a.net/i/1LIVE_HDS@179577/master.m3u8 => https 1l-lh.a.net -1 /i/1LIVE_HDS@179577/master.m3u8 +ftp://u:p%2B%2F2@ftp.pbt.com/ExportHD.mpg => ftp u:p%2B%2F2 ftp.pbt.com -1 /ExportHD.mpg +https://key.dns.com?key_id=2&model_id=12345&&access_key= => https key.dns.com -1 ?key_id=2&model_id=12345&&access_key= +http://example.com#tag => http example.com -1 #tag diff --git a/tests/ref/fate/vp5 b/tests/ref/fate/vp5 index 2469a3ec21a..09ebe62b25e 100644 --- a/tests/ref/fate/vp5 +++ b/tests/ref/fate/vp5 @@ -249,4 +249,4 @@ 0, 243, 243, 1, 233472, 0x6f530ac6 0, 244, 244, 1, 233472, 0x94f7466c 0, 245, 245, 1, 233472, 0xa8c1d365 -0, 246, 246, 1, 233472, 0xbf73f1b7 +0, 246, 246, 1, 233472, 0x4f3ef38c diff --git a/tests/ref/fate/vp9-encparams b/tests/ref/fate/vp9-encparams new file mode 100644 index 00000000000..917162b9ae3 --- /dev/null +++ b/tests/ref/fate/vp9-encparams @@ -0,0 +1,937 @@ +frame 0 +AVVideoEncParams 0 +qp 65 +nb_blocks 731 +block 0 0:0 64x64 33 +block 1 64:0 16x16 -12 +block 2 80:0 16x16 -12 +block 3 64:16 16x8 0 +block 4 64:24 16x8 0 +block 5 80:16 8x16 0 +block 6 88:16 8x16 0 +block 7 96:0 32x16 -23 +block 8 96:16 32x16 0 +block 9 64:32 16x32 0 +block 10 80:32 16x32 0 +block 11 96:32 16x32 16 +block 12 112:32 16x32 -32 +block 13 128:0 32x16 -32 +block 14 128:16 32x16 -32 +block 15 160:0 32x16 -23 +block 16 160:16 32x16 -32 +block 17 128:32 16x16 -32 +block 18 144:32 16x8 16 +block 19 144:40 16x8 16 +block 20 128:48 8x8 -12 +block 21 136:48 8x8 -12 +block 22 128:56 8x8 -12 +block 23 136:56 8x8 -12 +block 24 144:48 16x16 16 +block 25 160:32 8x8 33 +block 26 168:32 8x8 33 +block 27 160:40 8x8 33 +block 28 168:40 8x8 33 +block 29 176:32 8x8 33 +block 30 184:32 8x8 33 +block 31 176:40 8x8 33 +block 32 184:40 8x8 33 +block 33 160:48 8x16 0 +block 34 168:48 8x16 0 +block 35 176:48 16x8 0 +block 36 176:56 16x8 0 +block 37 192:0 8x16 -23 +block 38 200:0 8x16 -23 +block 39 208:0 16x8 -23 +block 40 208:8 16x8 -23 +block 41 192:16 16x16 0 +block 42 208:16 16x16 16 +block 43 224:0 16x32 16 +block 44 240:0 16x32 16 +block 45 192:32 8x8 33 +block 46 200:32 8x8 33 +block 47 192:40 8x8 33 +block 48 200:40 8x8 33 +block 49 208:32 16x16 -12 +block 50 192:48 8x8 33 +block 51 200:48 8x8 33 +block 52 192:56 8x8 33 +block 53 200:56 8x8 33 +block 54 208:48 8x8 16 +block 55 216:48 8x8 16 +block 56 208:56 8x8 16 +block 57 216:56 8x8 16 +block 58 224:32 16x16 -32 +block 59 240:32 16x16 -23 +block 60 224:48 16x16 16 +block 61 240:48 16x16 16 +block 62 256:0 16x16 -12 +block 63 272:0 16x16 -23 +block 64 256:16 16x16 16 +block 65 272:16 16x16 16 +block 66 288:0 32x16 -23 +block 67 288:16 32x16 0 +block 68 256:32 16x16 -23 +block 69 272:32 16x16 -32 +block 70 256:48 16x16 16 +block 71 272:48 16x16 33 +block 72 288:32 32x32 16 +block 73 320:0 8x8 -23 +block 74 328:0 8x8 -23 +block 75 320:8 8x8 -23 +block 76 328:8 8x8 -23 +block 77 336:0 8x8 -12 +block 78 344:0 8x8 -12 +block 79 336:8 8x8 -12 +block 80 344:8 8x8 -12 +block 81 320:16 8x8 -12 +block 82 328:16 8x8 -12 +block 83 320:24 8x8 -12 +block 84 328:24 8x8 -12 +block 85 336:16 16x16 -12 +block 86 320:32 8x8 0 +block 87 328:32 8x8 0 +block 88 320:40 8x8 0 +block 89 328:40 8x8 0 +block 90 336:32 16x16 -12 +block 91 320:48 16x16 16 +block 92 336:48 16x16 -12 +block 93 0:64 16x32 -23 +block 94 16:64 16x32 -23 +block 95 32:64 8x8 0 +block 96 40:64 8x8 0 +block 97 32:72 8x8 0 +block 98 40:72 8x8 0 +block 99 48:64 8x8 16 +block 100 56:64 8x8 16 +block 101 48:72 8x8 16 +block 102 56:72 8x8 16 +block 103 32:80 16x16 -32 +block 104 48:80 16x16 -32 +block 105 0:96 16x16 -32 +block 106 16:96 16x16 -32 +block 107 0:112 16x16 -32 +block 108 16:112 16x16 -32 +block 109 32:96 16x16 -12 +block 110 48:96 16x16 -12 +block 111 32:112 16x16 -12 +block 112 48:112 16x16 -32 +block 113 64:64 16x16 16 +block 114 80:64 8x8 0 +block 115 88:64 8x8 0 +block 116 80:72 8x8 0 +block 117 88:72 8x8 0 +block 118 64:80 16x16 -32 +block 119 80:80 16x16 -32 +block 120 96:64 16x16 16 +block 121 112:64 16x16 -32 +block 122 96:80 16x16 16 +block 123 112:80 16x16 -32 +block 124 64:96 16x16 -12 +block 125 80:96 8x16 -12 +block 126 88:96 8x16 -12 +block 127 64:112 16x16 -12 +block 128 80:112 16x16 0 +block 129 96:96 16x32 16 +block 130 112:96 16x32 -32 +block 131 128:64 8x8 0 +block 132 136:64 8x8 0 +block 133 128:72 8x8 0 +block 134 136:72 8x8 0 +block 135 144:64 16x16 0 +block 136 128:80 16x16 -12 +block 137 144:80 16x16 33 +block 138 160:64 16x16 33 +block 139 176:64 8x8 33 +block 140 184:64 8x8 33 +block 141 176:72 8x8 33 +block 142 184:72 8x8 33 +block 143 160:80 8x8 33 +block 144 168:80 8x8 33 +block 145 160:88 8x8 33 +block 146 168:88 8x8 33 +block 147 176:80 8x8 0 +block 148 184:80 8x8 0 +block 149 176:88 8x8 0 +block 150 184:88 8x8 0 +block 151 128:96 16x32 33 +block 152 144:96 16x32 33 +block 153 160:96 8x8 33 +block 154 168:96 8x8 33 +block 155 160:104 8x8 33 +block 156 168:104 8x8 33 +block 157 176:96 16x16 33 +block 158 160:112 8x8 16 +block 159 168:112 8x8 16 +block 160 160:120 8x8 16 +block 161 168:120 8x8 16 +block 162 176:112 8x8 33 +block 163 184:112 8x8 33 +block 164 176:120 8x8 33 +block 165 184:120 8x8 33 +block 166 192:64 8x8 33 +block 167 200:64 8x8 33 +block 168 192:72 8x8 33 +block 169 200:72 8x8 33 +block 170 208:64 8x8 33 +block 171 216:64 8x8 33 +block 172 208:72 8x8 33 +block 173 216:72 8x8 33 +block 174 192:80 8x8 33 +block 175 200:80 8x8 33 +block 176 192:88 8x8 33 +block 177 200:88 8x8 33 +block 178 208:80 8x16 33 +block 179 216:80 8x16 33 +block 180 224:64 16x16 0 +block 181 240:64 16x16 0 +block 182 224:80 8x8 -12 +block 183 232:80 8x8 -12 +block 184 224:88 8x8 -12 +block 185 232:88 8x8 -12 +block 186 240:80 8x16 -12 +block 187 248:80 8x16 -12 +block 188 192:96 8x8 33 +block 189 200:96 8x8 33 +block 190 192:104 8x8 33 +block 191 200:104 8x8 33 +block 192 208:96 16x16 33 +block 193 192:112 8x8 33 +block 194 200:112 8x8 33 +block 195 192:120 8x8 33 +block 196 200:120 8x8 33 +block 197 208:112 8x8 33 +block 198 216:112 8x8 33 +block 199 208:120 8x8 33 +block 200 216:120 8x8 33 +block 201 224:96 8x8 16 +block 202 232:96 8x8 16 +block 203 224:104 8x8 16 +block 204 232:104 8x8 16 +block 205 240:96 16x16 -32 +block 206 224:112 8x8 33 +block 207 232:112 8x8 33 +block 208 224:120 8x8 33 +block 209 232:120 8x8 33 +block 210 240:112 16x16 -32 +block 211 256:64 16x16 0 +block 212 272:64 16x16 16 +block 213 256:80 16x8 0 +block 214 256:88 16x8 0 +block 215 272:80 16x8 0 +block 216 272:88 16x8 0 +block 217 288:64 8x8 16 +block 218 296:64 8x8 16 +block 219 288:72 8x8 16 +block 220 296:72 8x8 16 +block 221 304:64 16x16 0 +block 222 288:80 16x8 -12 +block 223 288:88 16x8 -12 +block 224 304:80 8x8 -12 +block 225 312:80 8x8 -12 +block 226 304:88 8x8 -12 +block 227 312:88 8x8 -12 +block 228 256:96 16x16 -32 +block 229 272:96 16x16 -32 +block 230 256:112 16x16 -32 +block 231 272:112 16x16 -32 +block 232 288:96 16x16 -32 +block 233 304:96 16x16 -12 +block 234 288:112 16x16 -32 +block 235 304:112 16x16 0 +block 236 320:64 8x16 -12 +block 237 328:64 8x16 -12 +block 238 336:64 16x16 -12 +block 239 320:80 16x8 0 +block 240 320:88 16x8 0 +block 241 336:80 16x16 -12 +block 242 320:96 8x16 0 +block 243 328:96 8x16 0 +block 244 336:96 16x16 -12 +block 245 320:112 16x16 0 +block 246 336:112 16x16 -12 +block 247 0:128 16x16 16 +block 248 16:128 16x16 16 +block 249 0:144 16x16 -12 +block 250 16:144 16x16 -12 +block 251 32:128 8x8 16 +block 252 40:128 8x8 16 +block 253 32:136 8x8 16 +block 254 40:136 8x8 16 +block 255 48:128 16x16 16 +block 256 32:144 16x16 -23 +block 257 48:144 16x16 -23 +block 258 0:160 16x16 -12 +block 259 16:160 16x16 -12 +block 260 0:176 16x16 -32 +block 261 16:176 8x8 -32 +block 262 24:176 8x8 -32 +block 263 16:184 8x8 -32 +block 264 24:184 8x8 -32 +block 265 32:160 32x32 -12 +block 266 64:128 16x16 16 +block 267 80:128 16x16 16 +block 268 64:144 16x16 -23 +block 269 80:144 16x16 -12 +block 270 96:128 8x8 0 +block 271 104:128 8x8 0 +block 272 96:136 8x8 0 +block 273 104:136 8x8 0 +block 274 112:128 16x16 -32 +block 275 96:144 8x8 -12 +block 276 104:144 8x8 -12 +block 277 96:152 8x8 -12 +block 278 104:152 8x8 -12 +block 279 112:144 16x16 -32 +block 280 64:160 16x16 0 +block 281 80:160 16x16 0 +block 282 64:176 16x16 -32 +block 283 80:176 16x8 -12 +block 284 80:184 16x8 -12 +block 285 96:160 16x16 -12 +block 286 112:160 16x16 -32 +block 287 96:176 16x8 44 +block 288 96:184 16x8 44 +block 289 112:176 8x8 44 +block 290 120:176 8x8 44 +block 291 112:184 8x8 44 +block 292 120:184 8x8 44 +block 293 128:128 8x8 44 +block 294 136:128 8x8 44 +block 295 128:136 8x8 44 +block 296 136:136 8x8 44 +block 297 144:128 8x8 0 +block 298 152:128 8x8 0 +block 299 144:136 8x8 0 +block 300 152:136 8x8 0 +block 301 128:144 8x8 16 +block 302 136:144 8x8 16 +block 303 128:152 8x8 16 +block 304 136:152 8x8 16 +block 305 144:144 8x8 44 +block 306 152:144 8x8 44 +block 307 144:152 8x8 44 +block 308 152:152 8x8 44 +block 309 160:128 16x16 0 +block 310 176:128 8x8 16 +block 311 184:128 8x8 16 +block 312 176:136 8x8 16 +block 313 184:136 8x8 16 +block 314 160:144 8x8 33 +block 315 168:144 8x8 33 +block 316 160:152 8x8 33 +block 317 168:152 8x8 33 +block 318 176:144 8x8 33 +block 319 184:144 8x8 33 +block 320 176:152 8x8 33 +block 321 184:152 8x8 33 +block 322 128:160 8x8 16 +block 323 136:160 8x8 16 +block 324 128:168 8x8 16 +block 325 136:168 8x8 16 +block 326 144:160 8x8 44 +block 327 152:160 8x8 44 +block 328 144:168 8x8 44 +block 329 152:168 8x8 44 +block 330 128:176 16x8 33 +block 331 128:184 16x8 33 +block 332 144:176 8x8 16 +block 333 152:176 8x8 16 +block 334 144:184 8x8 16 +block 335 152:184 8x8 16 +block 336 160:160 8x8 16 +block 337 168:160 8x8 16 +block 338 160:168 8x8 16 +block 339 168:168 8x8 16 +block 340 176:160 8x8 16 +block 341 184:160 8x8 16 +block 342 176:168 8x8 16 +block 343 184:168 8x8 16 +block 344 160:176 8x8 33 +block 345 168:176 8x8 33 +block 346 160:184 8x8 33 +block 347 168:184 8x8 33 +block 348 176:176 8x16 33 +block 349 184:176 8x16 33 +block 350 192:128 16x16 0 +block 351 208:128 8x16 44 +block 352 216:128 8x16 44 +block 353 192:144 16x16 0 +block 354 208:144 8x8 33 +block 355 216:144 8x8 33 +block 356 208:152 8x8 33 +block 357 216:152 8x8 33 +block 358 224:128 8x8 33 +block 359 232:128 8x8 33 +block 360 224:136 8x8 33 +block 361 232:136 8x8 33 +block 362 240:128 16x16 16 +block 363 224:144 16x8 0 +block 364 224:152 16x8 0 +block 365 240:144 16x16 -12 +block 366 192:160 16x16 16 +block 367 208:160 8x8 44 +block 368 216:160 8x8 44 +block 369 208:168 8x8 44 +block 370 216:168 8x8 44 +block 371 192:176 8x8 33 +block 372 200:176 8x8 33 +block 373 192:184 8x8 33 +block 374 200:184 8x8 33 +block 375 208:176 8x8 33 +block 376 216:176 8x8 33 +block 377 208:184 8x8 33 +block 378 216:184 8x8 33 +block 379 224:160 8x8 0 +block 380 232:160 8x8 0 +block 381 224:168 8x8 0 +block 382 232:168 8x8 0 +block 383 240:160 16x16 0 +block 384 224:176 8x8 44 +block 385 232:176 8x8 44 +block 386 224:184 8x8 44 +block 387 232:184 8x8 44 +block 388 240:176 16x8 44 +block 389 240:184 16x8 44 +block 390 256:128 16x16 16 +block 391 272:128 16x16 16 +block 392 256:144 16x16 -12 +block 393 272:144 16x16 -12 +block 394 288:128 16x8 16 +block 395 288:136 16x8 16 +block 396 304:128 16x16 16 +block 397 288:144 16x16 -12 +block 398 304:144 16x16 -12 +block 399 256:160 16x16 -23 +block 400 272:160 16x16 -23 +block 401 256:176 8x8 44 +block 402 264:176 8x8 44 +block 403 256:184 8x8 44 +block 404 264:184 8x8 44 +block 405 272:176 16x8 33 +block 406 272:184 16x8 33 +block 407 288:160 32x16 -23 +block 408 288:176 32x16 -32 +block 409 320:128 8x8 -12 +block 410 328:128 8x8 -12 +block 411 320:136 8x8 -12 +block 412 328:136 8x8 -12 +block 413 336:128 8x16 -12 +block 414 344:128 8x16 -12 +block 415 320:144 16x16 -23 +block 416 336:144 16x16 -12 +block 417 320:160 16x16 -23 +block 418 336:160 16x16 0 +block 419 320:176 16x16 -32 +block 420 336:176 16x16 0 +block 421 0:192 16x16 -32 +block 422 16:192 8x8 -32 +block 423 24:192 8x8 -32 +block 424 16:200 8x8 -32 +block 425 24:200 8x8 -32 +block 426 0:208 16x16 0 +block 427 16:208 16x16 0 +block 428 32:192 32x32 -32 +block 429 0:224 16x16 0 +block 430 16:224 16x16 0 +block 431 0:240 16x16 0 +block 432 16:240 16x16 0 +block 433 32:224 32x16 0 +block 434 32:240 32x16 33 +block 435 64:192 8x8 44 +block 436 72:192 8x8 44 +block 437 64:200 8x8 44 +block 438 72:200 8x8 44 +block 439 80:192 8x8 44 +block 440 88:192 8x8 44 +block 441 80:200 8x8 44 +block 442 88:200 8x8 44 +block 443 64:208 8x8 44 +block 444 72:208 8x8 44 +block 445 64:216 8x8 44 +block 446 72:216 8x8 44 +block 447 80:208 8x8 -12 +block 448 88:208 8x8 -12 +block 449 80:216 8x8 -12 +block 450 88:216 8x8 -12 +block 451 96:192 8x8 -23 +block 452 104:192 8x8 -23 +block 453 96:200 8x8 -23 +block 454 104:200 8x8 -23 +block 455 112:192 16x16 -23 +block 456 96:208 16x16 -23 +block 457 112:208 16x16 0 +block 458 64:224 32x16 16 +block 459 64:240 32x16 0 +block 460 96:224 16x16 -12 +block 461 112:224 8x16 0 +block 462 120:224 8x16 0 +block 463 96:240 16x16 -12 +block 464 112:240 8x8 0 +block 465 120:240 8x8 0 +block 466 112:248 8x8 0 +block 467 120:248 8x8 0 +block 468 128:192 8x8 0 +block 469 136:192 8x8 0 +block 470 128:200 8x8 0 +block 471 136:200 8x8 0 +block 472 144:192 8x16 0 +block 473 152:192 8x16 0 +block 474 128:208 8x8 -23 +block 475 136:208 8x8 -23 +block 476 128:216 8x8 -23 +block 477 136:216 8x8 -23 +block 478 144:208 8x16 0 +block 479 152:208 8x16 0 +block 480 160:192 8x8 44 +block 481 168:192 8x8 44 +block 482 160:200 8x8 44 +block 483 168:200 8x8 44 +block 484 176:192 8x8 0 +block 485 184:192 8x8 0 +block 486 176:200 8x8 0 +block 487 184:200 8x8 0 +block 488 160:208 8x8 33 +block 489 168:208 8x8 33 +block 490 160:216 8x8 33 +block 491 168:216 8x8 33 +block 492 176:208 8x8 44 +block 493 184:208 8x8 44 +block 494 176:216 8x8 44 +block 495 184:216 8x8 44 +block 496 128:224 16x16 -32 +block 497 144:224 16x16 -12 +block 498 128:240 8x16 -12 +block 499 136:240 8x16 -12 +block 500 144:240 16x16 -23 +block 501 160:224 8x8 33 +block 502 168:224 8x8 33 +block 503 160:232 8x8 33 +block 504 168:232 8x8 33 +block 505 176:224 8x8 33 +block 506 184:224 8x8 33 +block 507 176:232 8x8 33 +block 508 184:232 8x8 33 +block 509 160:240 8x8 33 +block 510 168:240 8x8 33 +block 511 160:248 8x8 33 +block 512 168:248 8x8 33 +block 513 176:240 8x8 33 +block 514 184:240 8x8 33 +block 515 176:248 8x8 33 +block 516 184:248 8x8 33 +block 517 192:192 16x8 33 +block 518 192:200 16x8 33 +block 519 208:192 8x16 16 +block 520 216:192 8x16 16 +block 521 192:208 16x16 16 +block 522 208:208 8x8 0 +block 523 216:208 8x8 0 +block 524 208:216 8x8 0 +block 525 216:216 8x8 0 +block 526 224:192 8x8 -12 +block 527 232:192 8x8 -12 +block 528 224:200 8x8 -12 +block 529 232:200 8x8 -12 +block 530 240:192 8x16 0 +block 531 248:192 8x16 0 +block 532 224:208 16x16 -23 +block 533 240:208 16x8 16 +block 534 240:216 16x8 16 +block 535 192:224 16x16 33 +block 536 208:224 8x8 0 +block 537 216:224 8x8 0 +block 538 208:232 8x8 0 +block 539 216:232 8x8 0 +block 540 192:240 8x8 33 +block 541 200:240 8x8 33 +block 542 192:248 8x8 33 +block 543 200:248 8x8 33 +block 544 208:240 8x8 0 +block 545 216:240 8x8 0 +block 546 208:248 8x8 0 +block 547 216:248 8x8 0 +block 548 224:224 16x16 -23 +block 549 240:224 8x8 33 +block 550 248:224 8x8 33 +block 551 240:232 8x8 33 +block 552 248:232 8x8 33 +block 553 224:240 8x8 -23 +block 554 232:240 8x8 -23 +block 555 224:248 8x8 -23 +block 556 232:248 8x8 -23 +block 557 240:240 8x8 33 +block 558 248:240 8x8 33 +block 559 240:248 8x8 33 +block 560 248:248 8x8 33 +block 561 256:192 16x16 -23 +block 562 272:192 16x8 16 +block 563 272:200 16x8 16 +block 564 256:208 8x8 -23 +block 565 264:208 8x8 -23 +block 566 256:216 8x8 -23 +block 567 264:216 8x8 -23 +block 568 272:208 8x8 -32 +block 569 280:208 8x8 -32 +block 570 272:216 8x8 -32 +block 571 280:216 8x8 -32 +block 572 288:192 8x8 44 +block 573 296:192 8x8 44 +block 574 288:200 8x8 44 +block 575 296:200 8x8 44 +block 576 304:192 8x8 -32 +block 577 312:192 8x8 -32 +block 578 304:200 8x8 -32 +block 579 312:200 8x8 -32 +block 580 288:208 16x16 33 +block 581 304:208 8x16 -12 +block 582 312:208 8x16 -12 +block 583 256:224 16x16 -23 +block 584 272:224 16x16 -23 +block 585 256:240 8x8 -12 +block 586 264:240 8x8 -12 +block 587 256:248 8x8 -12 +block 588 264:248 8x8 -12 +block 589 272:240 8x8 0 +block 590 280:240 8x8 0 +block 591 272:248 8x8 0 +block 592 280:248 8x8 0 +block 593 288:224 16x16 33 +block 594 304:224 16x8 16 +block 595 304:232 16x8 16 +block 596 288:240 16x16 33 +block 597 304:240 8x8 44 +block 598 312:240 8x8 44 +block 599 304:248 8x8 44 +block 600 312:248 8x8 44 +block 601 320:192 8x8 -32 +block 602 328:192 8x8 -32 +block 603 320:200 8x8 -32 +block 604 328:200 8x8 -32 +block 605 336:192 8x16 0 +block 606 344:192 8x16 0 +block 607 320:208 8x8 -32 +block 608 328:208 8x8 -32 +block 609 320:216 8x8 -32 +block 610 328:216 8x8 -32 +block 611 336:208 8x16 0 +block 612 344:208 8x16 0 +block 613 320:224 16x16 -32 +block 614 336:224 16x16 0 +block 615 320:240 16x16 -32 +block 616 336:240 8x8 0 +block 617 344:240 8x8 0 +block 618 336:248 8x8 0 +block 619 344:248 8x8 0 +block 620 0:256 16x16 0 +block 621 16:256 16x16 -23 +block 622 0:272 16x16 -32 +block 623 16:272 16x16 -32 +block 624 32:256 16x16 -32 +block 625 48:256 8x8 44 +block 626 56:256 8x8 44 +block 627 48:264 8x8 44 +block 628 56:264 8x8 44 +block 629 32:272 16x16 -32 +block 630 48:272 8x8 44 +block 631 56:272 8x8 44 +block 632 48:280 8x8 44 +block 633 56:280 8x8 44 +block 634 64:256 8x8 0 +block 635 72:256 8x8 0 +block 636 64:264 8x8 0 +block 637 72:264 8x8 0 +block 638 80:256 8x8 0 +block 639 88:256 8x8 0 +block 640 80:264 8x8 0 +block 641 88:264 8x8 0 +block 642 64:272 8x8 -12 +block 643 72:272 8x8 -12 +block 644 64:280 8x8 -12 +block 645 72:280 8x8 -12 +block 646 80:272 16x16 0 +block 647 96:256 16x16 -23 +block 648 112:256 8x8 -12 +block 649 120:256 8x8 -12 +block 650 112:264 8x8 -12 +block 651 120:264 8x8 -12 +block 652 96:272 16x16 -12 +block 653 112:272 8x8 -23 +block 654 120:272 8x8 -23 +block 655 112:280 8x8 -23 +block 656 120:280 8x8 -23 +block 657 128:256 32x32 -32 +block 658 160:256 8x8 33 +block 659 168:256 8x8 33 +block 660 160:264 8x8 33 +block 661 168:264 8x8 33 +block 662 176:256 8x8 33 +block 663 184:256 8x8 33 +block 664 176:264 8x8 33 +block 665 184:264 8x8 33 +block 666 160:272 8x8 33 +block 667 168:272 8x8 33 +block 668 160:280 8x8 33 +block 669 168:280 8x8 33 +block 670 176:272 8x16 0 +block 671 184:272 8x16 0 +block 672 192:256 8x8 33 +block 673 200:256 8x8 33 +block 674 192:264 8x8 33 +block 675 200:264 8x8 33 +block 676 208:256 8x8 44 +block 677 216:256 8x8 44 +block 678 208:264 8x8 44 +block 679 216:264 8x8 44 +block 680 192:272 8x8 33 +block 681 200:272 8x8 33 +block 682 192:280 8x8 33 +block 683 200:280 8x8 33 +block 684 208:272 8x8 33 +block 685 216:272 8x8 33 +block 686 208:280 8x8 33 +block 687 216:280 8x8 33 +block 688 224:256 8x8 0 +block 689 232:256 8x8 0 +block 690 224:264 8x8 0 +block 691 232:264 8x8 0 +block 692 240:256 8x8 33 +block 693 248:256 8x8 33 +block 694 240:264 8x8 33 +block 695 248:264 8x8 33 +block 696 224:272 16x8 -32 +block 697 224:280 16x8 -32 +block 698 240:272 8x8 33 +block 699 248:272 8x8 33 +block 700 240:280 8x8 33 +block 701 248:280 8x8 33 +block 702 256:256 16x16 0 +block 703 272:256 8x8 33 +block 704 280:256 8x8 33 +block 705 272:264 8x8 33 +block 706 280:264 8x8 33 +block 707 256:272 8x8 33 +block 708 264:272 8x8 33 +block 709 256:280 8x8 33 +block 710 264:280 8x8 33 +block 711 272:272 8x8 33 +block 712 280:272 8x8 33 +block 713 272:280 8x8 33 +block 714 280:280 8x8 33 +block 715 288:256 16x16 33 +block 716 304:256 8x8 44 +block 717 312:256 8x8 44 +block 718 304:264 8x8 44 +block 719 312:264 8x8 44 +block 720 288:272 16x16 0 +block 721 304:272 8x16 44 +block 722 312:272 8x16 44 +block 723 320:256 16x8 -32 +block 724 320:264 16x8 -32 +block 725 336:256 8x8 0 +block 726 344:256 8x8 0 +block 727 336:264 8x8 0 +block 728 344:264 8x8 0 +block 729 320:272 16x16 -32 +block 730 336:272 16x16 0 +frame 1 +AVVideoEncParams 0 +qp 183 +nb_blocks 37 +block 0 0:0 64x64 21 +block 1 64:0 64x64 21 +block 2 128:0 64x64 11 +block 3 192:0 64x64 21 +block 4 256:0 64x64 21 +block 5 320:0 32x32 -10 +block 6 320:32 32x32 0 +block 7 0:64 64x64 0 +block 8 64:64 64x64 21 +block 9 128:64 64x64 32 +block 10 192:64 64x64 21 +block 11 256:64 64x64 11 +block 12 320:64 32x32 0 +block 13 320:96 32x32 0 +block 14 0:128 64x64 0 +block 15 64:128 64x64 21 +block 16 128:128 64x64 32 +block 17 192:128 64x64 32 +block 18 256:128 64x64 21 +block 19 320:128 32x32 -10 +block 20 320:160 32x32 -10 +block 21 0:192 64x64 11 +block 22 64:192 64x64 21 +block 23 128:192 64x64 21 +block 24 192:192 64x64 21 +block 25 256:192 64x64 32 +block 26 320:192 32x32 -10 +block 27 320:224 32x32 -10 +block 28 0:256 32x32 -10 +block 29 32:256 32x32 32 +block 30 64:256 32x32 0 +block 31 96:256 32x32 -10 +block 32 128:256 32x32 -32 +block 33 160:256 32x32 21 +block 34 192:256 64x32 21 +block 35 256:256 64x32 21 +block 36 320:256 32x32 -10 +frame 2 +AVVideoEncParams 0 +qp 188 +nb_blocks 40 +block 0 0:0 64x64 21 +block 1 64:0 64x64 21 +block 2 128:0 64x64 11 +block 3 192:0 64x64 21 +block 4 256:0 64x64 21 +block 5 320:0 32x32 -10 +block 6 320:32 32x32 0 +block 7 0:64 64x64 0 +block 8 64:64 64x64 21 +block 9 128:64 64x64 31 +block 10 192:64 64x64 21 +block 11 256:64 64x64 11 +block 12 320:64 32x32 0 +block 13 320:96 32x32 0 +block 14 0:128 64x64 0 +block 15 64:128 64x64 21 +block 16 128:128 32x64 31 +block 17 160:128 32x64 21 +block 18 192:128 64x64 31 +block 19 256:128 64x64 21 +block 20 320:128 32x32 -10 +block 21 320:160 32x32 -10 +block 22 0:192 64x64 11 +block 23 64:192 64x64 21 +block 24 128:192 64x64 21 +block 25 192:192 64x64 21 +block 26 256:192 64x64 31 +block 27 320:192 32x32 -10 +block 28 320:224 32x32 -10 +block 29 0:256 32x32 -10 +block 30 32:256 32x32 31 +block 31 64:256 32x32 0 +block 32 96:256 32x32 -10 +block 33 128:256 32x32 -32 +block 34 160:256 32x32 21 +block 35 192:256 32x32 31 +block 36 224:256 32x32 21 +block 37 256:256 32x32 21 +block 38 288:256 32x32 31 +block 39 320:256 32x32 -10 +frame 3 +AVVideoEncParams 0 +qp 192 +nb_blocks 39 +block 0 0:0 64x64 21 +block 1 64:0 64x64 21 +block 2 128:0 64x64 11 +block 3 192:0 64x64 21 +block 4 256:0 64x64 21 +block 5 320:0 32x32 -10 +block 6 320:32 32x32 0 +block 7 0:64 64x64 0 +block 8 64:64 64x64 21 +block 9 128:64 64x64 31 +block 10 192:64 64x64 21 +block 11 256:64 64x64 11 +block 12 320:64 32x32 0 +block 13 320:96 32x32 0 +block 14 0:128 64x64 0 +block 15 64:128 64x64 21 +block 16 128:128 64x64 31 +block 17 192:128 64x64 31 +block 18 256:128 64x64 21 +block 19 320:128 32x32 -10 +block 20 320:160 32x32 -10 +block 21 0:192 64x64 11 +block 22 64:192 64x64 21 +block 23 128:192 64x64 21 +block 24 192:192 64x64 21 +block 25 256:192 64x64 31 +block 26 320:192 32x32 -10 +block 27 320:224 32x32 -10 +block 28 0:256 32x32 -10 +block 29 32:256 32x32 31 +block 30 64:256 32x32 0 +block 31 96:256 32x32 -10 +block 32 128:256 32x32 -32 +block 33 160:256 32x32 21 +block 34 192:256 32x32 31 +block 35 224:256 32x32 21 +block 36 256:256 32x32 21 +block 37 288:256 32x32 31 +block 38 320:256 32x32 -10 +frame 4 +AVVideoEncParams 0 +qp 196 +nb_blocks 70 +block 0 0:0 64x64 21 +block 1 64:0 64x64 21 +block 2 128:0 64x64 11 +block 3 192:0 64x64 21 +block 4 256:0 64x64 21 +block 5 320:0 32x32 -10 +block 6 320:32 32x32 0 +block 7 0:64 64x64 0 +block 8 64:64 64x64 21 +block 9 128:64 32x32 11 +block 10 160:64 32x32 31 +block 11 128:96 16x16 21 +block 12 144:96 16x16 21 +block 13 128:112 16x16 31 +block 14 144:112 8x8 21 +block 15 152:112 8x8 21 +block 16 144:120 8x8 21 +block 17 152:120 8x8 21 +block 18 160:96 16x16 21 +block 19 176:96 8x8 21 +block 20 184:96 8x8 21 +block 21 176:104 8x8 21 +block 22 184:104 8x8 21 +block 23 160:112 16x16 11 +block 24 176:112 8x8 21 +block 25 184:112 8x8 21 +block 26 176:120 8x8 21 +block 27 184:120 8x8 21 +block 28 192:64 32x32 21 +block 29 224:64 32x32 0 +block 30 192:96 8x8 11 +block 31 200:96 8x8 11 +block 32 192:104 8x8 11 +block 33 200:104 8x8 11 +block 34 208:96 16x16 21 +block 35 192:112 8x8 21 +block 36 200:112 8x8 21 +block 37 192:120 8x8 21 +block 38 200:120 8x8 21 +block 39 208:112 16x16 21 +block 40 224:96 32x16 0 +block 41 224:112 32x16 21 +block 42 256:64 64x64 11 +block 43 320:64 32x32 0 +block 44 320:96 32x32 0 +block 45 0:128 64x64 0 +block 46 64:128 64x64 21 +block 47 128:128 64x64 31 +block 48 192:128 64x64 31 +block 49 256:128 64x64 21 +block 50 320:128 32x32 -10 +block 51 320:160 32x32 -10 +block 52 0:192 64x64 11 +block 53 64:192 64x64 21 +block 54 128:192 64x64 21 +block 55 192:192 64x64 21 +block 56 256:192 64x64 31 +block 57 320:192 32x32 -10 +block 58 320:224 32x32 -10 +block 59 0:256 32x32 -10 +block 60 32:256 32x32 31 +block 61 64:256 32x32 0 +block 62 96:256 32x32 -10 +block 63 128:256 32x32 -32 +block 64 160:256 32x32 21 +block 65 192:256 32x32 31 +block 66 224:256 32x32 21 +block 67 256:256 32x32 21 +block 68 288:256 32x32 31 +block 69 320:256 32x32 -10 diff --git a/tests/ref/fate/webm-dash-chapters b/tests/ref/fate/webm-dash-chapters new file mode 100644 index 00000000000..20ddfc031d6 --- /dev/null +++ b/tests/ref/fate/webm-dash-chapters @@ -0,0 +1,74 @@ +e7fde2ecc9683a7a5296dab33b028653 *tests/data/fate/webm-dash-chapters.webm +111220 tests/data/fate/webm-dash-chapters.webm +#extradata 0: 3469, 0xc6769ddc +#tb 0: 1/1000 +#media_type 0: audio +#codec_id 0: vorbis +#sample_rate 0: 44100 +#channel_layout 0: 4 +#channel_layout_name 0: mono +0, 0, 0, 5, 28, 0xefcf103e +0, 6, 6, 26, 198, 0xfbbe5eb5 +0, 32, 32, 46, 198, 0xabd95c6c +0, 99, 99, 26, 41, 0x954b12a5 +0, 105, 105, 5, 41, 0xbccd1463 +0, 110, 110, 5, 44, 0x4fa218a1 +0, 116, 116, 5, 43, 0xf87716d4 +0, 122, 122, 5, 32, 0x0fdc1057 +0, 128, 128, 5, 67, 0xd5352244 +0, 134, 134, 5, 68, 0x36e91faf +0, 140, 140, 5, 57, 0xe6f51928 +0, 145, 145, 5, 49, 0x3bb416e1 +0, 151, 151, 5, 54, 0x678f1777 +0, 157, 157, 5, 57, 0x56601ef3 +0, 163, 163, 5, 52, 0xc0fe1a12 +0, 169, 169, 26, 236, 0xfe396f02 +0, 195, 195, 46, 208, 0x018e62d3 +0, 241, 241, 46, 223, 0x9fa76917 +0, 308, 308, 26, 46, 0xd8c314f9 +0, 314, 314, 5, 46, 0x199018bc +0, 319, 319, 5, 46, 0xbe8314cd +0, 325, 325, 5, 45, 0xe0ad1622 +0, 331, 331, 5, 43, 0xe52a1659 +0, 337, 337, 5, 58, 0xd5e01f9c +0, 343, 343, 5, 67, 0x5bbc2201 +0, 348, 348, 26, 226, 0x04887569 +0, 375, 375, 46, 217, 0x4b6564ab +0, 421, 421, 46, 211, 0xb7e868da +0, 467, 467, 46, 198, 0x7bf65d8a +[CHAPTER] +id=1 +time_base=1/1000000000 +start=0 +start_time=0.000000 +end=5000000000 +end_time=5.000000 +TAG:title=start +[/CHAPTER] +[CHAPTER] +id=2 +time_base=1/1000000000 +start=5000000000 +start_time=5.000000 +end=10500000000 +end_time=10.500000 +TAG:title=Five Seconds +[/CHAPTER] +[CHAPTER] +id=3 +time_base=1/1000000000 +start=10500000000 +start_time=10.500000 +end=15000000000 +end_time=15.000000 +TAG:title=Ten point 5 seconds +[/CHAPTER] +[CHAPTER] +id=4 +time_base=1/1000000000 +start=15000000000 +start_time=15.000000 +end=19849000000 +end_time=19.849000 +TAG:title=15 sec - over soon +[/CHAPTER] diff --git a/tests/ref/lavf-fate/av1.mkv b/tests/ref/lavf-fate/av1.mkv new file mode 100644 index 00000000000..bc568a600ed --- /dev/null +++ b/tests/ref/lavf-fate/av1.mkv @@ -0,0 +1,3 @@ +bd676bfc89422755920099ec6ebdebe6 *tests/data/lavf-fate/lavf.av1.mkv +55657 tests/data/lavf-fate/lavf.av1.mkv +tests/data/lavf-fate/lavf.av1.mkv CRC=0x7c27cc15 diff --git a/tests/ref/lavf-fate/av1.mp4 b/tests/ref/lavf-fate/av1.mp4 new file mode 100644 index 00000000000..38d2a80afed --- /dev/null +++ b/tests/ref/lavf-fate/av1.mp4 @@ -0,0 +1,3 @@ +0388467214421a19ba65d10a74dc35c0 *tests/data/lavf-fate/lavf.av1.mp4 +55936 tests/data/lavf-fate/lavf.av1.mp4 +tests/data/lavf-fate/lavf.av1.mp4 CRC=0x7c27cc15 diff --git a/tests/ref/lavf-fate/avi_cram b/tests/ref/lavf-fate/avi_cram deleted file mode 100644 index 82882fbb22b..00000000000 --- a/tests/ref/lavf-fate/avi_cram +++ /dev/null @@ -1,3 +0,0 @@ -6fc88702c23b895c305c5e1f51a0904e *./tests/data/lavf-fate/lavf.avi -928260 ./tests/data/lavf-fate/lavf.avi -./tests/data/lavf-fate/lavf.avi CRC=0xa4770de2 diff --git a/tests/ref/lavf-fate/h264.mp4 b/tests/ref/lavf-fate/h264.mp4 new file mode 100644 index 00000000000..bb52f45758a --- /dev/null +++ b/tests/ref/lavf-fate/h264.mp4 @@ -0,0 +1,3 @@ +6d158b25efe7391c803f6f61c7a80aa0 *tests/data/lavf-fate/lavf.h264.mp4 +547908 tests/data/lavf-fate/lavf.h264.mp4 +tests/data/lavf-fate/lavf.h264.mp4 CRC=0x9da2c999 diff --git a/tests/ref/lavf-fate/mov_qtrle_mace6 b/tests/ref/lavf-fate/mov_qtrle_mace6 deleted file mode 100644 index f8428aaa492..00000000000 --- a/tests/ref/lavf-fate/mov_qtrle_mace6 +++ /dev/null @@ -1,3 +0,0 @@ -dcc9c4c182a5809dee9a9366f4533797 *./tests/data/lavf-fate/lavf.mov -1270387 ./tests/data/lavf-fate/lavf.mov -./tests/data/lavf-fate/lavf.mov CRC=0x9320cd26 diff --git a/tests/ref/lavf-fate/ogg_vp3 b/tests/ref/lavf-fate/ogg_vp3 deleted file mode 100644 index 9e9cc7ea6b6..00000000000 --- a/tests/ref/lavf-fate/ogg_vp3 +++ /dev/null @@ -1,3 +0,0 @@ -4bd51dac3194fa88ae33767c25b4b1e6 *./tests/data/lavf-fate/lavf.ogg -417621 ./tests/data/lavf-fate/lavf.ogg -./tests/data/lavf-fate/lavf.ogg CRC=0x037e3e79 diff --git a/tests/ref/lavf-fate/ogg_vp8 b/tests/ref/lavf-fate/ogg_vp8 deleted file mode 100644 index 78131f30de9..00000000000 --- a/tests/ref/lavf-fate/ogg_vp8 +++ /dev/null @@ -1,3 +0,0 @@ -c56d8dce728d46d4f0ab4c7cc9f86abc *./tests/data/lavf-fate/lavf.ogv -95009 ./tests/data/lavf-fate/lavf.ogv -./tests/data/lavf-fate/lavf.ogv CRC=0x8c067a66 diff --git a/tests/ref/lavf/alaw b/tests/ref/lavf/alaw deleted file mode 100644 index d93d6fc0ee8..00000000000 --- a/tests/ref/lavf/alaw +++ /dev/null @@ -1,3 +0,0 @@ -652d96e474869ddb01403743deb35117 *./tests/data/lavf/lavf.al -44100 ./tests/data/lavf/lavf.al -./tests/data/lavf/lavf.al CRC=0xf9643112 diff --git a/tests/ref/lavf/dv_fmt b/tests/ref/lavf/dv_fmt deleted file mode 100644 index 0263202c8e8..00000000000 --- a/tests/ref/lavf/dv_fmt +++ /dev/null @@ -1,9 +0,0 @@ -7830f9c6716ceb6011f865f1e521b951 *./tests/data/lavf/lavf.dv -3600000 ./tests/data/lavf/lavf.dv -./tests/data/lavf/lavf.dv CRC=0xd428d3ee -5569626370c7c72d40de2c4559e32856 *./tests/data/lavf/lavf.dv -3480000 ./tests/data/lavf/lavf.dv -./tests/data/lavf/lavf.dv CRC=0xa0088163 -2fb332aab8f2ba9c33b1b2368194392a *./tests/data/lavf/lavf.dv -3600000 ./tests/data/lavf/lavf.dv -./tests/data/lavf/lavf.dv CRC=0xbdaf7f52 diff --git a/tests/ref/lavf/fits b/tests/ref/lavf/fits deleted file mode 100644 index 489542b32be..00000000000 --- a/tests/ref/lavf/fits +++ /dev/null @@ -1,18 +0,0 @@ -ed9fd697d0d782df6201f6a2db184552 *./tests/data/lavf/graylavf.fits -5328000 ./tests/data/lavf/graylavf.fits -./tests/data/lavf/graylavf.fits CRC=0xbacf446c -48e6caf6a59e32f9a8a39979c9183a7f *./tests/data/lavf/gray16belavf.fits -10368000 ./tests/data/lavf/gray16belavf.fits -./tests/data/lavf/gray16belavf.fits CRC=0xae2b58d4 -be2f7112fd193c9a909304c81e662769 *./tests/data/lavf/gbrplavf.fits -15408000 ./tests/data/lavf/gbrplavf.fits -./tests/data/lavf/gbrplavf.fits CRC=0x04ed3828 -c89a72185cfad363aa9cc42e84fed301 *./tests/data/lavf/gbraplavf.fits -20448000 ./tests/data/lavf/gbraplavf.fits -./tests/data/lavf/gbraplavf.fits CRC=0x032a6409 -d539b9e02f5ab8fb85717c8adb60b6cc *./tests/data/lavf/gbrp16belavf.fits -30672000 ./tests/data/lavf/gbrp16belavf.fits -./tests/data/lavf/gbrp16belavf.fits CRC=0x81897ff7 -3dc3622fb09a338b406d8a12a30f2545 *./tests/data/lavf/gbrap16belavf.fits -40752000 ./tests/data/lavf/gbrap16belavf.fits -./tests/data/lavf/gbrap16belavf.fits CRC=0x247dd7b9 diff --git a/tests/ref/lavf/flv_fmt b/tests/ref/lavf/flv_fmt deleted file mode 100644 index f014bec7087..00000000000 --- a/tests/ref/lavf/flv_fmt +++ /dev/null @@ -1,3 +0,0 @@ -0d229f87b3aad778074ace499359d137 *./tests/data/lavf/lavf.flv -329554 ./tests/data/lavf/lavf.flv -./tests/data/lavf/lavf.flv CRC=0x4eac88c5 diff --git a/tests/ref/lavf/gray16be.fits b/tests/ref/lavf/gray16be.fits index 078d6c86789..058fa4ad19c 100644 --- a/tests/ref/lavf/gray16be.fits +++ b/tests/ref/lavf/gray16be.fits @@ -1,3 +1,3 @@ 15e85a553bbd07783f92377ed369308b *tests/data/lavf/lavf.gray16be.fits 5184000 tests/data/lavf/lavf.gray16be.fits -tests/data/lavf/lavf.gray16be.fits CRC=0x8b840cff +tests/data/lavf/lavf.gray16be.fits CRC=0x8cdcbeb2 diff --git a/tests/ref/lavf/ismv b/tests/ref/lavf/ismv index 865a6913cf7..e7361705fa5 100644 --- a/tests/ref/lavf/ismv +++ b/tests/ref/lavf/ismv @@ -1,9 +1,9 @@ -bd88b50defa57766619c092ea89f25a6 *tests/data/lavf/lavf.ismv -313165 tests/data/lavf/lavf.ismv +4c6bc5ac805a76bbbd886a69d2e61554 *tests/data/lavf/lavf.ismv +313169 tests/data/lavf/lavf.ismv tests/data/lavf/lavf.ismv CRC=0x9d9a638a -805a2557bf952c84835f3c10b6893e15 *tests/data/lavf/lavf.ismv -322071 tests/data/lavf/lavf.ismv +18678627921460328ea3fed238d0d57d *tests/data/lavf/lavf.ismv +322075 tests/data/lavf/lavf.ismv tests/data/lavf/lavf.ismv CRC=0xe8130120 -96053075a3f60d271131fe2d0765c267 *tests/data/lavf/lavf.ismv -312542 tests/data/lavf/lavf.ismv +b9a858caf55b1eff2273e746e9f72dc4 *tests/data/lavf/lavf.ismv +312546 tests/data/lavf/lavf.ismv tests/data/lavf/lavf.ismv CRC=0x9d9a638a diff --git a/tests/ref/lavf/mka b/tests/ref/lavf/mka index a244893cf2e..e66b418b1d5 100644 --- a/tests/ref/lavf/mka +++ b/tests/ref/lavf/mka @@ -1,3 +1,3 @@ -a91bfdb08ff5a1e62b039748d16b07f7 *tests/data/lavf/lavf.mka -43652 tests/data/lavf/lavf.mka +ea812a6619f4f0d266bec82fcfa54e78 *tests/data/lavf/lavf.mka +43580 tests/data/lavf/lavf.mka tests/data/lavf/lavf.mka CRC=0x3a1da17e diff --git a/tests/ref/lavf/mkv b/tests/ref/lavf/mkv index 05999b79fbc..0dd4521bc87 100644 --- a/tests/ref/lavf/mkv +++ b/tests/ref/lavf/mkv @@ -1,3 +1,3 @@ -3cddd26ed41f34925d3f34acabab174e *tests/data/lavf/lavf.mkv -320555 tests/data/lavf/lavf.mkv +c984a76e996f43943d2fe9eb5f2495e4 *tests/data/lavf/lavf.mkv +320432 tests/data/lavf/lavf.mkv tests/data/lavf/lavf.mkv CRC=0xec6c3c68 diff --git a/tests/ref/lavf/mkv_attachment b/tests/ref/lavf/mkv_attachment index 230dff369a6..e0e1a22d9cf 100644 --- a/tests/ref/lavf/mkv_attachment +++ b/tests/ref/lavf/mkv_attachment @@ -1,3 +1,3 @@ -4a25c53150c09537cd4fcbff8f8f14ff *tests/data/lavf/lavf.mkv_attachment -472706 tests/data/lavf/lavf.mkv_attachment +8182124c770de9579e6d0d6759d01655 *tests/data/lavf/lavf.mkv_attachment +472587 tests/data/lavf/lavf.mkv_attachment tests/data/lavf/lavf.mkv_attachment CRC=0xec6c3c68 diff --git a/tests/ref/lavf/mulaw b/tests/ref/lavf/mulaw deleted file mode 100644 index bd540847de2..00000000000 --- a/tests/ref/lavf/mulaw +++ /dev/null @@ -1,3 +0,0 @@ -ad492935e361f830f2f8302aa102701d *./tests/data/lavf/lavf.ul -44100 ./tests/data/lavf/lavf.ul -./tests/data/lavf/lavf.ul CRC=0x4515fa26 diff --git a/tests/ref/lavf/mxf b/tests/ref/lavf/mxf index 471fc5518ef..5b16496f067 100644 --- a/tests/ref/lavf/mxf +++ b/tests/ref/lavf/mxf @@ -1,7 +1,7 @@ -649009e3d3d62eb3b6c56334d057cc4d *tests/data/lavf/lavf.mxf +27b98795036b334e100c15c7e06d948f *tests/data/lavf/lavf.mxf 526393 tests/data/lavf/lavf.mxf tests/data/lavf/lavf.mxf CRC=0x8dddfaab -9076b7015cffe8aa72883e900a2041a5 *tests/data/lavf/lavf.mxf +783b475a818602f54e947094d57e2981 *tests/data/lavf/lavf.mxf 561721 tests/data/lavf/lavf.mxf tests/data/lavf/lavf.mxf CRC=0x96ff1b48 02bf8f0cd8951a49e277306691cb1538 *tests/data/lavf/lavf.mxf diff --git a/tests/ref/lavf/pixfmt b/tests/ref/lavf/pixfmt deleted file mode 100644 index ec75d4ce246..00000000000 --- a/tests/ref/lavf/pixfmt +++ /dev/null @@ -1,38 +0,0 @@ -5641dba168ff665af1cdb4a91e1afdd6 *./tests/data/pixfmt/yuv420p.yuv -304128 ./tests/data/pixfmt/yuv420p.yuv -ac68f9fdd9d55efd0306d9b004038761 *./tests/data/pixfmt/yuv422p.yuv -304128 ./tests/data/pixfmt/yuv422p.yuv -5641dba168ff665af1cdb4a91e1afdd6 *./tests/data/pixfmt/yuv444p.yuv -304128 ./tests/data/pixfmt/yuv444p.yuv -ac68f9fdd9d55efd0306d9b004038761 *./tests/data/pixfmt/yuyv422.yuv -304128 ./tests/data/pixfmt/yuyv422.yuv -507c7e9f0c97660385df977469ca9e6d *./tests/data/pixfmt/yuv410p.yuv -304128 ./tests/data/pixfmt/yuv410p.yuv -8594ea0b8d7c2c964525b0801b5351de *./tests/data/pixfmt/yuv411p.yuv -304128 ./tests/data/pixfmt/yuv411p.yuv -e176bd14185788110e055f945de7f95f *./tests/data/pixfmt/yuvj420p.yuv -304128 ./tests/data/pixfmt/yuvj420p.yuv -472028e46a81c98d9b2477507def4723 *./tests/data/pixfmt/yuvj422p.yuv -304128 ./tests/data/pixfmt/yuvj422p.yuv -c10442da177c9f1d12be3c53be6fa12c *./tests/data/pixfmt/yuvj444p.yuv -304128 ./tests/data/pixfmt/yuvj444p.yuv -6bb61113e7b70eb09dbcec356122a0e2 *./tests/data/pixfmt/rgb24.yuv -304128 ./tests/data/pixfmt/rgb24.yuv -6bb61113e7b70eb09dbcec356122a0e2 *./tests/data/pixfmt/bgr24.yuv -304128 ./tests/data/pixfmt/bgr24.yuv -6bb61113e7b70eb09dbcec356122a0e2 *./tests/data/pixfmt/rgb32.yuv -304128 ./tests/data/pixfmt/rgb32.yuv -efa7c0337cc00c796c6df615223716f1 *./tests/data/pixfmt/rgb565.yuv -304128 ./tests/data/pixfmt/rgb565.yuv -0df2a477af1415a1b8fbf2a3e552bc39 *./tests/data/pixfmt/rgb555.yuv -304128 ./tests/data/pixfmt/rgb555.yuv -1e080c12bd9755c41ecb8e19b756f406 *./tests/data/pixfmt/gray.yuv -304128 ./tests/data/pixfmt/gray.yuv -d87cf0c2e7a13cc693fe6ece22461c83 *./tests/data/pixfmt/monow.yuv -304128 ./tests/data/pixfmt/monow.yuv -d87cf0c2e7a13cc693fe6ece22461c83 *./tests/data/pixfmt/monob.yuv -304128 ./tests/data/pixfmt/monob.yuv -00b85790df5740bab95e2559d81603a7 *./tests/data/pixfmt/yuv440p.yuv -304128 ./tests/data/pixfmt/yuv440p.yuv -4d8d402c45d913038d4b725396719111 *./tests/data/pixfmt/yuvj440p.yuv -304128 ./tests/data/pixfmt/yuvj440p.yuv diff --git a/tests/ref/lavf/sunrast b/tests/ref/lavf/sunrast deleted file mode 100644 index 4db05051406..00000000000 --- a/tests/ref/lavf/sunrast +++ /dev/null @@ -1,3 +0,0 @@ -07518bcb0841bc677ce6aea8464ea240 *./tests/data/images/sun/02.sun -./tests/data/images/sun/%02d.sun CRC=0xe6c71946 -304123 ./tests/data/images/sun/02.sun diff --git a/tests/ref/lavf/ts b/tests/ref/lavf/ts index 09960f84d37..b004fc1b1c4 100644 --- a/tests/ref/lavf/ts +++ b/tests/ref/lavf/ts @@ -1,3 +1,3 @@ -38f4b14d43a0e416be8d598628997cbc *tests/data/lavf/lavf.ts -407020 tests/data/lavf/lavf.ts +371dc016eb3155116bea27e3b4eeb928 *tests/data/lavf/lavf.ts +389160 tests/data/lavf/lavf.ts tests/data/lavf/lavf.ts CRC=0x71287e25 diff --git a/tests/ref/lavf/voc_s16 b/tests/ref/lavf/voc_s16 deleted file mode 100644 index d026090aff3..00000000000 --- a/tests/ref/lavf/voc_s16 +++ /dev/null @@ -1,3 +0,0 @@ -db9fa22ff71992bd8b6cc80047223c92 *./tests/data/lavf/lavf.s16.voc -176615 ./tests/data/lavf/lavf.s16.voc -./tests/data/lavf/lavf.s16.voc CRC=0xe61e3bd0 diff --git a/tests/ref/lavf/wav_peak b/tests/ref/lavf/wav_peak deleted file mode 100644 index 861b246d72a..00000000000 --- a/tests/ref/lavf/wav_peak +++ /dev/null @@ -1,3 +0,0 @@ -105805963fb767d00da056f42f32d9f3 *./tests/data/lavf/lavf.peak.wav -89094 ./tests/data/lavf/lavf.peak.wav -./tests/data/lavf/lavf.peak.wav CRC=0x3a1da17e diff --git a/tests/ref/lavf/wav_peak_only b/tests/ref/lavf/wav_peak_only deleted file mode 100644 index b203d0345be..00000000000 --- a/tests/ref/lavf/wav_peak_only +++ /dev/null @@ -1,2 +0,0 @@ -f1a8aeeae8069f3992c4d780436c3d23 *./tests/data/lavf/lavf.peak_only.wav -832 ./tests/data/lavf/lavf.peak_only.wav diff --git a/tests/ref/lavf/yuv4mpeg b/tests/ref/lavf/yuv4mpeg deleted file mode 100644 index 8c1566ea0d4..00000000000 --- a/tests/ref/lavf/yuv4mpeg +++ /dev/null @@ -1,2 +0,0 @@ -ec8178cb152f9cdbfd9cb724d977db2e *./tests/data/lavf/lavf.y4m -3801808 ./tests/data/lavf/lavf.y4m diff --git a/tests/ref/seek/lavf-alaw b/tests/ref/seek/lavf-alaw deleted file mode 100644 index 8d517fa2bbd..00000000000 --- a/tests/ref/seek/lavf-alaw +++ /dev/null @@ -1,53 +0,0 @@ -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st:-1 flags:0 ts:-1.000000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st:-1 flags:1 ts: 1.894167 -ret: 0 st: 0 flags:1 dts: 1.894150 pts: 1.894150 pos: 41766 size: 882 -ret: 0 st: 0 flags:0 ts: 0.788345 -ret: 0 st: 0 flags:1 dts: 0.788345 pts: 0.788345 pos: 17383 size: 882 -ret: 0 st: 0 flags:1 ts:-0.317506 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st:-1 flags:0 ts: 2.576668 -ret:-EOF -ret: 0 st:-1 flags:1 ts: 1.470835 -ret: 0 st: 0 flags:1 dts: 1.470839 pts: 1.470839 pos: 32432 size: 882 -ret: 0 st: 0 flags:0 ts: 0.364989 -ret: 0 st: 0 flags:1 dts: 0.364989 pts: 0.364989 pos: 8048 size: 882 -ret: 0 st: 0 flags:1 ts:-0.740816 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st:-1 flags:0 ts: 2.153336 -ret:-EOF -ret: 0 st:-1 flags:1 ts: 1.047503 -ret: 0 st: 0 flags:1 dts: 1.047483 pts: 1.047483 pos: 23097 size: 882 -ret: 0 st: 0 flags:0 ts:-0.058322 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st: 0 flags:1 ts: 2.835828 -ret:-EOF -ret: 0 st:-1 flags:0 ts: 1.730004 -ret: 0 st: 0 flags:1 dts: 1.730023 pts: 1.730023 pos: 38147 size: 882 -ret: 0 st:-1 flags:1 ts: 0.624171 -ret: 0 st: 0 flags:1 dts: 0.624172 pts: 0.624172 pos: 13763 size: 882 -ret: 0 st: 0 flags:0 ts:-0.481678 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st: 0 flags:1 ts: 2.412517 -ret:-EOF -ret: 0 st:-1 flags:0 ts: 1.306672 -ret: 0 st: 0 flags:1 dts: 1.306667 pts: 1.306667 pos: 28812 size: 882 -ret: 0 st:-1 flags:1 ts: 0.200839 -ret: 0 st: 0 flags:1 dts: 0.200816 pts: 0.200816 pos: 4428 size: 882 -ret: 0 st: 0 flags:0 ts:-0.904989 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st: 0 flags:1 ts: 1.989161 -ret: 0 st: 0 flags:1 dts: 1.989161 pts: 1.989161 pos: 43861 size: 239 -ret: 0 st:-1 flags:0 ts: 0.883340 -ret: 0 st: 0 flags:1 dts: 0.883356 pts: 0.883356 pos: 19478 size: 882 -ret: 0 st:-1 flags:1 ts:-0.222493 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st: 0 flags:0 ts: 2.671655 -ret:-EOF -ret: 0 st: 0 flags:1 ts: 1.565850 -ret: 0 st: 0 flags:1 dts: 1.565850 pts: 1.565850 pos: 34527 size: 882 -ret: 0 st:-1 flags:0 ts: 0.460008 -ret: 0 st: 0 flags:1 dts: 0.460000 pts: 0.460000 pos: 10143 size: 882 -ret: 0 st:-1 flags:1 ts:-0.645825 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 diff --git a/tests/ref/seek/lavf-dv_fmt b/tests/ref/seek/lavf-dv_fmt deleted file mode 100644 index 0000ff5abef..00000000000 --- a/tests/ref/seek/lavf-dv_fmt +++ /dev/null @@ -1,53 +0,0 @@ -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size:144000 -ret: 0 st:-1 flags:0 ts:-1.000000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size:144000 -ret: 0 st:-1 flags:1 ts: 1.894167 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:3456000 size:144000 -ret: 0 st: 0 flags:0 ts: 0.800000 -ret: 0 st: 0 flags:1 dts: 0.800000 pts: 0.800000 pos:2880000 size:144000 -ret: 0 st: 0 flags:1 ts:-0.320000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size:144000 -ret: 0 st: 1 flags:0 ts: 2.576667 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:3456000 size:144000 -ret: 0 st: 1 flags:1 ts: 1.470833 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:3456000 size:144000 -ret: 0 st:-1 flags:0 ts: 0.365002 -ret: 0 st: 0 flags:1 dts: 0.360000 pts: 0.360000 pos:1296000 size:144000 -ret: 0 st:-1 flags:1 ts:-0.740831 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size:144000 -ret: 0 st: 0 flags:0 ts: 2.160000 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:3456000 size:144000 -ret: 0 st: 0 flags:1 ts: 1.040000 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:3456000 size:144000 -ret: 0 st: 1 flags:0 ts:-0.058333 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size:144000 -ret: 0 st: 1 flags:1 ts: 2.835833 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:3456000 size:144000 -ret: 0 st:-1 flags:0 ts: 1.730004 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:3456000 size:144000 -ret: 0 st:-1 flags:1 ts: 0.624171 -ret: 0 st: 0 flags:1 dts: 0.640000 pts: 0.640000 pos:2304000 size:144000 -ret: 0 st: 0 flags:0 ts:-0.480000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size:144000 -ret: 0 st: 0 flags:1 ts: 2.400000 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:3456000 size:144000 -ret: 0 st: 1 flags:0 ts: 1.306667 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:3456000 size:144000 -ret: 0 st: 1 flags:1 ts: 0.200833 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:3456000 size:144000 -ret: 0 st:-1 flags:0 ts:-0.904994 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size:144000 -ret: 0 st:-1 flags:1 ts: 1.989173 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:3456000 size:144000 -ret: 0 st: 0 flags:0 ts: 0.880000 -ret: 0 st: 0 flags:1 dts: 0.880000 pts: 0.880000 pos:3168000 size:144000 -ret: 0 st: 0 flags:1 ts:-0.240000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size:144000 -ret: 0 st: 1 flags:0 ts: 2.671667 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:3456000 size:144000 -ret: 0 st: 1 flags:1 ts: 1.565833 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:3456000 size:144000 -ret: 0 st:-1 flags:0 ts: 0.460008 -ret: 0 st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos:1728000 size:144000 -ret: 0 st:-1 flags:1 ts:-0.645825 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size:144000 diff --git a/tests/ref/seek/lavf-flv_fmt b/tests/ref/seek/lavf-flv_fmt deleted file mode 100644 index 9d86a811ea3..00000000000 --- a/tests/ref/seek/lavf-flv_fmt +++ /dev/null @@ -1,44 +0,0 @@ -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 205 size: 31074 -ret: 0 st:-1 flags:0 ts:-1.000000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 205 size: 31074 -ret: 0 st:-1 flags:1 ts: 1.894167 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 298404 size: 31134 -ret: 0 st: 0 flags:0 ts: 0.788000 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 298404 size: 31134 -ret:-1 st: 0 flags:1 ts:-0.317000 -ret:-1 st:-1 flags:0 ts: 2.576668 -ret: 0 st:-1 flags:1 ts: 1.470835 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 298404 size: 31134 -ret: 0 st: 0 flags:0 ts: 0.365000 -ret: 0 st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 149444 size: 31125 -ret:-1 st: 0 flags:1 ts:-0.741000 -ret:-1 st:-1 flags:0 ts: 2.153336 -ret: 0 st:-1 flags:1 ts: 1.047503 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 298404 size: 31134 -ret: 0 st: 0 flags:0 ts:-0.058000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 205 size: 31074 -ret: 0 st: 0 flags:1 ts: 2.836000 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 298404 size: 31134 -ret:-1 st:-1 flags:0 ts: 1.730004 -ret: 0 st:-1 flags:1 ts: 0.624171 -ret: 0 st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 149444 size: 31125 -ret: 0 st: 0 flags:0 ts:-0.482000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 205 size: 31074 -ret: 0 st: 0 flags:1 ts: 2.413000 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 298404 size: 31134 -ret:-1 st:-1 flags:0 ts: 1.306672 -ret: 0 st:-1 flags:1 ts: 0.200839 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 205 size: 31074 -ret: 0 st: 0 flags:0 ts:-0.905000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 205 size: 31074 -ret: 0 st: 0 flags:1 ts: 1.989000 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 298404 size: 31134 -ret: 0 st:-1 flags:0 ts: 0.883340 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 298404 size: 31134 -ret:-1 st:-1 flags:1 ts:-0.222493 -ret:-1 st: 0 flags:0 ts: 2.672000 -ret: 0 st: 0 flags:1 ts: 1.566000 -ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos: 298404 size: 31134 -ret: 0 st:-1 flags:0 ts: 0.460008 -ret: 0 st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 149444 size: 31125 -ret:-1 st:-1 flags:1 ts:-0.645825 diff --git a/tests/ref/seek/lavf-mkv b/tests/ref/seek/lavf-mkv index 6ed1f3a763f..8981f1d7b73 100644 --- a/tests/ref/seek/lavf-mkv +++ b/tests/ref/seek/lavf-mkv @@ -1,48 +1,48 @@ -ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 803 size: 208 +ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 680 size: 208 ret: 0 st:-1 flags:0 ts:-1.000000 -ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1019 size: 27837 +ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 896 size: 27837 ret: 0 st:-1 flags:1 ts: 1.894167 -ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292435 size: 27834 +ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292312 size: 27834 ret: 0 st: 0 flags:0 ts: 0.788000 -ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292435 size: 27834 +ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292312 size: 27834 ret: 0 st: 0 flags:1 ts:-0.317000 -ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1019 size: 27837 +ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 896 size: 27837 ret:-1 st: 1 flags:0 ts: 2.577000 ret: 0 st: 1 flags:1 ts: 1.471000 -ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320276 size: 209 +ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320153 size: 209 ret: 0 st:-1 flags:0 ts: 0.365002 -ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146987 size: 27925 +ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146864 size: 27925 ret: 0 st:-1 flags:1 ts:-0.740831 -ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1019 size: 27837 +ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 896 size: 27837 ret:-1 st: 0 flags:0 ts: 2.153000 ret: 0 st: 0 flags:1 ts: 1.048000 -ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292435 size: 27834 +ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292312 size: 27834 ret: 0 st: 1 flags:0 ts:-0.058000 -ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 803 size: 208 +ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 680 size: 208 ret: 0 st: 1 flags:1 ts: 2.836000 -ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320276 size: 209 +ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320153 size: 209 ret:-1 st:-1 flags:0 ts: 1.730004 ret: 0 st:-1 flags:1 ts: 0.624171 -ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146987 size: 27925 +ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146864 size: 27925 ret: 0 st: 0 flags:0 ts:-0.482000 -ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1019 size: 27837 +ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 896 size: 27837 ret: 0 st: 0 flags:1 ts: 2.413000 -ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292435 size: 27834 +ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292312 size: 27834 ret:-1 st: 1 flags:0 ts: 1.307000 ret: 0 st: 1 flags:1 ts: 0.201000 -ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 803 size: 208 +ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 680 size: 208 ret: 0 st:-1 flags:0 ts:-0.904994 -ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1019 size: 27837 +ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 896 size: 27837 ret: 0 st:-1 flags:1 ts: 1.989173 -ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292435 size: 27834 +ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292312 size: 27834 ret: 0 st: 0 flags:0 ts: 0.883000 -ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292435 size: 27834 +ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292312 size: 27834 ret: 0 st: 0 flags:1 ts:-0.222000 -ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1019 size: 27837 +ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 896 size: 27837 ret:-1 st: 1 flags:0 ts: 2.672000 ret: 0 st: 1 flags:1 ts: 1.566000 -ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320276 size: 209 +ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320153 size: 209 ret: 0 st:-1 flags:0 ts: 0.460008 -ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146987 size: 27925 +ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146864 size: 27925 ret: 0 st:-1 flags:1 ts:-0.645825 -ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1019 size: 27837 +ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 896 size: 27837 diff --git a/tests/ref/seek/lavf-mulaw b/tests/ref/seek/lavf-mulaw deleted file mode 100644 index 8d517fa2bbd..00000000000 --- a/tests/ref/seek/lavf-mulaw +++ /dev/null @@ -1,53 +0,0 @@ -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st:-1 flags:0 ts:-1.000000 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st:-1 flags:1 ts: 1.894167 -ret: 0 st: 0 flags:1 dts: 1.894150 pts: 1.894150 pos: 41766 size: 882 -ret: 0 st: 0 flags:0 ts: 0.788345 -ret: 0 st: 0 flags:1 dts: 0.788345 pts: 0.788345 pos: 17383 size: 882 -ret: 0 st: 0 flags:1 ts:-0.317506 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st:-1 flags:0 ts: 2.576668 -ret:-EOF -ret: 0 st:-1 flags:1 ts: 1.470835 -ret: 0 st: 0 flags:1 dts: 1.470839 pts: 1.470839 pos: 32432 size: 882 -ret: 0 st: 0 flags:0 ts: 0.364989 -ret: 0 st: 0 flags:1 dts: 0.364989 pts: 0.364989 pos: 8048 size: 882 -ret: 0 st: 0 flags:1 ts:-0.740816 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st:-1 flags:0 ts: 2.153336 -ret:-EOF -ret: 0 st:-1 flags:1 ts: 1.047503 -ret: 0 st: 0 flags:1 dts: 1.047483 pts: 1.047483 pos: 23097 size: 882 -ret: 0 st: 0 flags:0 ts:-0.058322 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st: 0 flags:1 ts: 2.835828 -ret:-EOF -ret: 0 st:-1 flags:0 ts: 1.730004 -ret: 0 st: 0 flags:1 dts: 1.730023 pts: 1.730023 pos: 38147 size: 882 -ret: 0 st:-1 flags:1 ts: 0.624171 -ret: 0 st: 0 flags:1 dts: 0.624172 pts: 0.624172 pos: 13763 size: 882 -ret: 0 st: 0 flags:0 ts:-0.481678 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st: 0 flags:1 ts: 2.412517 -ret:-EOF -ret: 0 st:-1 flags:0 ts: 1.306672 -ret: 0 st: 0 flags:1 dts: 1.306667 pts: 1.306667 pos: 28812 size: 882 -ret: 0 st:-1 flags:1 ts: 0.200839 -ret: 0 st: 0 flags:1 dts: 0.200816 pts: 0.200816 pos: 4428 size: 882 -ret: 0 st: 0 flags:0 ts:-0.904989 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st: 0 flags:1 ts: 1.989161 -ret: 0 st: 0 flags:1 dts: 1.989161 pts: 1.989161 pos: 43861 size: 239 -ret: 0 st:-1 flags:0 ts: 0.883340 -ret: 0 st: 0 flags:1 dts: 0.883356 pts: 0.883356 pos: 19478 size: 882 -ret: 0 st:-1 flags:1 ts:-0.222493 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 -ret: 0 st: 0 flags:0 ts: 2.671655 -ret:-EOF -ret: 0 st: 0 flags:1 ts: 1.565850 -ret: 0 st: 0 flags:1 dts: 1.565850 pts: 1.565850 pos: 34527 size: 882 -ret: 0 st:-1 flags:0 ts: 0.460008 -ret: 0 st: 0 flags:1 dts: 0.460000 pts: 0.460000 pos: 10143 size: 882 -ret: 0 st:-1 flags:1 ts:-0.645825 -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 0 size: 882 diff --git a/tests/ref/seek/lavf-ts b/tests/ref/seek/lavf-ts index e57651ef9c6..3347b7ead50 100644 --- a/tests/ref/seek/lavf-ts +++ b/tests/ref/seek/lavf-ts @@ -2,51 +2,51 @@ ret: 0 st: 0 flags:1 dts: 1.400000 pts: 1.440000 pos: 564 size: 24801 ret: 0 st:-1 flags:0 ts:-1.000000 ret: 0 st: 0 flags:1 dts: 1.400000 pts: 1.440000 pos: 564 size: 24801 ret: 0 st:-1 flags:1 ts: 1.894167 -ret: 0 st: 0 flags:1 dts: 1.880000 pts: 1.920000 pos: 189692 size: 24786 +ret: 0 st: 0 flags:1 dts: 1.880000 pts: 1.920000 pos: 181420 size: 24786 ret: 0 st: 0 flags:0 ts: 0.788333 ret: 0 st: 0 flags:1 dts: 1.400000 pts: 1.440000 pos: 564 size: 24801 ret: 0 st: 0 flags:1 ts:-0.317500 ret: 0 st: 0 flags:1 dts: 1.400000 pts: 1.440000 pos: 564 size: 24801 ret: 0 st: 1 flags:0 ts: 2.576667 -ret: 0 st: 1 flags:1 dts: 2.160522 pts: 2.160522 pos: 404576 size: 209 +ret: 0 st: 1 flags:1 dts: 2.160522 pts: 2.160522 pos: 386716 size: 209 ret: 0 st: 1 flags:1 ts: 1.470833 -ret: 0 st: 1 flags:1 dts: 1.429089 pts: 1.429089 pos: 159988 size: 208 +ret: 0 st: 1 flags:1 dts: 1.429089 pts: 1.429089 pos: 152844 size: 208 ret: 0 st:-1 flags:0 ts: 0.365002 ret: 0 st: 0 flags:1 dts: 1.400000 pts: 1.440000 pos: 564 size: 24801 ret: 0 st:-1 flags:1 ts:-0.740831 ret: 0 st: 0 flags:1 dts: 1.400000 pts: 1.440000 pos: 564 size: 24801 ret: 0 st: 0 flags:0 ts: 2.153333 -ret: 0 st: 1 flags:1 dts: 1.794811 pts: 1.794811 pos: 322608 size: 209 +ret: 0 st: 1 flags:1 dts: 1.794811 pts: 1.794811 pos: 308508 size: 209 ret: 0 st: 0 flags:1 ts: 1.047500 ret: 0 st: 0 flags:1 dts: 1.400000 pts: 1.440000 pos: 564 size: 24801 ret: 0 st: 1 flags:0 ts:-0.058333 -ret: 0 st: 1 flags:1 dts: 1.429089 pts: 1.429089 pos: 159988 size: 208 +ret: 0 st: 1 flags:1 dts: 1.429089 pts: 1.429089 pos: 152844 size: 208 ret: 0 st: 1 flags:1 ts: 2.835833 -ret: 0 st: 1 flags:1 dts: 2.160522 pts: 2.160522 pos: 404576 size: 209 +ret: 0 st: 1 flags:1 dts: 2.160522 pts: 2.160522 pos: 386716 size: 209 ret: 0 st:-1 flags:0 ts: 1.730004 -ret: 0 st: 1 flags:1 dts: 1.429089 pts: 1.429089 pos: 159988 size: 208 +ret: 0 st: 1 flags:1 dts: 1.429089 pts: 1.429089 pos: 152844 size: 208 ret: 0 st:-1 flags:1 ts: 0.624171 ret: 0 st: 0 flags:1 dts: 1.400000 pts: 1.440000 pos: 564 size: 24801 ret: 0 st: 0 flags:0 ts:-0.481667 ret: 0 st: 0 flags:1 dts: 1.400000 pts: 1.440000 pos: 564 size: 24801 ret: 0 st: 0 flags:1 ts: 2.412500 -ret: 0 st: 1 flags:1 dts: 2.160522 pts: 2.160522 pos: 404576 size: 209 +ret: 0 st: 1 flags:1 dts: 2.160522 pts: 2.160522 pos: 386716 size: 209 ret: 0 st: 1 flags:0 ts: 1.306667 -ret: 0 st: 1 flags:1 dts: 1.429089 pts: 1.429089 pos: 159988 size: 208 +ret: 0 st: 1 flags:1 dts: 1.429089 pts: 1.429089 pos: 152844 size: 208 ret: 0 st: 1 flags:1 ts: 0.200844 -ret: 0 st: 1 flags:1 dts: 1.429089 pts: 1.429089 pos: 159988 size: 208 +ret: 0 st: 1 flags:1 dts: 1.429089 pts: 1.429089 pos: 152844 size: 208 ret: 0 st:-1 flags:0 ts:-0.904994 ret: 0 st: 0 flags:1 dts: 1.400000 pts: 1.440000 pos: 564 size: 24801 ret: 0 st:-1 flags:1 ts: 1.989173 -ret: 0 st: 0 flags:0 dts: 1.960000 pts: 2.000000 pos: 235000 size: 15019 +ret: 0 st: 0 flags:0 dts: 1.960000 pts: 2.000000 pos: 224848 size: 15019 ret: 0 st: 0 flags:0 ts: 0.883344 ret: 0 st: 0 flags:1 dts: 1.400000 pts: 1.440000 pos: 564 size: 24801 ret: 0 st: 0 flags:1 ts:-0.222489 ret: 0 st: 0 flags:1 dts: 1.400000 pts: 1.440000 pos: 564 size: 24801 ret: 0 st: 1 flags:0 ts: 2.671678 -ret: 0 st: 1 flags:1 dts: 2.160522 pts: 2.160522 pos: 404576 size: 209 +ret: 0 st: 1 flags:1 dts: 2.160522 pts: 2.160522 pos: 386716 size: 209 ret: 0 st: 1 flags:1 ts: 1.565844 -ret: 0 st: 1 flags:1 dts: 1.429089 pts: 1.429089 pos: 159988 size: 208 +ret: 0 st: 1 flags:1 dts: 1.429089 pts: 1.429089 pos: 152844 size: 208 ret: 0 st:-1 flags:0 ts: 0.460008 ret: 0 st: 0 flags:1 dts: 1.400000 pts: 1.440000 pos: 564 size: 24801 ret: 0 st:-1 flags:1 ts:-0.645825 diff --git a/tests/ref/seek/lavf-yuv4mpeg b/tests/ref/seek/lavf-yuv4mpeg deleted file mode 100644 index 6be9ba94802..00000000000 --- a/tests/ref/seek/lavf-yuv4mpeg +++ /dev/null @@ -1,45 +0,0 @@ -ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 64 size:152064 -ret:-1 st:-1 flags:0 ts:-1.000000 -ret: 0 st:-1 flags:1 ts: 1.894167 -ret:-EOF -ret: 0 st: 0 flags:0 ts: 0.800000 -ret: 0 st: 0 flags:1 dts: 0.800000 pts: 0.800000 pos:3041464 size:152064 -ret:-1 st: 0 flags:1 ts:-0.320000 -ret: 0 st:-1 flags:0 ts: 2.576668 -ret:-EOF -ret: 0 st:-1 flags:1 ts: 1.470835 -ret:-EOF -ret: 0 st: 0 flags:0 ts: 0.360000 -ret: 0 st: 0 flags:1 dts: 0.360000 pts: 0.360000 pos:1368694 size:152064 -ret:-1 st: 0 flags:1 ts:-0.760000 -ret: 0 st:-1 flags:0 ts: 2.153336 -ret:-EOF -ret: 0 st:-1 flags:1 ts: 1.047503 -ret:-EOF -ret:-1 st: 0 flags:0 ts:-0.040000 -ret: 0 st: 0 flags:1 ts: 2.840000 -ret:-EOF -ret: 0 st:-1 flags:0 ts: 1.730004 -ret:-EOF -ret: 0 st:-1 flags:1 ts: 0.624171 -ret: 0 st: 0 flags:1 dts: 0.640000 pts: 0.640000 pos:2433184 size:152064 -ret:-1 st: 0 flags:0 ts:-0.480000 -ret: 0 st: 0 flags:1 ts: 2.400000 -ret:-EOF -ret: 0 st:-1 flags:0 ts: 1.306672 -ret:-EOF -ret: 0 st:-1 flags:1 ts: 0.200839 -ret: 0 st: 0 flags:1 dts: 0.200000 pts: 0.200000 pos: 760414 size:152064 -ret:-1 st: 0 flags:0 ts:-0.920000 -ret: 0 st: 0 flags:1 ts: 2.000000 -ret:-EOF -ret: 0 st:-1 flags:0 ts: 0.883340 -ret: 0 st: 0 flags:1 dts: 0.880000 pts: 0.880000 pos:3345604 size:152064 -ret:-1 st:-1 flags:1 ts:-0.222493 -ret: 0 st: 0 flags:0 ts: 2.680000 -ret:-EOF -ret: 0 st: 0 flags:1 ts: 1.560000 -ret:-EOF -ret: 0 st:-1 flags:0 ts: 0.460008 -ret: 0 st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos:1824904 size:152064 -ret:-1 st:-1 flags:1 ts:-0.645825 diff --git a/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr b/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr index 1965e3135f7..35cd719b63f 100644 --- a/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr +++ b/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr @@ -1,4 +1,4 @@ -2caca2edcb519c88543c38e129ded03b *tests/data/fate/vsynth1-dnxhd-1080i-colr.mov +5571f4ff9e29d352a7e373a14a9ed2ed *tests/data/fate/vsynth1-dnxhd-1080i-colr.mov 3031929 tests/data/fate/vsynth1-dnxhd-1080i-colr.mov 6f2d5429ffc4529a76acfeb28b560542 *tests/data/fate/vsynth1-dnxhd-1080i-colr.out.rawvideo stddev: 5.65 PSNR: 33.09 MAXDIFF: 55 bytes: 7603200/ 760320 diff --git a/tests/ref/vsynth/vsynth1-dv-fhd b/tests/ref/vsynth/vsynth1-dv-fhd new file mode 100644 index 00000000000..b81141f340f --- /dev/null +++ b/tests/ref/vsynth/vsynth1-dv-fhd @@ -0,0 +1,4 @@ +74315a8678d12c7f592c02990dc8952d *tests/data/fate/vsynth1-dv-fhd.dv +28800000 tests/data/fate/vsynth1-dv-fhd.dv +c95b309bc128b162e5c8241374eb66a9 *tests/data/fate/vsynth1-dv-fhd.out.rawvideo +stddev: 2.53 PSNR: 40.03 MAXDIFF: 35 bytes: 7603200/ 7603200 diff --git a/tests/ref/vsynth/vsynth1-dv-hd b/tests/ref/vsynth/vsynth1-dv-hd new file mode 100644 index 00000000000..6b6d6e8159d --- /dev/null +++ b/tests/ref/vsynth/vsynth1-dv-hd @@ -0,0 +1,4 @@ +22d1d62a834fe8416fe79c51760012c1 *tests/data/fate/vsynth1-dv-hd.dv +14400000 tests/data/fate/vsynth1-dv-hd.dv +34b78cf725346c7f819c9d6209b8299a *tests/data/fate/vsynth1-dv-hd.out.rawvideo +stddev: 4.30 PSNR: 35.45 MAXDIFF: 74 bytes: 7603200/ 7603200 diff --git a/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr b/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr index aac70c28679..f5ac6047160 100644 --- a/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr +++ b/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr @@ -1,4 +1,4 @@ -e7e5cd8d6d1596596ded1007f37c3193 *tests/data/fate/vsynth2-dnxhd-1080i-colr.mov +eee674d012c850c1d2bb5e816b668cdf *tests/data/fate/vsynth2-dnxhd-1080i-colr.mov 3031929 tests/data/fate/vsynth2-dnxhd-1080i-colr.mov ec40a8014b819d02951b2f06bee7b514 *tests/data/fate/vsynth2-dnxhd-1080i-colr.out.rawvideo stddev: 1.54 PSNR: 44.33 MAXDIFF: 33 bytes: 7603200/ 760320 diff --git a/tests/ref/vsynth/vsynth2-dv-fhd b/tests/ref/vsynth/vsynth2-dv-fhd new file mode 100644 index 00000000000..948bf2269f8 --- /dev/null +++ b/tests/ref/vsynth/vsynth2-dv-fhd @@ -0,0 +1,4 @@ +1f96ce7c1a5f09ec9d30c51c7271cf77 *tests/data/fate/vsynth2-dv-fhd.dv +28800000 tests/data/fate/vsynth2-dv-fhd.dv +cff30e2430730522bf67c6d94cf1352e *tests/data/fate/vsynth2-dv-fhd.out.rawvideo +stddev: 1.16 PSNR: 46.82 MAXDIFF: 21 bytes: 7603200/ 7603200 diff --git a/tests/ref/vsynth/vsynth2-dv-hd b/tests/ref/vsynth/vsynth2-dv-hd new file mode 100644 index 00000000000..c6dcb5a953c --- /dev/null +++ b/tests/ref/vsynth/vsynth2-dv-hd @@ -0,0 +1,4 @@ +4270e5d552e0a05193f44bff75c2d271 *tests/data/fate/vsynth2-dv-hd.dv +14400000 tests/data/fate/vsynth2-dv-hd.dv +15dbe911532aca81c67bdd2846419027 *tests/data/fate/vsynth2-dv-hd.out.rawvideo +stddev: 1.75 PSNR: 43.26 MAXDIFF: 34 bytes: 7603200/ 7603200 diff --git a/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr b/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr index 093489e45f3..08aa30d8c49 100644 --- a/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr +++ b/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr @@ -1,4 +1,4 @@ -f5a7d7f837a750784458c60fb7b4d091 *tests/data/fate/vsynth3-dnxhd-1080i-colr.mov +92a2f67cf77abf3428fe2d4f53ba2027 *tests/data/fate/vsynth3-dnxhd-1080i-colr.mov 3031929 tests/data/fate/vsynth3-dnxhd-1080i-colr.mov f907fd2d48bedbc5283fbfc3fb9f61a0 *tests/data/fate/vsynth3-dnxhd-1080i-colr.out.rawvideo stddev: 6.92 PSNR: 31.32 MAXDIFF: 50 bytes: 86700/ 8670 diff --git a/tests/ref/vsynth/vsynth3-dv-fhd b/tests/ref/vsynth/vsynth3-dv-fhd new file mode 100644 index 00000000000..08ca9ef8896 --- /dev/null +++ b/tests/ref/vsynth/vsynth3-dv-fhd @@ -0,0 +1,4 @@ +5b8b7f1dc31d7076af891e94c2e88c06 *tests/data/fate/vsynth3-dv-fhd.dv +28800000 tests/data/fate/vsynth3-dv-fhd.dv +a038ad7c3c09f776304ef7accdea9c74 *tests/data/fate/vsynth3-dv-fhd.out.rawvideo +stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 86700/ 86700 diff --git a/tests/ref/vsynth/vsynth3-dv-hd b/tests/ref/vsynth/vsynth3-dv-hd new file mode 100644 index 00000000000..d069e6980ed --- /dev/null +++ b/tests/ref/vsynth/vsynth3-dv-hd @@ -0,0 +1,4 @@ +2f81f3ccec178ba2fd9d3e3b46f33670 *tests/data/fate/vsynth3-dv-hd.dv +14400000 tests/data/fate/vsynth3-dv-hd.dv +a038ad7c3c09f776304ef7accdea9c74 *tests/data/fate/vsynth3-dv-hd.out.rawvideo +stddev: 0.00 PSNR:999.99 MAXDIFF: 0 bytes: 86700/ 86700 diff --git a/tests/ref/vsynth/vsynth_lena-dnxhd-1080i-colr b/tests/ref/vsynth/vsynth_lena-dnxhd-1080i-colr index ae855cd4619..c21babba0ff 100644 --- a/tests/ref/vsynth/vsynth_lena-dnxhd-1080i-colr +++ b/tests/ref/vsynth/vsynth_lena-dnxhd-1080i-colr @@ -1,4 +1,4 @@ -3a681c0e174ccf85fe1504cdf8546788 *tests/data/fate/vsynth_lena-dnxhd-1080i-colr.mov +f80be8c3350ca8b22ae8aa8724b2ef20 *tests/data/fate/vsynth_lena-dnxhd-1080i-colr.mov 3031929 tests/data/fate/vsynth_lena-dnxhd-1080i-colr.mov ce4993a69ef55c8c4b18138716f17b6f *tests/data/fate/vsynth_lena-dnxhd-1080i-colr.out.rawvideo stddev: 1.33 PSNR: 45.59 MAXDIFF: 22 bytes: 7603200/ 760320 diff --git a/tests/ref/vsynth/vsynth_lena-dv-fhd b/tests/ref/vsynth/vsynth_lena-dv-fhd new file mode 100644 index 00000000000..51a4505011d --- /dev/null +++ b/tests/ref/vsynth/vsynth_lena-dv-fhd @@ -0,0 +1,4 @@ +3a33e512f8b3f4213477c98d4e7e2559 *tests/data/fate/vsynth_lena-dv-fhd.dv +28800000 tests/data/fate/vsynth_lena-dv-fhd.dv +b97e0a057202359ef93f2ec0b9fdfec4 *tests/data/fate/vsynth_lena-dv-fhd.out.rawvideo +stddev: 1.03 PSNR: 47.80 MAXDIFF: 14 bytes: 7603200/ 7603200 diff --git a/tests/ref/vsynth/vsynth_lena-dv-hd b/tests/ref/vsynth/vsynth_lena-dv-hd new file mode 100644 index 00000000000..aba756c5f1a --- /dev/null +++ b/tests/ref/vsynth/vsynth_lena-dv-hd @@ -0,0 +1,4 @@ +01a61c53943a421fa6a5e03dbc205972 *tests/data/fate/vsynth_lena-dv-hd.dv +14400000 tests/data/fate/vsynth_lena-dv-hd.dv +4db4175c80ea1f16b7ec303611b8873a *tests/data/fate/vsynth_lena-dv-hd.out.rawvideo +stddev: 1.49 PSNR: 44.66 MAXDIFF: 27 bytes: 7603200/ 7603200 diff --git a/tests/regression-funcs.sh b/tests/regression-funcs.sh deleted file mode 100755 index 0c7d34bea33..00000000000 --- a/tests/regression-funcs.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/bin/sh -# -# common regression functions for ffmpeg -# -# - -test="${1#regtest-}" -test_ref=$2 -raw_src_dir=$3 -target_exec=$4 -target_path=$5 -threads=${6:-1} -cpuflags=${8:-all} -target_samples=$9 - -datadir="./tests/data" -target_datadir="${target_path}/${datadir}" - -this="$test.$test_ref" -outfile="$datadir/$test_ref/" - - # various files -ffmpeg="$target_exec ${target_path}/ffmpeg${PROGSUF}" -raw_src="${target_path}/$raw_src_dir/%02d.pgm" -raw_dst="$datadir/$this.out.yuv" -pcm_src="$target_datadir/asynth1.sw" -pcm_src_1ch="$target_datadir/asynth-16000-1.wav" -pcm_ref_1ch="$datadir/$test_ref-16000-1.ref.wav" -crcfile="$datadir/$this.crc" -target_crcfile="$target_datadir/$this.crc" - -cleanfiles="$raw_dst $crcfile" -trap 'rm -f -- $cleanfiles' EXIT - -mkdir -p "$datadir" -mkdir -p "$outfile" - -[ "${V-0}" -gt 0 ] && echov=echov || echov=: - -echov(){ - echo "$@" >&3 -} - -. $(dirname $0)/md5.sh - -AVCONV_OPTS="-nostdin -nostats -y -cpuflags $cpuflags" -COMMON_OPTS="-flags +bitexact -idct simple -sws_flags +accurate_rnd+bitexact -fflags +bitexact" -DEC_OPTS="$COMMON_OPTS -threads $threads" -ENC_OPTS="$COMMON_OPTS -threads $threads -dct fastint" - -run_avconv() -{ - $echov $ffmpeg $AVCONV_OPTS $* - $ffmpeg $AVCONV_OPTS $* -} - -do_avconv() -{ - f="$1" - shift - set -- $* ${target_path}/$f - run_avconv $* - do_md5sum $f - echo $(wc -c $f) -} - -do_avconv_nomd5() -{ - f="$1" - shift - set -- $* ${target_path}/$f - run_avconv $* - if [ $f = $raw_dst ] ; then - $tiny_psnr $f $raw_ref - elif [ $f = $pcm_dst ] ; then - $tiny_psnr $f $pcm_ref 2 - else - echo $(wc -c $f) - fi -} - -do_avconv_crc() -{ - f="$1" - shift - run_avconv $* -f crc "$target_crcfile" - echo "$f $(cat $crcfile)" -} diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 00000000000..c0958f40cb6 --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1,19 @@ +/aviocat +/ffbisect +/bisect.need +/crypto_bench +/cws2fws +/fourcc2pixfmt +/ffescape +/ffeval +/ffhash +/graph2dot +/ismindex +/pktdumper +/probetest +/qt-faststart +/sidxindex +/trasher +/seek_print +/uncoded_frame +/zmqsend diff --git a/tools/Makefile b/tools/Makefile index 370ee354165..001093105ba 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -5,6 +5,9 @@ TOOLS-$(CONFIG_ZLIB) += cws2fws tools/target_dec_%_fuzzer.o: tools/target_dec_fuzzer.c $(COMPILE_C) -DFFMPEG_DECODER=$* +tools/target_bsf_%_fuzzer.o: tools/target_bsf_fuzzer.c + $(COMPILE_C) -DFFMPEG_BSF=$* + tools/target_dem_fuzzer.o: tools/target_dem_fuzzer.c $(COMPILE_C) diff --git a/tools/aviocat.c b/tools/aviocat.c index 2aa08b92ed9..816ab700efc 100644 --- a/tools/aviocat.c +++ b/tools/aviocat.c @@ -26,14 +26,14 @@ static int usage(const char *argv0, int ret) { - fprintf(stderr, "%s [-b bytespersec] [-d duration] [-oi ] [-oo ] input_url output_url\n", argv0); + fprintf(stderr, "%s [-b bytespersec] [-d duration] [-oi ] [-oo ] [-v] input_url output_url\n", argv0); fprintf(stderr, ": AVOptions expressed as key=value, :-separated\n"); return ret; } int main(int argc, char **argv) { - int bps = 0, duration = 0, ret, i; + int bps = 0, duration = 0, verbose = 0, ret, i; const char *input_url = NULL, *output_url = NULL; int64_t stream_pos = 0; int64_t start_time; @@ -65,6 +65,8 @@ int main(int argc, char **argv) return usage(argv[0], 1); } i++; + } else if (!strcmp(argv[i], "-v")) { + verbose = 1; } else if (!input_url) { input_url = argv[i]; } else if (!output_url) { @@ -82,6 +84,14 @@ int main(int argc, char **argv) fprintf(stderr, "Unable to open %s: %s\n", input_url, errbuf); return 1; } + if (verbose) { + int64_t size = avio_seek(input, 0, AVSEEK_SIZE); + if (size >= 0) { + fprintf(stderr, "aviocat: input size: %"PRId64"\n", size); + } else { + fprintf(stderr, "aviocat: input size: unknown\n"); + } + } if (duration && !bps) { int64_t size = avio_size(input); if (size < 0) { diff --git a/tools/enum_options.c b/tools/enum_options.c index 77e1f9f799d..548e427b7ab 100644 --- a/tools/enum_options.c +++ b/tools/enum_options.c @@ -88,20 +88,22 @@ static void show_opts(const AVClass *class) static void show_format_opts(void) { - AVInputFormat *iformat = NULL; - AVOutputFormat *oformat = NULL; + const AVInputFormat *iformat = NULL; + const AVOutputFormat *oformat = NULL; + void *iformat_opaque = NULL; + void *oformat_opaque = NULL; printf("@section Generic format AVOptions\n"); show_opts(avformat_get_class()); printf("@section Format-specific AVOptions\n"); - while ((iformat = av_iformat_next(iformat))) { + while ((iformat = av_demuxer_iterate(&iformat_opaque))) { if (!iformat->priv_class) continue; printf("@subsection %s AVOptions\n", iformat->priv_class->class_name); show_opts(iformat->priv_class); } - while ((oformat = av_oformat_next(oformat))) { + while ((oformat = av_muxer_iterate(&oformat_opaque))) { if (!oformat->priv_class) continue; printf("@subsection %s AVOptions\n", oformat->priv_class->class_name); @@ -111,13 +113,14 @@ static void show_format_opts(void) static void show_codec_opts(void) { + void *iter = NULL; AVCodec *c = NULL; printf("@section Generic codec AVOptions\n"); show_opts(avcodec_get_class()); printf("@section Codec-specific AVOptions\n"); - while ((c = av_codec_next(c))) { + while ((c = av_codec_iterate(&iter))) { if (!c->priv_class) continue; printf("@subsection %s AVOptions\n", c->priv_class->class_name); diff --git a/tools/patcheck b/tools/patcheck index 101a542ff32..fe52938f299 100755 --- a/tools/patcheck +++ b/tools/patcheck @@ -50,7 +50,6 @@ hiegrep ';;' 'double ;' $* hiegrep2 '\b_[a-zA-Z0-9_]{1,}' '__(asm|attribute)([^a-zA-Z0-9]|$)' 'reserved identifer' $* hiegrep '//[-/<\* ]*$' 'empty comment' $* hiegrep '/\*[-<\* ]*\*/' 'empty comment' $* -hiegrep 'for *\( *'"$ERE_PRITYP"' ' 'not gcc 2.95 compatible' $* hiegrep '(static|inline|const) *\1[^_a-zA-Z]' 'duplicate word' $* hiegrep 'INIT_VLC_USE_STATIC' 'forbidden ancient vlc type' $* hiegrep '=[-+\*\&] ' 'looks like compound assignment' $* diff --git a/tools/probetest.c b/tools/probetest.c index 2c6c1de2463..6f0e002b743 100644 --- a/tools/probetest.c +++ b/tools/probetest.c @@ -38,9 +38,10 @@ static const char *single_format; static void probe(AVProbeData *pd, int type, int p, int size) { int i = 0; - AVInputFormat *fmt = NULL; + const AVInputFormat *fmt = NULL; + void *fmt_opaque = NULL; - while ((fmt = av_iformat_next(fmt))) { + while ((fmt = av_demuxer_iterate(&fmt_opaque))) { if (fmt->flags & AVFMT_NOFILE) continue; if (fmt->read_probe && @@ -65,9 +66,10 @@ static void probe(AVProbeData *pd, int type, int p, int size) static void print_times(void) { int i = 0; - AVInputFormat *fmt = NULL; + const AVInputFormat *fmt = NULL; + void *fmt_opaque = NULL; - while ((fmt = av_iformat_next(fmt))) { + while ((fmt = av_demuxer_iterate(&fmt_opaque))) { if (fmt->flags & AVFMT_NOFILE) continue; if (time_array[i] > 1000000) { diff --git a/tools/python/convert.py b/tools/python/convert.py index 662b4290665..64cf76b2d86 100644 --- a/tools/python/convert.py +++ b/tools/python/convert.py @@ -27,6 +27,7 @@ def get_arguments(): parser.add_argument('--outdir', type=str, default='./', help='where to put generated files') parser.add_argument('--infmt', type=str, default='tensorflow', help='format of the deep learning model') parser.add_argument('infile', help='path to the deep learning model with weights') + parser.add_argument('--dump4tb', type=str, default='no', help='dump file for visualization in tensorboard') return parser.parse_args() @@ -44,9 +45,12 @@ def main(): basefile = os.path.split(args.infile)[1] basefile = os.path.splitext(basefile)[0] outfile = os.path.join(args.outdir, basefile) + '.model' + dump4tb = False + if args.dump4tb.lower() in ('yes', 'true', 't', 'y', '1'): + dump4tb = True if args.infmt == 'tensorflow': - convert_from_tensorflow(args.infile, outfile) + convert_from_tensorflow(args.infile, outfile, dump4tb) if __name__ == '__main__': main() diff --git a/tools/python/convert_from_tensorflow.py b/tools/python/convert_from_tensorflow.py index 37049e58df9..8c0a9be7be7 100644 --- a/tools/python/convert_from_tensorflow.py +++ b/tools/python/convert_from_tensorflow.py @@ -20,58 +20,133 @@ import tensorflow as tf import numpy as np import sys, struct +import convert_header as header __all__ = ['convert_from_tensorflow'] -# as the first step to be compatible with vf_sr, it is not general. -# it will be refined step by step. +class Operand(object): + IOTYPE_INPUT = 1 + IOTYPE_OUTPUT = 2 + IOTYPE_INTERMEDIATE = IOTYPE_INPUT | IOTYPE_OUTPUT + DTYPE_FLOAT = 1 + DTYPE_UINT8 = 4 + index = 0 + def __init__(self, name, dtype, dims): + self.name = name + self.dtype = dtype + self.dims = dims + self.iotype = 0 + self.used_count = 0 + self.index = Operand.index + Operand.index = Operand.index + 1 + self.iotype2str = {Operand.IOTYPE_INPUT: 'in', Operand.IOTYPE_OUTPUT: 'out', Operand.IOTYPE_INTERMEDIATE: 'inout'} + self.dtype2str = {Operand.DTYPE_FLOAT: 'DT_FLOAT', Operand.DTYPE_UINT8: 'DT_UINT8'} + + def add_iotype(self, iotype): + self.iotype = self.iotype | iotype + if iotype == Operand.IOTYPE_INPUT: + self.used_count = self.used_count + 1 + + def __str__(self): + return "{}: (name: {}, iotype: {}, dtype: {}, dims: ({},{},{},{}) used_count: {})".format(self.index, + self.name, self.iotype2str[self.iotype], self.dtype2str[self.dtype], + self.dims[0], self.dims[1], self.dims[2], self.dims[3], self.used_count) + + def __lt__(self, other): + return self.index < other.index class TFConverter: - def __init__(self, graph_def, nodes, outfile): + def __init__(self, graph_def, nodes, outfile, dump4tb): self.graph_def = graph_def self.nodes = nodes self.outfile = outfile + self.dump4tb = dump4tb self.layer_number = 0 self.output_names = [] self.name_node_dict = {} self.edges = {} - self.conv_activations = {'Relu':0, 'Tanh':1, 'Sigmoid':2, 'LeakyRelu':4} - self.conv_paddings = {'VALID':2, 'SAME':1} + self.conv_activations = {'Relu':0, 'Tanh':1, 'Sigmoid':2, 'None':3, 'LeakyRelu':4} + self.conv_paddings = {'VALID':0, 'SAME':1} self.converted_nodes = set() - self.op2code = {'Conv2D':1, 'DepthToSpace':2} + self.conv2d_scope_names = set() + self.conv2d_scopename_inputname_dict = {} + self.op2code = {'Conv2D':1, 'DepthToSpace':2, 'MirrorPad':3, 'Maximum':4, 'MathBinary':5, 'MathUnary':6} + self.mathbin2code = {'Sub':0, 'Add':1, 'Mul':2, 'RealDiv':3, 'Minimum':4} + self.mathun2code = {'Abs':0} + self.mirrorpad_mode = {'CONSTANT':0, 'REFLECT':1, 'SYMMETRIC':2} + self.name_operand_dict = {} + + + def add_operand(self, name, type): + node = self.name_node_dict[name] + if name not in self.name_operand_dict: + dtype = node.attr['dtype'].type + if dtype == 0: + dtype = node.attr['T'].type + dims = [-1,-1,-1,-1] + if 'shape' in node.attr: + dims[0] = node.attr['shape'].shape.dim[0].size + dims[1] = node.attr['shape'].shape.dim[1].size + dims[2] = node.attr['shape'].shape.dim[2].size + dims[3] = node.attr['shape'].shape.dim[3].size + operand = Operand(name, dtype, dims) + self.name_operand_dict[name] = operand; + self.name_operand_dict[name].add_iotype(type) + return self.name_operand_dict[name].index def dump_for_tensorboard(self): graph = tf.get_default_graph() tf.import_graph_def(self.graph_def, name="") - # tensorboard --logdir=/tmp/graph tf.summary.FileWriter('/tmp/graph', graph) + print('graph saved, run "tensorboard --logdir=/tmp/graph" to see it') - def get_conv2d_params(self, node): - knode = self.name_node_dict[node.input[1]] - bnode = None - activation = 'None' - next = self.edges[node.name][0] - if next.op == 'BiasAdd': - self.converted_nodes.add(next.name) - bnode = self.name_node_dict[next.input[1]] - next = self.edges[next.name][0] - if next.op in self.conv_activations: - self.converted_nodes.add(next.name) - activation = next.op - return knode, bnode, activation + def get_conv2d_params(self, conv2d_scope_name): + knode = self.name_node_dict[conv2d_scope_name + '/kernel'] + bnode = self.name_node_dict[conv2d_scope_name + '/bias'] + if conv2d_scope_name + '/dilation_rate' in self.name_node_dict: + dnode = self.name_node_dict[conv2d_scope_name + '/dilation_rate'] + else: + dnode = None + + # the BiasAdd name is possible be changed into the output name, + # if activation is None, and BiasAdd.next is the last op which is Identity + if conv2d_scope_name + '/BiasAdd' in self.edges: + anode = self.edges[conv2d_scope_name + '/BiasAdd'][0] + if anode.op not in self.conv_activations: + anode = None + else: + anode = None + return knode, bnode, dnode, anode - def dump_conv2d_to_file(self, node, f): + + def dump_complex_conv2d_to_file(self, node, f): assert(node.op == 'Conv2D') self.layer_number = self.layer_number + 1 self.converted_nodes.add(node.name) - knode, bnode, activation = self.get_conv2d_params(node) - dilation = node.attr['dilations'].list.i[0] - padding = node.attr['padding'].s - padding = self.conv_paddings[padding.decode("utf-8")] + scope_name = TFConverter.get_scope_name(node.name) + #knode for kernel, bnode for bias, dnode for dilation, anode for activation + knode, bnode, dnode, anode = self.get_conv2d_params(scope_name) + + if dnode is not None: + dilation = struct.unpack('i', dnode.attr['value'].tensor.tensor_content[0:4])[0] + else: + dilation = 1 + + if anode is not None: + activation = anode.op + else: + activation = 'None' + + padding = node.attr['padding'].s.decode("utf-8") + # conv2d with dilation > 1 generates tens of nodes, not easy to parse them, so use this tricky method. + if dilation > 1 and scope_name + '/stack' in self.name_node_dict: + if self.name_node_dict[scope_name + '/stack'].op == "Const": + padding = 'SAME' + padding = self.conv_paddings[padding] ktensor = knode.attr['value'].tensor filter_height = ktensor.tensor_shape.dim[0].size @@ -82,7 +157,8 @@ def dump_conv2d_to_file(self, node, f): kernel = kernel.reshape(filter_height, filter_width, in_channels, out_channels) kernel = np.transpose(kernel, [3, 0, 1, 2]) - np.array([self.op2code[node.op], dilation, padding, self.conv_activations[activation], in_channels, out_channels, filter_height], dtype=np.uint32).tofile(f) + has_bias = 1 + np.array([self.op2code[node.op], dilation, padding, self.conv_activations[activation], in_channels, out_channels, filter_height, has_bias], dtype=np.uint32).tofile(f) kernel.tofile(f) btensor = bnode.attr['value'].tensor @@ -92,6 +168,53 @@ def dump_conv2d_to_file(self, node, f): bias = btensor.tensor_content f.write(bias) + input_name = self.conv2d_scopename_inputname_dict[scope_name] + input_operand_index = self.add_operand(input_name, Operand.IOTYPE_INPUT) + + if anode is not None: + output_operand_index = self.add_operand(anode.name, Operand.IOTYPE_OUTPUT) + else: + output_operand_index = self.add_operand(self.edges[bnode.name][0].name, Operand.IOTYPE_OUTPUT) + np.array([input_operand_index, output_operand_index], dtype=np.uint32).tofile(f) + + + def dump_simple_conv2d_to_file(self, node, f): + assert(node.op == 'Conv2D') + self.layer_number = self.layer_number + 1 + self.converted_nodes.add(node.name) + + node0 = self.name_node_dict[node.input[0]] + node1 = self.name_node_dict[node.input[1]] + if node0.op == 'Const': + knode = node0 + input_name = node.input[1] + else: + knode = node1 + input_name = node.input[0] + + ktensor = knode.attr['value'].tensor + filter_height = ktensor.tensor_shape.dim[0].size + filter_width = ktensor.tensor_shape.dim[1].size + in_channels = ktensor.tensor_shape.dim[2].size + out_channels = ktensor.tensor_shape.dim[3].size + if filter_height * filter_width * in_channels * out_channels == 1: + kernel = np.float32(ktensor.float_val[0]) + else: + kernel = np.frombuffer(ktensor.tensor_content, dtype=np.float32) + kernel = kernel.reshape(filter_height, filter_width, in_channels, out_channels) + kernel = np.transpose(kernel, [3, 0, 1, 2]) + + has_bias = 0 + dilation = 1 + padding = node.attr['padding'].s.decode("utf-8") + np.array([self.op2code[node.op], dilation, self.conv_paddings[padding], self.conv_activations['None'], + in_channels, out_channels, filter_height, has_bias], dtype=np.uint32).tofile(f) + kernel.tofile(f) + + input_operand_index = self.add_operand(input_name, Operand.IOTYPE_INPUT) + output_operand_index = self.add_operand(node.name, Operand.IOTYPE_OUTPUT) + np.array([input_operand_index, output_operand_index], dtype=np.uint32).tofile(f) + def dump_depth2space_to_file(self, node, f): assert(node.op == 'DepthToSpace') @@ -99,32 +222,124 @@ def dump_depth2space_to_file(self, node, f): block_size = node.attr['block_size'].i np.array([self.op2code[node.op], block_size], dtype=np.uint32).tofile(f) self.converted_nodes.add(node.name) + input_operand_index = self.add_operand(node.input[0], Operand.IOTYPE_INPUT) + output_operand_index = self.add_operand(node.name, Operand.IOTYPE_OUTPUT) + np.array([input_operand_index, output_operand_index], dtype=np.uint32).tofile(f) - def generate_layer_number(self): - # in current hard code implementation, the layer number is the first data written to the native model file - # it is not easy to know it at the beginning time in the general converter, so first do a dry run for compatibility - # will be refined later. - with open('/tmp/tmp.model', 'wb') as f: - self.dump_layers_to_file(f) - self.converted_nodes.clear() + def dump_mirrorpad_to_file(self, node, f): + assert(node.op == 'MirrorPad') + self.layer_number = self.layer_number + 1 + mode = node.attr['mode'].s + mode = self.mirrorpad_mode[mode.decode("utf-8")] + np.array([self.op2code[node.op], mode], dtype=np.uint32).tofile(f) + pnode = self.name_node_dict[node.input[1]] + self.converted_nodes.add(pnode.name) + paddings = pnode.attr['value'].tensor.tensor_content + f.write(paddings) + self.converted_nodes.add(node.name) + input_operand_index = self.add_operand(node.input[0], Operand.IOTYPE_INPUT) + output_operand_index = self.add_operand(node.name, Operand.IOTYPE_OUTPUT) + np.array([input_operand_index, output_operand_index], dtype=np.uint32).tofile(f) + + + def dump_maximum_to_file(self, node, f): + assert(node.op == 'Maximum') + self.layer_number = self.layer_number + 1 + ynode = self.name_node_dict[node.input[1]] + y = ynode.attr['value'].tensor.float_val[0] + np.array([self.op2code[node.op]], dtype=np.uint32).tofile(f) + np.array([y], dtype=np.float32).tofile(f) + self.converted_nodes.add(node.name) + input_operand_index = self.add_operand(node.input[0], Operand.IOTYPE_INPUT) + output_operand_index = self.add_operand(node.name, Operand.IOTYPE_OUTPUT) + np.array([input_operand_index, output_operand_index], dtype=np.uint32).tofile(f) + + + def dump_mathbinary_to_file(self, node, f): + self.layer_number = self.layer_number + 1 + self.converted_nodes.add(node.name) + i0_node = self.name_node_dict[node.input[0]] + i1_node = self.name_node_dict[node.input[1]] + np.array([self.op2code['MathBinary'], self.mathbin2code[node.op]], dtype=np.uint32).tofile(f) + if i0_node.op == 'Const': + scalar = i0_node.attr['value'].tensor.float_val[0] + np.array([1], dtype=np.uint32).tofile(f) # broadcast: 1 + np.array([scalar], dtype=np.float32).tofile(f) + np.array([0], dtype=np.uint32).tofile(f) # broadcast: 0 + input_operand_index = self.add_operand(i1_node.name, Operand.IOTYPE_INPUT) + np.array([input_operand_index], dtype=np.uint32).tofile(f) + elif i1_node.op == 'Const': + scalar = i1_node.attr['value'].tensor.float_val[0] + np.array([0], dtype=np.uint32).tofile(f) + input_operand_index = self.add_operand(i0_node.name, Operand.IOTYPE_INPUT) + np.array([input_operand_index], dtype=np.uint32).tofile(f) + np.array([1], dtype=np.uint32).tofile(f) + np.array([scalar], dtype=np.float32).tofile(f) + else: + np.array([0], dtype=np.uint32).tofile(f) + input_operand_index = self.add_operand(i0_node.name, Operand.IOTYPE_INPUT) + np.array([input_operand_index], dtype=np.uint32).tofile(f) + np.array([0], dtype=np.uint32).tofile(f) + input_operand_index = self.add_operand(i1_node.name, Operand.IOTYPE_INPUT) + np.array([input_operand_index], dtype=np.uint32).tofile(f) + output_operand_index = self.add_operand(node.name, Operand.IOTYPE_OUTPUT) + np.array([output_operand_index], dtype=np.uint32).tofile(f) + + + def dump_mathunary_to_file(self, node, f): + self.layer_number = self.layer_number + 1 + self.converted_nodes.add(node.name) + i0_node = self.name_node_dict[node.input[0]] + np.array([self.op2code['MathUnary'], self.mathun2code[node.op]], dtype=np.uint32).tofile(f) + input_operand_index = self.add_operand(i0_node.name, Operand.IOTYPE_INPUT) + np.array([input_operand_index], dtype=np.uint32).tofile(f) + output_operand_index = self.add_operand(node.name, Operand.IOTYPE_OUTPUT) + np.array([output_operand_index],dtype=np.uint32).tofile(f) def dump_layers_to_file(self, f): for node in self.nodes: if node.name in self.converted_nodes: continue + + # conv2d with dilation generates very complex nodes, so handle it in special + if self.in_conv2d_scope(node.name): + if node.op == 'Conv2D': + self.dump_complex_conv2d_to_file(node, f) + continue + if node.op == 'Conv2D': - self.dump_conv2d_to_file(node, f) + self.dump_simple_conv2d_to_file(node, f) elif node.op == 'DepthToSpace': self.dump_depth2space_to_file(node, f) + elif node.op == 'MirrorPad': + self.dump_mirrorpad_to_file(node, f) + elif node.op == 'Maximum': + self.dump_maximum_to_file(node, f) + elif node.op in self.mathbin2code: + self.dump_mathbinary_to_file(node, f) + elif node.op in self.mathun2code: + self.dump_mathunary_to_file(node, f) + + + def dump_operands_to_file(self, f): + operands = sorted(self.name_operand_dict.values()) + for operand in operands: + #print('{}'.format(operand)) + np.array([operand.index, len(operand.name)], dtype=np.uint32).tofile(f) + f.write(operand.name.encode('utf-8')) + np.array([operand.iotype, operand.dtype], dtype=np.uint32).tofile(f) + np.array([operand.dims[0], operand.dims[1], operand.dims[2], operand.dims[3]], dtype=np.uint32).tofile(f) def dump_to_file(self): - self.generate_layer_number() with open(self.outfile, 'wb') as f: - np.array([self.layer_number], dtype=np.uint32).tofile(f) + f.write(header.str.encode('utf-8')) + np.array([header.major, header.minor], dtype=np.uint32).tofile(f) self.dump_layers_to_file(f) + self.dump_operands_to_file(f) + np.array([self.layer_number, len(self.name_operand_dict)], dtype=np.uint32).tofile(f) def generate_name_node_dict(self): @@ -178,24 +393,67 @@ def generate_edges(self): self.edges[input] = [node] + @staticmethod + def get_scope_name(name): + index = name.rfind('/') + if index == -1: + return "" + return name[0:index] + + + def in_conv2d_scope(self, name): + inner_scope = TFConverter.get_scope_name(name) + if inner_scope == "": + return False; + for scope in self.conv2d_scope_names: + index = inner_scope.find(scope) + if index == 0: + return True + return False + + + def generate_conv2d_scope_info(self): + # mostly, conv2d is a sub block in graph, get the scope name + for node in self.nodes: + if node.op == 'Conv2D': + scope = TFConverter.get_scope_name(node.name) + # for the case tf.nn.conv2d is called directly + if scope == '': + continue + # for the case tf.nn.conv2d is called within a scope + if scope + '/kernel' not in self.name_node_dict: + continue + self.conv2d_scope_names.add(scope) + + # get the input name to the conv2d sub block + for node in self.nodes: + scope = TFConverter.get_scope_name(node.name) + if scope in self.conv2d_scope_names: + if node.op == 'Conv2D' or node.op == 'Shape': + for inp in node.input: + if TFConverter.get_scope_name(inp) != scope: + self.conv2d_scopename_inputname_dict[scope] = inp + + def run(self): self.generate_name_node_dict() self.generate_output_names() self.remove_identity() self.generate_edges() + self.generate_conv2d_scope_info() - #check the graph with tensorboard with human eyes - #self.dump_for_tensorboard() + if self.dump4tb: + self.dump_for_tensorboard() self.dump_to_file() -def convert_from_tensorflow(infile, outfile): +def convert_from_tensorflow(infile, outfile, dump4tb): with open(infile, 'rb') as f: # read the file in .proto format graph_def = tf.GraphDef() graph_def.ParseFromString(f.read()) nodes = graph_def.node - converter = TFConverter(graph_def, nodes, outfile) + converter = TFConverter(graph_def, nodes, outfile, dump4tb) converter.run() diff --git a/tools/python/convert_header.py b/tools/python/convert_header.py new file mode 100644 index 00000000000..ad4491729a5 --- /dev/null +++ b/tools/python/convert_header.py @@ -0,0 +1,26 @@ +# Copyright (c) 2019 +# +# This file is part of FFmpeg. +# +# FFmpeg is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# FFmpeg is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with FFmpeg; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# ============================================================================== + +str = 'FFMPEGDNNNATIVE' + +# increase major and reset minor when we have to re-convert the model file +major = 1 + +# increase minor when we don't have to re-convert the model file +minor = 6 diff --git a/tools/target_bsf_fuzzer.c b/tools/target_bsf_fuzzer.c new file mode 100644 index 00000000000..5d9f90075d4 --- /dev/null +++ b/tools/target_bsf_fuzzer.c @@ -0,0 +1,153 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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/imgutils.h" + +#include "libavcodec/avcodec.h" +#include "libavcodec/bsf_internal.h" +#include "libavcodec/bytestream.h" +#include "libavcodec/internal.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +static void error(const char *err) +{ + fprintf(stderr, "%s", err); + exit(1); +} + +static AVBitStreamFilter *f = NULL; + +static const uint64_t FUZZ_TAG = 0x4741542D5A5A5546ULL; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + const uint64_t fuzz_tag = FUZZ_TAG; + const uint8_t *last = data; + const uint8_t *end = data + size; + AVBSFContext *bsf = NULL; + AVPacket in, out; + uint64_t keyframes = 0; + int res; + + if (!f) { +#ifdef FFMPEG_BSF +#define BSF_SYMBOL0(BSF) ff_##BSF##_bsf +#define BSF_SYMBOL(BSF) BSF_SYMBOL0(BSF) + extern AVBitStreamFilter BSF_SYMBOL(FFMPEG_BSF); + f = &BSF_SYMBOL(FFMPEG_BSF); +#else + extern AVBitStreamFilter ff_null_bsf; + f = &ff_null_bsf; +#endif + av_log_set_level(AV_LOG_PANIC); + } + + res = av_bsf_alloc(f, &bsf); + if (res < 0) + error("Failed memory allocation"); + + if (size > 1024) { + GetByteContext gbc; + int extradata_size; + size -= 1024; + bytestream2_init(&gbc, data + size, 1024); + bsf->par_in->width = bytestream2_get_le32(&gbc); + bsf->par_in->height = bytestream2_get_le32(&gbc); + bsf->par_in->bit_rate = bytestream2_get_le64(&gbc); + bsf->par_in->bits_per_coded_sample = bytestream2_get_le32(&gbc); + + if (f->codec_ids) { + int i, id; + for (i = 0; f->codec_ids[i] != AV_CODEC_ID_NONE; i++); + id = f->codec_ids[bytestream2_get_byte(&gbc) % i]; + bsf->par_in->codec_id = id; + bsf->par_in->codec_tag = bytestream2_get_le32(&gbc); + } + + extradata_size = bytestream2_get_le32(&gbc); + + bsf->par_in->sample_rate = bytestream2_get_le32(&gbc); + bsf->par_in->channels = (unsigned)bytestream2_get_le32(&gbc) % FF_SANE_NB_CHANNELS; + bsf->par_in->block_align = bytestream2_get_le32(&gbc); + keyframes = bytestream2_get_le64(&gbc); + + if (extradata_size < size) { + bsf->par_in->extradata = av_mallocz(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (bsf->par_in->extradata) { + bsf->par_in->extradata_size = extradata_size; + size -= bsf->par_in->extradata_size; + memcpy(bsf->par_in->extradata, data + size, bsf->par_in->extradata_size); + } + } + if (av_image_check_size(bsf->par_in->width, bsf->par_in->height, 0, bsf)) + bsf->par_in->width = bsf->par_in->height = 0; + } + + res = av_bsf_init(bsf); + if (res < 0) { + av_bsf_free(&bsf); + return 0; // Failure of av_bsf_init() does not imply that a issue was found + } + + av_init_packet(&in); + av_init_packet(&out); + out.data = NULL; + out.size = 0; + while (data < end) { + // Search for the TAG + while (data + sizeof(fuzz_tag) < end) { + if (data[0] == (fuzz_tag & 0xFF) && AV_RN64(data) == fuzz_tag) + break; + data++; + } + if (data + sizeof(fuzz_tag) > end) + data = end; + + res = av_new_packet(&in, data - last); + if (res < 0) + error("Failed memory allocation"); + memcpy(in.data, last, data - last); + in.flags = (keyframes & 1) * AV_PKT_FLAG_DISCARD + (!!(keyframes & 2)) * AV_PKT_FLAG_KEY; + keyframes = (keyframes >> 2) + (keyframes<<62); + data += sizeof(fuzz_tag); + last = data; + + while (in.size) { + res = av_bsf_send_packet(bsf, &in); + if (res < 0 && res != AVERROR(EAGAIN)) + break; + res = av_bsf_receive_packet(bsf, &out); + if (res < 0) + break; + av_packet_unref(&out); + } + av_packet_unref(&in); + } + + res = av_bsf_send_packet(bsf, NULL); + while (!res) { + res = av_bsf_receive_packet(bsf, &out); + if (res < 0) + break; + av_packet_unref(&out); + } + + av_bsf_free(&bsf); + return 0; +} diff --git a/tools/target_dec_fuzzer.c b/tools/target_dec_fuzzer.c index 8ba25b4e8a4..66ee99a91df 100644 --- a/tools/target_dec_fuzzer.c +++ b/tools/target_dec_fuzzer.c @@ -54,6 +54,9 @@ #include "libavcodec/bytestream.h" #include "libavformat/avformat.h" +//For FF_SANE_NB_CHANNELS, so we dont waste energy testing things that will get instantly rejected +#include "libavcodec/internal.h" + int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); extern AVCodec * codec_list[]; @@ -85,64 +88,29 @@ static int subtitle_handler(AVCodecContext *avctx, void *frame, return ret; } -// Class to handle buffer allocation and resize for each frame -typedef struct FuzzDataBuffer { - size_t size_; - uint8_t *data_; -} FuzzDataBuffer; - -static void FDBCreate(FuzzDataBuffer *FDB) { - FDB->size_ = 0x1000; - FDB->data_ = av_malloc(FDB->size_); - if (!FDB->data_) - error("Failed memory allocation"); -} - -static void FDBDesroy(FuzzDataBuffer *FDB) { av_free(FDB->data_); } - -static void FDBRealloc(FuzzDataBuffer *FDB, size_t size) { - size_t needed = size + AV_INPUT_BUFFER_PADDING_SIZE; - av_assert0(needed > size); - if (needed > FDB->size_) { - av_free(FDB->data_); - FDB->size_ = needed; - FDB->data_ = av_malloc(FDB->size_); - if (!FDB->data_) - error("Failed memory allocation"); - } -} - -static void FDBPrepare(FuzzDataBuffer *FDB, AVPacket *dst, const uint8_t *data, - size_t size) -{ - FDBRealloc(FDB, size); - memcpy(FDB->data_, data, size); - size_t padd = FDB->size_ - size; - if (padd > AV_INPUT_BUFFER_PADDING_SIZE) - padd = AV_INPUT_BUFFER_PADDING_SIZE; - memset(FDB->data_ + size, 0, padd); - av_init_packet(dst); - dst->data = FDB->data_; - dst->size = size; -} - // Ensure we don't loop forever const uint32_t maxiteration = 8096; +const uint64_t maxpixels_per_frame = 4096 * 4096; +uint64_t maxpixels; + +uint64_t maxsamples_per_frame = 256*1024*32; +uint64_t maxsamples; static const uint64_t FUZZ_TAG = 0x4741542D5A5A5546ULL; int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { const uint64_t fuzz_tag = FUZZ_TAG; - FuzzDataBuffer buffer; const uint8_t *last = data; const uint8_t *end = data + size; uint32_t it = 0; uint64_t ec_pixels = 0; + uint64_t nb_samples = 0; int (*decode_handler)(AVCodecContext *avctx, AVFrame *picture, int *got_picture_ptr, const AVPacket *avpkt) = NULL; AVCodecParserContext *parser = NULL; - + uint64_t keyframes = 0; + AVDictionary *opts = NULL; if (!c) { #ifdef FFMPEG_DECODER @@ -152,6 +120,12 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { codec_list[0] = &DECODER_SYMBOL(FFMPEG_DECODER); avcodec_register(&DECODER_SYMBOL(FFMPEG_DECODER)); +#if FFMPEG_DECODER == tiff || FFMPEG_DECODER == tdsc + extern AVCodec DECODER_SYMBOL(mjpeg); + codec_list[1] = &DECODER_SYMBOL(mjpeg); + avcodec_register(&DECODER_SYMBOL(mjpeg)); +#endif + c = &DECODER_SYMBOL(FFMPEG_DECODER); #else avcodec_register_all(); @@ -165,17 +139,64 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { case AVMEDIA_TYPE_VIDEO : decode_handler = avcodec_decode_video2; break; case AVMEDIA_TYPE_SUBTITLE: decode_handler = subtitle_handler ; break; } + switch (c->id) { + case AV_CODEC_ID_APE: maxsamples_per_frame /= 256; break; + } + maxpixels = maxpixels_per_frame * maxiteration; + maxsamples = maxsamples_per_frame * maxiteration; + switch (c->id) { + case AV_CODEC_ID_BINKVIDEO: maxpixels /= 32; break; + case AV_CODEC_ID_CFHD: maxpixels /= 128; break; + case AV_CODEC_ID_DIRAC: maxpixels /= 8192; break; + case AV_CODEC_ID_DST: maxsamples /= 8192; break; + case AV_CODEC_ID_DXV: maxpixels /= 32; break; + case AV_CODEC_ID_FFWAVESYNTH: maxsamples /= 16384; break; + case AV_CODEC_ID_G2M: maxpixels /= 64; break; + case AV_CODEC_ID_GDV: maxpixels /= 512; break; + case AV_CODEC_ID_GIF: maxpixels /= 16; break; + case AV_CODEC_ID_HAP: maxpixels /= 128; break; + case AV_CODEC_ID_HEVC: maxpixels /= 16384; break; + case AV_CODEC_ID_HNM4_VIDEO: maxpixels /= 128; break; + case AV_CODEC_ID_IFF_ILBM: maxpixels /= 128; break; + case AV_CODEC_ID_INDEO4: maxpixels /= 128; break; + case AV_CODEC_ID_LSCR: maxpixels /= 16; break; + case AV_CODEC_ID_MOTIONPIXELS:maxpixels /= 256; break; + case AV_CODEC_ID_MP4ALS: maxsamples /= 65536; break; + case AV_CODEC_ID_MSRLE: maxpixels /= 16; break; + case AV_CODEC_ID_MSS2: maxpixels /= 16384; break; + case AV_CODEC_ID_MSZH: maxpixels /= 128; break; + case AV_CODEC_ID_PNG: maxpixels /= 128; break; + case AV_CODEC_ID_APNG: maxpixels /= 128; break; + case AV_CODEC_ID_QTRLE: maxpixels /= 16; break; + case AV_CODEC_ID_RASC: maxpixels /= 16; break; + case AV_CODEC_ID_SANM: maxpixels /= 16; break; + case AV_CODEC_ID_SCPR: maxpixels /= 32; break; + case AV_CODEC_ID_SCREENPRESSO:maxpixels /= 64; break; + case AV_CODEC_ID_SMACKVIDEO: maxpixels /= 64; break; + case AV_CODEC_ID_SNOW: maxpixels /= 128; break; + case AV_CODEC_ID_TGV: maxpixels /= 32; break; + case AV_CODEC_ID_TRUEMOTION2: maxpixels /= 1024; break; + case AV_CODEC_ID_VP7: maxpixels /= 256; break; + case AV_CODEC_ID_VP9: maxpixels /= 4096; break; + case AV_CODEC_ID_ZEROCODEC: maxpixels /= 128; break; + } + - AVCodecContext* ctx = avcodec_alloc_context3(NULL); + AVCodecContext* ctx = avcodec_alloc_context3(c); AVCodecContext* parser_avctx = avcodec_alloc_context3(NULL); if (!ctx || !parser_avctx) error("Failed memory allocation"); - ctx->max_pixels = 4096 * 4096; //To reduce false positive OOM and hangs + if (ctx->max_pixels == 0 || ctx->max_pixels > maxpixels_per_frame) + ctx->max_pixels = maxpixels_per_frame; //To reduce false positive OOM and hangs + ctx->refcounted_frames = 1; //To reduce false positive timeouts and focus testing on the refcounted API + + ctx->max_samples = maxsamples_per_frame; if (size > 1024) { GetByteContext gbc; int extradata_size; + int flags; size -= 1024; bytestream2_init(&gbc, data + size, 1024); ctx->width = bytestream2_get_le32(&gbc); @@ -183,10 +204,51 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ctx->bit_rate = bytestream2_get_le64(&gbc); ctx->bits_per_coded_sample = bytestream2_get_le32(&gbc); // Try to initialize a parser for this codec, note, this may fail which just means we test without one - if (bytestream2_get_byte(&gbc) & 1) + flags = bytestream2_get_byte(&gbc); + if (flags & 1) parser = av_parser_init(c->id); + if (flags & 2) + ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; + if (flags & 4) { + ctx->err_recognition = AV_EF_AGGRESSIVE | AV_EF_COMPLIANT | AV_EF_CAREFUL; + if (flags & 8) + ctx->err_recognition |= AV_EF_EXPLODE; + } + if ((flags & 0x10) && c->id != AV_CODEC_ID_H264) + ctx->flags2 |= AV_CODEC_FLAG2_FAST; + + if (flags & 0x40) + av_force_cpu_flags(0); extradata_size = bytestream2_get_le32(&gbc); + + ctx->sample_rate = bytestream2_get_le32(&gbc) & 0x7FFFFFFF; + ctx->channels = (unsigned)bytestream2_get_le32(&gbc) % FF_SANE_NB_CHANNELS; + ctx->block_align = bytestream2_get_le32(&gbc) & 0x7FFFFFFF; + ctx->codec_tag = bytestream2_get_le32(&gbc); + if (c->codec_tags) { + int n; + for (n = 0; c->codec_tags[n] != FF_CODEC_TAGS_END; n++); + ctx->codec_tag = c->codec_tags[ctx->codec_tag % n]; + } + keyframes = bytestream2_get_le64(&gbc); + ctx->request_channel_layout = bytestream2_get_le64(&gbc); + + ctx->idct_algo = bytestream2_get_byte(&gbc) % 25; + + if (flags & 0x20) { + switch (ctx->codec_id) { + case AV_CODEC_ID_AC3: + case AV_CODEC_ID_EAC3: + av_dict_set_int(&opts, "cons_noisegen", bytestream2_get_byte(&gbc) & 1, 0); + av_dict_set_int(&opts, "heavy_compr", bytestream2_get_byte(&gbc) & 1, 0); + av_dict_set_int(&opts, "target_level", (int)(bytestream2_get_byte(&gbc) % 32) - 31, 0); + av_dict_set_int(&opts, "dmix_mode", (int)(bytestream2_get_byte(&gbc) % 4) - 1, 0); + break; + } + } + + if (extradata_size < size) { ctx->extradata = av_mallocz(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); if (ctx->extradata) { @@ -199,16 +261,16 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ctx->width = ctx->height = 0; } - int res = avcodec_open2(ctx, c, NULL); + int res = avcodec_open2(ctx, c, &opts); if (res < 0) { - av_free(ctx); + avcodec_free_context(&ctx); av_free(parser_avctx); av_parser_close(parser); + av_dict_free(&opts); return 0; // Failure of avcodec_open2() does not imply that a issue was found } parser_avctx->codec_id = ctx->codec_id; - FDBCreate(&buffer); int got_frame; AVFrame *frame = av_frame_alloc(); if (!frame) @@ -216,6 +278,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { // Read very simple container AVPacket avpkt, parsepkt; + av_init_packet(&avpkt); + av_init_packet(&parsepkt); while (data < end && it < maxiteration) { // Search for the TAG while (data + sizeof(fuzz_tag) < end) { @@ -226,7 +290,12 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (data + sizeof(fuzz_tag) > end) data = end; - FDBPrepare(&buffer, &parsepkt, last, data - last); + res = av_new_packet(&parsepkt, data - last); + if (res < 0) + error("Failed memory allocation"); + memcpy(parsepkt.data, last, data - last); + parsepkt.flags = (keyframes & 1) * AV_PKT_FLAG_DISCARD + (!!(keyframes & 2)) * AV_PKT_FLAG_KEY; + keyframes = (keyframes >> 2) + (keyframes<<62); data += sizeof(fuzz_tag); last = data; @@ -237,6 +306,14 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { int ret = av_parser_parse2(parser, parser_avctx, &avpkt.data, &avpkt.size, parsepkt.data, parsepkt.size, parsepkt.pts, parsepkt.dts, parsepkt.pos); + if (avpkt.data == parsepkt.data) { + avpkt.buf = av_buffer_ref(parsepkt.buf); + if (!avpkt.buf) + error("Failed memory allocation"); + } else { + if (av_packet_make_refcounted(&avpkt) < 0) + error("Failed memory allocation"); + } parsepkt.data += ret; parsepkt.size -= ret; parsepkt.pos += ret; @@ -248,8 +325,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { avpkt.flags |= AV_PKT_FLAG_KEY; avpkt.flags |= parsepkt.flags & AV_PKT_FLAG_DISCARD; } else { - avpkt = parsepkt; - parsepkt.size = 0; + av_packet_move_ref(&avpkt, &parsepkt); } // Iterate through all data @@ -257,9 +333,15 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { av_frame_unref(frame); int ret = decode_handler(ctx, frame, &got_frame, &avpkt); - ec_pixels += ctx->width * ctx->height; + ec_pixels += (ctx->width + 32LL) * (ctx->height + 32LL); if (it > 20 || ec_pixels > 4 * ctx->max_pixels) ctx->error_concealment = 0; + if (ec_pixels > maxpixels) + goto maximums_reached; + + nb_samples += frame->nb_samples; + if (nb_samples > maxsamples) + goto maximums_reached; if (ret <= 0 || ret > avpkt.size) break; @@ -268,22 +350,27 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { avpkt.data += ret; avpkt.size -= ret; } + av_packet_unref(&avpkt); } + av_packet_unref(&parsepkt); } +maximums_reached: - av_init_packet(&avpkt); - avpkt.data = NULL; - avpkt.size = 0; + av_packet_unref(&avpkt); do { got_frame = 0; + av_frame_unref(frame); decode_handler(ctx, frame, &got_frame, &avpkt); } while (got_frame == 1 && it++ < maxiteration); + fprintf(stderr, "pixels decoded: %"PRId64", samples decoded: %"PRId64", iterations: %d\n", ec_pixels, nb_samples, it); + av_frame_free(&frame); avcodec_free_context(&ctx); avcodec_free_context(&parser_avctx); av_parser_close(parser); - FDBDesroy(&buffer); + av_packet_unref(&parsepkt); + av_dict_free(&opts); return 0; } diff --git a/tools/target_dem_fuzzer.c b/tools/target_dem_fuzzer.c index 409599636d4..cc097da0d78 100644 --- a/tools/target_dem_fuzzer.c +++ b/tools/target_dem_fuzzer.c @@ -70,6 +70,8 @@ static int64_t io_seek(void *opaque, int64_t offset, int whence) if (offset > INT64_MAX - c->filesize) return -1; offset += c->filesize; + } else if (whence == AVSEEK_SIZE) { + return c->filesize; } if (offset < 0 || offset > c->filesize) return -1; diff --git a/tools/venc_data_dump.c b/tools/venc_data_dump.c new file mode 100644 index 00000000000..3a6ce94268f --- /dev/null +++ b/tools/venc_data_dump.c @@ -0,0 +1,195 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General 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/common.h" +#include "libavutil/dict.h" +#include "libavutil/error.h" +#include "libavutil/video_enc_params.h" + +#include "libavformat/avformat.h" + +#include "libavcodec/avcodec.h" + +static int decode_read(AVCodecContext *decoder, AVFrame *frame, int flush, int max_frames) +{ + const int ret_done = flush ? AVERROR_EOF : AVERROR(EAGAIN); + int ret = 0; + + while (ret >= 0 && + (max_frames == 0 || decoder->frame_number < max_frames)) { + AVFrameSideData *sd; + + ret = avcodec_receive_frame(decoder, frame); + if (ret < 0) + return (ret == ret_done) ? 0 : ret; + + fprintf(stdout, "frame %d\n", decoder->frame_number - 1); + + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_VIDEO_ENC_PARAMS); + if (sd) { + AVVideoEncParams *par = (AVVideoEncParams*)sd->data; + + fprintf(stdout, "AVVideoEncParams %d\n", par->type); + fprintf(stdout, "qp %d\n", par->qp); + for (int i = 0; i < FF_ARRAY_ELEMS(par->delta_qp); i++) + for (int j = 0; j < FF_ARRAY_ELEMS(par->delta_qp[i]); j++) { + if (par->delta_qp[i][j]) + fprintf(stdout, "delta_qp[%d][%d] %"PRId32"\n", i, j, par->delta_qp[i][j]); + } + + if (par->nb_blocks) { + fprintf(stdout, "nb_blocks %d\n", par->nb_blocks); + for (int i = 0; i < par->nb_blocks; i++) { + AVVideoBlockParams *b = av_video_enc_params_block(par, i); + + fprintf(stdout, "block %d %d:%d %dx%d %"PRId32"\n", + i, b->src_x, b->src_y, b->w, b->h, b->delta_qp); + } + } + } + + av_frame_unref(frame); + + if (max_frames && decoder->frame_number == max_frames) + return 1; + } + + return (max_frames == 0 || decoder->frame_number < max_frames) ? 0 : 1; +} + +static int decoder_init(AVFormatContext *demuxer, int stream_idx, + AVCodecContext **dec, AVDictionary **opts) +{ + const AVCodec *codec; + int ret; + + if (stream_idx < 0 || stream_idx >= demuxer->nb_streams) + return AVERROR(EINVAL); + + codec = avcodec_find_decoder(demuxer->streams[stream_idx]->codecpar->codec_id); + if (!codec) + return AVERROR_DECODER_NOT_FOUND; + + *dec = avcodec_alloc_context3(codec); + if (!*dec) + return AVERROR(ENOMEM); + + ret = avcodec_open2(*dec, NULL, opts); + if (ret < 0) + return ret; + + return 0; +} + +int main(int argc, char **argv) +{ + AVFormatContext *demuxer = NULL; + AVCodecContext *decoder = NULL; + AVDictionary *opts = NULL; + + AVPacket *pkt = NULL; + AVFrame *frame = NULL; + + unsigned int stream_idx, max_frames; + const char *filename, *thread_type = NULL, *nb_threads = NULL; + int ret = 0; + + if (argc <= 3) { + fprintf(stderr, "Usage: %s [ ]\n", argv[0]); + return 0; + } + + filename = argv[1]; + stream_idx = strtol(argv[2], NULL, 0); + max_frames = strtol(argv[3], NULL, 0); + if (argc > 5) { + nb_threads = argv[4]; + thread_type = argv[5]; + } + + ret = av_dict_set(&opts, "threads", nb_threads, 0); + ret |= av_dict_set(&opts, "thread_type", thread_type, 0); + ret |= av_dict_set(&opts, "export_side_data", "venc_params", 0); + + ret = avformat_open_input(&demuxer, filename, NULL, NULL); + if (ret < 0) { + fprintf(stderr, "Error opening input file: %d\n", ret); + return ret; + } + + ret = decoder_init(demuxer, stream_idx, &decoder, &opts); + if (ret < 0) { + fprintf(stderr, "Error initializing decoder\n"); + goto finish; + } + + pkt = av_packet_alloc(); + frame = av_frame_alloc(); + if (!pkt || !frame) { + ret = AVERROR(ENOMEM); + goto finish; + } + + while (ret >= 0) { + ret = av_read_frame(demuxer, pkt); + if (ret < 0) + goto flush; + if (pkt->stream_index != stream_idx) { + av_packet_unref(pkt); + continue; + } + + ret = avcodec_send_packet(decoder, pkt); + if (ret < 0) { + fprintf(stderr, "Error decoding: %d\n", ret); + goto finish; + } + av_packet_unref(pkt); + + ret = decode_read(decoder, frame, 0, max_frames); + if (ret < 0) { + fprintf(stderr, "Error decoding: %d\n", ret); + goto finish; + } else if (ret > 0) { + ret = 0; + goto finish; + } + } + +flush: + avcodec_send_packet(decoder, NULL); + ret = decode_read(decoder, frame, 1, max_frames); + if (ret < 0) { + fprintf(stderr, "Error flushing: %d\n", ret); + goto finish; + } + ret = 0; + +finish: + av_dict_free(&opts); + av_packet_free(&pkt); + av_frame_free(&frame); + avcodec_free_context(&decoder); + avformat_close_input(&demuxer); + + return ret; +} diff --git a/tools/zmqsend.c b/tools/zmqsend.c index 7bd7fe4199c..f26fa9c1c24 100644 --- a/tools/zmqsend.c +++ b/tools/zmqsend.c @@ -155,7 +155,7 @@ int main(int argc, char **argv) ret = 1; goto end; } - memcpy(recv_buf, zmq_msg_data(&msg), recv_buf_size); + memcpy(recv_buf, zmq_msg_data(&msg), recv_buf_size - 1); recv_buf[recv_buf_size-1] = 0; printf("%s\n", recv_buf); zmq_msg_close(&msg);